Security Advisory

BadHost: One Character Bypasses Host-Based Security Across Most of the Python AI Stack

Starlette Host Header URL Reconstruction Flaw
CVE-2026-48710  /  GHSA-86qp-5c8j-p5mr  /  X41-2026-002
Target audience: operators of self-hosted and local LLM infrastructure.

A single character injected into the HTTP Host header bypasses path-based authorization in Starlette, the routing core of FastAPI. Through FastAPI, this primitive (now tracked as CVE-2026-48710 and branded BadHost by the discoverers) reaches a large segment of the Python AI tooling ecosystem: vLLM (where the bug was discovered), LiteLLM, Text Generation Inference, most OpenAI-shim proxies, MCP servers, agent harnesses, eval dashboards, and model-management UIs. The exploit primitive is one character. The fix is Starlette 1.0.1, quietly released with a CVSS 6.5 Moderate score that materially understates the downstream impact. The discoverers themselves characterize the bug as critical.

A free online scanner for CVE-2026-48710 is available at badhost.org, run jointly by X41 D-Sec, Persistent Security Industries, and Bintech.

At a glance

CVECVE-2026-48710 (BadHost)
Downstream severityCritical (per the discoverers)
Upstream severity, as scoredModerate (CVSS 6.5) — understated
ExploitabilityTrivial. One malformed header.
Auth requiredNone
AutomatableYes. Suitable for mass scanning.
Discovered inA source code audit of vLLM by X41 D-Sec
Online scannerbadhost.org
PatchStarlette 1.0.1 (quietly released)
Affectedstarlette >= 0.8.3, < 1.0.1 and everything that depends on it

Known-impacted downstream stacks: FastAPI (the dominant downstream consumer of Starlette and the foundation of most modern Python web and AI services), LiteLLM proxy, vLLM (the project the bug was discovered against), Text Generation Inference and related wrappers, most "OpenAI-API-shim" projects fronting local model runners, a large number of MCP servers, agent harnesses, eval dashboards, model registries, and internal admin panels built on FastAPI.

The bug

Starlette reconstructs request.url by concatenating the HTTP Host header with the request path and re-parsing the result. The Host value is not validated against the RFC 9112 / RFC 3986 grammar before reconstruction. A Host header containing /, ?, or # shifts the path, query, and fragment boundaries during re-parse, so request.url.path no longer matches the path the ASGI server actually received and routed against.

The router dispatches on the real wire path. Middleware sees the poisoned, re-parsed path. Any path-based security decision made in middleware can be bypassed while the underlying route still executes.

Minimal proof of concept:

curl -i -H 'Host: foo'  http://target/admin    # 403, blocked
curl -i -H 'Host: foo?' http://target/admin    # 200, served

The exploit primitive is one character.

Severity: scored low, lands hard

The upstream Starlette advisory was published with a CVSS score of 6.5 (Moderate), characterizing the issue strictly at the library layer as a path string mismatch. The patch shipped quietly, without an accompanying ecosystem-wide warning. That framing materially understates the downstream impact. The discoverers, who actually examined what the primitive does to consumers of the library, characterize CVE-2026-48710 as critical.

This is a primitive, not an outcome. What it costs the ecosystem is determined by what downstream consumers do with request.url. X41's analysis found multiple popular open source projects whose middleware gates security-relevant decisions on request.url, with demonstrated chains from this single-character primitive to authentication bypass, SSRF, and remote code execution.

Why this matters to the LLM and AI ecosystem

FastAPI is built on Starlette, and FastAPI is the base layer for the bulk of the Python AI plumbing deployed today. The transitive blast radius is not "a Python web framework"; it is most of the model-serving, gateway, proxy, eval, agent, and MCP-server infrastructure that has been stood up in the last two years.

Four points worth keeping in mind:

  1. The bug was found in vLLM. X41 D-Sec discovered it during a source code audit of vLLM sponsored by OSTIF.org, not while looking at Starlette. The path from "Starlette quirk" to "LLM-serving primitive" is not theoretical; it is the discovery path.
  2. Lab-style deployments expose the bug. These services are routinely deployed direct-to-uvicorn (or hypercorn, daphne, granian) on internal networks, lab subnets, workstations, and bench hardware. The reverse-proxy mitigation that protects production websites is frequently absent in research, eval, and dev deployments.
  3. The endpoints being protected are high-value: admin routes, model management, key issuance, prompt and tool configuration, finetune submission, dataset upload, and shell-adjacent agent tooling.
  4. The bar for automation is on the floor. One character in a request header is drop-in compatible with any existing internet scanning toolchain and with any agentic or automated reconnaissance pipeline.

Impact

Where Starlette-based middleware (including FastAPI middleware) enforces authorization or routing restrictions using request.url or request.url.path, the following should be assumed reachable by an unauthenticated remote attacker:

  • Bypass of path-prefix authentication (/admin, /v1/models, /internal, /metrics, /shutdown, tool-execution endpoints)
  • Bypass of tenant or workspace scoping enforced in middleware
  • Smuggling of requests into endpoints intended to be reachable only from authenticated sessions or internal hops
  • SSRF against cloud metadata services and internal hosts where the gated endpoint performs outbound fetches
  • RCE where the gated endpoint exposes tool execution, plugin loading, arbitrary model loading from URL, file upload, or code-eval style functionality

For LLM gateways specifically, the admin and key-management surface of services like LiteLLM, and the model and runtime control surface of services like vLLM, must be treated as exposed if the deployment is direct-to-ASGI.

Exposure check

The fastest way to test a reachable endpoint is the BadHost online scanner: badhost.org. For systematic review, you are likely exposed if any of the following are true:

  • You run any FastAPI or Starlette application directly on uvicorn, hypercorn, daphne, or granian without an HTTP/1.1-compliant reverse proxy in front.
  • You run LiteLLM, vLLM, or similar LLM proxies and servers as the directly-reachable HTTP endpoint.
  • You terminate HTTP/3 or QUIC at a frontend whose Host validation behavior you have not verified.
  • Your application reads request.url.path in any authentication, authorization, audit, rate-limiting, or routing-decision code.
  • Any internal-only service is reachable from a workstation, VPN subnet, lab network, or other "we trust this network" segment.

Mitigation

Primary

Upgrade Starlette to 1.0.1 or later. Rebuild and redeploy every container, virtualenv, and bundled artifact that pins or vendors Starlette. Bundled installs are common in LLM tooling; pip list on the host is not enough. Audit images.

Secondary (defense in depth)

  • Replace request.url and request.url.path with request.scope["path"] in every middleware, dependency, and decorator that makes security decisions. Grep the codebase. This bug class will recur; reading the un-reconstructed value is the durable fix.
  • Place a reverse proxy that rejects malformed Host headers in front of every ASGI-served application. nginx, Apache httpd, and Cloudflare reject the PoC by default. Verify your specific config.
  • For HTTP/3-terminated frontends, test Host header handling explicitly with the X41 PoC before relying on the proxy as a mitigation.

Detection and scanning

Three options, in order of effort:

  1. Online scanner. The BadHost project, run jointly by X41 D-Sec, Persistent Security Industries, and Bintech, offers a free remote scanner for any reachable HTTP endpoint at badhost.org. Suitable for quick triage of a few services.
  2. Source code scanning. X41 has published a scanner, Semgrep rules, and CodeQL queries at github.com/x41sec/poc/tree/master/starlette-host-header. Run these across any Python codebase that touches Starlette or FastAPI to find affected middleware patterns. The repository also includes a runtime PoC for confirming exposure on a deployed instance.
  3. Log review. Cheap signals worth running retroactively:
    • Any access log entry where the Host header contains any of: /, ?, #, \, @. Legitimate clients do not emit these characters in Host.
    • Any divergence between the dispatched route and the request.url.path your application recorded for that request. Any non-equal pair is the exploit signature.
    • For LiteLLM / vLLM specifically: requests to administrative or model-control endpoints whose audit trail records an unexpected path string for the same dispatched handler.

Timeline

2026-01-27Issue identified during X41 source code audit of vLLM
2026-02-04Starlette vendor notified, PoC supplied
2026-02-05Vendor acknowledged
2026-03-01Patch proposed by vendor
2026-05-21Patch released (Starlette 1.0.1, scored CVSS 6.5 Moderate)
2026-05-22Public disclosure, CVE-2026-48710 assigned, badhost.org launched

Note: end-to-end the vendor was pushed for roughly four months before shipping, with the proposed patch sitting for nearly three of those months between proposal and release. The fix was released quietly, scored CVSS 6.5 Moderate at the library layer, with no ecosystem-wide notice corresponding to the actual downstream blast radius. Public disclosure and patch availability landed on the same day, so operators should assume zero exploit development lead time.

References