
PoC Docker lab: chaining file upload bypass + stored XSS to create admin accounts. Educational resource for pen testers.
CSP、CORS、CSRFの保護が有効な状態でも、ファイルアップロードのバイパスとストアドXSSを連鎖させてバックドア管理者アカウントを作成する方法を示す、意図的に脆弱性を持たせたWebアプリケーション。
完全なブログ記事: KurtiseBear Blog
これは防御的なセキュリティトレーニングのための教育用ラボです。公開アクセス可能な場所にデプロイしないでください。
このアプリケーションには実際のセキュリティ対策が実装されています:
'self' に制限(ただし 'unsafe-inline' と 'unsafe-eval' は許可)低権限ユーザーアカウントを持つ攻撃者は、2つの脆弱性を連鎖させてこれらすべてをバイパスします:
ファイルアップロードのバイパス -- アップロードフォームはクライアント側の accept 属性で .pdf のみに制限しているが、サーバー側ではファイルタイプの検証をまったく行わない。攻撃者はJavaScriptを含む .js ファイルをアップロードする。ダウンロードエンドポイントは同一オリジンから提供されるため、CSPとCORSはブロックしない。
メッセージ件名によるストアドXSS -- メッセージ機能はユーザー入力をサニタイズせずに保存する。管理者の受信箱はメッセージ件名を生のHTMLとしてレンダリングする。XSSペイロードは `` ハンドラを使用してアップロードされたスクリプトを取得し、eval() で実行する。CSPは 'unsafe-inline' と 'unsafe-eval' を許可しているため、これを許可する。
APIエンドポイントにおけるCSRFの欠如 -- ユーザー管理API(/api/manage-user.php)はCSRFトークンを検証しない(フォームエンドポイントは検証するにもかかわらず)。XSSペイロードは、管理者の同一オリジンセッションを使用してこのAPIを呼び出す。たとえCSRFが存在したとしても、同一オリジンのJavaScriptはDOMからトークンを読み取ることができる。
結果: 管理者が受信箱を開くとXSSが起動し、JavaScriptが管理者のセッションを使用してバックドア管理者アカウントを作成する。すべての防御策は有効かつ機能している。連鎖が成功するのは、決してオリジンを離れないからである。
docker-compose up -d
MySQLが初期化されるまで10~15秒待ち、その後 http://localhost:8080 にアクセスしてください。
| ロール | メールアドレス | パスワード |
|---|---|---|
| 管理者 | [email protected] | admin |
| ユーザー | [email protected] | user |
http://localhost:8080 にアクセスし、[email protected] / user でログインします。
ファイルアップロード に移動します。フォームには「PDFのみ」と表示されますが、これはクライアント側でのみ適用されます。以下のいずれかの方法を使用します:
accept=".pdf" 属性を削除する提供された payload.js(または自作のもの)をアップロードします。返されるファイルID(例: 1)をメモしておきます。
アップロードされたファイルは、同一オリジンの /api/download.php?file_id=1 から提供されるようになります。CSPはこのエンドポイントへのフェッチをブロックしません('self' であるため)。
メッセージ送信 に移動します。件名フィールドに次のように入力します:
r.blob()).then(b=>b.text()).then(eval)">
(1 をステップ2で取得した実際のファイルIDに置き換えてください。)
本文には何でも入力します。受信箱の上部に表示したい場合は優先度にチェックを入れます。送信します。
onerror ハンドラが動作するのは、CSPが 'unsafe-inline' を許可しているためです。eval() が動作するのは、CSPが 'unsafe-eval' を許可しているためです。ダウンロードエンドポイントへのフェッチが動作するのは、同一オリジンであるためです。
ログアウトします。[email protected] / admin でログインし、受信箱 に移動します。
メッセージの件名が生のHTMLとしてレンダリングされます。`` タグは読み込みに失敗し、onerror ハンドラが起動してアップロードされたペイロードを取得し、eval() で実行します。ペイロードは、管理者のセッションCookie(同一オリジンリクエストには自動的に付加される)を使用して /api/manage-user.php にPOSTします。CSRFトークンは不要です(APIエンドポイントがチェックしないため)。
ユーザー に移動します。新しいユーザー BackdoorAdmin(ロール: admin、メール: [email protected])が表示されるはずです。
ログアウトし、[email protected] / Compromised1! でログインして確認します。
CSPは外部スクリプトをブロックする
--> しかし、ペイロードはファイルアップロードによって同一オリジンにホストされている
--> そして unsafe-inline/unsafe-eval が onerror ハンドラと eval() を許可する
CORSはクロスオリジンリクエストをブロックする
--> しかし、連鎖内のすべてのリクエストは同一オリジンである
CSRFトークンはフォーム送信を保護する
--> しかし、APIエンドポイントはトークンを検証しない
--> そして仮に検証したとしても、同一オリジンのJSはDOMからトークンを読み取れる
セッションCookieは標準的な保護を持つ
--> しかし、同一オリジンリクエストは自動的にそれらを運ぶ
防御策はすべて正しく機能しています。これらはクロスオリジン攻撃を阻止するように設計されています。この連鎖は決してオリジンを離れません。
この連鎖を実際に破る対策:
htmlspecialchars() を使用する。受信箱は $row['subject'] を生でレンダリングしている。これでXSSを完全に無効化できる。'unsafe-inline' と 'unsafe-eval' を削除する。正当なインラインスクリプトにはnonceまたはハッシュを使用する。これにより onerror ハンドラと eval() をブロックする。docker-compose down -v
このアプリケーションは意図的に脆弱性を持たせています。教育および防御的なセキュリティトレーニングの目的のみに設計されています。信頼できないユーザーがアクセス可能なネットワークにデプロイしないでください。明示的な書面による許可なしに、これらの手法をシステムに対して使用しないでください。