2012年11月24日土曜日

セキュリティ情報:PHP5.4.8、PHP5.3.18以前にhashdos脆弱性

PHP5.4.8以前、PHP5.3.18以前には、mbstring.encoding_translationが有効になっている場合にhashdos攻撃に脆弱であることが分かりましたので報告します。

一昨日、PHP5.4.9とPHP5.3.19が公開されましたが、changelogを読んで驚きました。
Mbstring:
    Fixed bug #63447 (max_input_vars doesn't filter variables when mbstring.encoding_translation = On).
http://www.php.net/ChangeLog-5.php#5.4.9より引用
mbstring.encoding_translationが有効になっている場合、max_input_varsが有効にならないというのです。これは、hashdos脆弱性の対策が無効になることを意味します。

これは大変だということで、PHP5.4.8とPHP5.4.9で検証してみました。その結果を以下に示します。
mbstring.encoding_translation
Off On
PHP5.4.8 OK NG
PHP5.4.9 OK OK

ご覧のように、確かにPHP5.4.8で、かつmbstring.encoding_translationが有効の場合、max_input_varsのチェックが無効です。これは、hashdos攻撃を受けることを意味しますので、試しにやってみました。多数のhashdosリクエストをPOSTした状態の top コマンドの表示を下記に示します。MaxClientsは50です。


50個のapache2プロセスがすべてhashdosリクエストの処理に占有されています。このリクエスト、1プロセスのみで動作させた場合 5~6分かかるので、50プロセス並行だと4~5時間占有されることになります。この間、新たなリクエストを受け付けることができなくなります。

検証はPHP5.4.8以外にPHP5.4.0でも実施しましたが、PHP5.4.8と同じ結果となりました。おそらく、PHP5.4.8以前のすべてのバージョンでこの問題があると予想されます。

CentOSやUbuntuのPHPパッケージは問題ない

CentOSやUbuntuのyumやapt-getでPHPパッケージを導入した環境でも調べてみましたが、不思議なことに、この問題は再現しませんでした。原因は不明ですが、これらのPHPパッケージでは、max_input_varsの確認方法として、オリジナルとは別の実装が採用されている可能性があります。
調査したディストリビューションとPHPパッケージのバージョン、確認結果を下表に示します。少し古いバージョンですがご了承下さい。個別の判定については、後述のチェック用スクリプトをご活用下さい。

LinuxディストリビューションPHPパッケージバージョン結果
CentOS release 5.5php-5.1.6-27.el5_7.5OK
CentOS release 6.2php-5.3.3-3.el6_2.6.i686OK
Ubuntu 10.04.1 LTS5.3.2-1ubuntu4.14OK
Ubuntu 12.04 LTS5.3.10-1ubuntu3OK

チェック用スクリプト

hashdosの状態を確認するためのスクリプトを作成してみましたので、Webサイトのチェックにご活用下さい。 このスクリプトを任意のファイル名(PHPスクリプトとして実行できる拡張子)でチェック対象Webサーバーに保存して下さい。Webブラウザを用いて、このスクリプトを実行すると、結果が表示されます。JavaScriptを有効にして下さい。チェック終了後はスクリプトを削除して下さい。
<?php
  if (@$_GET['mode'] === 'check') {
    header('Content-Type: text/plain');
    echo (int)count($_POST);
    exit;
  }
  $max_input_vars = ini_get('max_input_vars');
  $postnumber = (int)$max_input_vars + 10;
  if ($max_input_vars === false) {
    $max_input_vars = 'undefined';
  }
?>
<html>
<head>
<title>PHP hashdos checker</title>
</head>
<body>
PHP version : <?php echo htmlspecialchars(phpversion()); ?><br>
max_input_vars : <?php echo htmlspecialchars($max_input_vars); ?><br>
mbstring.encoding_translation : <?php
   echo htmlspecialchars(ini_get('mbstring.encoding_translation')); ?><br>
<div id='result'></div>
<div id='judgment'></div>
<script>
var n = <?php echo (int)$postnumber; ?>;
var data = '';
for (var i = 1; i <= n; i++) {
  data += i + '=&';
}
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
  if (req.readyState == 4 && req.status == 200) {
    var count = req.responseText;
    document.getElementById('result').appendChild(
      document.createTextNode('Number of POST parameters : ' + count));
    if (count < n) {
      judgment = 'This web server is NOT vulnerable to hashdos.';
    } else {
      judgment = 'This web server is VULNERABLE to hashdos.';
    }
    document.getElementById('judgment').appendChild(document.createTextNode(judgment));
  }
}
req.open('POST', '?mode=check' );
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
req.send(data);
</script>
</body>
</html>

いくつか、動作例を下記に示します。

環境結果
PHP5.4.8PHP version : 5.4.8
max_input_vars : 1000
mbstring.encoding_translation : 1
Number of POST parameters : 1010
This web server is VULNERABLE to hashdos.
PHP5.4.9
(mbstring.encoding_translation = On)
PHP version : 5.4.9
max_input_vars : 1000
mbstring.encoding_translation : 1
Number of POST parameters : 0
This web server is NOT vulnerable to hashdos.
PHP5.4.9
(mbstring.encoding_translation = Off)
PHP version : 5.4.9
max_input_vars : 1000
mbstring.encoding_translation :
Number of POST parameters : 1000
This web server is NOT vulnerable to hashdos.
Ubuntu 12.04 LTS
5.3.10-1ubuntu3
PHP version : 5.3.10-1ubuntu3
max_input_vars : 1000
mbstring.encoding_translation : 1
Number of POST parameters : 1001
This web server is NOT vulnerable to hashdos.

上記から、興味深いことが分かります。
PHP5.4.9では、mbstring.encoding_translationがOnの時とOffの時で、防御のための挙動が変わります。Offの場合は、変数の数が max_input_varsまで切り詰められます。Onの場合は、POST変数がすべて削除されます。
また、Ubuntu12.04のPHPパッケージを導入している場合、mbstring.encoding_translationに関わりなく(上記には出ていませんが)、max_input_vars + 1 に変数の数が切り詰められます。私の調べた範囲では、LinuxディストリビューションのPHPパッケージはすべてこうなっていて、「LinuxディストリビューションのPHPパッケージはmax_input_varsのチェック方法が異なるのではないか」と思った根拠はこれです。

対策

再現条件がはっきりしないため、上記チェックスクリプトで、脆弱性の確認をお勧めします。対策が必要な場合は、下記の手段があります。
  • PHPの最新版を導入する(本稿執筆時点でPHP5.4.9、またはPHP5.3.19)
  • mbstring.encoding_translation をOffに設定する(可能な場合)
  • hashdosの回避策を実装する
  • hashdosのリスクを受容する
hashdosの回避策については、下記エントリを参考にしてください。
現在分かっているところでは、hashdosのリスクは、攻撃を受けている最中Webサイトがリクエストに応じられなくなることです。情報漏洩や改ざんはありません。最悪、数時間~数日間サイトが使えなくなっても良いなら、リスクを受容するという選択肢もあるでしょう。


免責

このセキュリティ情報は予告なしに改訂される場合があります。このセキュリティ情報を適用した結果について徳丸浩およびHASHコンサルティング株式会社は一切の責任を負わず、利用者の利益のために、あるがままの状態で公開するものとします。

PR

HASHコンサルティング株式会社では、Webサイトを安全に守るためのセキュリティサービスを提供しています。WAF(Web Application Firewall)による効果的な脆弱性対策(hashdosを含む)や、リスクの評価、対策方法の策定、セキュリティの教育などを提供します。詳しくはお気軽にお問い合わせ下さい

フォロワー

ブログ アーカイブ