2011年6月29日水曜日

徳丸浩の日記をこちらに移転します

自力でのtdiary運用も面倒になってきたので、独自ドメインで運用できるbloggerに日記を移転しようと思っています。まずは新規の日記から試験的に運用してみます。

2011年6月20日月曜日

PHPにおけるファイルアップロードの脆弱性CVE-2011-2202

 PHPの現行バーション(PHP5.3.6以前)には、ファイルアップロード時のファイル名がルート直下の場合、先頭のスラッシュを除去しないでファイル名が渡される問題があります。CVE-2011-2202として報告されています。
 後述するように影響を受けるアプリケーションは少ないと思われますが、念のためアプリケーションの確認を推奨します。また、次バージョンPHP5.3.7のRC1で修正されていることを確認しましたので、PHP5.3.7正式版が公開され次第、できるだけ早期に導入することを推奨します。

※このエントリは、はてなダイアリーの過去のエントリからの転載です。

概要

 PHPにはファイルアップロードの機能が提供されています。アップロード時のリクエストに付与される元ファイル名は、PHPスクリプトでは、$_FILES[]['name']で受け取れますが、この際にはファイルのディレクトリ名やドライブレター(C:のような)は除去され、ファイル名と拡張子だけがアプリケーションに渡される仕様です。
 しかし、「/upload.png」のようにルート直下のファイル名を指定した場合は、先頭のスラッシュ「/」は除去されず、フルパスでファイル名が渡されます。これがCVE-2011-2202脆弱性です。

確認

 「体系的に学ぶ 安全なWebアプリケーションの作り方(以下、徳丸本)」の「4.12ファイルアップロード」にまつわる問題に出てくるサンプルスクリプト4c-001.phpと4c-002.phpを用いて、この問題を検証してみましょう。以下は4c-002.phpのソースです。このスクリプトには脆弱性があります。
<?php
$tmpfile = $_FILES["imgfile"]["tmp_name"];
$tofile = $_FILES["imgfile"]["name"];

if (! is_uploaded_file($tmpfile)) {
  die('ファイルがアップロードされていません');
// 画像を img ディレクトリに移動
} else  if (! move_uploaded_file($tmpfile, 'img/' . $tofile)) {
  die('ファイルをアップロードできません');
}
$imgurl = 'img/' . urlencode($tofile);
?>
<body>
<a href="<?php echo htmlspecialchars($imgurl); ?>"><?php
 echo htmlspecialchars($tofile, ENT_NOQUOTES, 'UTF-8'); ?></a>
をアップロードしました<BR>
<img src="<?php echo htmlspecialchars($imgurl); ?>">
</body>
 このスクリプトを用いてupload.pngというファイルをアップロードします。元ファイルがC:\HOME\PHP\upload.pngというファイル名だった場合、以下のようにファイルアップロードのリクエストには、filename=C:\HOME\PHP\upload.pngという形で添付されています。しかし、PHPスクリプトには、upload.pngというファイル名のみが渡されます。
次に、Fiddlerなどを使って、以下のようにファイル名をfilename=/upload.pngと改変してみます。

 この際には、アプリケーションには先頭のスラッシュを保ったままで渡されます。以下の画面上に表示されたファイル名を確認してください。


 ファイル名が/home/upload.pngや../upload.pngの場合は、ディレクトリ部分は削除されます。

この脆弱性の影響

 $_FILES[]['name']が絶対パス名を返した場合、このファイル名の使い方次第では問題になる可能性があります。
 影響がでるPHPスクリプトは以下の条件の両方を満たすものであると考えられます。
  • $_FILES[]['name']をそのまま使ってmove_uploaded_fileによりファイルを移動している
  • PHPスクリプトが、Webサーバーのルートディレクトリに対して書き込み権限をもつユーザで実行している
 前者は、具体的には以下のようなスクリプトです。
move_uploaded_file($_FILES["imgfile"]["tmp_name"], $_FILES["imgfile"]["name"]);
 このスクリプトは正常系の動作では、PHPスクリプトがおかれているディレクトリにファイルを書き込むわけで、乱暴としか言いようがありません。PHPファイルを書き換えて任意のスクリプトが実行できるわけで、CVE-2011-2202脆弱性がなくても致命的な脆弱性であると考えられます。
 PHPスクリプトがWebサーバーのルートディレクトリに書き込み権限を持つ場合の典型例は、Apacheが管理者権限で動いている場合です。脆弱性診断をしているとたまにそのようなWebサイトを見かけますが、非常に危険です。最近では、Apacheインストールするとデフォルトでは管理者権限のないユーザ(nobody、apache、www-dataなど)で動作するようになるので、わざわざ危険な設定に変更してはいけません(追記:Windows版のPHPデフォルトではSYSTEMユーザという強い権限で動作します)。

対策

 上記の二条件に当てはまるPHPスクリプトは至急対策が必要ですが、CVE-2011-2202以前にWebアプリケーション脆弱性であるからです。以下の対処を推奨します。
  • アップロードされたファイルを安全な場所に移動する(徳丸本4.12節参照)
  • Apache等を管理者権限のないユーザで動作させる
 これらは、CVE-2011-2202の影響を受けないための最低条件です。例えば、先に紹介した4c-002.phpCVE-2011-2202の影響を受けませんが、他の脆弱性はあります(脆弱な例なので)。詳細は、徳丸本4.12節を参照してください。

まとめ

 CVE-2011-2202の概要、影響、対策について報告しました。
 大半のアプリケーションは、CVE-2011-2202の影響によりセキュリティリスクは増加しないと考えられますが、この機会にファイルアップロード部分に脆弱性がないかチェックするとよいでしょう。また、早期にPHP5.3.7への移行を実施してください。
 この脆弱性リスクNVD(National Vulnerability Database)では、6.4と判定していますが、redhatのCVE DATABASEでは2.6と低く判定しています。私は、redhatの判定を支持します。この例のように、脆弱性リスクは判定者によって異なる場合があるため、単独の情報を鵜呑みにしないことが大切です。脆弱性の内容を理解して自社アプリケーションの影響を判断するのが正しい対処ではありますが、よく分からない場合はリスクの高い方に従っておくべきですし、その場合はソフトウェア提供者などが推奨する対策(パッチ適用バージョンアップ、回避策など)を実施するべきでしょう。

追記(2011/06/20 17:00)

 @shishi4twさんのつぶやきで、該当脆弱性報告者のブログを知りました。また、@umqさんの指摘から、Windows版のApacheが管理者権限(SYSTEM)で動作することを確認しました。
 脆弱性報告者のブログの方には、デモの動画も用意されているので見ると面白いと思います。デモでは、c:\boot.iniを上書きして、Windowsを起動できないようにしています。このデモは印象的ですが、実はPoCのPHPスクリプトは、CVE-2011-2202がなくても同等以上の悪いことができます。PHPスクリプトアップロードして、このスクリプトを外部から参照することにより実行すればよいわけで、system関数などでOSコマンドを呼び出すことできるし、unlink関数でC:\boot.iniを消することも可能です。
 Windows上のApachePHPを動かしている場合は、Unix/Linux上の場合に比べてPHPが強い権限で動いていることから、より注意が必要ですが、CVE-2011-2202がなくてもそれは同じです。

フォロワー