Stefanさんの承諾を得て日本語訳を公開しています。このブログの「the Month of PHP Bugs」カテゴリでMOPBの翻訳ページを一覧できます。分かりやすいように意訳できる部分は意訳します。厳密に原文の通り訳していないので正確性を重視される方は原文をご覧ください。
■クレジット
発見者:Stefan Esser
攻撃コード:Stefan Esser
■PoCまたは攻撃コード
MOPB-42-2007.php
■リファレンス
なし
■サマリ
php_stream_filter_create()関数は容易にコーディングが行えるようフィルタ名にワイルドカードをサポートしています。フィルタ名が分からずフィルタ名にドットがある場合、それ以降は切り詰められ*が追加されます。この処理はフィルタ名がドットで終了する場合に必要な追加のバイトを考慮していません。この結果php://filter URLにアクセスするとオフバイワン脆弱性が発生します。
■影響するバージョン
PHP 5.2.1未満
■詳細情報
php_stream_fileter_create()関数によってフィルタが作成される際には、最初にフィルタ名のハッシュテーブルが検索され、見つからない場合は要求されたフィルタをサポートするワイルドカードフィルタないか検索します。これは以下のコードにより処理されます。
if (SUCCESS == zend_hash_find(filter_hash, (char*)filtername, n, (void**)&factory)) { filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC); } else if ((period = strrchr(filtername, '.'))) { /* try a wildcard */ char *wildname; wildname = estrdup(filtername); period = wildname + (period - filtername); while (period && !filter) { *period = '\0'; strcat(wildname, ".*"); if (SUCCESS == zend_hash_find(filter_hash, wildname, strlen(wildname), (void**)&factory)) { filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC); } *period = '\0'; period = strrchr(wildname, '.'); } efree(wildname); }
この関数はドットでフィルタ名が終了する場合に必要なバイト分を保持していないので、決してフィルタ名がドットで終了しないことを仮定していることは明らかです。この為、ドットで終了するフィルタ名はオフバイワンオーバーフローを発生させ、バッファの最後のバイトの次のアドレスに\0文字を書き込みます。
この脆弱性が攻撃可能かどうかはヒープの実装に大きく依存しています。PHP 5.2.0の新しいメモリマネージャはこの脆弱性の攻撃をリトルエンディアンシステムで可能にしています。
■PoC、攻撃コードまたは再現手順
この脆弱性をテストするには以下のコードを実行します。
<?php $url = "php://filter/read=OFF_BY_ONE./resource=/etc/passwd"; fopen($url, "r"); ?>
このPoCは1バイトのバッファオーバーフローを発生させます。ヒープの実装によってはPHPはクラッシュするかもしれませんし、何事もなかったように実行されるかもしれません。PHP 5.2.0ではビープブロックの長さを保持しているアドレスの下位バイトを上書きするので常にクラッシュします。もしSuhosinパッチを適用している実行している場合、オーバーフロー警告を発生させプロセスを終了します。コード実行を行う攻撃コードは将来このサイトに追加されます。後ほど確認してください。
■備考
この脆弱性がローカルからの攻撃可能な脆弱性と考えないでください。攻撃はphp://filter URLで可能でありallow_url_fopenやallow_url_include設定にはかかわらず攻撃できます。include脆弱性やファイル関数にURLを指定できる場合、リモートからバッファオーバーフローを攻撃できます。