HTTP Request Smuggling / HTTP Desync Attack
Last updated
Last updated
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングを学び、実践する: HackTricks Training GCP Red Team Expert (GRTE)
この脆弱性は、フロントエンドプロキシとバックエンドサーバー間の非同期化により、攻撃者がHTTP リクエストを送信できる場合に発生します。このリクエストは、フロントエンドプロキシ(ロードバランス/リバースプロキシ)によって単一のリクエストとして解釈され、バックエンドサーバーによって2つのリクエストとして解釈されます。 これにより、ユーザーは自分のリクエストの後にバックエンドサーバーに到着する次のリクエストを変更することができます。
メッセージが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
を無効にしてください**。一部のガジェットは新しい行、キャリッジリターン、および不正なContent-Lengthを悪用します。
HTTPリクエストスムーギング攻撃は、フロントエンドとバックエンドサーバーがContent-Length
(CL)およびTransfer-Encoding
(TE)ヘッダーを解釈する際の不一致を利用して、あいまいなリクエストを送信することによって作成されます。これらの攻撃は、主にCL.TE、TE.CL、およびTE.TEとして異なる形で現れます。各タイプは、フロントエンドとバックエンドサーバーがこれらのヘッダーを優先する方法のユニークな組み合わせを表します。脆弱性は、サーバーが同じリクエストを異なる方法で処理することから生じ、予期しない、そして潜在的に悪意のある結果を引き起こします。
前の表にTE.0技術を追加する必要があります。CL.0技術のように、Transfer Encodingを使用します。
フロントエンド (CL): Content-Length
ヘッダーに基づいてリクエストを処理します。
バックエンド (TE): Transfer-Encoding
ヘッダーに基づいてリクエストを処理します。
攻撃シナリオ:
攻撃者は、Content-Length
ヘッダーの値が実際のコンテンツ長と一致しないリクエストを送信します。
フロントエンドサーバーは、Content-Length
の値に基づいてリクエスト全体をバックエンドに転送します。
バックエンドサーバーは、Transfer-Encoding: chunked
ヘッダーによりリクエストをチャンクとして処理し、残りのデータを別の後続リクエストとして解釈します。
例:
フロントエンド (TE): Transfer-Encoding
ヘッダーに基づいてリクエストを処理します。
バックエンド (CL): Content-Length
ヘッダーに基づいてリクエストを処理します。
攻撃シナリオ:
攻撃者は、チャンクサイズ(7b
)と実際のコンテンツ長(Content-Length: 4
)が一致しないチャンク化されたリクエストを送信します。
フロントエンドサーバーは、Transfer-Encoding
を尊重し、リクエスト全体をバックエンドに転送します。
バックエンドサーバーは、Content-Length
を尊重し、リクエストの最初の部分(7b
バイト)だけを処理し、残りを意図しない後続リクエストの一部として残します。
例:
サーバー: 両方がTransfer-Encoding
をサポートしていますが、一方は難読化を介して無視されるように騙される可能性があります。
攻撃シナリオ:
攻撃者は、難読化されたTransfer-Encoding
ヘッダーを持つリクエストを送信します。
どちらのサーバー(フロントエンドまたはバックエンド)が難読化を認識できないかに応じて、CL.TEまたはTE.CLの脆弱性が悪用される可能性があります。
リクエストの未処理部分は、サーバーの1つによって見られ、後続のリクエストの一部となり、スムーギングを引き起こします。
例:
両方のサーバーは、Content-Length
ヘッダーのみに基づいてリクエストを処理します。
このシナリオは通常、スムーギングには至らず、両方のサーバーがリクエストの長さを解釈する方法に整合性があります。
例:
Content-Length
ヘッダーが存在し、ゼロ以外の値を持ち、リクエストボディにコンテンツがあることを示すシナリオを指します。バックエンドはContent-Length
ヘッダーを無視します(これは0として扱われます)が、フロントエンドはそれを解析します。
これは、サーバーがリクエストの終了を決定する方法に影響を与えるため、スムーギング攻撃を理解し、作成する上で重要です。
例:
前のものと同様ですが、TEを使用しています。
技術はこちらで報告されています
例:
この技術は、初期のHTTPデータを読み取る際にウェブサーバーを破壊することが可能なシナリオでも有用ですが、接続を閉じることなく行います。この方法では、HTTPリクエストのボディが次のHTTPリクエストとして扱われます。
例えば、この書き込みで説明されているように、WerkzeugではいくつかのUnicode文字を送信することでサーバーが破壊されることが可能でした。しかし、HTTP接続が**Connection: keep-alive
ヘッダーで作成された場合、リクエストのボディは読み取られず、接続はまだオープンのままとなるため、リクエストのボディは次のHTTPリクエスト**として扱われます。
ホップバイホップヘッダーを悪用することで、プロキシにContent-LengthまたはTransfer-Encodingヘッダーを削除させることができ、HTTPリクエストスムージングを悪用することが可能です。
For more information about hop-by-hop headers visit:
hop-by-hop headersHTTPリクエストスムーギングの脆弱性を特定するには、サーバーが操作されたリクエストに応答するのにかかる時間を観察するタイミング技術を使用することがよくあります。これらの技術は、特にCL.TEおよびTE.CLの脆弱性を検出するのに役立ちます。これらの方法に加えて、こうした脆弱性を見つけるために使用できる他の戦略やツールもあります。
Method:
アプリケーションが脆弱な場合、バックエンドサーバーが追加データを待機するリクエストを送信します。
Example:
Observation:
フロントエンドサーバーはContent-Length
に基づいてリクエストを処理し、メッセージを早期に切り捨てます。
バックエンドサーバーはチャンクメッセージを期待しており、決して到着しない次のチャンクを待つため、遅延が発生します。
Indicators:
タイムアウトや応答の長い遅延。
バックエンドサーバーから400 Bad Requestエラーを受信することがあり、時には詳細なサーバー情報が含まれます。
Method:
アプリケーションが脆弱な場合、バックエンドサーバーが追加データを待機するリクエストを送信します。
Example:
Observation:
フロントエンドサーバーはTransfer-Encoding
に基づいてリクエストを処理し、メッセージ全体を転送します。
バックエンドサーバーはContent-Length
に基づいてメッセージを期待しており、決して到着しない追加データを待つため、遅延が発生します。
Differential Response Analysis:
リクエストのわずかに異なるバージョンを送信し、サーバーの応答が予期しない方法で異なるかどうかを観察します。これはパースの不一致を示す可能性があります。
Using Automated Tools:
Burp Suiteの「HTTP Request Smuggler」拡張機能のようなツールは、さまざまな曖昧なリクエストを送信し、応答を分析することでこれらの脆弱性を自動的にテストできます。
Content-Length Variance Tests:
実際のコンテンツ長と一致しないさまざまなContent-Length
値を持つリクエストを送信し、サーバーがその不一致をどのように処理するかを観察します。
Transfer-Encoding Variance Tests:
曖昧または不正なTransfer-Encoding
ヘッダーを持つリクエストを送信し、フロントエンドとバックエンドサーバーがその操作にどのように異なる応答を示すかを監視します。
タイミング技術の効果を確認した後、クライアントリクエストを操作できるかどうかを検証することが重要です。簡単な方法は、リクエストを毒することを試みることです。たとえば、/
へのリクエストが404応答を返すようにします。前述のBasic Examplesで説明したCL.TE
およびTE.CL
の例は、クライアントのリクエストを毒して404応答を引き出す方法を示していますが、クライアントは異なるリソースにアクセスしようとしています。
Key Considerations
リクエストスムーギングの脆弱性を他のリクエストに干渉してテストする際には、以下の点に留意してください:
Distinct Network Connections: 「攻撃」と「通常」のリクエストは、別々のネットワーク接続を介して送信する必要があります。両方のリクエストに同じ接続を使用することは、脆弱性の存在を検証しません。
Consistent URL and Parameters: 両方のリクエストに対して同一のURLとパラメータ名を使用することを目指してください。現代のアプリケーションは、URLとパラメータに基づいて特定のバックエンドサーバーにリクエストをルーティングすることがよくあります。これらを一致させることで、両方のリクエストが同じサーバーによって処理される可能性が高まります。これは成功する攻撃の前提条件です。
Timing and Racing Conditions: 「通常」のリクエストは、「攻撃」リクエストからの干渉を検出するために設計されており、他の同時アプリケーションリクエストと競合します。したがって、「攻撃」リクエストの直後に「通常」リクエストを送信してください。忙しいアプリケーションでは、脆弱性の確認のために複数の試行が必要になる場合があります。
Load Balancing Challenges: フロントエンドサーバーがロードバランサーとして機能している場合、リクエストをさまざまなバックエンドシステムに分配することがあります。「攻撃」と「通常」のリクエストが異なるシステムに到達した場合、攻撃は成功しません。このロードバランシングの側面は、脆弱性を確認するためにいくつかの試行を必要とする場合があります。
Unintended User Impact: あなたの攻撃が他のユーザーのリクエスト(検出のために送信した「通常」のリクエストではない)に意図せず影響を与える場合、これはあなたの攻撃が他のアプリケーションユーザーに影響を与えたことを示します。継続的なテストは他のユーザーを混乱させる可能性があるため、慎重なアプローチが必要です。
時には、フロントエンドプロキシがセキュリティ対策を強化し、受信リクエストを精査します。しかし、これらの対策はHTTPリクエストスムーギングを利用することで回避でき、制限されたエンドポイントへの不正アクセスを可能にします。たとえば、/admin
へのアクセスは外部から禁止されている場合があり、フロントエンドプロキシがそのような試みを積極的にブロックしています。それにもかかわらず、このプロキシはスムーグルされたHTTPリクエスト内の埋め込まれたリクエストを検査しない可能性があり、これによりこれらの制限を回避するための抜け穴が残ります。
以下の例は、HTTPリクエストスムーギングを使用してフロントエンドのセキュリティ制御を回避する方法を示しています。特に、通常フロントエンドプロキシによって保護されている/admin
パスをターゲットにしています:
CL.TE Example
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
パスへのアクセスを許可します。
アプリケーションは、通常、フロントエンドサーバーを使用して、バックエンドサーバーに渡す前に受信リクエストを変更します。典型的な変更には、クライアントのIPをバックエンドに中継するためにX-Forwarded-For: <クライアントのIP>
のようなヘッダーを追加することが含まれます。これらの変更を理解することは重要であり、保護を回避する方法や隠された情報やエンドポイントを明らかにする方法を示す可能性があります。
プロキシがリクエストをどのように変更するかを調査するには、バックエンドがレスポンスでエコーするPOSTパラメータを見つけます。次に、このパラメータを最後に使用してリクエストを作成します。以下のように:
この構造では、後続のリクエストコンポーネントが search=
の後に追加され、これはレスポンスに反映されるパラメータです。この反映により、後続のリクエストのヘッダーが露出します。
ネストされたリクエストの Content-Length
ヘッダーを実際のコンテンツ長に合わせることが重要です。小さな値から始めて徐々に増加させることが推奨されます。値が低すぎると反映されたデータが切り捨てられ、値が高すぎるとリクエストがエラーになる可能性があります。
この技術は TE.CL 脆弱性の文脈でも適用可能ですが、リクエストは search=\r\n0
で終了する必要があります。改行文字に関係なく、値は検索パラメータに追加されます。
この方法は、フロントエンドプロキシによって行われたリクエストの変更を理解するために主に使用され、基本的には自己主導の調査を行います。
POST 操作中に特定のリクエストをパラメータの値として追加することで、次のユーザーのリクエストをキャプチャすることが可能です。これを実現する方法は次のとおりです:
次のリクエストをパラメータの値として追加することで、後続のクライアントのリクエストを保存できます:
このシナリオでは、commentパラメータは、公開アクセス可能なページの投稿のコメントセクション内の内容を保存することを目的としています。その結果、次のリクエストの内容はコメントとして表示されます。
しかし、この技術には制限があります。一般的に、これはスムーズなリクエストで使用されるパラメータ区切り文字までのデータしかキャプチャしません。URLエンコードされたフォーム送信の場合、この区切り文字は&
文字です。これは、被害者ユーザーのリクエストからキャプチャされた内容が最初の&
で停止することを意味し、これはクエリ文字列の一部である可能性さえあります。
さらに、このアプローチはTE.CL脆弱性でも有効であることに注意する価値があります。その場合、リクエストはsearch=\r\n0
で終了する必要があります。改行文字に関係なく、値は検索パラメータに追加されます。
HTTPリクエストスムージングは、反射型XSSに脆弱なウェブページを悪用するために利用でき、重要な利点を提供します:
対象ユーザーとの対話は不要です。
HTTPリクエストヘッダーのような通常は到達不可能なリクエストの部分でXSSを悪用することができます。
ウェブサイトがUser-Agentヘッダーを介して反射型XSSに対して脆弱な場合、以下のペイロードはこの脆弱性を悪用する方法を示しています:
このペイロードは、脆弱性を悪用するために構成されています:
一見典型的なPOST
リクエストを開始し、スムーギングの開始を示すTransfer-Encoding: chunked
ヘッダーを含めます。
次に、チャンク化されたメッセージボディの終わりを示す0
が続きます。
その後、スムーグルされたGET
リクエストが導入され、User-Agent
ヘッダーにスクリプト<script>alert(1)</script>
が注入され、サーバーがこの後続のリクエストを処理する際にXSSがトリガーされます。
スムーギングを通じてUser-Agent
を操作することで、ペイロードは通常のリクエスト制約を回避し、標準的ではないが効果的な方法で反射型XSS脆弱性を悪用します。
ユーザーコンテンツが**Content-type
がtext/plain
のレスポンスに反映される場合、XSSの実行が防止されます。サーバーがHTTP/0.9をサポートしている場合、これを回避できる可能性があります**!
HTTP/0.9は以前のバージョンで、1.0の前にあり、GET動詞のみを使用し、ヘッダーではなくボディのみで応答します。
この書き込みでは、リクエストスムーギングとユーザーの入力に応じて応答する脆弱なエンドポイントを使用してHTTP/0.9でリクエストをスムーグルするために悪用されました。レスポンスに反映されるパラメータには**偽のHTTP/1.1レスポンス(ヘッダーとボディを含む)**が含まれていたため、レスポンスにはContent-Type
がtext/html
の有効な実行可能JSコードが含まれます。
アプリケーションは、リダイレクトURLのHost
ヘッダーからホスト名を使用して、あるURLから別のURLにリダイレクトすることがよくあります。これは、ApacheやIISのようなウェブサーバーで一般的です。たとえば、末尾にスラッシュがないフォルダーをリクエストすると、スラッシュを含めるためにリダイレクトされます:
結果は:
この無害に見える動作は、HTTPリクエストスムージングを使用してユーザーを外部サイトにリダイレクトするように操作できます。例えば:
この密輸されたリクエストは、次に処理されるユーザーリクエストが攻撃者が制御するウェブサイトにリダイレクトされる原因となる可能性があります:
結果は:
このシナリオでは、ユーザーのJavaScriptファイルのリクエストがハイジャックされます。攻撃者は、応答として悪意のあるJavaScriptを提供することで、ユーザーを危険にさらす可能性があります。
ウェブキャッシュポイズニングは、フロントエンドインフラストラクチャがコンテンツをキャッシュする任意のコンポーネントがある場合に実行できます。通常、これはパフォーマンスを向上させるためです。サーバーの応答を操作することで、キャッシュをポイズンすることが可能です。
以前、サーバーの応答を変更して404エラーを返す方法を観察しました(基本的な例を参照)。同様に、サーバーを騙して/static/include.js
のリクエストに対して/index.html
のコンテンツを提供させることも可能です。その結果、/static/include.js
のコンテンツはキャッシュ内で/index.html
のものに置き換えられ、ユーザーが/static/include.js
にアクセスできなくなり、サービス拒否(DoS)につながる可能性があります。
この技術は、オープンリダイレクトの脆弱性が発見された場合や、オープンリダイレクトへのオンサイトリダイレクトがある場合に特に強力になります。このような脆弱性を利用して、攻撃者の制御下にあるスクリプトで/static/include.js
のキャッシュコンテンツを置き換えることができ、実質的にすべてのクライアントに対して広範なクロスサイトスクリプティング(XSS)攻撃を可能にします。
以下は、キャッシュポイズニングとオープンリダイレクトへのオンサイトリダイレクトを組み合わせた悪用の例です。目的は、攻撃者が制御するJavaScriptコードを提供するために/static/include.js
のキャッシュコンテンツを変更することです:
埋め込まれたリクエストが /post/next?postId=3
をターゲットにしていることに注意してください。このリクエストは /post?postId=4
にリダイレクトされ、Host ヘッダーの値を利用してドメインを決定します。Host ヘッダーを変更することで、攻撃者はリクエストを自分のドメインにリダイレクトできます(オンサイトリダイレクトからオープンリダイレクト)。
成功した ソケットポイズニング の後、/static/include.js
に対する GET リクエスト を開始する必要があります。このリクエストは、以前の オンサイトリダイレクトからオープンリダイレクト リクエストによって汚染され、攻撃者が制御するスクリプトの内容を取得します。
その後、/static/include.js
に対するリクエストは、攻撃者のスクリプトのキャッシュされた内容を提供し、効果的に広範な XSS 攻撃を開始します。
ウェブキャッシュポイズニングとウェブキャッシュの欺瞞の違いは何ですか?
ウェブキャッシュポイズニング では、攻撃者がアプリケーションに悪意のあるコンテンツをキャッシュに保存させ、そのコンテンツが他のアプリケーションユーザーにキャッシュから提供されます。
ウェブキャッシュの欺瞞 では、攻撃者がアプリケーションに他のユーザーに属する機密コンテンツをキャッシュに保存させ、攻撃者がそのコンテンツをキャッシュから取得します。
攻撃者は、機密のユーザー固有のコンテンツを取得するためのスムージングリクエストを作成します。次の例を考えてみてください:
このスムグルされたリクエストが静的コンテンツ(例:/someimage.png
)用のキャッシュエントリを汚染すると、被害者の/private/messages
からの機密データが静的コンテンツのキャッシュエントリの下にキャッシュされる可能性があります。その結果、攻撃者はこれらのキャッシュされた機密データを取得できる可能性があります。
この投稿では、サーバーにTRACEメソッドが有効になっている場合、HTTPリクエストスムグリングを利用することが可能であると提案されています。これは、このメソッドがサーバーに送信された任意のヘッダーをレスポンスのボディの一部として反映するためです。例えば:
以下のようなレスポンスを送信します:
この動作を悪用する例としては、最初にHEADリクエストをスムグルことが挙げられます。このリクエストは、GETリクエストのヘッダーのみで応答されます(その中に**Content-Type
が含まれます)。そして、HEADの直後にTRACEリクエストをスムグルことで、送信されたデータを反映させることができます。
HEADの応答にはContent-Length
ヘッダーが含まれるため、TRACEリクエストの応答はHEAD応答のボディとして扱われ、したがって任意のデータを反映させることになります。
この応答は接続上の次のリクエストに送信されるため、例えばキャッシュされたJSファイルで任意のJSコードを注入するために使用される可能性があります**。
この投稿を引き続き参照することが推奨されており、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 Response Smuggling / DesyncブラウザHTTPリクエストスムーギング(クライアントサイド)
HTTP/2ダウングレードにおけるリクエストスムーギング
https://hipotermia.pw/bb/http-desync-idorから
From: https://hipotermia.pw/bb/http-desync-account-takeover
https://github.com/bahruzjabiyev/t-reqs-http-fuzzer: このツールは、奇妙なリクエストスムーギングの不一致を見つけるのに役立つ文法ベースのHTTPファズァです。
AWSハッキングを学び、練習する:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングを学び、練習する:HackTricks Training GCP Red Team Expert (GRTE)