国内外のメディアで「画像ファイルに攻撃用のPHPコードが含まれていた」と比較的大きく取り上げられています。しかし、この攻撃手法は古くから知られていた方法です。条件は多少厳しくなりますがPerl, Ruby, Pythonでも同様の攻撃は考えられます。PHPの場合は言語仕様的に他の言語に比べ攻撃が容易です。
典型的な攻撃のシナリオは次の通りです。
追記:Tokenizerを使った例に修正しました。
- アバダなどの画像ファイルをアップロードできるサイトを探す
- ローカルファイルインクルードバグを探す
- 画像ファイルにサイトが利用している言語のコードを埋め込む
- 攻撃コードを含んだファイルを画像ファイルとしてアップロードする
- ローカルファイルインクルードバグを利用して攻撃コードを実行する
PHPの場合、リモートインクルードバグを攻撃するための攻撃用コードをホストさせる為に不正な画像ファイルをアップロードするケースも考えられます。自分のサイトにファイルインクルードバグが無いからといって対策を怠ってはいけません。
よくあるバグはアップロードされたファイルの妥当性チェックです。以下は全て不十分な対策です。
- ファイルの拡張子をチェックする
- イメージ関数等を利用して画像の種類を判別する
- ファイルヘッダを確認する
- ファイル種別を判別するコマンドを利用する
よく見かけるコードは拡張子とイメージ関数などを利用してファイル種別を判別し、一致している場合に許可するコードになっています。
単純な攻撃コードを除去する簡単な対策はこれらのチェックをした後、画像ファイルを別型式に変換し、元の形式に戻す方法です。例えば、GIFファイルのアップロードならGIF->PNG->GIFと変換すると攻撃コードは削除されます。(ライブラリが自動的に元のファイルに含まれたデータを変換後に再現させるような動作をしなければ普通は削除できます。削除と言うより変換エラーにより問題が検出でき、攻撃コードを含んだテキスト部分も除去できます。PHPの場合、変換後もColor Tableを利用してコードとして実行できる部分を作ることは可能です)
PHPのケースだけ考えるのであれば、正規表現関数で”<?php”を検索するのも有効です。(<script language=”php”> 、ASP, ショートタグを有効にしている場合はこれらのタグも)ただし、正規表現には注意が必要です。(現実的にはあまり短いパターンだとデータ部分にもマッチする可能性が高いです。この部分は蛇足です。PHPのコードが含まれているかチェックする最も簡単な方法はtokenizerを使ってトークンを数える方法です。)
次のコードは完全にチェックできます。
<?php if (count(token_get_all($image)) >= 2) { die('Attack detected'); }
次のコードは不十分です。
mb_regex_encoding('ASCII'); if (mb_eregi('<\\?php', $image)) { die('Attack detected'); } mb_regex_encoding('ASCII'); if (mb_eregi('^.*<\\?php.*$', $image)) { die('Attack detected'); } if (preg_match('/<\\?php./i', $image)) { die('Attack detected'); }
if (eregi('^<\\?php', $image)) { die('Attack detected'); }
なぜ後者が不十分かは常識ですよね?
追記:コメントをいろいろ頂いてバージョンアップしています。皆さん、ありがとうございます。
?をエスケープし忘れていたので追加、行単位比較時のマッチ漏れ示そうと思っていたのが間違った正規表現だったので修正しました。ところで<?や<%がスクリプト開始タグとして利用できる場合はこれらもチェックしなければなりません。<script language=”php”>は何時でも使えるのでこれは常にチェックが必要です。
別の完全な方法としてtokenizerを利用してPHP開始タグトークンを検出する方法もあります。Webサーバーに組み込まれたPHPの場合、tokenizerが組み込まれていない場合がほとんどです。正規表現を使う場合もtokenizerを使う場合もFalse Positive(誤検出)に注意してください。”<?”はバイナリに結構出現します。
このエントリと関連しているエントリにPerlアプリのYaBB脆弱性に関するエントリを書いています。
YaBBはアバダなども登録できるBBSアプリのようです。上記のエントリでもローカルファイルインクルードバグを利用した攻撃に脆弱である可能性が高いと指摘しています。
このエントリを書いた後にha.ckersでJavaScriptを画像ファイルに隠す方法が紹介されていることにも気が付きました。
画像ファイルに限らずスクリプトファイル以外へのスクリプトの埋め込む攻撃手法は随分前から知られている手法です。例えば通常のバイナリファイルなどは格好のターゲットになりえます。この様な攻撃から守る方法の一つにファイルを圧縮してしまう方法があります。圧縮するとそのままでは利用できないのでファイルをアップロード・ダウンロードできるようなサービスを提供する場合、サイトで再圧縮したファイルのみダウンロード可能にする、などの方法が考えられます。圧縮を2重にかけると2回解凍しなければなりません。この方法が利用者にとって不便であれば、画像と同じように解凍->圧縮する方法も考えられます。アーカイバに脆弱性が発見されることもしばしばあるのでサイトが攻撃されるリスクが増加することも知っておく必要があります。