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

(更新日: 2007/03/04)

“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 Bugs」はPHP4の16ビットリファレンスカウンタに対して可能な一つ攻撃コードから始まりました。ローカルコンピュータから攻撃可能でしたが、PHPはこれらのオーバーフローに対する防衛策を全く持ってないので他の攻撃経路も存在ます。人気が高い多くのPHPアプリケーションはユーザが設定したデータに対して現在でもunserialize関数を使用しています。このため、unserialize関数を利用してリモートから攻撃可能です。

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

PHP開発者はPHP 4.4.5をリリースする際にこの重要なセキュリティフィックスの記載していません。

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

シリアライズされた変数をunserialize関数によってアンシリアライズすると、この脆弱性をリモートからさえ攻撃可能とすること紹介します。PHPはアンシリアライズされるオブジェクトの__wakeupメソッドを実行してい、意図しないコードを実行する可能性があるため、unserialize関数をユーザが送信したデータに対して利用することはセキュリティ上非常に好ましくないです。にも関わらず、PHPマニュアルはunserialize関数をクッキーに保存したシリアライズ化されたデータをデコードする為に、phpBB2のように人気の高いPHPアプリケーションに長い間使われてきた関数であるとしています。

攻撃者が送信した文字列をunserialize関数に利用すると、任意コードが可能となるZVALリファレンスカウンタのオーバーフローが発生させることができます。

次のgdbデバッギングセッションはphpBB2 2.0.22フォーラムに対してリモートから攻撃行った状態を表示しています。

バックトレースとレジスタから、PHPのunserialize関数が、この例ではマップされていないメモリを指していますが、攻撃者が設定したオフセット0x99887766のコードを実行しようとし、クラッシュしたことが解ります。もしオフセットにシェルコードがあればシェルコードを実行します。

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

■備考
PHP 4.4.5のunserialize関数は65500までの参照が作れるようにソースコード中にハードコードされ、脆弱性はありません。

私はCVSリポジトリにこの修正を4ヶ月前にコミットしていました。PHPセキュリティレスポンスチームはこの重要なセキュリティフィックスが行われていた事を忘れていたようです。そして、これはよくあるPHPの問題です。アドバイザリがなければ一般ユーザは重要なセキュリティフィックスがあった事を知りようがありません。これに対して「悪い輩」はCVSコミットリストに監視する事により、4ヶ月も前からこの情報を持っています。

もしアップグレードできない場合、500KB以上の非常に長い攻撃文字列が必要なため、SuhosinまたはWebアプリケーションファイアーウォールで防御できます。

Comments

comments

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です