Also known as: X41-2026-002 / GHSA-86qp-5c8j-p5mr / PYSEC-2026-161
Discovered by X41 D-Sec during an OSTIF-sponsored audit
Scanner & automation by Nemesis
request.url from the
Host header without sanitization, letting attackers
forge a request.url.path that bypasses path-based
auth middleware.
Thousands of FastAPI and Starlette applications are affected,
including vLLM, LiteLLM, MCP servers, and AI agent frameworks.
Automatically discovers MCP endpoints and common inference API paths (vLLM, LiteLLM, OpenAI-compatible). Best for scanning AI infrastructure where the exact stack is unknown.
Host headers containing invalid characters instead
of using them for URL construction.request.url.path is inherently
fragile — auth should be tied to the endpoint itself, not the path
used to reach it. Prefer Starlette's
requires() decorator
or FastAPI's, Depends() and Security()
which are enforced on actual endpoints instead of a path-based middleware.Host header before forwarding, which neutralizes this attack.
ASGI servers pass the raw header through to the framework — a reverse proxy
prevents that.scope["path"] instead of
request.url.path if you must use middleware. The ASGI scope
path comes from the HTTP request line and cannot be manipulated via the
Host header.
Starlette < 1.0.1 builds request.url by concatenating the HTTP
Host header with the request path. An attacker can send a crafted
request like GET /protected with a Host: example.com/health?x=
header. The request will reach the /proteced path, but request.url
would be https://example.com/health?x=/protected, and request.url.path
would return /health instead of the real request path.
Any middleware that uses this value to decide whether to enforce authentication can be bypassed.
More details can be found in the X41-2026-002 advisory.
Any Python application built on Starlette or FastAPI that uses
starlette < 1.0.1
and uses request.url (or starlette.datastructures.URL(scope=...))
in a middleware to make security decisions based on its path
(e.g. allowlists, denylists, CSRF exemptions, rate limiting, payment gates),
and runs on any ASGI server (Daphne, Granian, Gunicorn, Hypercorn, Anycorn, Uvicorn).
Use the scanner above, grep your codebase for request.url.path
in middleware files, or try the tools from the
X41
open-source repository.
This includes LLM inference servers like vLLM, LLM proxy servers like LiteLLM, AI agent frameworks, MCP gateways, and custom APIs. MCP servers are especially at risk because the MCP spec mandates unauthenticated OAuth discovery endpoints, providing a reliable path for exploitation
This vulnerability is not specific to LLMs, but many LLM inference servers
(vLLM), LLM proxy servers (LiteLLM), AI agent frameworks, and MCP gateway
implementations are built on FastAPI/Starlette
and use path-based auth to protect API endpoints. A bypass can expose model
access, API keys, and internal tooling. Google ADK-Python, Ray Serve, and
BentoML also use Starlette middleware and are potentially affected when
custom auth middleware is added. Any custom MCP server, FastMCP integration,
or AI agent backend using Starlette routing with auth middleware should be
tested. Note: FastAPI's built-in Depends() security uses route
matching, not request.url.path, so standard dependency-injection
auth is safe — the risk is in custom BaseHTTPMiddleware
or raw ASGI middleware.
Yes. RFC-compliant reverse proxies (nginx, Caddy, Traefik, HAProxy) validate
and reject invalid Host headers, which neutralizes
the injection. However, many deployments — especially dev, staging, and
self-hosted instances — expose ASGI servers directly without a proxy.
The scanner first confirms a protected endpoint denies access without credentials. Tier 1 then tests whether the middleware uses a denylist (fail-open) pattern by injecting a random path into the Host header — this catches misconfigured middleware in just a few requests. If Tier 1 fails, Tier 2 discovers known unauthenticated paths and injects those for allowlist-based (fail-closed) middleware. Raw TCP sockets are used because standard HTTP clients normalize the Host header, which would prevent the test.
Anthropic's Claude Mythos found 10,000+ vulnerabilities through Project
Glasswing — but not this one. The reason is structural: CVE-2026-48710
is not a bug in one file or one repo. It spans three independent layers
— ASGI servers pass the raw Host header, Starlette trusts it for URL
construction, and middleware authors assume request.url.path is
safe for auth decisions. Each component behaves correctly in isolation.
The vulnerability only emerges from the interaction between them, across
specifications (HTTP, ASGI, Starlette, MCP). Finding it required manual
security research — understanding how these layers combine and building
end-to-end exploit labs to confirm the attack. That is a fundamentally
different shape of work than pointing an AI agent at a single codebase.
Once the bug class was understood, measuring its real-world impact was a separate effort: writing custom CodeQL queries and scanning dependent projects at scale — Starlette alone has more than 400k dependents on GitHub. This impact assessment work is valuable but distinct from the discovery itself.
Yes. The
X41
open-source repository includes a Python PoC exploit, Semgrep rules for
static detection, and CodeQL queries for large-scale scanning. You can use the
Semgrep rules to check your own codebase for request.url.path
usage in middleware, or run the CodeQL queries against any Python project to
find vulnerable patterns.
A collaboration between