Hello there, ('ω')ノ
ねらい
このLABは、アプリが機密ページ(例:/my-account)を返しているのに、CDN/リバースプロキシのキャッシュ規則が特定ファイル名だけ厳密一致でキャッシュ(例:/robots.txt)している歪みを突きます。 区切り文字(; や ?)とパス正規化(..%2f)の解釈差を組み合わせ、オリジンには /my-account を見せつつ、キャッシュには /robots.txt として保存させることで、被害者のCSRFトークンを盗み、最終的にadministratorのメールを変更します。
全体像(ストーリー)
- 自分のアカウントでログインし、/my-accountの仕組み(CSRFトークン有・機密情報含む)を把握
- 区切り文字(; と ?)がオリジンでパスを打ち切るかどうか検証
- キャッシュ側が/robots.txtだけを厳密一致でキャッシュすることを特定(X-Cache: miss → hit)
- キャッシュは..%2f を正規化して/robots.txtに解決する一方、オリジンは/my-accountとして扱う、という分岐点を発見
- 悪性URL(例:/my-account;%2f%2e%2e%2frobots.txt?wcd)を被害者に踏ませ、被害者版の /my-account HTMLを/robots.txt としてキャッシュさせる
- 攻撃者が同じURLを取りに行き、キャッシュから被害者のCSRFトークンを取得
- そのトークンでCSRF PoCを作り、adminのメールアドレスを変更してクリア
実践:一手ずつ「なぜそうするか」を添えて
1) 機密ページの性質を把握(CSRFトークンあり)
- 操作:wiener:peter でログイン → 「My account」でメール変更まで行い、POST /my-account/change-emailにcsrfが入っているのを確認。
- なぜ:このcsrfを盗めれば、管理者のメール変更を攻撃者が代行できます。
2) 区切り文字の挙動を確かめる(オリジンのパス解釈)
- 操作:/my-account/abc → 404(オリジンが抽象化しない) /my-accountabc → 404(キャッシュの気配なし)
- Intruderで /my-account§§abc を使って区切り候補を総当たり。
- 観察:; と ? が区切りとして機能。
- なぜ:オリジンは; や ? 以降を無視して/my-accountを処理する可能性が高い(サーブレットのパスパラメータやクエリ扱い)。
3) キャッシュの「厳密一致」対象を特定(/robots.txt)
- 操作:/robots.txtを2回リクエストし、X-Cache: miss → hitになることを確認。
- さらに /aaa/..%2frobots.txt でも 200 かつ hit になることを確認。
- 解釈:キャッシュは..%2f を解決して /robots.txt に正規化し、ファイル名一致でキャッシュする。 一方で、先のテストからオリジンは/aaa/..%2fmy-accountを正規化せず404を返すなど、オリジンとキャッシュで正規化の扱いが違う。
4) 二つの解釈差を合体させる(WCDの核)
- 目標:オリジンには/my-accountとして認証済みページを返させ、キャッシュには/robots.txt として保存させる。
構成要素:
- ;(区切り)でオリジンのパスを/my-accountに固定
- 区切りの後に%2f%2e%2e%2frobots.txt(= /../robots.txt)を付け、キャッシュ側に/robots.txtとして正規化保存させる
- ?wcdのようなキャッシュバスターを付け、タイミングずれ時にURLを変えて再実行可に
- 悪性URL例:
/my-account;%2f%2e%2e%2frobots.txt?wcd
- 検証:RepeaterでこのURLを送ると、初回はX-Cache: miss、二度目でhitになり、機密情報(APIキーなど)を含む /my-account の中身が/robots.txt としてキャッシュされることを確認。
5) 被害者に踏ませて、管理者版をキャッシュに載せる
- 操作:Exploit Serverで次のように自動遷移を仕込む。
<script>
document.location = "https://YOUR-LAB-ID.web-security-academy.net/my-account;%2f%2e%2e%2frobots.txt?wcd";
</script>
これをDeliver exploit to victim。
- 理屈:管理者は常時ログインしている前提。被害者がこのURLへ移動 → オリジンは /my-account を返す(管理者のセッションでレンダリング、CSRFトークン入り)→ キャッシュは /robots.txt として保存。 つまりキャッシュに「管理者版の /my-account HTML」が載る。
6) 30秒以内に自分で同じURLをGETし、キャッシュからCSRFを回収
- 操作:Repeaterで同じURLを送る(例:/my-account;%2f%2e%2e%2frobots.txt?wcd)。
- 観察:レスポンスはX-Cache: hit、本文に管理者のCSRFトークンが含まれている。
- コツ:時間が経ったらバスターを変えてwcd2などに更新し、再度「Deliver → 取得」を繰り返す。
7) 管理者のメールを変更(CSRF)
- 操作:Proxyの履歴からPOST /my-account/change-emailをRepeaterへ。 ボディ中のcsrfを、今盗んだ管理者のトークンに置き換える。 メール値は自分のものと異なる値にする(変更が明確になるように)。
- 自動化:右クリック → Engagement tools > Generate CSRF PoC → HTMLをExploit Serverに貼り付け → Deliver。
- 成功:ラボがSolvedに。
なぜ成立するのか(頭の中のモデル)
- オリジン視点:/my-account;(以降は無視) → 認証必須ページを生成、ユーザーごとのCSRFトークンも埋め込む。
- キャッシュ視点:リクエスト全体を正規化し、..%2f → /解決で/robots.txtと判断、そこに厳密一致規則で保存。
- 結果:同じURLを誰が取りに来ても、キャッシュの /robots.txtキーに載った他人のHTMLが返る。
つまずきポイント&対処
X-Cache が常に miss のまま まず/robots.txt単体で miss→hit を確認。 その後に/my-account;%2f%2e%2e%2frobots.txt?wcdの順で試行。 うまくいかない場合は; → ?や別の区切り候補も試し、ヒント掲載のDelimiter listを参照。
ブラウザでログインページに飛ばされる 実ブラウザはセッション不整合でリダイレクトしがち。 攻撃者側の取得はBurp Repeaterで行う(LAB手順どおり)。
CSRFトークンが見つからない HTMLをテキスト検索でname="csrf"やvalue=を探す。 時間切れならwcdの値を変えて再度「Deliver → 取得」。
実務目線の防御
- 機密レスポンスはキャッシュ不可:ヘッダでCache-Control: no-store、Vary適切化。
- キャッシュとオリジンの正規化ロジックを一致:; や ..%2fの扱いを揃え、正規化後にポリシー適用。
- 厳密一致の例外を見直す:/robots.txtなどの特別扱いをやめ、ディレクトリ配下のルールに統一。
- WAF/ルータで ; を拒否:パスパラメータ区切りの悪用を事前に遮断。
- CSRFトークンはCookie結合:双方向バインディング(SameSite + Double Submit 等)で盗難の影響を限定。
コピペ用スニペット
悪性URL(例)
/my-account;%2f%2e%2e%2frobots.txt?wcd
Exploit Server本文例
<script> document.location = "https://YOUR-LAB-ID.web-security-academy.net/my-account;%2f%2e%2e%2frobots.txt?wcd"; </script>
まとめ
このLABのキモは、「オリジンは /my-account、キャッシュは /robots.txt」という二重解釈を作ること。 区切り文字 ;でオリジンのパスを打ち切り、後段の..%2frobots.txtをキャッシュだけが正規化して厳密一致キャッシュに落とす。
被害者に踏ませて管理者版HTMLをキャッシュに載せ、同じURLでCSRFトークンを回収、最後にCSRF PoCでメール変更。
この思考(区切り・正規化・厳密一致の三角形)は、あらゆるWeb Cache Deceptionでそのまま再現性が高いパターンです。
Best reagrds, (^^ゞ