Shikata Ga Nai

Private? There is no such things.

Lab: CSRF vulnerability with no defenses

Hello there, ('ω')ノ

概要(ラボの目的と達成条件)

このラボは、CSRF(クロスサイト・リクエスト・フォージェリ) の基本を学ぶための入門演習です。 対象アプリの「メールアドレス変更」機能には 一切の CSRF 防御がなく、被害者がログインした状態で攻撃者が用意したページを開くと、勝手にメールアドレスを変更されてしまう状況になっています。

ラボの達成条件は:

  • 攻撃用 HTML(CSRF ページ)を作成し、Exploit Server にアップロード
  • そのページを犠牲者に閲覧させることで、犠牲者のメールアドレスを任意の値に変更する

ことです。


攻撃者の全体的思考(高レベル)

攻撃者が最初に考えるのは:

  1. 「どのリクエストを偽装できれば被害になるか?」 → このラボでは、メールアドレス変更の POST リクエスト がターゲット。

  2. 「ブラウザが自動的にクッキーを送ってくれる状況を作れるか?」 → 被害者がログイン済みで、攻撃ページを開いたときに → SameSite 制限や CSRF トークンがなければ、そのまま実行される

  3. 「そのリクエストを HTML からどうやって送るか?」自動送信フォーム(<form> + JavaScript の submit) を使えばよい。

今回は「CSRF 防御なし」という前提なので、かなりシンプルな HTML だけで攻撃が成立します。 攻撃者にとってはラッキーな状況、開発者にとっては最悪のパターンです。


前提(入力、想定される処理)

メール変更機能はおおむね次のような HTTP リクエストになっていると考えられます:

  • メソッド:POST
  • パス:/my-account/change-email
  • フォームパラメータ:

    • email(新しいメールアドレス)

例:

POST /my-account/change-email HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Cookie: session=xxxxxxxxxxxx
Content-Type: application/x-www-form-urlencoded

email=wiener@example.com

サーバ側の処理イメージ:

  1. Cookie の session を見て「どのユーザーか」を判定
  2. リクエストボディの email を新しいメールアドレスとして保存
  3. 成功メッセージを返す

ここで CSRF トークンの検証も、Referer チェックも、SameSite=Lax/Strict のような保護もない ため、 別サイトから同じ POST を送られても成立してしまうのが問題です。


ステップバイステップ(操作ごとに解説)

ステップ1:自分でログインし、正規のメール変更リクエストを観察

操作:

  1. Burp のブラウザからターゲットサイトを開く
  2. ユーザー wiener / peter でログイン
  3. 「My account」などの画面から メールアドレス変更フォームに移動
  4. 適当なメールアドレスを入れて「Update email」をクリック

なぜこの操作か:

  • 攻撃者はまず「正規のリクエストの形を知る」必要があります。
  • どの URL に、どのパラメータで送ればメールが変わるのか確認します。
  • この「正しいリクエスト」をそのまま 被害者に対する偽リクエストとして再利用します。

ステップ2:Burp Proxy でリクエストを確認

操作:

  • Burp の Proxy → HTTP history を開き、 POST /my-account/change-email を探す。

なぜこの操作か:

  • 実際にフォーム送信されたときの パラメータ名、URL、メソッド を正確に把握するため。
  • CSRF では 「ほぼ同じリクエストを、別サイトから自動送信させる」 のが基本戦略です。

ステップ3:CSRF 用の HTML を組み立てる

Burp Suite Professional なら自動生成も可能ですが、仕組みを理解するために手書きの HTML を見てみます。

攻撃に使う HTML の骨格:

<form method="POST" action="https://YOUR-LAB-ID.web-security-academy.net/my-account/change-email">
    <input type="hidden" name="email" value="victim-new@attacker.com">
</form>
<script>
    document.forms[0].submit();
</script>

重要ポイント:

  • method="POST" → 本物のリクエストと同じメソッドにする必要があります。
  • action="https://.../my-account/change-email"正確な URL を指定しないと期待通り動きません。
  • <input type="hidden" name="email" ...> → 画面には表示しないが、任意のメールアドレスを送れるようにする。
  • <script>document.forms[0].submit();</script> → ページを開いただけで 自動でフォームが送信されるようにする。 (被害者にボタンを押させる必要がなくなる)

なぜこの操作か:

  • 被害者にとっては「別サイトを見ているだけ」でも、
  • 実際にはブラウザ内部で ターゲットサイトへの POST が自動実行される状態を作るためです。
  • Cookie(セッション)はブラウザが自動添付するので、攻撃者側は何もする必要がありません。

ステップ4:Exploit Server に HTML を保存

操作:

  1. ラボ画面右上の Exploit server へのリンクをクリック
  2. Body のテキストエリアに、先ほどの HTML を貼り付け
  3. タイトルなどは任意で、Store ボタンをクリック

なぜこの操作か:

  • ラボ環境では、攻撃者用の ホスティング環境(Exploit Server)が用意されています。
  • 実戦では、自分のドメインやブログなどで同様の HTML をホストするイメージです。

ステップ5:自分で攻撃を試して動作確認する

操作:

  1. 攻撃者アカウント(wiener)でログインした状態のまま
  2. Exploit Server で View exploit をクリック
  3. その後、ターゲットサイトの My account ページを再度開き、 メールアドレスが攻撃 HTML に書いたものに変わっているかを確認

なぜこの操作か:

  • 本当に CSRF が成立しているかどうかを 自分で検証するため
  • ここで動作しない場合、URL ミスやフォーム構造の誤りを修正します。

※ ヒントにあるように、自分のメールアドレスを本番用のものにしてしまうと被害者用に使えなくなるので、 テスト時と本番時で メールアドレスを変える のが重要です。


ステップ6:被害者用にメールアドレスを変更して Deliver

操作:

  1. Exploit Server の HTML の中の value="..." で指定しているメールアドレスを テスト時とは 別のアドレス(まだ誰も使っていないもの)に変更
  2. Store で保存
  3. Deliver to victim をクリック

これで、被害者(ラボ内部の「ターゲットユーザー」)が Exploit Server のページを見に行き、 CSRF によってメールアドレスが変更され、ラボが Solved になります。


Burp 操作(Proxy / Repeater / Engagement tools)

1. Proxy でリクエストを取得

  • Proxy → HTTP history
  • POST /my-account/change-email を選択

2. Burp Suite Professional の場合(CSRF PoC 自動生成)

  • 対象リクエストを右クリック → Engagement tools → Generate CSRF PoC
  • オプションで Include auto-submit script(自動送信スクリプトを含める)にチェック
  • Regenerate → 生成された HTML をコピーして Exploit Server へ貼り付け

3. Community Edition の場合(手動で HTML 作成)

  • 対象リクエストを右クリック → Copy URL
  • その URL を action="" に貼り付け、先のテンプレート HTML を自分で組む

テンプレートの例(改めて):

<form method="POST" action="https://YOUR-LAB-ID.web-security-academy.net/my-account/change-email">
    <input type="hidden" name="email" value="victim-new@attacker.com">
</form>
<script>
    document.forms[0].submit();
</script>

レスポンス確認方法(何を見れば成功か)

CSRF が成功しているかどうかは、最終的にアカウント情報が変わっているかで確認します。

  • 自分で試したとき:

    • My account ページの メールアドレス が、攻撃 HTML に書いた値に変わっていれば成功
  • 被害者に Deliver した後:

    • ラボ画面上部に 「Congratulations, you solved the lab!」 が表示される

HTTP レベルで確認する場合:

  • POST /my-account/change-email のレスポンスが 302 リダイレクト成功メッセージを返していること
  • その後の GET /my-account のレスポンスに、新しい email が反映されていること

トラブルシュート(失敗パターンと対策)

症状 考えられる原因 対策
メールアドレスが変わらない action の URL が間違っている Burp のリクエストから正確にコピーし直す
自分で View exploit しても何も起きない ログインしていない、またはセッション切れ 先にターゲットサイトにログインし直す
400 / 404 が返ってくる メソッドやパラメータ名が違う Burp のリクエストと HTML を1行ずつ照合
Deliver してもラボが解決しない そのメールアドレスが既に誰かに使われている ヒント通り、未使用のメールアドレスに変更して再送

防御(開発者向け対策)

開発者側でこのような CSRF 攻撃を防ぐには:

  1. CSRF トークン方式の導入(最重要)

    • フォームごとにランダムなトークンを埋め込み、 その値をサーバ側で検証する
    • トークンは セッションごと / リクエストごとにユニークにする
  2. SameSite Cookie 属性の活用

    • Set-Cookie: session=...; SameSite=Lax などを設定し、 クロスサイトからのクッキー送信を制限
  3. 重要操作を GET ではなく POST/PUT/DELETE に限定

    • (今回はすでに POST だが、GET で状態変更しないことも CSRF の基本)
  4. Referer / Origin ヘッダの検証(補助策)

    • 送信元オリジンが自サイトかどうかを確認し、異なる場合は拒否
  5. UI レベルの確認(再認証やパスワード再入力)

    • メール変更など重要な操作にはパスワード再入力を要求する

Best regards, (^^ゞ