utsuroi xss labs Lab 02 / 5 — Apprentice
02

Stored XSS — 商品レビュー欄

Stored XSS into HTML context with nothing encoded

うつろい EC — 商品ページにはカスタマーレビュー機能がある。投稿されたレビューは保存され、その商品ページを開く全ユーザに表示される。

しかし、レビュー本文はエスケープされずにそのまま HTML に展開されている。Stored XSS の脆弱性。Reflected XSS と違って、一度投稿すれば永続的に、その商品ページを見る全ユーザのブラウザでペイロードが実行される。

左の「攻撃者ビュー」でレビューを投稿し、右の「被害者ビュー」(別ユーザがこの商品ページを開いたときの挙動)で alert を発火させてください。

shop.utsuroi.example.com / products/shop-skincare-01
攻撃者として: 下のフォームからレビューを投稿。本文に XSS ペイロードを仕込む。
shop.utsuroi.example.com / products/shop-skincare-01
サーバ側のコード(脆弱な実装)
// レビュー保存(POST 時)
$reviews[] = [
    'author' => $_POST['author'],
    'body'   => $_POST['body']  // ← エスケープなしで保存
];
save_to_db($reviews);

// レビュー表示(GET 時)
foreach ($reviews as $r) {
    echo '<div class="review-body">' . $r['body'] . '</div>';
    //                                    ↑ DBの値をそのまま innerHTML
}

Reflected XSS と違い、ペイロードが永続化されるため、攻撃者が一度投稿するだけで以降この商品ページを開く全ユーザの環境で実行されます。被害範囲が桁違いに大きい。

ヒント 1 — Lab 01 と同じペイロードが効くか?

レビュー本文は HTML 本文として展開されるので、Lab 01 と同じく <script> タグや <img onerror> が使えます。

ヒント 2 — 攻撃者ビューと被害者ビューの違い

左で投稿したレビューは右にも自動的に反映されます(同じデータベースを参照しているため)。被害者ビューは投稿フォームが見えないだけで、レビュー一覧の表示処理は同じです。

正解(ネタバレ)

左の攻撃者ビューのレビュー本文に以下を投稿:

<script>alert('Stored XSS')</script>

「投稿」ボタンを押すと、両方の iframe にレビューが追加されます。被害者ビュー側でも HTML として展開されて alert が発火 → 「✓ Stored XSS 成立」が表示されます。

これは「攻撃者が一度投稿すれば、以降この商品ページを開く全ユーザのブラウザで実行される」ことの再現です。実際の事案では Cookie 窃取など、より深刻な攻撃が行われます(Lab 04 で詳述予定)。