Shikata Ga Nai

Private? There is no such things.

【PRACTITIONER】Scanning non-standard data structures

Hello there, ('ω')ノ

非標準データ構造に潜むStored XSS

0. 概要(TL;DR)

セッションクッキーが <username>:<token> という非標準の構造を持ち、その username サブフィールドが Stored XSS のシンクになっている。username はサーバ側のアクセスログ/分析(admin閲覧用)に記録され、admin がそれを閲覧した際にスクリプトが発火する。

通常はBurp Scannerの「Scan selected insertion point」でこの非標準構造内の脆弱性を発見する想定のラボだが、本診断では手動で構造を解析し、curl/Pythonで自律的に攻略した。整合性チェック(Integrity violation detected)でリクエスト自体は500になるものの、改ざんしたusernameはログに記録され続ける点を突き、admin のブラウザで GET /admin/delete?username=carlos を実行させて carlos を削除した。

  • 脆弱箇所: セッションクッキー session=<username>:<token> の username 部分
  • ペイロード(username部): '"><svg/onload=fetch(\/admin/delete?username=carlos`)>`
  • 結果: admin閲覧時にXSS発火 → carlos削除 → Solved

1. 対象と前提

項目
対象 https://0a19002a04f9b74780e6214c00a1008b.web-security-academy.net/
自アカウント wiener:peter
目標 Stored XSS で admin セッションを悪用し carlos を削除する
想定ツール Burp Scanner(本診断では未使用=手動で代替)

2. 非標準データ構造の解析

2.1 クッキー構造

ログイン後のクッキー(URLデコード後):

session = wiener:qYsn0vrs6XGxH3fcHqqiEwta5nPMo3Pf
          └ username ┘ └────────── token ──────────┘
  • base64ではなく平文の username:token
  • token はログイン毎に変化(qYsn...JMG2...leWY...)。値が毎回変わることから、token は username 由来の決定的MAC ではなく、独立したセッションIDと推定(saltつきHMACの可能性も完全には排除できないため断定はしない)。

2.2 整合性チェックの存在

username 部分を改ざんすると全エンドポイントで 500 を返す:

クッキー 結果
wiener:<token>(正規) 200
wiener':<token>(username改変) 500 Integrity violation detected
ZZMARK:<token> 500 Integrity violation detected
wiener:<token>ZZMARK(token改変) 302(セッション無効→ログアウト)

→ 挙動差(token改変=302/ログアウト、username改変=500/Integrity violation)から、サーバは token から引いたセッションの username と、クッキー平文の username の一致を検証していると推定(実装が文字列比較かMAC検証かは外形からは不明)。いずれにせよ cookie だけで username を自由に変えると 500 になる。

2.3 Stored XSS のシンク(admin専用)

改ざん username を送っても、wiener が閲覧可能な //my-account/analytics のいずれにも反映されない(§検証で marker 不検出)。/adminAdmin interface only available if logged in as an administrator

→ シンクは admin が閲覧する分析ダッシュボード等にクッキー由来の username が記録・描画されるもの、と消去法で推定(攻撃者側からは直接観測できず、後述のSolved到達という結果から逆算した推定)。これが Burp Scanner が「Stored XSS」と判定する所以であり、非標準構造ゆえ手動発見が難しい(=ラボの主旨)。なお「Stored」判定も、別主体(admin)が後刻に閲覧して発火した結果からの推定で、消去法による分類である点を付記する。


3. 攻撃

3.1 着眼点:整合性違反でも username は記録される

整合性チェックはリクエスト処理を 500 で止めるが、アクセスログ/分析への username 記録はそれより前段(信頼境界の手前)で行われていると推定される(直接観測はできておらず、<XSS>:<token> 送信→500 受信→数秒後に carlos 削除・Solved という結果からの逆算)。すなわち攻撃者は 500 を受け取るだけだが、XSSペイロードはadmin閲覧用のログに保存され、後刻 admin の閲覧で発火する。

3.2 ペイロード設計(外部collector不要)

Burp Collaborator が無いため、cookie窃取+手動再利用ではなく、XSSがadminの認証済みブラウザ内で管理操作を直接実行する方式を採用。

username部に投入したペイロード:

'"><svg/onload=fetch(`/admin/delete?username=carlos`)>
  • 先頭 '"> で HTML属性/タグを脱出(単一・二重引用符の両コンテキストに対応)。
  • <svg/onload=...> の onload を空白なしで書く(属性値を引用符で囲まずに済み、レンダリングコンテキスト依存を回避)。文字列はバックティックで表現し引用符衝突を防ぐ。
  • admin のブラウザは同一オリジン・認証済みのため、fetch が cookie を自動送出し GET /admin/delete?username=carlos が成立。なお XSS経由の実行はCSRF防御の有無に関わらず成功する(XSSはCSRF対策を迂回する)ため、本実証から「CSRFトークン不要」とは断定できない。確実に言えるのは GET で状態変更(ユーザー削除)ができる設計自体が不備である点。

3.3 実行

クッキー session=<URLエンコードしたペイロード>%3a<token> を付けて //my-account?id=wiener/post?postId=1 にリクエスト(いずれも 500 を返すが username は記録される)。その後ラボ状態をポーリングしたところ、約8〜16秒で admin bot が閲覧 → XSS発火 → carlos削除 → is-solved を確認。


4. 根本原因

# 問題 説明
R1 非標準データ構造の未検証 セッションクッキーの username サブフィールドを、構造化データとして適切にサニタイズ/エスケープせずログに記録・描画している。
R2 Stored XSS(出力エンコード欠如) admin閲覧画面で username を HTMLエスケープせず描画している。
R3 信頼境界の手前でのログ収集(整合性違反データの保存) 整合性チェックで拒否(500)するリクエストの改ざん済みフィールドを、検証より前段でログ・分析に保存している(推定)。検証後に保存する設計なら攻撃面が消える。
R4 重要操作(ユーザー削除)がGETで実行可能 /admin/delete が GET で状態変更できる設計。XSS経由ではCSRF防御は迂回されるため、GET/POSTに関わらずXSSがあれば実行されるが、GETで状態変更可能な点自体が不備。

5. 影響

  • 攻撃者は一般ユーザー権限のみで、admin のブラウザ上で管理操作(ユーザー削除を実証、他の管理GET操作も同様に実行可能と推定)を代行させられる=admin権限での操作代行(confused deputy)。攻撃者がセッション/認証情報を奪取して継続的に admin として振る舞える「乗っ取り」までは本診断では未実証。
  • XSSのため管理画面に表示される他ユーザー情報等の機微情報の読み取りも可能。document.cookie の窃取は HttpOnly 属性の有無を未確認のため成否は断定しない。
  • 非標準構造ゆえ通常のスキャン/レビューで見逃されやすく、検知が遅れるリスクが高い。

6. CVSS v3.1 評価

公式計算式をPythonで実装して算出(環境値込み)。

区分 スコア
Base 7.4(High)
Temporal 7.1
Environmental 7.1(High)

ベクター: CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:H/A:N/E:H/RL:O/RC:C/CR:M/IR:M/AR:M

メトリクス 理由
AV N ネットワーク越しに攻撃可能。
AC L 構造解析後は決定論的に発火(ペイロードは安定)。
PR L 有効なセッショントークンを得るため一般ユーザーのログインが必要。
UI R admin(被害者)がシンク画面を閲覧する必要がある。
S C 脆弱コンポーネント(ログ/分析描画)の欠陥が、別権限主体(adminセッション)でのコード実行=管理操作を引き起こす。境界越え。
C L XSSにより admin の管理画面に描画される機微情報(他ユーザー情報等)の読み取りが可能なため限定的な機密影響を計上(cookie窃取は HttpOnly 未確認のため根拠にしない)。
I H admin権限で carlos を削除=重大なデータ削除(実証)。同シンクから他の管理GET操作も実行可能と推定され、完全性への影響は大。
A N 直接の可用性影響なし。
環境値 CR:M/IR:M/AR:M 汎用診断のため資産価値を中位(M)と仮定。

7. 対策

優先度順:

  1. 【根治・R1/R3/R4を同時に無力化】平文フィールドの排除: クッキーに平文 username を持たせず、token のみでサーバ側から username を引く。これで非標準構造のサブフィールド(=挿入点)そのものが消滅する。出力エンコードと並ぶ最上位の根治策。
  2. 【即効・R2】出力エンコード: admin閲覧画面を含む全描画箇所で username をコンテキストに応じてHTMLエスケープ(フレームワークの自動エスケープを既定有効化)。
  3. 【多層防御】CSP導入: Content-Security-Policyscript-src 制限・inline/イベントハンドラ禁止)で、エンコード漏れがあっても svg/onload 実行を抑止。
  4. 【R3】ログ/分析パイプラインの両面安全化: ログ取り込み時にサニタイズし、かつ各コンシューマ(ダッシュボード/SIEM/CSV等)で出力エンコード。描画箇所だけの対策では別コンシューマで再発する。整合性違反データは検証後に(必要なら無害化して)保存する。
  5. 【R4】重要操作の保護: /admin/delete 等を POST化+CSRFトークン必須にし、GETでの状態変更を不可にする(XSS自体はCSRF防御を迂回するため、本項はR1〜R3と併用して初めて有効)。
  6. 【運用】非標準構造の継続スキャン: 構造化データ(独自区切りのクッキー等)の各挿入点を定期的にスキャン対象に含める(本ラボの教訓)。

8. 証跡まとめ

項目
クッキー構造 session=<username>:<token>(平文)
整合性エラー username改変時 Integrity violation detected(500)
XSSシンク admin閲覧の分析/ログ描画(username未エスケープ)
投入ペイロード '"><svg/onload=fetch(\/admin/delete?username=carlos`)>`
結果 admin発火→carlos削除→Solved

Best regards, (^^ゞ