Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
45
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

@Howtoplay

HTTP Request Smuggling を理解する

はじめに

ISC2年、IPFactory所属のsh0です。
これは、portswiggerのHTTP Request Smugglingの記事を参考に自分なりの理解を書いたものです。

翻訳や解釈が間違っている場合はコメントをいただけると幸いです。

この記事を読むにあたって必要な知識
  • Content-Length Header
    • リクエスト or レスポンスのデータのバイト数を指定するヘッダー
    • Content-Length: バイト数
    • 改行(\r\n)もバイト数に含まれる

以下のContent-Lengthは6

a
b

  • Transfer-Encoding: chunked

送信するデータを、chunk(塊)毎に16進数でバイト数を記す。
最後に0のみからなる行と空行を示してレスポンスを返す。(改行はバイト数に含まれない
スクリーンショット 2019-12-16 18.11.38.png

  • POSTパラメータの改行

以下のように改行してもパラメータxの値と見なされる。

スクリーンショット 2019-12-17 15.30.38.png


これからは、下図のネットワーク構成をもとに話を進めていきます
コメント 2020-04-02 115524.png

HTTP Request Smuggling(HRS)とは

フロントエンドサーバとバックエンドサーバでリクエストの終端の解釈が異なる場合に発生する脆弱性

CL.TE vulnerabilities

例えば、フロントエンドがContent-Length(以下CL)のみに対応していて、バックエンドがTransfer-Encoding(以下TE)にのみ対応していた場合のリクエストの処理の流れを見ていく。
スクリーンショット 2019-12-16 14.03.09.png

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

図示すると、以下のようになる。(黄色い部分がサーバが解釈する範囲)
1111.png

これを利用すると、攻撃者は次のユーザーのリクエストの開始時に任意のコンテンツを追加することができる。

TE.CL vulnerabilities

次に、フロントエンドがTEでバックエンドがCLのパターンを以下のリクエストを例に見ていく。
スクリーンショット 2019-12-21 16.56.23.png
まず、フロントエンドが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のあるアプリケーションに対して、以下のリクエストを送るとレスポンスが遅延する。
スクリーンショット 2019-12-16 15.55.59.png
なぜなら、フロントエンドが、Content-Length: 4に従い1\r\nAまでを処理してバックエンドに転送する。
それを受け取ったバックエンドは、Transfer-Encoding:chunkedに従い処理するが、0がないため次のchunkがくるものだと解釈し一定時間待つため遅延が発生する。

CL.TE vulnerabilitiesの見つけ方②

CL.TE vulnerabilitiesのあるアプリケーションに対して、以下のリクエストを送ったとする。
スクリーンショット 2019-12-16 17.47.18.png
まず、フロントエンドがContent-Length: 38に従い、0からDummy-Header:まで処理してバックエンドに転送する。
バックエンドは、Transfer-Encoding: chunkedに従い処理するため、GET /hack HTTP/1.1\r\nDummy-Header:がバックエンドに残る。
次に、以下のようなリクエストを送ると、
スクリーンショット 2019-12-21 17.10.45.png
バックエンドに残存していたデータがリクエストの先頭に付加されるため、結果的に以下のようなリクエストになる。
スクリーンショット 2019-12-21 17.10.36.png
/にアクセスしたはずが、HRSにより、/hackという存在しないパスにリクエストを送っているため
Not Foundが返ってきて、脆弱性があると判断する事ができる。

TE.CL vulnerabilitiesの見つけ方①

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

TE.CL vulnerabilitiesの見つけ方②

TE.CL vulnerabilitiesのあるアプリケーションに対して、以下のリクエストを送ったとする。
スクリーンショット 2019-12-17 15.50.15.png
まず、フロントエンドがTransfer-Encoding: chunkedに従い、7cから0\r\nまで処理してバックエンドに転送する。
バックエンドは、Content-Length: 4に従い、7c\r\nまで処理するため、GET /404 HTTP/1.1以降がバックエンドに残る。
次に、以下のようなリクエストを送ると
スクリーンショット 2019-12-21 17.24.17.png
残存していたGET /404 HTTP/1.1がリクエストの先頭に付加されて結果的に以下のようなリクエストになる。(x=以降は全てxの値と見なされる)
スクリーンショット 2019-12-21 17.25.53.png
/にアクセスしたはずが、HRSにより、/404という存在しないパスにリクエストを送っているため
Not Foundが返ってきて、HRSが成功している事がわかる。

HRSによるフロントエンドのセキュリティバイパス

フロントエンドサーバでしかアクセス制御を実装していない場合、HRSを利用すればバイパスすることができる。
例えば、CL.TE vulnerabilitiesのあるアプリケーションで/adminへのアクセスがフロントエンドで禁止されていて、尚且つ、localhostからしかアクセスできない場合
以下のようなリクエストを送ればそれらをバイパスすることができる。
スクリーンショット 2019-12-18 21.33.38.png
まず、フロントエンドがContent-Length: 47に従って、0\r\nからx=までをバックエンドに転送する。
バックエンドは、Transfer-Encoding:chunkedに従って処理するため、GET /admin HTTP/1.1からx=までがバックエンドに残る。
再度同じリクエストを送ると、バックエンドに残存していたデータがリクエストの先頭に付加されて以下のようなリクエストになるため、/adminにアクセスすることができる。
スクリーンショット 2019-12-18 21.39.52.png

フロントエンドによるヘッダーの付加

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

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

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

他のユーザーのリクエストを取得する

掲示板のような、ユーザの入力値を保存する事ができる機能がある場合、他のユーザーのリクエストを取得する事ができる。

例えば、CL.TE vulnerabilitiesのあるアプリケーションで
以下のようにcommentパラメータに指定した値が保存される機能がある場合
スクリーンショット 2019-12-21 11.31.03.png
3.png

以下のようなリクエストを送ると
スクリーンショット 2019-12-21 11.25.04.png
フロントエンドが、Content-Length: 284に従い0からcomment=までをバックエンドに転送する。
バックエンドは、Transfer-Encoding: chunkedにより、POST /post/comment HTTP/1.1からcomment=までがバックエンドに残る。
この状態で、他のユーザーが何らかのリクエストを送った場合、先ほどのリクエストのcommentパラメータの値として解釈されるため、他のユーザーのリクエストを取得する事ができる。

HRSによるXSS

HRSによるXSSは以下の点で一般的な反射型XSSより優れている
1. XSSを含んだリンクを第三者に踏ませる必要がない
2. 一般的にヘッダー関連の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によるオープンリダイレクト

以下のように、ディレクトリに対してパスの末尾に/をつけずにアクセスした場合

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

サーバーはホストヘッダーのホスト名を使用して、末尾に/を追加して301リダイレクトを返します。

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

この挙動自体は普通だが、HRSができる場合これをオープンリダイレクトに昇華させることができる。

WebサイトがCL.TE vulnerabilitiesがある場合以下のリクエストを送ると、フロントエンドがContent-Length: 59に従い0\r\nFoo: Xまでを処理してバックエンドに転送する。
それを受け取ったバックエンドはTransfer-Encoding: chunkedによりGET /home HTTP/1.1Foo: Xまでがバックエンドに残る。

Invalid-request
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

そのため、次に以下のようなリクエストを送った場合

Request
GET / HTTP/1.1
Host: vulnerable-website.com

バックエンドに残存していたリクエストが先頭に付加されて以下のようなリクエストになるため、外部ドメインにリダイレクトされる。

Request
GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET / HTTP/1.1
Host: vulnerable-website.com
Response
HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/

HRSによるWeb cache poisoning

Web cache poisoningとは
悪意のあるコンテンツをキャッシュサーバにキャッシュさせる攻撃

前述のHRSによるオープンリダイレクトにおいて
フロントエンドがキャッシュ機能を備えている場合、HRSで悪意のあるコンテンツをキャッシュさせることによって、それ以降にそのURLを要求したユーザーは、攻撃者のWebサイトへリダイレクトさせることができる。

攻撃者がCL.TE vulnerabilitiesのあるサイトに対して、①のようなリクエストを送ったとする。

webcachepoisoning.png

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とは
キャッシュサーバに、通常キャッシュしてはいけない秘密情報を含んだページをキャッシュさせて秘密情報を盗む攻撃

攻撃者が①のようなリクエストを送ったとする。
deception.png

CLに対応しているフロントエンドは、黄色い部分を解釈してバックエンドに送信する。
TEに対応しているバックエンドは、黄色い部分だけを解釈するため、水色の部分は次のリクエストの先頭に付加される

次に、被害者が通常のリクエストを⑤を送ると、フロントエンドで処理されてバックエンドに送信する
バックエンドでは、残存していたリクエストが先頭に付加されてGET /private/messagesへのリクエストになる。
Cookieが被害者のものであるため、被害者のプライベートメッセージがレスポンスとして返ってくる。
フロントエンドは、GET /static/test.pngのレスポンスとして被害者のプライベートメッセージをキャッシュするためWeb cache deception攻撃が成立する。

参考資料

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
45
Help us understand the problem. What are the problem?