以下の内容はhttps://cysec148.hatenablog.com/entry/2025/09/16/183117より取得しました。


HTTP request smuggling, confirming a TE.CL vulnerability via differential responses

Hello there, ('ω')ノ

要約(結論)

このラボは フロントエンドが Transfer-Encoding(chunked)を使い、バックエンドが Content-Length を使う(TE.CL)不一致 を突く典型例です。Burp Repeater で HTTP/1.1 にし、「Update Content-Length」をオフにした状態で添付のリクエストを 2回連続で送ると、2回目のレスポンスが 404 Not Found になり、smuggling が成立したことが確認できます。


準備(Burp 設定・理由)

  • Burp → Repeater を使用する。 理由:リクエストを自由に編集して再送する(連続で送る)必要があるため。
  • Inspector の Request attributes で Protocol を HTTP/1.1 に手動で設定する。 理由:このテクニックは HTTP/1 系のメッセージフレーミングに依存するため。HTTP/2 では動作しない。
  • Repeater のオプションで “Update Content-Length” をチェック解除(オフ)しておく。 理由:Burp が自動で Content-Length を修正してしまうと、人間が意図した不整合(Content-Length と Transfer-Encoding の矛盾)を作れないため。

送信するリクエスト(そのまま Repeater に貼る。YOUR-LAB-ID を置換)

POST / HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-length: 4
Transfer-Encoding: chunked

5e
POST /404 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1
0(改行コードが必要)
(もうひとつ改行コードが必要)

YOUR-LAB-ID はあなたのラボ ID に置き換えてください。 また Repeater の “Update Content-Length” はオフのままにしてください。


リクエストの行ごとの意味と「なぜそうするか」

  • POST / HTTP/1.1 意味:クライアント(あなた)がフロントに対して通常の POST を送っている。 なぜ:この最初のリクエストをトリガにしてバックに smuggle させるためのコンテナにする。

  • Content-length: 4(わざと小さめ) 意味:バックエンドが Content-Length を使う(今回のラボ設計)と仮定しており、バックはこの値を読んでボディ分だけを受け取る。 なぜ小さいか:バックはこの 4 バイトだけを「このリクエストのボディ」として消費し、残りを次のリクエスト(smuggled)として扱わせるため。

  • Transfer-Encoding: chunked 意味:フロントはチャンク(chunked)処理ができるため、このヘッダを見てチャンクを解釈する。 なぜ両方置くか:TE.CL はまさに「フロントは Transfer-Encoding を使って解釈、バックは Content-Length を使う」不一致を作るため。両方のヘッダを混在させることで差が生じる。

  • 空行のあと 5e(チャンク長) 意味:チャンク長は 16進数表記。5e は 0x5e(94)バイトのチャンクが続くことを示す。 なぜ:フロントはこのチャンク長を見てチャンク本体(ここでは smuggled な別リクエストを含むデータ)を読み、チャンクを展開(あるいはそのまま転送)する。チャンクの長さを十分に大きく取ることで、埋め込みたいリクエスト全体を含められるようにする。

  • チャンク内の POST /404 HTTP/1.1x=1(smuggled リクエスト) 意味:フロントがチャンクを処理すると、このデータはフロント→バックへ「通常のボディ」として届くはず、しかしバックは Content-Length=4 を参照しているので、このチャンク内の先頭 4 バイトだけを「この POST のボディ」として消費し、残りを次のリクエスト(GET /404 ではなくこの POST の残り→結果的に次のリクエストの先頭)として扱う挙動を誘う。 なぜここに POST /404 を入れるか:ラボではこの経路を通すとバックで GET /404 を処理させることができ、次のクライアント側の送信で 404 が返ってくる差分が観察できるように設計されている(ラボの挙動・ハンドラに依存)。

  • 最後の 0(ゼロチャンク) 意味:chunked の終端(ゼロ長チャンク)は「このチャンク転送はここで終わる」ことを示す。フロントはここでチャンク処理を終える。 なぜ:フロントのチャンク処理が終わった段階で、チャンク内に置いた余剰データがバック側で「次のリクエスト」として扱われる状態を作るため。


なぜ「同じリクエストを 2回送る」のか(差分応答の直感)

  1. 1回目送信:smuggled なリクエスト(POST /404GET /404 等)がバックエンドの処理キューに「先に入る」状態を作る。フロントは元の POST を処理してクライアントへ応答するが、バック側のキューには余分なリクエストが残る。
  2. 2回目送信:バックはキューに残っている smuggled リクエストを先に処理してしまい、その結果が 404 などの差分応答として現れる。クライアント側(あなたが見ている Repeater)では 2 回目の応答が 404 になれば成功。
  3. 直感:フロントとバックで「どのバイトをどのリクエストの一部とみなすか」がズレているため、連続した送信で挙動(応答)が変わる=脆弱性の存在。

確認ポイント(レスポンスで見るべき点)

  • 2 回目のステータスコードが 404 Not Found になっていること(これが成立確認)。
  • レスポンスヘッダやボディの違いをメモする(差分の証拠)。
  • 必要なら Burp の HTTP history でタイミングや順序を確認する(1 回目/2 回目の応答時間差やヘッダの差を確認)。

よくある失敗と対処

  • HTTP/2 のまま送ってしまう → 必ず Repeater で HTTP/1.1 に切り替える。
  • Burp が Content-Length を自動修正してしまう → Repeater の “Update Content-Length” をオフにする。
  • チャンク長や改行がずれている → 改行は CRLF(\r\n)で送る実装の違いに注意。Burp は通常正しい改行を使うが、手動編集でずれることがあるので注意深く。
  • フロントがヘッダを正規化/削除している → フロントの実装によっては Transfer-Encoding を外してしまう場合があり、その場合は脆弱性が発現しない(ラボは TE.CL を想定して設計されている)。

防御(実務で取るべき対策の要点)

  • フロントで正規化(normalize)して、矛盾する組み合わせ(Transfer-Encoding と Content-Length の同時存在)を拒否する。
  • フロントとバックでメッセージ境界の解釈ルールを統一する。
  • 不要なヘッダはフロントで削除してからバックへ転送する。
  • フロントでチャンクを展開してからバックへ転送(あるいは逆にチャンクを全部拒否)する など、構成ポリシーを整理する。
  • 監査ログでフロントとバックのリクエスト数不整合を検出する仕組み を入れておく(フロントで 1 リクエストに対しバックで 2 以上処理されていないか等)。

まとめ(手順のおさらい)

  1. Burp Repeater を開き、Protocol を HTTP/1.1 にする。
  2. Repeater のオプションで Update Content-Length をオフにする。
  3. 上のリクエストを貼り、YOUR-LAB-ID を置換して 送信を 2 回行う。
  4. 2 回目で 404 Not Found が返ってくれば成功(TE.CL の差分スマグリングが成立)。

Best regards, (^^ゞ




以上の内容はhttps://cysec148.hatenablog.com/entry/2025/09/16/183117より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14