【悪用厳禁】ファイルアップロード機能を利用してサイトをハッキングする方法を中学生が解説
最終更新 2022/12/31 11:49
Webにおいては、PHPやcgiなどの動的スクリプトを使うと数行のコードでファイルアップロード処理を実装できますが、そこには多くの脆弱性が潜んでいます。
本記事では、攻撃者側の視点に立ち、実際にはどのような手法でファイルアップロード機能を悪用するのかについてハッカー中学生(笑)の私が解説したいと思います。
当然、本記事の内容をご自身が管理されているサイト以外に試した場合、不正アクセス禁止法や関連法に抵触する可能性がありますので、あくまでもペネトレーションテスト用途のみに本記事の内容を利用してください。
サーバー側処理に関連する拡張子一覧
以下のような拡張子を持つファイルへのリクエストを受けると、対応しているサーバーアプリケーションが動作している場合には実行されます。
攻撃者の多くは、まず「普通に」スクリプトをアップロードできないか試みます。バックエンド側の対策方法としては、ブラックリスト方式では危険なため、ホワイトリスト方式でチェックするのがお勧めです。
PHP: .php, .php2, .php3, .php4, .php5, .php6, .php7, .phps, .phps, .pht, .phtm, .phtml, .pgif, .shtml, .htaccess, .phar, .inc, .hphp, .ctp, .module
ASP: .asp, .aspx, .config, .ashx, .asmx, .aspq, .axd, .cshtm, .cshtml, .rem, .soap, .vbhtm, .vbhtml, .asa, .cer, .shtml
Jsp: .jsp, .jspx, .jsw, .jsv, .jspf, .wss, .do, .action
Coldfusion: .cfm, .cfml, .cfc, .dbm
Flash: .swf
Perl: .pl, .cgi
Erlang Yaws Web Server: .yaws
「useful extensions」https://book.hacktricks.xyz/pentesting-web/file-upload
拡張子の細工 (二重拡張子)
誤った方法で「拡張子の取得」を行っているアップローダーでは、二重拡張子を用いることで拡張子を詐称できる可能性があります。
例: annzennna-fairu.png.php / koreha-kikenndeha-arimasenn.jpg.CgI
拡張子の細工 (特殊文字)
システムの制御文字や2バイト文字などはTwitterで日々の成長を投稿するような駆け出しエンジニアが作ったアップローダーであれば容易にbypassできます悪用されると対処が難しいです。
NULLバイト(%00)による攻撃はヌルバイト攻撃とも呼ばれ、成功率が高い印象です。また、改行(%0a)なども試してみると良いでしょう。
例: test.png%00.php / safe-file.%0ajpg%00.CgI
拡張子の細工 (冗長なファイルパス)
各ファイルシステムには対応しているファイル名の長さに限界があります。
例えば、WindowsのNTFSなら単体ファイル名の最大長は256桁です。
これより長いファイル名を送り付けた場合、文字列の最後が「.png」になっていても257文字目以降の部分が切られて、「.php」になってしまう可能性があります。
例: hogehoge{以下244文字}.php.png
拡張子の細工 (代替データストリームの悪用)
詳しくは書きませんが、Windows専用の特殊ファイルみたいなものです。
ファイルを隠したりもできます。詳しく解説している記事がありましたので、本記事では紹介しません。
代替データストリーム(ADS)について色々調べてみた - Qiita
ファイルの種類偽造(Content-Typeの編集)
Content-Type情報はクライアント側で任意に指定できるので、「image/png」という名目の「hogehoge.php」を送り付ける事も可能です。
ファイルの種類偽造(シグネチャの編集)
一部のファイル形式(PNGなど)は、「この種類のファイルであること」を示すために先頭の数バイトがシグネチャになっています。
例えば、PNGでは以下のようなシグネチャ(ヘッダー)になっています。
先頭のシグネチャには「正常なPNGのヘッダー」を指定しておきながら、中身はPHPのコードを含めるといったことができます。
ファイルサイズの上限突破
htmlフォーム内にMAX_FILE_SIZEという定数が埋め込まれている事がありますが、これはあくまでもブラウザに対して「このサイズまでアップロードを許可しますよ」と事前に通知するためのものです。
しかし、POST内のMAX_FILE_SIZEでファイルサイズが許可された範囲内かチェックするようなスクリプトが組まれていた場合、定数を編集することで任意のサイズのファイルをアップロードすることができます。
pwnedには至りませんが、サーバーの容量を圧迫することもできます。
ディレクトリトラバーサル攻撃(絶対パス指定)
ルート( / や C:\ )から直接ファイルパスを指定する方法です。
対策方法としては、PHPであればbasename()などがあります。
例: /var/www/html/test.php
ディレクトリトラバーサル攻撃(相対パス指定)
「../」を使ってファイルパスを指定する方法です。
同様にbasename()などで対策できますが、copy()などでアップロード処理を実装している場合にはディレクトリトラバーサル攻撃に対して脆弱になる可能性があります。
例: ../html/../html/test.php
SQLインジェクション
サイトによっては、ファイルの管理をデータベースで行っている場合があります。もちろんファイル本体は大きなサイズを持ちますので、そこに保存されるのは「名前」だけです。そこで、SQLiができる可能性があります。
ファイル名の特殊文字を併用すると任意ファイルをアップロードできます。
例: '; insert into files(FilePath) values (concat("test.php", char(0))); --
一体何をアップロードするのか
最終的にはファイルマネージャーをアップロードしますが、多くの場合、攻撃の段階では簡易的な乗っ取りスクリプトをアップロードします。
例えば、GETの内容をそのままsystem()で実行するスクリプトや、「何でもアップロードできるファイルアップローダー」などです。
以下のような極めて簡素なファイルを、以前実際に見かけました。<?php system($_GET["command"]);
バックドアを仕掛ける場所
バックドアとしてファイルマネージャーをアップロードする場合、当然管理者にバレないような場所に隠そうとします。
そのため、wp-contentやtempなど普段閲覧しないようなディレクトリに、安全そうなファイル名(__php_cache1234.php)や誤解しやすいファイル名(,.png.php)で保存します。
まとめ
ここまでの内容を踏まて、安全なファイルアップロード処理を実装する上で考慮すべきポイントをまとめておきます。
・ファイル名は、自由指定ではなくハッシュなどからサーバー側で生成する
・拡張子を確認する際には、特殊文字があった場合処理を停止し、basename関数などでディレクトリトラバーサル攻撃を防ぎ、取得に際しては「最後のドット以降」を取るように気を付ける
・MAX_FILE_SIZEを信用せず、サイズの確認にMAX_FILE_SIZEは使わない
・Content-Typeは信用しない
・シグネチャは嘘付きなので信用しない
・クライアント側の値は一切信用しない
・そもそも危ないリクエストは受け付けない(WAF導入など)
・ユーザーに何かをアップロードしてもらう事を諦める←本末転倒
参考文献