MOPB-32-2007:PHP 4.4.5/4.4.6 session_decode() Double Free Vulnerability

(Last Updated On: )

Stefanさんの承諾を得て日本語訳を公開しています。このブログの「the Month of PHP Bugs」カテゴリでMOPBの翻訳ページを一覧できます。分かりやすいように意訳できる部分は意訳します。厳密に原文の通り訳していないので正確性を重視される方は原文をご覧ください。

■クレジット
発見者:Stefan Esser
攻撃コード:Stefan Esser

■PoCまたは攻撃コード
MOPB-32-2007.php
http://www.php-security.org/MOPB/code/MOPB-32-2007.php

■リファレンス
MOPB-31-2007

■サマリ
MOPB-31-2007で紹介したセキュリティ問題がPHP開発者によって修正が試みられた際、最初の2回の試みは修正にはならず我々が紹介した攻撃経路(exploit paths)を防いだだけで、他の攻撃経路は利用可能でした。

正しい修正が考案されている間に、間違った修正がPHP4にバックポートされ、標準のPHPアンシリアライザにダブルフリー脆弱性が導入されてしまいました。この脆弱性はセッションデータを改ざんすることにより任意コードの実行を可能としています。

■影響するバージョン
PHP 4.4.5 ~ 4.4.6

■詳細情報
標準のPHPアンシリアライザに追加された防御策の一つは、変数が$GLOBALS配列を上書きせず、セッションデータは$_SESSION配列に保存されることを確実に行えるよう以下のコードを追加することでした。

namelen = q – p;
name = estrndup(p, namelen);
q++;

if (zend_hash_find(&EG(symbol_table), name, namelen + 1, (void **) &tmp) == SUCCESS) {
if ((Z_TYPE_PP(tmp) == IS_ARRAY && Z_ARRVAL_PP(tmp) == &EG(symbol_table))
|| *tmp == PS(http_session_vars)) {
efree(name);
goto skip;
}
}

if (has_value) {
ALLOC_INIT_ZVAL(current);
if (php_var_unserialize(&current, (const unsigned char **)&q, endptr, &var_ha…)) {
php_set_session_var(name, namelen, current, &var_hash TSRMLS_CC);
}
zval_ptr_dtor(&current);
}
PS_ADD_VARL(name, namelen);
skip:
efree(name);

残念ながらこのコードははじめに変数nameを解放し、そのしてskip:ラベルにジャンプしname変数をさらに開放しています。PHP5では似たようなコードを使っていますが変数は一度しか解放されません。

Zendメモリマネージャの小さなブロックのキャッシュ機能により2つのキャッシュが同じアドレスを持つことになります。これはブロックサイズが同じとなる次の2つのメモリ確保が同じメモリ領域となることを意味します。例えば、この問題は文字列と配列を同じメモリ領域に割り当て、Zendハッシュテーブルを文字列により直接改ざん可能にします。これをPoCでデモンストレーションしています。

■PoC、攻撃コードまたは再現手順
添付のPoCはPHP文字列変数とPHP配列変数を同じメモリ位置に配置するためにこの脆弱性を利用しています。PHP文字列変数を操作し、攻撃コードは配列のデストラクタポインタが0X88776655のコードを実行するように書き換えています。実際の攻撃ではこのオフセットはシェルコードへのオフセットでなければなりません。

$ gdb ./php-4.4.5-basic
(gdb) run MOPB-32-2007.php
Starting program: ./php-4.4.5-basic MOPB-32-2007.php

Program received signal SIGSEGV, Segmentation fault.
0x88776655 in ?? ()
(gdb) bt
#0 0x88776655 in ?? ()
#1 0x0813f546 in zend_hash_destroy (ht=0x81d21dc) at Zend/zend_hash.c:558
#2 0x0813a391 in _zval_dtor (zvalue=0x81d22fc) at Zend/zend_variables.c:51
#3 0x081321aa in _zval_ptr_dtor (zval_ptr=0x81d22c0) at Zend/zend_execute_API.c:289
#4 0x08140894 in zend_hash_del_key_or_index (ht=0x81a0b2c, arKey=0x81d73c4 ‘_’ , “a”,
nKeyLength=19, h=2758057915, flag=0) at Zend/zend_hash.c:529
#5 0x0814d753 in execute (op_array=0x81d1f8c) at Zend/zend_execute.c:2323
#6 0x0813b9ec in zend_execute_scripts (type=8, retval=0x0, file_count=3) at Zend/zend.c:935
#7 0x08110040 in php_execute_script (primary_file=0xbfe01784) at main/main.c:1757
#8 0x08157fe8 in main (argc=2, argv=0xbfe01824) at sapi/cli/php_cli.c:838
(gdb) i r $eip
eip 0x88776655 0x88776655
(gdb)

■備考
これが間違ったセキュリティ修正のバックポートがさらに危険なセキュリティホールを導入してしまう非常に良い例です。

全てのセッションデータ攻撃と同じように悪意のあるセッションデータを利用した攻撃は、PHPアプリケーションやPHPモジュールの脆弱性によりリモートから攻撃可能であることを理解することが重要です。例えば、HPHP-05-2006のような脆弱性がZend Platformには存在しています。

(訳注:「それ、リモートから攻撃できないでしょ」と間違った突っ込みを入れる開発者がいるのでわざわざ書いていると思われます。最近の攻撃方法の傾向からも一つの脆弱性だけを利用して攻撃するより、複数の脆弱性を利用した攻撃が一般化しているのは明らかです)

投稿者: yohgaki