MOPB-04-2007:PHP 4 unserialize() ZVAL Reference Counter Overflow

(Last Updated On: 2018年8月16日)

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

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

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

■リファレンス
MOPB-01-2007
http://www.php-security.org/MOPB/MOPB-01-2007.html

■サマリ
「The Month of PHP Bugs」はPHP4の16ビットリファレンスカウンタの攻撃方法の一つから始まりました。紹介した攻撃方法はローカルアクセスが必要でした。しかし、PHPはこれらのオーバーフローから防御を全く行っていないので、別のアタックベクタ(攻撃経路)も存在します。unserialize関数を用いるとリモートからこの攻撃を利用でき、phpBB2など、広く利用されている多くのPHPアプリケーションが現在でもunserialize関数をユーザが送信したデータに利用しています。

■影響するバージョン
PHP 4.4.4以下

PHP開発者はこの重要なセキュリティバグフィックス情報をPHP4.4.5リリース情報に記載する事を忘れています。

■詳細情報
一般的な16ビットのリファレンスカウンタ問題はMOPB-01-2007で、どのようにオーバーフローが発生しそれが任意コード実行を可能にするか解説済みです。

ここではシリアライズされたPHP変数をunserialize関数でアンシリアライズを行うことによりこの脆弱性がリモートから攻撃可能であることを解説します。PHPはオブジェクトの__wakeupメソッドを呼び、意図しないスクリプトが実行される可能性があるためunserialize関数をユーザ入力に利用するのは決して良いアイデアではありません。しかし、PHPマニュアルでは「長い間データ交換フォーマットとして利用され、phpBB2のように、多くの人気の高いPHPアプリケーションでクッキーに保存されたシリアライズ済みのデータのデコードに利用されています。」としています。

攻撃者が設定した文字列にunserilize関数を使用すると、任意コード実行が可能となるZVALのリファレンスカウンタオーバーフローが発生する可能性があります。

次のgdbのデバッギングセッションは、phpBB2 2.0.22に対して、どのようにリモートから攻撃を行なったか表しています。

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

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1211611456 (LWP 9582)]
0x99887766 in ?? ()
(gdb) i r eip
eip            0x99887766       0x99887766
(gdb) zbacktrace
[0xbf845ac0] unserialize() /var/www/forum/includes/sessions.php:283
[0xbf84c560] session_pagestart() /var/www/forum/index.php:31
(gdb)

バックトレースとレジスタからPHPのunserializeが0x99887766 のコード実行を試みた事が分かります。0x99887766は攻撃者が設定したオフセットでこの例ではマップされていないメモリを指しています。このためクラッシュしています。しかし、もしシェルコードへのポインタが設定された場合、シェルコードを実行します。

■PoC、攻撃コードまたは再現手順
添付した攻撃コードはどのようにunserialize関数によって文字列をアンシリアライズする事によりインストラクションポインタを制御するかデモンストレーションしています。コードを実行させるにはシェルコードが必要とするオフセットを設定します。攻撃コードの文字列は、非常に長くクッキーに保存できない為、例示したphpBB2に対してそのままでは利用できません。

■備考
PHP 4.4.5はソースコードで65000リファレンスまでしか作れないunserialize関数になっており、既に脆弱性ではありません。PHPセキュリティレスポンスチームはこの重要なセキュリティフィックスが行われていた事を忘れていたようです。

しかしながら、もしPHPをいアップグレードできないのであれは、攻撃文字列が500KB以上必要であるため、SuhosinモジュールまたはWebアプリケーションファイアーウォールで防御可能です。

投稿者: yohgaki