はじめに
ISC2年、IPFactory所属のsh0です。
これは、portswiggerのHTTP Request Smugglingの記事を参考に自分なりの理解を書いたものです。
翻訳や解釈が間違っている場合はコメントをいただけると幸いです。
この記事を読むにあたって必要な知識
HTTP Request Smuggling(HRS)とは
フロントエンドサーバとバックエンドサーバでリクエストの終端の解釈が異なる場合に発生する脆弱性
CL.TE vulnerabilities
例えば、フロントエンドがContent-Length(以下CL)のみに対応していて、バックエンドがTransfer-Encoding(以下TE)にのみ対応していた場合のリクエストの処理の流れを見ていく。

まず、CLに対応しているフロントエンドがContent-Lengthに従い9バイト(0\r\n\r\nHACK)までを処理してバックエンドに転送する。
TEに対応しているバックエンドはTransfer-Encoding: chunkedに従い0を受け取った時点でリクエストの終了と判断するため、HACKは次のリクエストの開始として扱う。
このように、フロントエンドがCLでバックエンドがTEの形をCL.TE vulnerabilitiesと呼ぶ
図示すると、以下のようになる。(黄色い部分がサーバが解釈する範囲)

これを利用すると、攻撃者は次のユーザーのリクエストの開始時に任意のコンテンツを追加することができる。
TE.CL vulnerabilities
次に、フロントエンドがTEでバックエンドがCLのパターンを以下のリクエストを例に見ていく。

まず、フロントエンドがTransfer-Encoding: chunkedに従い、4〜0まで処理してバックエンドに転送する。
バックエンドは、Content-Length: 3に従い、4\r\nまで処理するため、HACK\r\n0\r\nは次のリクエストに回される。
TEの難読化
フロントエンドとバックエンドの両方がTransfer-Encodingをサポートしている場合に、ヘッダーを難読化することで、どちらか一方にだけTEヘッダーを処理するよう仕向けることができる。
難読化の例
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked
CL.TE vulnerabilitiesの見つけ方①
CL.TE vulnerabilitiesのあるアプリケーションに対して、以下のリクエストを送るとレスポンスが遅延する。

なぜなら、フロントエンドが、Content-Length: 4に従い1\r\nAまでを処理してバックエンドに転送する。
それを受け取ったバックエンドは、Transfer-Encoding:chunkedに従い処理するが、0がないため次のchunkがくるものだと解釈し一定時間待つため遅延が発生する。
CL.TE vulnerabilitiesの見つけ方②
CL.TE vulnerabilitiesのあるアプリケーションに対して、以下のリクエストを送ったとする。

まず、フロントエンドがContent-Length: 38に従い、0からDummy-Header:まで処理してバックエンドに転送する。
バックエンドは、Transfer-Encoding: chunkedに従い処理するため、GET /hack HTTP/1.1\r\nDummy-Header:がバックエンドに残る。
次に、以下のようなリクエストを送ると、

バックエンドに残存していたデータがリクエストの先頭に付加されるため、結果的に以下のようなリクエストになる。

/にアクセスしたはずが、HRSにより、/hackという存在しないパスにリクエストを送っているため
Not Foundが返ってきて、脆弱性があると判断する事ができる。
TE.CL vulnerabilitiesの見つけ方①
TE.CL vulnerabilitiesのあるアプリケーションに対して、以下のリクエストを送るとレスポンスが遅延する。

なぜなら、フロントエンドが、Transfer-Encoding: chunkedに従い0をバックエンドに転送する。
それを受け取ったバックエンドは、Content-Length: 6に従い、残りのデータがくるのを待つため、遅延が発生する。
TE.CL vulnerabilitiesの見つけ方②
TE.CL vulnerabilitiesのあるアプリケーションに対して、以下のリクエストを送ったとする。

まず、フロントエンドがTransfer-Encoding: chunkedに従い、7cから0\r\nまで処理してバックエンドに転送する。
バックエンドは、Content-Length: 4に従い、7c\r\nまで処理するため、GET /404 HTTP/1.1以降がバックエンドに残る。
次に、以下のようなリクエストを送ると

残存していたGET /404 HTTP/1.1がリクエストの先頭に付加されて結果的に以下のようなリクエストになる。(x=以降は全てxの値と見なされる)

/にアクセスしたはずが、HRSにより、/404という存在しないパスにリクエストを送っているため
Not Foundが返ってきて、HRSが成功している事がわかる。
HRSによるフロントエンドのセキュリティバイパス
フロントエンドサーバでしかアクセス制御を実装していない場合、HRSを利用すればバイパスすることができる。
例えば、CL.TE vulnerabilitiesのあるアプリケーションで/adminへのアクセスがフロントエンドで禁止されていて、尚且つ、localhostからしかアクセスできない場合
以下のようなリクエストを送ればそれらをバイパスすることができる。

まず、フロントエンドがContent-Length: 47に従って、0\r\nからx=までをバックエンドに転送する。
バックエンドは、Transfer-Encoding:chunkedに従って処理するため、GET /admin HTTP/1.1からx=までがバックエンドに残る。
再度同じリクエストを送ると、バックエンドに残存していたデータがリクエストの先頭に付加されて以下のようなリクエストになるため、/adminにアクセスすることができる。

フロントエンドによるヘッダーの付加
フロントエンドがバックエンドにリクエストを転送する際に、ヘッダーを付加することがある。(XFFなど)
そのようなアプリケーションにHRSをした時に、フロントエンドが通常付加するヘッダーがないために、バックエンドが正常にリクエストを処理できず、意図した挙動にならない事がある。
しかし、以下のように入力値をレスポンスに反映するような機能がある場合、この付加されるヘッダーをレスポンスに含める事ができる。

このアプリケーションがCL.TE vulnerabilitiesがある場合以下のようなリクエストを送ると

フロントエンドがContent-Length: 51に従い、0からsearch=までを処理して何らかのヘッダーを付加してバックエンドに転送する。
バックエンドは、Transfer-Encoding: chunkedに従って処理するため、POST / HTTP/1.1からsearch=までがバックエンドに残る。
もう一度リクエストを送ると、フロントエンドがヘッダーを付加してバックエンドに転送し、バックエンドに残存していたデータがリクエストの先頭に付加されて以下のようなリクエストになるため、ヘッダーが画面に出力される。

他のユーザーのリクエストを取得する
掲示板のような、ユーザの入力値を保存する事ができる機能がある場合、他のユーザーのリクエストを取得する事ができる。
例えば、CL.TE vulnerabilitiesのあるアプリケーションで
以下のようにcommentパラメータに指定した値が保存される機能がある場合


以下のようなリクエストを送ると

フロントエンドが、Content-Length: 284に従い0からcomment=までをバックエンドに転送する。
バックエンドは、Transfer-Encoding: chunkedにより、POST /post/comment HTTP/1.1からcomment=までがバックエンドに残る。
この状態で、他のユーザーが何らかのリクエストを送った場合、先ほどのリクエストのcommentパラメータの値として解釈されるため、他のユーザーのリクエストを取得する事ができる。
HRSによるXSS
HRSによるXSSは以下の点で一般的な反射型XSSより優れている
- XSSを含んだリンクを第三者に踏ませる必要がない
- 一般的にヘッダー関連のXSSは、罠ページを経由して利用者のブラウザからXSS入りのヘッダを送信することはできないため、XSSの脅威は存在しない。
しかし、HRSによるXSSは、リクエストがバックエンドサーバーに残存するため、次のリクエストをした利用者端末でXSSを発火させることができるため実際に脅威となりえる。
以下のようにuserAgentが出力されるformがある場合、userAgentにXSSを入れて、HRSすることができればこのページをGETしてくる次の利用者の端末でXSSを発火させることができる。
<form action="/" method="POST">
<input type="hidden" name="userAgent" value="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0">
<input type="text" name="data">
<input type="submit" value="送信する">
</form>
HRSによるオープンリダイレクト
以下のように、ディレクトリに対してパスの末尾に/をつけずにアクセスした場合
GET /home HTTP/1.1
Host: normal-website.com
サーバーはホストヘッダーのホスト名を使用して、末尾に/を追加して301リダイレクトを返します。
HTTP/1.1 301 Moved Permanently
Location: https://normal-website.com/home/
この挙動自体は普通だが、HRSができる場合これをオープンリダイレクトに昇華させることができる。
WebサイトがCL.TE vulnerabilitiesがある場合以下のリクエストを送ると、フロントエンドがContent-Length: 59に従い0\r\n~Foo: Xまでを処理してバックエンドに転送する。
それを受け取ったバックエンドはTransfer-Encoding: chunkedによりGET /home HTTP/1.1~Foo: Xまでがバックエンドに残る。
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 59
Transfer-Encoding: chunked
0
GET /home HTTP/1.1
Host: attacker-website.com
Foo: X
そのため、次に以下のようなリクエストを送った場合
GET / HTTP/1.1
Host: vulnerable-website.com
バックエンドに残存していたリクエストが先頭に付加されて以下のようなリクエストになるため、外部ドメインにリダイレクトされる。
GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET / HTTP/1.1
Host: vulnerable-website.com
HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/
HRSによるWeb cache poisoning
Web cache poisoningとは
悪意のあるコンテンツをキャッシュサーバにキャッシュさせる攻撃
攻撃者がCL.TE vulnerabilitiesのあるサイトに対して、①のようなリクエストを送ったとする。
CLに対応しているフロントエンドは、黄色い部分だけを解釈してバックエンドに送信するため、緑色の部分が次のリクエストの先頭に付加される
TEに対応しているバックエンドは、黄色い部分だけを解釈するため、水色の部分は次のリクエストの先頭に付加される
次に、被害者が通常のリクエスト⑤を送ると、フロントエンドに残存していたリクエストが付加されてGET /static/include.jsへのリクエストになる。
バックエンドでも、残存していたリクエストが先頭に付加されて、GET /home/へのリクエストになる。
そのため、レスポンスは、301 Moved Permanentlyになる。
さらに、フロントエンドでは、GET /static/include.jsのレスポンスが、301 Moved Permanentlyという情報がキャッシュされるため
GET /static/include.jsにアクセスした利用者は攻撃者のサイトにリダイレクトさせらる。
HRSによるWeb cache deception
Web cache deceptionとは
キャッシュサーバに、通常キャッシュしてはいけない秘密情報を含んだページをキャッシュさせて秘密情報を盗む攻撃
CLに対応しているフロントエンドは、黄色い部分を解釈してバックエンドに送信する。
TEに対応しているバックエンドは、黄色い部分だけを解釈するため、水色の部分は次のリクエストの先頭に付加される
次に、被害者が通常のリクエストを⑤を送ると、フロントエンドで処理されてバックエンドに送信する
バックエンドでは、残存していたリクエストが先頭に付加されてGET /private/messagesへのリクエストになる。
Cookieが被害者のものであるため、被害者のプライベートメッセージがレスポンスとして返ってくる。
フロントエンドは、GET /static/test.pngのレスポンスとして被害者のプライベートメッセージをキャッシュするためWeb cache deception攻撃が成立する。
参考資料
- https://portswigger.net/web-security/request-smuggling
- https://portswigger.net/research/practical-web-cache-poisoning
- https://portswigger.net/web-security/request-smuggling/finding
- https://portswigger.net/web-security/request-smuggling/exploiting
- https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn



