HTTP Request Smuggling を理解する

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

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

<details><summary>この記事を読むにあたって必要な知識</summary><div>

- **Content-Length Header**
 - リクエスト or レスポンスのデータのバイト数を指定するヘッダー
 - `Content-Length: バイト数`
 - **改行（`\r\n`）もバイト数に含まれる**

以下のContent-Lengthは6

```
a
b

```

- **`Transfer-Encoding: chunked`**


送信するデータを、chunk（塊）毎に**16進数**でバイト数を記す。
最後に0のみからなる行と空行を示してレスポンスを返す。（**改行はバイト数に含まれない**）
<img width="719" alt="スクリーンショット 2019-12-16 18.11.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/253aa5fb-ffee-168b-cbff-ccdcd5457ba6.png">


- **POSTパラメータの改行**

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

<img width="1428" alt="スクリーンショット 2019-12-17 15.30.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/44ab19da-968d-0352-0d4f-b1cf651da9dc.png">

</div></details>
**これからは、下図のネットワーク構成をもとに話を進めていきます**
![コメント 2020-04-02 115524.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/a950a63b-7b24-8fc0-4723-afda04e6df05.png)


#HTTP Request Smuggling（HRS）とは
**フロントエンドサーバとバックエンドサーバでリクエストの終端の解釈が異なる場合に発生する脆弱性**


##CL.TE vulnerabilities
例えば、フロントエンドが`Content-Length(以下CL)`のみに対応していて、バックエンドが`Transfer-Encoding(以下TE)`にのみ対応していた場合のリクエストの処理の流れを見ていく。
<img width="468" alt="スクリーンショット 2019-12-16 14.03.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/b63804fb-9d1b-cb22-24cb-ed54e82cf982.png">

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

図示すると、以下のようになる。（黄色い部分がサーバが解釈する範囲）
![1111.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/fdfcc9e5-ec8e-6fe4-a16f-a202f76790e3.png)


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


##TE.CL vulnerabilities
次に、フロントエンドがTEでバックエンドがCLのパターンを以下のリクエストを例に見ていく。
![スクリーンショット 2019-12-21 16.56.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/2a234d46-5799-b769-221f-87011fa78f1f.png)
まず、フロントエンドが`Transfer-Encoding: chunked`に従い、`4〜0`まで処理してバックエンドに転送する。
バックエンドは、`Content-Length: 3`に従い、`4\r\n`まで処理するため、`HACK\r\n0\r\n`は次のリクエストに回される。

##TEの難読化
フロントエンドとバックエンドの両方が`Transfer-Encoding`をサポートしている場合に、ヘッダーを難読化することで、どちらか一方にだけTEヘッダーを処理するよう仕向けることができる。

<details><summary>難読化の例</summary><div>

```
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
```
</div></details>



##CL.TE vulnerabilitiesの見つけ方①
CL.TE vulnerabilitiesのあるアプリケーションに対して、以下のリクエストを送るとレスポンスが遅延する。
<img width="455" alt="スクリーンショット 2019-12-16 15.55.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/bcfcfb52-b30f-e54f-7ef1-a74271fcfc96.png">
なぜなら、フロントエンドが、`Content-Length: 4`に従い`1\r\nA`までを処理してバックエンドに転送する。
それを受け取ったバックエンドは、`Transfer-Encoding:chunked`に従い処理するが、`0`がないため次のchunkがくるものだと解釈し一定時間待つため遅延が発生する。

##CL.TE vulnerabilitiesの見つけ方②
CL.TE vulnerabilitiesのあるアプリケーションに対して、以下のリクエストを送ったとする。
<img width="594" alt="スクリーンショット 2019-12-16 17.47.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/291021b9-e616-a051-ec59-de40f51511eb.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/62a69d5d-ec2f-5dc2-46b7-1e25f1ae7fea.png)
バックエンドに残存していたデータがリクエストの先頭に付加されるため、結果的に以下のようなリクエストになる。
![スクリーンショット 2019-12-21 17.10.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/9a07f3d9-7a5b-463d-579b-37666054c9cf.png)
`/`にアクセスしたはずが、HRSにより、`/hack`という存在しないパスにリクエストを送っているため
`Not Found`が返ってきて、脆弱性があると判断する事ができる。


##TE.CL vulnerabilitiesの見つけ方①
TE.CL vulnerabilitiesのあるアプリケーションに対して、以下のリクエストを送るとレスポンスが遅延する。
<img width="454" alt="スクリーンショット 2019-12-16 16.11.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/a6bce101-eda1-2a45-7f54-3f843f54fce7.png">
なぜなら、フロントエンドが、`Transfer-Encoding: chunked`に従い`0`をバックエンドに転送する。
それを受け取ったバックエンドは、`Content-Length: 6`に従い、残りのデータがくるのを待つため、遅延が発生する。


##TE.CL vulnerabilitiesの見つけ方②
TE.CL vulnerabilitiesのあるアプリケーションに対して、以下のリクエストを送ったとする。
<img width="657" alt="スクリーンショット 2019-12-17 15.50.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/127c9d18-caf6-d499-c5ec-e203559ffa1e.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/73aa6307-1bda-7fc1-4fa3-bdeef453f977.png)
残存していた`GET /404 HTTP/1.1`がリクエストの先頭に付加されて結果的に以下のようなリクエストになる。（`x=`以降は全て`x`の値と見なされる）
![スクリーンショット 2019-12-21 17.25.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/99062e8a-4251-ec3c-d339-bd87218e8b89.png)
`/`にアクセスしたはずが、HRSにより、`/404`という存在しないパスにリクエストを送っているため
`Not Found`が返ってきて、HRSが成功している事がわかる。


##HRSによるフロントエンドのセキュリティバイパス
フロントエンドサーバでしかアクセス制御を実装していない場合、HRSを利用すればバイパスすることができる。
例えば、**CL.TE vulnerabilities**のあるアプリケーションで`/admin`へのアクセスがフロントエンドで禁止されていて、尚且つ、`localhost`からしかアクセスできない場合
以下のようなリクエストを送ればそれらをバイパスすることができる。
 ![スクリーンショット 2019-12-18 21.33.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/4c01c8b0-3156-92f4-fdc5-fe3057d79611.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/f05153fb-96e1-f855-c570-5b9cdc1d8349.png)



##フロントエンドによるヘッダーの付加
フロントエンドがバックエンドにリクエストを転送する際に、ヘッダーを付加することがある。（XFFなど）
そのようなアプリケーションにHRSをした時に、フロントエンドが通常付加するヘッダーがないために、バックエンドが正常にリクエストを処理できず、意図した挙動にならない事がある。
しかし、以下のように入力値をレスポンスに反映するような機能がある場合、この付加されるヘッダーをレスポンスに含める事ができる。
![スクリーンショット 2019-12-21 10.46.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/6742cd81-4e13-ea3e-7aeb-36eec90b5007.png)


このアプリケーションが**CL.TE vulnerabilities**がある場合以下のようなリクエストを送ると
![スクリーンショット 2019-12-21 10.54.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/04755082-5773-37de-51e6-f818330d715b.png)



フロントエンドが`Content-Length: 51`に従い、`0`から`search=`までを処理して**何らかのヘッダーを付加**してバックエンドに転送する。
バックエンドは、`Transfer-Encoding: chunked`に従って処理するため、`POST / HTTP/1.1`から`search=`までがバックエンドに残る。
もう一度リクエストを送ると、フロントエンドがヘッダーを付加してバックエンドに転送し、バックエンドに残存していたデータがリクエストの先頭に付加されて以下のようなリクエストになるため、ヘッダーが画面に出力される。
![スクリーンショット 2019-12-21 10.56.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/4bec42fc-624f-b578-44ea-cb4a2e010324.png)


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

例えば、**CL.TE vulnerabilities**のあるアプリケーションで
以下のように`comment`パラメータに指定した値が保存される機能がある場合
![スクリーンショット 2019-12-21 11.31.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/c3a3406b-6331-e22f-26be-bd82e8d552e7.png)
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/a2fdc8d2-686f-d347-d822-a9985f33cdff.png)


以下のようなリクエストを送ると
![スクリーンショット 2019-12-21 11.25.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/3e9289be-e925-ba52-af3b-f645e814acad.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の脅威は存在しない。<br>しかし、HRSによるXSSは、リクエストがバックエンドサーバーに残存するため、次のリクエストをした利用者端末でXSSを発火させることができるため実際に脅威となりえる。

以下のようにuserAgentが出力されるformがある場合、userAgentにXSSを入れて、HRSすることができればこのページをGETしてくる次の利用者の端末でXSSを発火させることができる。

```html:
<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によるオープンリダイレクト
以下のように、ディレクトリに対してパスの末尾に`/`をつけずにアクセスした場合

```a:Request
GET /home HTTP/1.1
Host: normal-website.com
```
サーバーはホストヘッダーのホスト名を使用して、末尾に`/`を追加して301リダイレクトを返します。

```a:Response
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`までがバックエンドに残る。

```burp: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
```
そのため、次に以下のようなリクエストを送った場合

```a:Request
GET / HTTP/1.1
Host: vulnerable-website.com
```
バックエンドに残存していたリクエストが先頭に付加されて以下のようなリクエストになるため、外部ドメインにリダイレクトされる。

```a:Request
GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET / HTTP/1.1
Host: vulnerable-website.com
```

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


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

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

<!--<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/91c65111-8b61-64be-9cf4-101a5baf2272.png" height=600 width=1000>-->
![webcachepoisoning.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/8caba161-fd4a-553e-5727-7d581aaa85ad.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
<details><summary>Web cache deceptionとは</summary><br>キャッシュサーバに、通常キャッシュしてはいけない秘密情報を含んだページをキャッシュさせて秘密情報を盗む攻撃</details>

攻撃者が①のようなリクエストを送ったとする。
![deception.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253111/2e6e0d76-0d46-704c-7065-cc5bf891d020.png)

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