Starlette CVE-2026-48710: BadHost Authentication Bypass
Starlette CVE-2026-48710 (nicknamed 'BadHost') is a critical authentication bypass vulnerability affecting foundational Python web libraries like FastAPI, vLLM, and LiteLLM. An attacker can inject path boundary characters into the Host header to bypass path-based security middleware; this article provides dependency audits and HTTP log hunting scripts.
On this page 0% read
Executive Summary
A critical authentication bypass vulnerability, designated CVE-2026-48710 and nicknamed “BadHost”, has been disclosed in the Starlette web framework (affecting all versions prior to 1.0.1) Ostif.
Because Starlette serves as the foundational ASGI toolkit for widely used Python frameworks and applications—including FastAPI, vLLM, and LiteLLM—the blast radius of this vulnerability is exceptionally large, exposing millions of production web services and AI agents to pre-authenticated security bypasses CyberKendra. This article provides a comprehensive impact determination, key facts, and a complete Python audit script to identify vulnerable versions and log indicators of active exploitation.
Key Facts
cve: "CVE-2026-48710"
alias: "BadHost"
vendor: "Starlette"
product: "Starlette (ASGI toolkit)"
disclosed_date: "2026-05-20"
vulnerability: "Authentication bypass via Host header URL reconstruction injection"
cwe: ["CWE-346", "CWE-284"]
affected_products: ["Starlette", "FastAPI", "vLLM", "LiteLLM"]
affected_versions: ["Starlette < 1.0.1"]
fixed_versions: ["1.0.1"]
nvd_cvss_v31: "9.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
exploitation_status: "active_exploit_publicly_available"
zero_day_status: "confirmed_active_zero_day_exploitation"
Source Confidence & Evidence Mapping
- confirmed: Ostif security report details the exact root-cause URL reconstruction bug inside Starlette’s
request.urlimplementation, confirming authentication bypass Ostif. - confirmed: CyberKendra publishes a complete proof-of-concept concept showing how injected path boundaries (e.g.
/,?) inside theHostheader trick Starlette’s routing middleware CyberKendra. - confirmed: Downstream vendor alerts from FastAPI and LiteLLM recommend immediate upgrades of their underlying Starlette dependency to version
1.0.1or higher.
Impact Determination
| Classification | Criteria | Required evidence | Remediation trigger | Closure condition |
|---|---|---|---|---|
| Confirmed compromise | Web/WAF logs show HTTP requests with path-delimiter characters (e.g. /, ?, #) in the Host header, followed by unauthorized access to administrative or restricted paths. | Logs showing Host header values like example.com/health?x= targeting /admin with 200 OK status. | Revoke all session keys, isolate internal API services, and run backend audits. | Upgrade Starlette to 1.0.1 or later and verify backend logs for no further anomalous Host headers. |
| Presumed exposed | The web application is built on FastAPI or Starlette with version < 1.0.1, exposing path-based middleware authorization gates. | requirements.txt or poetry.lock lockfile showing starlette < 1.0.1 in active production environments. | Upgrade the library dependency immediately to a patched release. | Dependency verification outputs showing starlette >= 1.0.1 installed. |
| Potentially exposed | A Python project is running but lockfiles or backend web log records are not audited. | Asset register indicating Python-based ASGI servers (FastAPI/Uvicorn) without version check. | Execute the dependency and log audit script. | Confirm if the asset is confirmed compromised, presumed exposed, or not exposed. |
| Not exposed | The system utilizes non-affected framework stacks, or Starlette version is verified >= 1.0.1. | Verified dependency files showing version 1.0.1 or later. | None for this CVE. | Version verification bundle is archived. |
Timeline
- 2026-05-20: Public disclosure of CVE-2026-48710 (“BadHost”) following coordinate disclosures and accidental Chromium bug-tracker leaks CyberKendra.
- 2026-05-22: Public release of working proof-of-concept exploits showing complete middleware bypasses Ostif.
- 2026-05-26: Large-scale scanning observed across public cloud subnets targeting FastAPI/vLLM endpoints.
What Happened
Starlette handles URL reconstruction dynamically by concatenating the raw HTTP Host header with the request path to build a unified URI string. Because the Host header was not validated against standard RFC grammar, attackers could inject custom query delimiters (/health?x=) directly into the Host field:
- The Request:
GET /adminwithHost: example.com/health?x= - The Reconstruction: Starlette builds the internal URL as
http://example.com/health?x=/admin. - The Bypass: The underlying ASGI server processes the request path
/admin, but Starlette’s routing/auth middleware inspects the reconstructedrequest.url.pathwhich returns/health. If/healthis whitelisted, the authentication check is bypassed entirely, granting unauthenticated access to the restricted/adminresources.
Technical Analysis
The fundamental error occurs during URL parsing inside Starlette’s utility functions. By failing to validate that the host contains only valid hostname characters, re-parsing the reconstructed URL causes the injection parameters to overwrite the target path, shifting the path boundary forward.
Affected Assets and Blast Radius
asset_selectors:
- "starlette"
- "fastapi"
- "litellm"
- "vllm"
highest_value_assets:
- "Internet-facing FastAPI applications utilizing path-based middleware"
- "vLLM or LiteLLM AI endpoints exposed with API key authentication"
- "Internal corporate admin dashboards written in Python ASGI frameworks"
credentials_and_data_at_risk:
- "Internal LLM access and model weights (via vLLM/LiteLLM bypass)"
- "Backend database records accessible through administrative endpoints"
- "Administrative session credentials and user data"
Indicators And Detection Selectors
vulnerabilities: ["CVE-2026-48710", "BadHost"]
packages: ["starlette", "fastapi"]
telemetry_selectors:
- "starlette"
- "fastapi"
- "Host:"
- "/health"
Detection and Hunting
This hunting script audits Python dependencies (requirements.txt, poetry.lock, Pipfile.lock) for vulnerable Starlette versions, and scans web application log files (e.g. Nginx or Uvicorn logs) for exploit attempts featuring path delimiters in the Host field:
#!/usr/bin/env python3
import json
import os
import re
import sys
from pathlib import Path
ROOT = Path(os.environ.get("ROOT", sys.argv[1] if len(sys.argv) > 1 else ".")).resolve()
TELEMETRY_DIR = Path(os.environ.get("TELEMETRY_DIR", "telemetry-export")).resolve()
OUT = Path(os.environ.get("OUT", "hp-starlette-cve-2026-48710-scope")).resolve()
CVE = "CVE-2026-48710"
VULN_ID = "BadHost"
FIXED_VERSION = "1.0.1"
def read_text(path):
try:
return path.read_text(encoding="utf-8", errors="ignore")
except Exception:
return ""
def is_vulnerable_version(ver_str):
ver = re.findall(r"\d+", ver_str)
if not ver:
return False
v_ints = tuple(int(x) for x in ver[:3])
# Vulnerable: starlette < 1.0.1
if v_ints < (1, 0, 1):
return True
return False
OUT.mkdir(parents=True, exist_ok=True)
findings = {
"vulnerable_dependencies": [],
"exploit_attempts": []
}
# 1. Audit Python Dependency Files
for lockfile in ROOT.rglob("*"):
if not lockfile.is_file() or any(part in {".git", "node_modules"} for part in lockfile.parts):
continue
# Audit requirements.txt
if lockfile.name == "requirements.txt":
content = read_text(lockfile)
matches = re.findall(r"starlette\s*==\s*([0-9a-zA-Z\.]+)", content, re.IGNORECASE)
for version in matches:
if is_vulnerable_version(version):
findings["vulnerable_dependencies"].append({
"file": str(lockfile),
"package": "starlette",
"version_found": version,
"vulnerable": True,
"remediation": "Update starlette to 1.0.1 or later"
})
# Audit poetry.lock / poetry configs
elif lockfile.name == "poetry.lock":
content = read_text(lockfile)
# Search for package starlette block
blocks = re.findall(r'name\s*=\s*"starlette"\s*\n\s*version\s*=\s*"([^"]+)"', content)
for version in blocks:
if is_vulnerable_version(version):
findings["vulnerable_dependencies"].append({
"file": str(lockfile),
"package": "starlette",
"version_found": version,
"vulnerable": True,
"remediation": "poetry update starlette"
})
# 2. Audit Web Server / WAF Log Exports for Injected Host Headers
# Indicator: Host headers containing characters: '/', '?', '#'
for path in ROOT.rglob("*"):
if not path.is_file() or any(part in {".git", "node_modules"} for part in path.parts):
continue
if path.suffix in {".log", ".txt", ".json", ".csv"}:
body = read_text(path)
# Scan log lines for Host headers containing injection delimiters (e.g. host="example.com/health")
# In typical logs: search for Host values containing slashes, question marks, or hashes
host_inject_matches = re.finditer(r'(?:host|host_header)["\s:]+([^"\s]+[\/\?\#][^"\s]*)', body, re.IGNORECASE)
for match in host_inject_matches:
injected_host = match.group(1)
findings["exploit_attempts"].append({
"file": str(path),
"indicator": "Host header injection pattern matching BadHost RCE",
"injected_host_value": injected_host,
"context_snippet": body[max(0, match.start() - 60):min(len(body), match.end() + 60)].replace("\n", " ")
})
with open(OUT / "findings.json", "w") as f:
json.dump(findings, f, indent=2)
print(f"[{CVE} Audit Complete] Findings saved to: {OUT / 'findings.json'}")
Remediation & Credential Rotation Plan
Containment & Mitigation
- Host Header Validation: Ensure that front-facing reverse proxies (like Nginx, Apache, or AWS ALB) are explicitly configured to validate and normalize the
Hostheader before passing the request downstream to Uvicorn/Starlette. - Block Redirections: Add WAF rules to drop any incoming requests where the
Hostheader contains non-standard URI characters such as/,?, or#.
Eradication & Recovery
- Upgrade Starlette Dependency: Mandate immediate dependency upgrades of Starlette to version
1.0.1or later:- Command:
pip install --upgrade starletteorpoetry update starlette
- Command:
- Rotate Exposed Secrets: If web logs show successful requests (
200 OK) featuring a poisoned Host header targeting restricted administration directories, immediately revoke and rotate all backend API keys and session credentials accessible through the compromised services.