勝手に補足:レガシーPHPのセキュリティ対策,大丈夫ですか?未修正の脆弱性(2)

 第4回 未修正の脆弱性(2):レガシーPHPのセキュリティ対策,大丈夫ですか?|gihyo.jp … 技術評論社という記事を読みました。とても重要なことが記載されている一方で、記述の間違い及び記述もれがあるため、読者の便宜のため勝手に補足したいと思います。具体的に、PHP4系の最終版PHP4.4.9にてパッチの提供されない4つのぜい弱性についての話題です。一部のぜい弱性はPHP5.2系でもパッチが出る見込みがないものです。

CGIDoS

 元記事ではCVE-2009-3660として紹介されていますが、CVEおよびNVDのサイトを見ると、Efront 3.5.4以前の脆弱性とあります。明らかに該当しないので調べてみると、CVE-2008-3660の間違いのようですね。2008を2009とTYPOしたのでしょう。
 脆弱性の中味ですが、元記事に以下のように書かれています。

この脆弱性は不正なパスを指定することにより簡単にCGIバイナリをクラッシュしてしまう脆弱性です。

http://gihyo.jp/dev/serial/01/legacy-php/0004

 ちょっと欲求不満の残る書き方ですね。「不正なパス」とはなんでしょうか。NVDのサイトを見ると以下のように説明されています。

PHP 4.4.x before 4.4.9, and 5.x through 5.2.6, when used as a FastCGI module, allows remote attackers to cause a denial of service (crash) via a request with multiple dots preceding the extension, as demonstrated using foo..php.

http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2008-3660

 太字で示したように、foo..phpのように複数のドットを持ったファイル名があると、PHPがクラッシュするようです。
 なお、NVDの上記説明では、PHP4.4.9より前となっていますが、実際にはPHP4.4.9も該当するようです(大垣さんの記事の方が正しい)。
 アプリケーションに処理が渡る前にPHPがクラッシュしてしまうので、対策はアプリケーションの外側で行う必要があります。以下のいずれかによる対策が考えられます。

 元記事はmod_rewriteによる対策を紹介しています。

パッチを適用せずにクラッシュを回避するには,簡易なものでもよいのでWAFを導入します。簡易なWAFとはApachemod_rewriteを利用したプロキシをWAFとするなどの方法があります。

http://gihyo.jp/dev/serial/01/legacy-php/0004

 これはちょっと分かりにくいですね。NVDの説明にあるように、複数のドットがあるパス名が問題なので、このようなパスに対するmod_rewriteのルールを記述して、エラーページを表示する方法が考えられます。

x86 FPUクラッシュ

 この項はCVE識別番号が抜けています。私は最初、どのCVEに該当するのか探せなかったのですが、おそらくCVE-2010-4645(CVENVD)ですね。特定の数値を浮動小数点数に変換すると無限ループになるというもので、PHP以外のJavaRubyでも同種の問題がありました。PHP5.3.5以降、PHP5.2.17では対処されています。
 PHP4.4.9(Windows版)にて以下が無限ループになることを確認しました。

<?php
  $a = "2.2250738585072011e-308";
  $b = (double)$a;
?>

 元記事の対策は以下の通り。

入力値をバリデーションしてクラッシュを防ぐことが考えられます。

http://gihyo.jp/dev/serial/01/legacy-php/0004

 確かにそうなのですが、どうバリデーションすればよいかは書いていません。おそらくこの連載は、PHP4セキュリティ保守サービスという有償サービスの宣伝という意味があるようで、肝心なことは書いていないのかもしれません。そこで、勝手に回避策を紹介しましょう。
 アプリケーションでこの問題を回避するためには、攻撃数値文字列を *浮動小数点数に変換する前に* チェックする必要があります。これは少々やっかいです。指数形式が必要ないのであれば、以下のような正規表現でバリデーションする方法が考えられます。

if (preg_match('/\A-?[0-9]+(\.[0-9]+)?\z/', $a) == 0) {
  die('数値形式が不正です');
}

 厳密には、これだけではダメです。以下のように、指数形式を用いないPoCも存在するからです。

$a = '0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072011';

 小数点以下、0が307個並びます。このようなケースもあるので、全体の桁数を適当に制限するとよいでしょう。

exifのクラッシュ

 記事にあるように、PHP5.3.5以前に存在したCVE-2011-0708です(CVENVD)。すなわち、PHPの最新版5.3.6で修正されたばかりということになります。PHP5.2では修正されていません。
 ただし、64ビット環境でのみ脆弱となるようです。CVE、NVDにも明記されていますが、日本語の方が読みやすいので、ここはJVNから引用しましょう。

64-bit プラットフォーム上で稼働している PHPExif 拡張モジュール内にある exif.c は、不正な形式のキャストを処理してしまうため、サービス運用妨害 (DoS) 状態となる脆弱性が存在します。

http://jvndb.jvn.jp/ja/contents/2011/JVNDB-2011-001389.html

 結局このぜい弱性の影響を受けるのは、以下の全てを満たす場合と思われます。

  • PHP5.3.5以前(現時点の最新版5.3.6以外すべて)
  • PHPの64ビット版を使用している
  • exif_read_data()により、外部からアップロードされた画像を処理している

 従って、PHP4.4.9の32ビット版を使っている環境では、このぜい弱性は対処しなくて良いでしょう。仮に、上の全てに該当する場合は、exif_read_data()による処理を別の関数などに書き換えることで対策します。
 なお、Exploit Databaseには、ぜい弱性の条件が以下のように記述されています。

Using the following configuration, a system is most likely vulnerable:
(a) PHP 64bit version
(b) PHP compiled with --enable-exif
(c) memory_limit = -1

http://www.exploit-db.com/exploits/16261/

 このため、memory_limitを必要最小限の値にする(-1は無制限を意味する)ことで、保険的な対策になるかもしれません。ただし、私はこの点については未検証です。memory_limitを必要最小限にすることはDoS耐性を高める一般的な対策となるので、exifを使ってない場合でも、制限しておくと良いでしょう。

shmopの不正メモリ参照

 これもCVE識別番号が書いてありません。おそらくCVE-2011-1092だと思われます(CVENVD)。shmop_read()関数の整数オーバーフローの脆弱性ですね。PHP5.3.6(現時点の最新版)で修正されたものなので、レガシーPHPに限らず、相当のPHP記述のサイトが対象になります。これもPHP5.2では対策される見込みがありません。
 大垣さんは、この脆弱性を結構重く見ているようですが、私はそうは思いません。その理由は以下の通りです。

  • リモートから脆弱性を悪用されるシナリオが想定しにくい
  • ローカルの攻撃の場合でも権限昇格が起きるわけではない

 Redhatのレポートの末尾には以下のようなコメントがあります。私はこのコメントに同意します。

Statement:

Red Hat does not consider this to be a security issue. Input passed to these functions should be under the full control of the script author, thus no trust boundary is crossed. Additionally, an administrator would have to disable, or excessively increase the memory_limit settings in the PHP configuration file to trigger this bug.

https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2011-1092

 Expolitとしては以下が公表されています。

01	<?php
02	# Exploit Title: PHP <=5.3.5 Integer Overflow DoS
03	# Date: 12-03-11
04	# Author: Jose Carlos Norte - www.rooibo.com
05	# Software Link: www.php.net
06	# Version: <= 5.3.5
07	# Tested on: Ubuntu Linux
08	# CVE : CVE-2011-1092
09	 
10	$shm_key = ftok(__FILE__, 't');
11	$shm_id = shmop_open($shm_key, "c", 0644, 100);
12	$shm_data = shmop_read($shm_id, 1, 2147483647);
13	//if there is no segmentation fault past this point, we have 2gb of memory!
14	//or we are in a patched php
15	echo "this php version is not vulnerable!";
16	 
17	?>

 shmop_read関数の第3数に2147483647(32ビット符号付き整数の最大数)が指定されています。このように、非常に大きな値をshmop_read関数の第3引数に指定する必要がありますが、そのような状況がリモートから可能になることは、あまりないと考えられます。
 一方、ローカルからの攻撃については、権限昇格を伴わないと思われるので、攻撃の動機がありません。
 対策については、shmopを使っていないのであれば、元記事の回避策が有効です。

shmopモジュールをロードしない。shmop関数をdisable_functionで指定する。

http://gihyo.jp/dev/serial/01/legacy-php/0004

 もしもshmopを使っている場合は、shmop_read関数の使用状況を調べ、万一外部から第3引数をコントロールできる場合は、その設計を見直すか、第3引数の範囲チェックを行います。

[PR]