HTTP Request Smuggling / HTTP Desync Attack
何を指すか
この脆弱性は、フロントエンドプロキシとバックエンドサーバーの間の非同期化により、攻撃者がフロントエンドプロキシ(ロードバランス/リバースプロキシ)によって1つのリクエストとして解釈され、バックエンドサーバーによって2つのリクエストとして解釈される HTTP リクエストを送信できるようにします。 これにより、ユーザーは自分の後にバックエンドサーバーに到着する次のリクエストを変更できます。
理論
メッセージに Transfer-Encoding ヘッダーフィールドと Content-Length ヘッダーフィールドの両方が含まれる場合、後者は無視される必要があります。
Content-Length
Content-Length エンティティヘッダーは、受信者に送信されるエンティティボディのバイト数を示します。
Transfer-Encoding: chunked
Transfer-Encoding ヘッダーは、ペイロードボディを安全にユーザーに転送するために使用されるエンコーディング形式を指定します。 Chunked は、大きなデータがチャンクのシリーズで送信されることを意味します。
現実
フロントエンド(ロードバランス/リバースプロキシ)は content-length または transfer-encoding ヘッダーを処理し、バックエンドサーバーはもう一方を処理して、2つのシステムの間で非同期化を引き起こします。 これは、攻撃者がリバースプロキシに1つのリクエストを送信でき、フロントエンドサーバーが2つの異なるリクエストとして解釈する可能性があるため、非常に重大です。この技術の危険性は、バックエンドサーバーが注入された2番目のリクエストを次のクライアントから来たものとして解釈し、そのクライアントの実際のリクエストが注入されたリクエストの一部として含まれる可能性があることにあります。
特異性
HTTP では、改行文字は 2 バイトで構成されることを覚えておいてください:
Content-Length: このヘッダーは、リクエストのボディのバイト数を示す10進数を使用します。ボディは最後の文字で終了することが期待されており、リクエストの最後に改行が必要ではありません。
Transfer-Encoding: このヘッダーは、次のチャンクのバイト数を示す16進数をボディで使用します。チャンクは新しい行で終了する必要がありますが、この新しい行は長さ指示子には含まれません。この転送方法は、サイズ 0 のチャンクに続く 2 つの新しい行で終了する必要があります:
0
Connection: 私の経験に基づいて、リクエストスムギリングの最初のリクエストで
Connection: keep-alive
を使用することをお勧めします。
基本的な例
Burp Suite を使用してこれを悪用しようとする場合は、リピーターで Update Content-Length
と Normalize HTTP/1 line endings
を無効にしてください。一部のガジェットは改行、キャリッジリターン、および不正なコンテンツ長を悪用しています。
HTTP リクエストスムギリング攻撃は、フロントエンドとバックエンドサーバーが Content-Length
(CL)および Transfer-Encoding
(TE)ヘッダーを解釈する方法の違いを悪用する曖昧なリクエストを送信して作成されます。これらの攻撃は、主に CL.TE、TE.CL、および TE.TE として表れ、各タイプはこれらのヘッダーの優先順位を一意に組み合わせたものです。脆弱性は、サーバーが同じリクエストを異なる方法で処理することから生じ、予期しないおよび潜在的に悪意のある結果につながる可能性があります。
脆弱性タイプの基本的な例
CL.TE 脆弱性(フロントエンドが Content-Length を使用し、バックエンドが Transfer-Encoding を使用)
フロントエンド(CL):
Content-Length
ヘッダーに基づいてリクエストを処理します。バックエンド(TE):
Transfer-Encoding
ヘッダーに基づいてリクエストを処理します。攻撃シナリオ:
攻撃者は、
Content-Length
ヘッダーの値が実際のコンテンツ長と一致しないリクエストを送信します。フロントエンドサーバーは、
Content-Length
の値に基づいてリクエスト全体をバックエンドに転送します。バックエンドサーバーは、
Transfer-Encoding: chunked
ヘッダーによりリクエストをチャンク化し、残りのデータを別々の後続リクエストとして解釈します。例:
TE.CL 脆弱性(フロントエンドが Transfer-Encoding を使用し、バックエンドが Content-Length を使用)
フロントエンド(TE):
Transfer-Encoding
ヘッダーに基づいてリクエストを処理します。バックエンド(CL):
Content-Length
ヘッダーに基づいてリクエストを処理します。攻撃シナリオ:
攻撃者は、チャンクサイズ(
7b
)と実際のコンテンツ長(Content-Length: 4
)が一致しないチャンクリクエストを送信します。フロントエンドサーバーは、
Transfer-Encoding
を尊重し、リクエスト全体をバックエンドに転送します。バックエンドサーバーは、
Content-Length
を尊重し、リクエストの初期部分のみを処理し(7b
バイト)、残りを意図しない後続リクエストの一部として残します。例:
TE.TE脆弱性(両方で使用されるTransfer-Encoding、隠蔽あり)
サーバー: 両方が
Transfer-Encoding
をサポートしていますが、隠蔽を介して無視される可能性があります。攻撃シナリオ:
攻撃者は、Transfer-Encodingヘッダーを隠蔽してリクエストを送信します。
どちらのサーバー(フロントエンドまたはバックエンド)が隠蔽を認識できないかに応じて、CL.TEまたはTE.CL脆弱性が悪用される可能性があります。
1つのサーバーが見たリクエストの未処理部分が、後続のリクエストの一部となり、スマグリングが発生します。
例:
CL.CLシナリオ(フロントエンドとバックエンドの両方で使用されるContent-Length):
両方のサーバーは、
Content-Length
ヘッダーだけを基にリクエストを処理します。このシナリオは通常、両サーバーがリクエストの長さをどのように解釈するかに合意があるため、スマグリングにはつながりません。
例:
CL != 0シナリオ:
Content-Length
ヘッダーが存在し、ゼロ以外の値を持ち、リクエストボディにコンテンツがあることを示すシナリオを指します。スマグリング攻撃を理解し、作成する際に重要であり、サーバーがリクエストの終わりをどのように決定するかに影響を与えます。
例:
ウェブサーバーの破壊
このテクニックは、初期HTTPデータを読み取りながらウェブサーバーを壊すことが可能であり、接続を閉じることなく行います。これにより、HTTPリクエストのボディが次のHTTPリクエストと見なされます。
例えば、この解説で説明されているように、WerkzeugではUnicode文字を送信するとサーバーが壊れる可能性がありました。ただし、HTTP接続がヘッダー**Connection: keep-alive
で作成された場合、リクエストのボディは読み取られず、接続は開いたままになります。そのため、リクエストのボディ**は次のHTTPリクエストとして処理されます。
ホップバイホップヘッダーを介した強制
ホップバイホップヘッダーを悪用することで、プロキシにContent-LengthヘッダーまたはTransfer-Encodingヘッダーを削除するよう指示することができ、HTTPリクエストスマグリングを悪用することが可能です。
HTTPリクエストスマグリングの見つけ方
HTTPリクエストスマグリングの脆弱性を特定する方法の1つは、サーバーが操作されたリクエストにどれくらいの時間で応答するかを観察するタイミング技術を使用することです。これらの技術は、CL.TEおよびTE.CLの脆弱性を検出するのに特に役立ちます。これらの方法以外にも、このような脆弱性を見つけるために使用できる他の戦略やツールがあります。
タイミング技術を使用してCL.TE脆弱性を見つける方法
手法:
脆弱性がある場合、バックエンドサーバーが追加のデータを待機するようにするリクエストを送信します。
例:
観察:
フロントエンドサーバーは
Content-Length
に基づいてリクエストを処理し、メッセージを途中で切断します。バックエンドサーバーは、チャンク形式のメッセージを期待しており、到着しない次のチャンクを待機することで遅延が発生します。
指標:
タイムアウトや応答の遅延が発生すること。
バックエンドサーバーから400 Bad Requestエラーを受け取ることがあり、詳細なサーバー情報が含まれることがあります。
タイミング技術を使用してTE.CL脆弱性を見つける方法
手法:
脆弱性がある場合、バックエンドサーバーが追加のデータを待機するようにするリクエストを送信します。
例:
観察:
フロントエンドサーバーは
Transfer-Encoding
に基づいてリクエストを処理し、メッセージ全体を転送します。バックエンドサーバーは、
Content-Length
に基づいたメッセージを期待しており、到着しない追加のデータを待機することで遅延が発生します。
脆弱性を見つけるための他の方法
差分応答解析:
異なるバージョンのリクエストをわずかに変更して送信し、サーバーの応答が予期しない方法で異なるかどうかを観察し、解析の不一致を示すことがあります。
自動化ツールの使用:
Burp Suiteの 'HTTP Request Smuggler' 拡張機能のようなツールを使用すると、曖昧なリクエストのさまざまな形式を送信し、応答を分析してこれらの脆弱性を自動的にテストできます。
Content-Lengthの変動テスト:
実際のコンテンツ長さと一致しない
Content-Length
値を持つリクエストを送信し、サーバーがそのような不一致をどのように処理するかを観察します。Transfer-Encodingの変動テスト:
曖昧または不正な
Transfer-Encoding
ヘッダーを持つリクエストを送信し、フロントエンドサーバーとバックエンドサーバーがそのような操作に対して異なる方法で応答するかを監視します。
HTTPリクエストスマグリングの脆弱性テスト
タイミング技術の効果を確認した後、クライアントリクエストを操作できるかどうかを検証することが重要です。簡単な方法は、リクエストを毒することを試みることです。たとえば、/
へのリクエストが404レスポンスを返すようにすることが挙げられます。基本例で以前に議論されたCL.TE
およびTE.CL
の例は、クライアントのリクエストを毒する方法を示しており、クライアントが異なるリソースにアクセスしようとしているにもかかわらず、404レスポンスを引き起こす方法を示しています。
主な考慮事項
他のリクエストに干渉してリクエストスマグリングの脆弱性をテストする際には、次の点に注意してください:
異なるネットワーク接続: "攻撃"と "通常" のリクエストは別々のネットワーク接続経由で送信される必要があります。両方のリクエストに同じ接続を使用すると、脆弱性の存在が検証されません。
一貫したURLとパラメータ: 両方のリクエストに同じURLとパラメータ名を使用するようにしてください。現代のアプリケーションでは、URLとパラメータに基づいて特定のバックエンドサーバーにリクエストをルーティングすることがよくあります。これらを一致させることで、両方のリクエストが同じサーバーで処理される可能性が高まり、成功するための前提条件となります。
タイミングと競合条件: "通常" のリクエストは、"攻撃" リクエストからの干渉を検出するために、他の同時アプリケーションリクエストと競合します。したがって、"通常" のリクエストは、"攻撃" リクエストの直後に送信してください。忙しいアプリケーションでは、確定的な脆弱性確認のために複数の試行が必要な場合があります。
負荷分散の課題: フロントエンドサーバーが負荷分散装置として機能する場合、リクエストをさまざまなバックエンドシステムに分散することがあります。 "攻撃" と "通常" のリクエストが異なるシステムに送られると、攻撃は成功しません。この負荷分散の側面については、脆弱性を確認するために複数の試行が必要な場合があります。
意図しないユーザーへの影響: 攻撃が誤って他のユーザーのリクエストに影響を与える場合(検出用に送信した "通常" のリクエストではない場合)、攻撃が他のアプリケーションユーザーに影響を与えたことを示します。継続的なテストは他のユーザーに影響を与える可能性があり、慎重なアプローチが必要となる場合があります。
HTTPリクエストスマグリングの悪用
HTTPリクエストスマグリングを利用したフロントエンドセキュリティの回避
時々、フロントエンドプロキシは着信リクエストを検査してセキュリティ対策を施します。しかし、HTTPリクエストスマグリングを悪用することで、これらの対策を回避し、制限されたエンドポイントへの不正アクセスを可能にすることができます。たとえば、/admin
へのアクセスが外部から禁止されている場合、フロントエンドプロキシはそのような試みを積極的にブロックするかもしれません。しかし、このプロキシは、スマグリングされたHTTPリクエスト内の埋め込まれたリクエストを検査しない可能性があり、これらの制限をバイパスするための抜け道を残しています。
HTTPリクエストスマグリングを使用してフロントエンドセキュリティコントロールをバイパスする方法を示す以下の例を考えてみましょう。通常、フロントエンドプロキシによって保護されている /admin
パスを特定的に対象としています:
CL.TEの例
CL.TE攻撃では、初期リクエストにはContent-Length
ヘッダーが利用されますが、埋め込まれた後続リクエストにはTransfer-Encoding: chunked
ヘッダーが利用されます。フロントエンドプロキシは初期のPOST
リクエストを処理しますが、埋め込まれたGET /admin
リクエストを検査せず、/admin
パスへの未承認アクセスを許可します。
TE.CLの例
逆に、TE.CL攻撃では、最初のPOST
リクエストでTransfer-Encoding: chunked
を使用し、その後の埋め込まれたリクエストはContent-Length
ヘッダーに基づいて処理されます。CL.TE攻撃と同様に、フロントエンドプロキシはスマグルされたGET /admin
リクエストを見落とし、誤って制限された/admin
パスへのアクセスを許可します。
フロントエンドリクエスト書き換えの公開
アプリケーションはしばしば、リクエストをバックエンドサーバーに渡す前に変更するためにフロントエンドサーバーを使用します。典型的な変更には、X-Forwarded-For: <クライアントのIP>
などのヘッダーを追加して、クライアントのIPをバックエンドに中継することが含まれます。これらの変更を理解することは重要です。なぜなら、これにより保護をバイパスしたり、隠された情報やエンドポイントを明らかにする方法が明らかになる可能性があるからです。
プロキシがリクエストをどのように変更するかを調査するには、バックエンドが応答でエコーするPOSTパラメーターを見つけます。その後、このパラメーターを最後に使用して、次のようなリクエストを作成します。
この構造では、search=
の後に続くリクエストコンポーネントが追加され、それがレスポンスで反映されるパラメータとなります。この反映により、後続リクエストのヘッダーが公開されます。
ネストされたリクエストのContent-Length
ヘッダーを実際のコンテンツ長と一致させることが重要です。小さな値から始め、徐々に増やすことが推奨されます。値が低すぎると反映されるデータが切り捨てられ、値が高すぎるとリクエストがエラーになる可能性があります。
このテクニックはTE.CL脆弱性の文脈でも適用可能ですが、リクエストはsearch=\r\n0
で終了する必要があります。改行文字に関係なく、値は検索パラメータに追加されます。
この方法は主に、フロントエンドプロキシによって行われたリクエストの変更を理解するために役立ち、基本的には自己調査を行います。
他のユーザーのリクエストをキャプチャする
次のユーザーのリクエストをキャプチャすることは可能であり、POST操作中に特定のリクエストをパラメータの値として追加することで実現できます。以下は、これがどのように実行されるかの例です:
このシナリオでは、comment parameter は、公開されたページのコメントセクション内のコンテンツを保存するために意図されています。したがって、次のリクエストのコンテンツはコメントとして表示されます。
ただし、このテクニックには制限があります。一般的に、スマグルされたリクエストで使用されるパラメーター区切り記号までのデータのみをキャプチャします。URLエンコードされたフォームの送信では、この区切り記号は &
文字です。これは、被害者ユーザーのリクエストからキャプチャされたコンテンツが最初の &
で停止することを意味します。これはクエリ文字列の一部である可能性さえあります。
さらに、このアプローチはTE.CL脆弱性でも有効であることに注意する価値があります。このような場合、リクエストは search=\r\n0
で終了する必要があります。改行文字に関係なく、値は検索パラメーターに追加されます。
反射型XSSを悪用するためにHTTPリクエストスマグリングを使用する
HTTPリクエストスマグリングを利用して、反射型XSS に脆弱なWebページを悪用することができ、次の利点があります:
ターゲットユーザーとのやり取りが不要です。
通常は到達不可能なリクエストの一部でXSSを悪用することができます。例えば、HTTPリクエストヘッダー。
User-Agentヘッダーを介して反射型XSSに脆弱なウェブサイトのシナリオでは、次のペイロードがこの脆弱性を悪用する方法を示しています:
このペイロードは、以下の手順で脆弱性を悪用するために構造化されています:
Transfer-Encoding: chunked
ヘッダーを持つ、一見通常のPOST
リクエストを開始し、スマグリングの開始を示します。0
を続け、チャンク形式のメッセージボディの終わりをマークします。次に、スマグリングされた
GET
リクエストが導入され、User-Agent
ヘッダーにスクリプト<script>alert(1)</script>
が挿入され、サーバーがこの後続リクエストを処理する際にXSSをトリガーします。
User-Agent
をスマグリングを通じて操作することで、ペイロードは通常のリクエスト制約をバイパスし、非標準ですが効果的な方法で反射型XSS脆弱性を悪用します。
HTTP/0.9
ユーザーコンテンツが**text/plain
などのContent-type
で応答に反映される場合、XSSの実行を防ぎます。サーバーがHTTP/0.9をサポートしている場合、これをバイパスする可能性があります**!
HTTP/0.9は以前の1.0よりも前で、GET動詞のみを使用し、ヘッダーを返さず、本文のみを返します。
この解説では、HTTP/0.9を使用したリクエストスマグリングとユーザーの入力を返信する脆弱なエンドポイントを悪用し、HTTP/0.9のリクエストをスマグリングしました。応答に反映されるパラメータには**偽のHTTP/1.1応答(ヘッダーと本文を含む)**が含まれていたため、応答にはContent-Type
がtext/html
の有効な実行可能なJSコードが含まれます。
HTTPリクエストスマグリングを使用したオンサイトリダイレクトの悪用
アプリケーションはしばしば、リダイレクトURL内のHost
ヘッダーからホスト名を使用して、1つのURLから別のURLにリダイレクトします。これは、ApacheやIISなどのWebサーバーで一般的です。たとえば、スラッシュなしでフォルダをリクエストすると、スラッシュを含めたリダイレクトが発生します。
結果は:
Though seemingly harmless, this behavior can be manipulated using HTTP request smuggling to redirect users to an external site. For example:
次に処理されるユーザーのリクエストが、攻撃者が制御するウェブサイトにリダイレクトされる可能性がある、この密輸されたリクエストは次のものです:
結果は:
HTTPリクエストスマグリングを利用したWebキャッシュポイズニングの悪用
フロントエンドインフラストラクチャのコンポーネントがコンテンツをキャッシュする場合、Webキャッシュポイズニングを実行できます。サーバーのレスポンスを操作することで、キャッシュを汚染することが可能です。
以前、サーバーの応答を変更して404エラーを返す方法を観察しました(基本的な例を参照)。同様に、サーバーをだまして、/static/include.js
のリクエストに対して/index.html
のコンテンツを返すようにすることが可能です。その結果、/static/include.js
のコンテンツが/index.html
のものでキャッシュされ、ユーザーがアクセスできなくなり、サービス拒否(DoS)攻撃につながる可能性があります。
この技術は、オープンリダイレクトの脆弱性が発見された場合や、オンサイトリダイレクトがオープンリダイレクトにつながる場合に特に有効です。これらの脆弱性を悪用して、/static/include.js
のキャッシュされたコンテンツを攻撃者が制御するスクリプトに置き換え、更新された/static/include.js
をリクエストするすべてのクライアントに対して広範囲なクロスサイトスクリプティング(XSS)攻撃を可能にします。
以下は、オンサイトリダイレクトがオープンリダイレクトに結びついたキャッシュポイズニングの悪用の図解です。目的は、/static/include.js
のキャッシュコンテンツを攻撃者が制御するJavaScriptコードで提供するように変更することです:
埋め込まれたリクエストを /post/next?postId=3
に向けることに注意してください。このリクエストは、Host ヘッダーの値を利用してドメインを決定し、/post?postId=4
にリダイレクトされます。攻撃者はHost ヘッダーを変更することで、リクエストを自身のドメインにリダイレクトできます(オンサイトリダイレクトからオープンリダイレクトへ)。
成功したソケットポイズニングの後、/static/include.js
へのGETリクエストが開始されるべきです。このリクエストは、以前のオンサイトリダイレクトからオープンリダイレクトリクエストによって汚染され、攻撃者が制御するスクリプトの内容を取得します。
その後、/static/include.js
への任意のリクエストは、攻撃者のスクリプトのキャッシュされたコンテンツを提供し、広範なXSS攻撃を実行します。
Webキャッシュ欺瞞を実行するためのHTTPリクエストスマグリングの使用
WebキャッシュポイズニングとWebキャッシュ欺瞞の違いは何ですか?
Webキャッシュポイズニングでは、攻撃者はアプリケーションに悪意のあるコンテンツをキャッシュに保存させ、このコンテンツがキャッシュから他のアプリケーションユーザーに提供されます。
Webキャッシュ欺瞞では、攻撃者はアプリケーションに別のユーザーの機密コンテンツをキャッシュに保存させ、その後攻撃者がこのコンテンツをキャッシュから取得します。
攻撃者は、機密のユーザー固有のコンテンツを取得するスマグルされたリクエストを作成します。以下はその例です:
HTTPリクエストスマグリングを介したWebキャッシュ毒物の悪用
この投稿では、サーバーがTRACEメソッドを有効にしている場合、HTTPリクエストスマグリングを悪用する可能性があると提案されています。これは、このメソッドがサーバーに送信された任意のヘッダーをレスポンスの本文の一部として反映するためです。例えば:
次のようなレスポンスを送信します:
この動作を悪用する例として、まずHEADリクエストを紛れ込ませることが挙げられます。このリクエストは、GETリクエストのヘッダーのみで応答されます(Content-Type
を含む)。そして、HEADの直後にTRACEリクエストを紛れ込ませ、これにより送信されたデータが反映されます。
HEADの応答にはContent-Length
ヘッダーが含まれているため、TRACEリクエストの応答はHEADリクエストの本文として扱われ、応答に任意のデータが反映されます。
この応答は次のリクエストに接続経由で送信されるため、これは例えばキャッシュされたJSファイルで任意のJSコードを挿入するために使用できます。
HTTPレスポンススプリッティングを介したTRACEの悪用
この投稿を引き続き参照することで、TRACEメソッドを悪用する別の方法が提案されています。コメントにあるように、HEADリクエストとTRACEリクエストを紛れ込ませることで、HEADリクエストへの応答に反映されるデータを一部制御することが可能です。HEADリクエストの本文の長さは基本的にContent-Length
ヘッダーで示され、TRACEリクエストの応答によって形成されます。
したがって、新しいアイデアは、このContent-LengthとTRACE応答で与えられたデータを知ることで、TRACE応答にContent-Lengthの最後のバイトの後に有効なHTTP応答が含まれるようにすることが可能であり、攻撃者は次の応答を完全に制御することができます(これはキャッシュポイズニングを実行するために使用できます)。
例:
次のようなレスポンスを生成します(HEADレスポンスにContent-Lengthがあることに注意してください。これにより、TRACEレスポンスがHEAD本文の一部となり、HEADのContent-Lengthが終了すると有効なHTTPレスポンスがスマグルされます)。
HTTPリクエストスマグリングをHTTPレスポンスデシンクロ化で武装化する
HTTPリクエストスマグリングの脆弱性を見つけたが、そのエクスプロイト方法がわからない場合は、以下の別のエクスプロイト方法を試してみてください:
pageHTTP Response Smuggling / Desyncその他のHTTPリクエストスマグリングテクニック
ブラウザHTTPリクエストスマグリング(クライアント側)
HTTP/2のダウングレードにおけるリクエストスマグリング
Turbo intruderスクリプト
CL.TE
From https://hipotermia.pw/bb/http-desync-idor
TE.CL
From: https://hipotermia.pw/bb/http-desync-account-takeover
ツール
https://github.com/bahruzjabiyev/t-reqs-http-fuzzer: このツールは文法ベースのHTTP Fuzzerであり、奇妙なリクエストスマグリングの不一致を見つけるのに役立ちます。
参考文献
Last updated