FileUploadAttack

【脆弱性】本当は怖いファイルアップロード攻撃の理解と修正方法(PHP編)

投稿日: カテゴリー: セキュアコーディング
Pocket

ファイルアップロード攻撃は相当な脅威にも関わらず話題にならないので記事にしてみました。
ページ下部にDemo動画を載せていますので参考にしてください。
いつも通りの注意の”自分の管理下にあるか許可を得たコンテンツ以外に絶対実行しないでください”に加え、”自分の管理下にあるか許可を得たコンテンツに実施した場合も実施後、絶対放置しないでください“。ファイルアップロード攻撃はバックドアを設置する行為と同等に考えてください。

ファイルアップロード攻撃ってそもそも何?

ファイルアップロード攻撃はアップロードしたファイルを通して本来の仕様に意図しない動作を引き起こす攻撃です。
具体的には次のようなことができます。

サーバ上の任意のファイルを読み込む、任意のコマンドを実行される

(/etc/passwd)などサーバの機密情報の書かれたファイルが直接狙われます。exec()、system()などを利用できれば任意のコマンドを実行されます。
※exec()は結果を出力しない、system()は結果を出力します。
アップロード先URL/?filepath=任意のパス

<?php include_once($_GET[‘filepath’); ?>

SQLインジェクションの脆弱性がないにも関わらず任意のSQLを実行される

コーポレートサイト部分がWordpressとしているサイトがあったとすれば以下をファイルに記述してアップロードするだけで任意のSQLコマンドを実行できるようになります。

アップロード先URL/?sql=任意のSQLコマンド

<?php require(‘/var/www/html/corp/wp-config.php’);mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);$posts=$wpdb->get_results($_GET[‘sql’]);echo var_export($posts, true); ?>

さすがにそんな状態放置しているサイトなんてないだろうと思われるかもしれませんが、独自実装のアップロード機能やセキュリティ対策をした結果悪化させるなどのケースで度々見かけます。

ファイルアップロード攻撃の対策

やられれば致命傷な脆弱性なのでしっかり方針を立てて対策を行う必要があります。
上から順に汎用性が高く対策の効果が高い方針となっています。最低限、1,3は実施すべきです。
2はオープンソースのCMSやオープンソースのライブラリを利用してると非公開の条件はクリアできませんが工夫のやりようはあります。

  1. アップロードディレクトリではファイルの実行権限を付与しない。
  2. アップロード先を非公開とし推測できないパスとする。
  3. 指定ファイル形式や指定パス以外をアップロードさせない
  4. 保存時は非公開鍵を用いてファイルの内容に可逆的な変換を施し内容を書き換えて保存する 。
1.アップロードディレクトリではファイルの実行権限を付与しない

アップロード先で実行されることが根本的な原因であるため権限を外してアクセスできないようにします。

unmask()とchmod()が候補に上がりますが公式マニュアルにある通りマルチスレッドなWebサーバを想定しているのでunmask()よりchmod()の方が良いでしょう。

chmod($upload_path, 0644);

以下のような設定方法はシングルスレッドが確約される時に限定しましょう。

<?php
umask(077);//—rwxrwx を無効にする。
$fOpne = fopen(“/uploadpath/filename”, “W”);
?>

注意:

マルチスレッドな Web サーバーでこの関数を使用することは避けてください。 ファイルを生成後、chmod() を使用してファイル権限を 変更するのがより良い方法です。全て同じ umask が使用されるので、 umask() の使用は、スクリプトを同時に実行する場合や Web サーバー自身の予期しない動作を引き起こす原因になる可能性があります。

mkdirが思い通りの権限設定にならないとき考えるべき2つのポイント

mkdirのパーミッションは4桁で指定する

mkdir(‘./hoge/huga’, 777, true);で作成すると411となります。パーミション部分は4桁の8進数で指定します。

なんで411になるかと言えば、777は8進数に置き換えた1411を指定したと解釈されるためです。それでは4桁目は何かといえば、スティッキービット(Sticky Bit)です。スティッキービット(Sticky Bit)が設定されたファイル・ディレクトリでは、ファイル・ディレクトリの所有者またはrootしか削除できなくなるという設定です。

mkdir(‘./hoge/huga’, 0777, true);

umaskの影響を考える

printf (“%03o”,umask());を実行するとだいたいの環境で022が返ってくると思います。これはUNIX環境のunmaskの設定でこの状態でパーミション666を指定して新規ファイルを作成するとパーミッションは644となります。パーミションの設定時はunmaskを予め考えた値を設定しましょう。先に述べた通りマルチスレッド環境でunmask(0000)としてから実行するとバグの元になるので推奨しません。

2.アップロード先を非公開とし推測できないパスとする

PHPでファイルを作成したままにしておくとパスがわかると情報にアクセスできてしまいます。UUIDやハッシュ値で管理しましょう。

3.指定ファイル形式や指定パス以外をアップロードさせない

指定ファイル形式、拡張子は簡単に偽装されます。javascriptを画像に圧縮する方法などあるので完全に防ぐことができる訳ではありませんが以下のようにMIME_TYPEを検証するのも一定の対策にはなります。($_FILES[‘upfile’][‘mime’]の値はブラウザ側で偽装可能なので対策にはなりません)

if ($ext = array_search(

    mime_content_type($_FILES[‘upfile’][‘tmp_name’]),

   array(‘png’ => ‘image/png’,‘jpg’ => ‘image/jpeg’,),true

)){

    echo ‘画像ファイルのようです’;

}

指定パス以外をアップロードさせないは要するにファイル名によるDirectory Traversal(Path Traversal)対策です。せっかくアップロードディレクトリではファイルの実行権限を付与しないような実装にしても、ファイルパスが指定できた場合、全く違う場所にアップロードして実行される可能性があります。この対策に利用されるのがrealpath() 関数です。realpath() 関数を利用することで絶対パスを強制され「../../etc/」など相対パスによる自由な指定ができなくなります。さらにbasename()関数を用いてパスを含む文字列をファイル名のみに変換します。

$UpLoadFileName = basename(realpath($fileName ));

4.保存時は非公開鍵を用いてファイルの内容に可逆的な変換を施し内容を書き換えて保存する

アップロードしたファイルがダウンロードされるまで利用されないなど保管時の状態で利用がない場合であれば極めて有効な策となります。非公開と言ってもくれぐれもソースコードへのハードコードは避けてください。

やられサイトを用いたFile Upload Attackのデモ動画

大きめの画面で見ないとメモに何が書いてあるのかわからないので動画下にメモ内容を記載してますので参考ください。

”動画の説明部分”

ファイルアップロード攻撃の紹介
1.攻撃用のファイル(FUPoC.php)を用意します。
2.Burp Suiteというツールでリクエストを中継改ざんできるようにします。
3.ファイル選択で用意したファイルを選択します。
4.そのままアップロードしようとすると以下のメッセージが出て失敗します。
Your image was not uploaded.
5.どうやらイメージの形式でないとアップロードは拒否されるようです
6.今度はアップロードのリクエストをインターセプトしてjpegと偽装します。
Content-Type: text/php

Content-Type: image/jpeg
7.アップロードに成功したようなので
次にアップロードした階層にあるファイルにアクセスします
http://192.168.56.101/dvwa/hackable/uploads/FUPoC.php
8.PHPが有効に動いているため”Hacked!”が表示されています。
9.実行したいコマンドをパラメーターに入れていきます。
?cmd=pwd
?cmd=uname%20-a
?cmd=cat%20../../../../etc/passwd
10.はい、後はやりたい放題ですね
11.どんなソースだったのか見てみましょう。

以上です。参考になれば幸いです。