【ファイルアップロード編】二重拡張子・MIME偽装・RCEの入り口|CTF思考フレームワーク #06
📖 この記事はシリーズの一部です
「CTF思考フレームワーク」 Webフォーム編 #06 / 公開中 7記事 → シリーズ一覧を見る →
ファイルアップロード機能って、便利だけど怖いんですよね。プロフィール画像、添付資料、CSVインポート…受け取る側のサーバから見れば、「外部から実行可能ファイルが入ってくる入り口」になりかねない場所。今回はアップロードフォームを攻撃者目線で観察して、どこを狙われるか・どう守るかを整理していきます🗂️
CTFでも実務でも、ファイルアップロードは「一発でRCE(リモートコード実行)」につながる大物脆弱性が眠りがち。今日はそのチェックポイントを攻撃者と防御側の両方から見ていきましょう。
👀 観察フェーズ:まず何を見る?

アップロードは外部から実行可能ファイルが入る入口。観察ポイントが多いから慎重にね。
アップロードフォームを見たら、まずは「サーバ側がどこまで信用しているか」を観察します。チェックポイントはこの6つ👇
- 許可拡張子の方式:HTMLの
accept属性、JSバリデーション、サーバ側のチェック。どれが本命? - ファイルサイズ・件数の上限:DoSや大量投入の余地はないか。
- 保存先のパス:Webルート配下か、外部ストレージか。配信URLの形は?
- ファイル名の扱い:ユーザー入力をそのまま使うか、サーバで再生成するか。
- レスポンスの差分:成功時・拒否時のメッセージで「何で弾かれたか」が分かるか。
- 後続処理の有無:画像変換・PDF解析・ウイルススキャンなどがあれば、ImageMagickやGhostscript系の脆弱性も狙える。
特に「クライアント側だけのチェック」は攻撃者に刺さる甘い穴。サーバ側の防御がないと一発でやられます。

え、accept属性って飾りなの…?信じてた💧
🤔 仮説フェーズ:攻撃者は何を考える?

ファイルアップはRCEに直結する大物だから、攻撃者も本気で来るよ。
攻撃者は「許可リスト」「保存先パス」「実行可能性」の3軸でアップロード機能を分解します。代表的な4つの仮説を見ていきます。
💭 仮説①:拡張子チェックがブラックリスト方式なら、.phtml .php5 .phar .shtml など「リストから漏れている実行可能拡張子」が通るはず。
💭 仮説②:MIMEだけで判定しているなら、本物の画像にPHPコードを埋め込んだポリグロットが「JPEGとして」受け入れられて、保存先で実行されるはず。
💭 仮説③:保存先がWebルート配下なら、アップロード成功=直接URLでアクセス可能。実行権限が残っていればそれだけでRCE成立。
こうやって「許可リスト」「保存先パス」「実行可能性」の3軸で攻撃面を分解していくのが定石です。
🎯 仮説①:拡張子チェックがブラックリスト方式
.phpだけ拒否していると、.phtml .php5 .phar .shtmlなどリストから漏れた実行可能拡張子が通る可能性。許可リスト方式でないと網羅的に防げません。

え、.phpだけブロックしててもダメなの…?.phtmlとか初耳だよ💧
🖼️ 仮説②:MIMEタイプだけで判定している
クライアントが申告するContent-Typeを信じている実装だと、本物の画像にPHPコードを埋め込んだポリグロットが「JPEGとして」受理されます。Content-Typeは攻撃者が自由に書ける場所。

うわ、画像に見せかけたPHPってそんなことできるの…!?
📁 仮説③:保存先がWebルート配下
アップロード成功=直接URLでアクセス可能な構成だと、実行可能ファイルが置かれた瞬間にRCEが成立。Webルート外に置いてアプリ経由で配信するのが原則。

/uploads/にそのまま保存されるサイト多いよね…あれ全部危ないの!?
↩️ 仮説④:ファイル名の正規化が抜けている
ファイル名に../../var/www/html/のような相対パスを含められると、保存先ディレクトリを飛び越えて任意のパスに書き込みされるパストラバーサルが成立。basename()相当の正規化が必須。

ファイル名に../入れるだけでどこにでも保存できちゃうの…ホラー💧
こうやって「許可リスト」「保存先パス」「実行可能性」「ファイル名正規化」の4軸で分解するのが定石。次は実際に検証する手順へ💡
🔬 検証フェーズ:どうやって確かめる?

アップロード検証は画像→テキスト→実行ファイルの順で段階的に進めるのがコツ。
仮説を1つずつ確かめていきます。必ず自分の環境か、許可された環境でやってください。
🔬 STEP 1:許可拡張子を炙り出す
まずは普通の画像をアップしてレスポンスを観察。次に .txt .html .svg .php .phtml .jsp など順に試して、どのエラーメッセージが返るかで「拒否ロジックの粒度」を推定します。Burp IntruderのSnipersで拡張子辞書を回すのが定番。
🔬 STEP 2:保存先パスを特定する
アップロード成功後のレスポンスやHTML中のURL、画像表示パスを観察。/uploads/ /static/ /files/ など、Webルート配下に直接置かれているなら危険サイン。直接URLでアクセスしてHTTPステータスとContent-Typeを確認します。
🔬 STEP 3:実行可能性をテストする
画像の末尾にPHPコードを埋めたポリグロットを送信し、保存後のURLを叩く。.php が拒否されたら .phtml .php3 .php5 .phar など実行可能な別拡張子を試す。Apacheなら .htaccess アップロードで挙動を上書きできるかもチェック。

うわ、拡張子辞書を回すとかポリグロットとか…ツールであっさり試せちゃうの!?💧

そう。だからサーバ側で中身を見て・Webルート外・実行権限なしの3点が大事なんだ。
⚔️ 攻撃フェーズ:実際の手口

“なぜ通るか”を理解するのがゴール。RCEに直結するから本気で対策しよう。
実際の攻撃シナリオを3つ並べておきます。どれもサーバ側のバリデーション不足がトリガー。
🎯 シナリオ①:二重拡張子で実行ファイルを通す
shell.php.jpg や shell.jpg.php のようにドットを2つ含めて投入。サーバが「最後のドット以降」だけ見ていると .php が通り、Webルートに置かれた瞬間 https://victim/uploads/shell.php.jpg でアクセスしてRCE。Apacheの古い AddHandler 設定では「途中に .php を含むファイル」も実行されがち。
🎯 シナリオ②:MIMEとマジックバイトを偽装した画像シェル
本物のJPEGの先頭バイト(FF D8 FF)を維持したまま、ファイル末尾に <?php system($_GET["c"]); ?> を埋め込む(ポリグロット)。サーバはマジックバイトだけ見て「これはJPEGだ」と判定して通過。拡張子を .phtml や .php5 に偽装すれば実行される構成も多い。Burp Suiteで Content-Type: image/jpeg を付けて投げるだけで通ることも。
🎯 シナリオ③:パストラバーサルで任意の場所に保存
ファイル名に ../../var/www/html/shell.php のような相対パスを混ぜて送信。サーバ側で basename() 相当の正規化が抜けていると、保存先ディレクトリを飛び越して任意のパスに書き込まれる。.htaccess を上書きできれば、画像ディレクトリでもPHPを実行可能にする悪魔合体技も成立。
CTF{upload_validation_must_be_server_side}どれも共通するのは「サーバ側の最終バリデーションが甘い」という一点。クライアント側は飾りです。

えっ、画像に偽装したPHPとかパストラバーサルでサーバ書き換えとか…ホラー過ぎるよ💧
🛡️ 防御フェーズ:どう守る?

やっとお待ちかね、守る側の打ち手。アップロードは3つの原則を守れば大体ふさげる。
守る側のチェックリストはこちら。「中身を見る」「Webルート外に置く」「実行権限を外す」の3点セットが基本。
- 許可リスト方式でMIMEと拡張子の両方を検証。jpg/png/pdfなど業務上必要なものだけ通す。
- マジックバイト検証でファイルの中身を確認。Content-Typeやクライアント申告は信用しない。
- 保存先はWebルート外。配信時はアプリ経由でストリーミングし、直接実行不可にする。
- ファイル名はサーバ側で再生成(UUIDなど)。ユーザー入力をパスに混ぜない。
- 実行権限を剥がす:保存先ディレクトリの実行権限を落とし、.htaccessや上書き設定で念押し。
- サイズ・枚数制限とウイルススキャン。画像は再エンコード(リサンプル)してメタデータ攻撃を無効化。
✅ ポイントは 「クライアントの申告を一切信じず、サーバ側で中身を見て、Webルート外に置く」 の3点セット。これが守れていれば大体のアップロード脆弱性は塞がります。

なるほど…中身チェック・配置場所・実行権限の3点か。どれか1個でも守れてたら被害は防げるってことだね!

その通り。多層防御がここでも効く。アップロードは特にRCEのリスクが高いから手厚くね🛡️
🛡️ 今日からできる対策ツール
パスワードの使い回しや手動管理はどんなに気をつけても限界があります。🔑 パスワード管理ツール「ワンパス」なら、複雑なパスワードを安全に保管して「1つのマスターパスワード」だけ覚えられるので、今日から始める防御策としてしっくりきます。
※ 上記は他社サービスへのリンクです。購入は各自でご判断ください。
⚠️ よくある落とし穴
よくある実装ミスを順番に。
- 「クライアント側のaccept属性で安心」と思い込む。Burp等で簡単に書き換えられます。
- 拡張子を「最後のドット以降」で取得して、二重拡張子を見逃す。
- Content-Typeヘッダだけで判定して、ファイルの中身を見ない。
- 保存先をWebルート配下にして、直接URLで叩けてしまう。
- ファイル名にユーザー入力をそのまま使い、パストラバーサルを許す。
- ImageMagickのバージョンが古く、ImageTragick系の脆弱性が残っている。
🧰 ツール早見表
CTFや診断で使う道具。
| ツール | 用途 | ひと言 |
|---|---|---|
| Burp Suite | リクエスト改ざん全般 | Content-Type書き換え・リクエストリプレイの主役 |
| file コマンド | マジックバイト確認 | サーバ側で何を見るべきかの参考になる |
| exiftool | メタデータ抽出・操作 | 画像偽装の小道具にも、防御の確認にも |
| Upload Scanner (Burp拡張) | 自動アップロード検査 | ペイロードのバリエーションを一気に試せる |
🎓 本気で学びたい人へ
Webセキュリティを「趣味」から「仕事」に変えたい方へ。🎓 ササエルはインフラエンジニアに特化したオンラインスクールで、セキュリティ設計の基礎から体系的に学べます。
📚 もっと深く学びたい人へ
体系的に攻撃と防御の両面を学びたいなら『ホワイトハッカー入門 第2版』が分かりやすい入口です📚
📚 次に読みたい
CTF・セキュリティ学習ハブページへ


