Stefanさんの承諾を得て日本語訳を公開しています。このブログの「the Month of PHP Bugs」カテゴリでMOPBの翻訳ページを一覧できます。分かりやすいように意訳できる部分は意訳します。厳密に原文の通り訳していないので正確性を重視される方は原文をご覧ください。
■クレジット
発見者:Stefan Esser
攻撃コード:Stefan Esser
■PoCまたは攻撃コード
MOPB-16-2007.php
http://www.php-security.org/MOPB/code/MOPB-16-2007.php
■リファレンス
なし
■サマリ
PHP 5.2.0のPECL(訳注:非標準モジュール。http://pecl.php.net/ )zipモジュールの人気の高いdotdeb PHPディストリビューションなどでデフォルトで有効化されています。この拡張モジュールはZIPファイルの取扱う関数とzip:// URLラッパーを提供します。
zip:// ラッパー内のURLパーサのスタックオーバーフローは簡単に任意コード実行に利用できます。zip:// ラッパーはallow_url_include / allow_url_fopen に影響されないので(訳注:筆者も危険性を認識していたので数年前にallow_url_fopenが無効な際はURLリソースすべてアクセスできない方が良いのでは、とPHPストーリムの作者にメールした事があります。実現しませんでしたが。)PHPがどのような設定であっても(訳注:zip拡張モジュールさえロードされていれば)バッファーオーバーフローを発生させ、任意コードをリモートから実行可能です。
■影響するバージョン
PHP 5.2.0以下、PECL ZIP 1.8.3以下を利用しているPHP
■詳細情報
軽率なコーディングによる不十分なバウンダリチェックはまだ頻繁に見かけられます。しかし、全くバウンダリチェック無しでスタック変数にデータをコピーするのはほとんど見ません。
zip:// URLラッパーはスタック変数にバウンダリチェックなしでコピーを行います。このラッパーは最初のURL部分(.ZIPアーカイブのファイル名を保存する部分)をトランケートし、スキーマ以降部分全てをMAX_PATH_LENGTH(Linuxではおよそ4096バイト、BSDでは1024バイト)のスタックバッファにコピーします。
このコピーは、ソースURLが0バイトのコピーも可能であるよう、バイナリセーフです。
以下のgdbのデバッギングセッションはインストラクションポインタが上書きされた後の環境を表示しています。
$ gdb ./php-5.2.1 GNU gdb 6.4-debian ... (gdb) run MOPB-16-2007.php ... Program received signal SIGSEGV, Segmentation fault. [Switching to Thread -1214888256 (LWP 7794)] 0x55555555 in ?? () (gdb) i r eip eip 0x55555555 0x55555555 (gdb) x/2x $esp 0xbffb5110: 0x08418e00 0xb792f04c (gdb) x/1s 0xb792f04c 0xb792f04c: "zip://", 'A' ... (gdb) x/5i 0xb792f04c 0xb792f04c: jp 0xb792f0b7 0xb792f04e: jo 0xb792f08a 0xb792f050: das 0xb792f051: das 0xb792f052: inc %ecx
このデバッギングセッションからはオフセット0x55555555のコード実行させようとしてる事が分かります。スタックのレイアウトからesp+4にzip:// URLへの完全なポインタがあり、逆アセンブルされたコードからzip://は実際にx86コードである事も分かります。この脆弱性を利用するには、ここで示したように、POP/RETへのオフセットが必要なだけです。
$ su - mopb $ ./php-5.2.1 MOPB-16-2007.php ... and now on another shell $ id uid=100(user) gid=100(user) groups=100(user) $ nc 127.0.0.1 4444 id uid=1107(mopb) gid=1107(mopb) groups=1107(mopb)
■PoC、攻撃コードまたは再現手順
実際の攻撃は十分に長いzip:// URLを作成し、それをロードさせることで可能になります。実際の攻撃では攻撃者はリモートからPHPアプリケーションにzip:// URLを開かせようと試みるでしょう。例えば、Wordpress 2.0のpingback(?)機能は攻撃者が送信したURLを開こうとします。
この脆弱性の攻撃にはメインバイナリに依存するPOP/RETシークエンスのオフセットが必要です。攻撃コードをテストするには0x55667788の様に常にクラッシュするオフセットを利用します。
PHPをコンパイルした時のオプション(訳注:–omit-frame-pointerなど)によりスタックレイアウトが多少異なっているかも知れません。URLの長さは調整が必要な場合があります。
■備考
PHP開発者の間では、多くのアプリケーションがURLインクルードの脆弱性または設計仕様によりURLの読み込みが可能であるにも関わらず、この脆弱性はローカルな脆弱性としています。アバダーのアップロード関数やWordpressのPingbackコード(最新版には含まれません)がその例です。