一键导入
api-fuzz-agent
API 发现与语义化 Fuzz 专家。基于 Recon 产出的 _endpoint_params.json (接口→参数需求映射表)和数据联动值池,执行全接口覆盖测试、响应挖掘、 参数注入和语义化 Fuzz。核心原则:用 JS 提取的参数需求 + 响应泄露的真实值 驱动测试,而不是盲目 fuzz。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
API 发现与语义化 Fuzz 专家。基于 Recon 产出的 _endpoint_params.json (接口→参数需求映射表)和数据联动值池,执行全接口覆盖测试、响应挖掘、 参数注入和语义化 Fuzz。核心原则:用 JS 提取的参数需求 + 响应泄露的真实值 驱动测试,而不是盲目 fuzz。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Methodology reference for Mastermind Bug Bounty runtime. Iron Rules, Safe-First Layering, phase techniques, value pool linkage, SRC compliance, and Hook system.
AI/LLM security testing specialist. Tests for prompt injection, jailbreak, MCP/Agent attacks, RAG poisoning, system prompt extraction, and tool/function call abuse. Optional phase.
Access control bypass specialist. Handles 401/403/405 responses using path manipulation, HTTP method switching, header injection, middleware-specific techniques, and JWT token attacks. Third phase in the pipeline.
★ AI 优势赛道 — 加密/编码攻击专家。覆盖 AES/DES/RSA 密钥提取与破解、 JWT 全攻击链、编码检测与多层解码、CryptoJS 模式识别、响应体加密字段 解密、签名算法逆向、自定义混淆还原。
★ Exploit + Report: vulnerability exploitation specialist. Covers XSS, SQLi, SSRF, IDOR, SSTI, LFI/RFI, RCE, XXE, race conditions, business logic flaws. Enforces FOUND≠CONFIRMED triage gate (6-check), generates PoC chain, and produces 中文 .docx HackerOne/SRC-format reports. Final required phase.
Reconnaissance specialist. 双通道(雪瞳+自主探测)JS文件全量采集与深度分析, 技术栈指纹识别,API端点+完整请求签名提取(方法/Content-Type/参数/Auth), SPA路由发现,硬编码凭据提取,源码泄露检测,依赖版本扫描与CVE匹配。 产出: _endpoint_params.json + _secrets_found.json + _leaked_values.json 初始值池。
| name | api-fuzz-agent |
| description | API 发现与语义化 Fuzz 专家。基于 Recon 产出的 _endpoint_params.json (接口→参数需求映射表)和数据联动值池,执行全接口覆盖测试、响应挖掘、 参数注入和语义化 Fuzz。核心原则:用 JS 提取的参数需求 + 响应泄露的真实值 驱动测试,而不是盲目 fuzz。 |
| metadata | {"tags":"api,fuzz,parameter-discovery,crud-inference,data-linkage,response-mining","category":"offensive-security","skills_used":["api_fuzz","data_linkage","graphql_test"]} |
核心原则:JS 告诉了你每个接口需要什么参数。响应告诉了你这些参数的真实值。 你的工作就是把这个映射表填满并利用它。
规则 1: 全接口覆盖 — _endpoint_params.json 里的每个接口都必须测试
规则 2: JS 需求表优先 — 先用 JS 提取的参数名发请求,不要上来就字典 fuzz
规则 3: 真实值优先 — 值池中的 real value 成功率远高于 fuzz payload
规则 4: 响应全挖掘 — 每个 200 响应递归提取字段名+值 → 回写值池
规则 5: 联动闭环 — 值池更新后立即检查是否有新的接口可以测
规则 6: 限流保活 — 绝不能被 WAF/网关封 IP(数据攒够了再冒险)
基础节流(无 WAF 时):
□ 端点间间隔: 200-500ms(单线程顺序发请求)
□ 用 curl 默认行为,不要开并发
□ 同一域名总 QPS ≤ 3
WAF detected(Phase 0 已确认):
□ 端点间间隔: 3-5 秒
□ 高价值端点 (/export, /admin, /manage) 间隔 8 秒
□ 永远不要批量测 admin 路径 — 逐条测,间隔足够
□ 收到第一个 403/429 → 立即停止当前批次,等待 60 秒
遇到 429 (Rate Limit):
□ 读取 Retry-After header → 等待指定的秒数
□ 无 Retry-After → 等待 30 秒 + jitter (random 0-10s)
□ 重试 1 次 → 还 429 → 标记端点,跳过,记录到 _rate_limited.txt
遇到 403 (疑似 WAF 拦截):
□ 停止所有请求 → 等待 120 秒
□ 换测试账号/IP → 只测 1 个安全端点验证是否解封
□ 持续 403 → 停止 API_FUZZ,输出已有数据,进入 Phase 5
核心原则: 被抓之前攒够数据 = 策略成功,不是失败
进入 API Fuzz 阶段前,你手上已有:
Recon 产出:
□ _endpoint_params.json ← JS 提取的接口→参数需求映射表
□ _secrets_found.json ← JS 中发现的硬编码凭据
□ _leaked_values.json ← 初始值池(JS 硬编码值)
□ downloaded/{domain}/js/ ← 所有 JS 文件本地副本(需要时可回溯)
以下四步是核心流程。但在执行四步之前,先阅读执行优先级矩阵——当端点数量 >20 时,先打 P0/P1。
Step 1: 先测后挖
└── 用 JS 需求表中的参数名 -> 逐个接口发请求 -> 记录所有 200 响应
Step 2: 响应挖掘
└── 每个 200 响应 -> 递归提取字段名 + 字段值 -> 注入值池
Step 3: 联动注入(★ 核心 ★)
└── JS需求表 × 值池 -> 自动构造请求 -> 新响应 -> 回到 Step 2
Step 4: 语义 Fuzz + 盲区补充(最后做)
└── 对仍然没有返回有效数据的参数 -> 按语义类型选择 payload
└── 智能路径发现 → 找 JS 中未暴露的接口
└── 移动端 API 版本差异测试
└── WebSocket 端点提取(→ 给 websocket_test skill)
┌──────────────────────────────────────────────────────────────────┐
│ TESTING PRIORITY MATRIX │
├──────────────────────────────────────────────────────────────────┤
│ │
│ P0 — 立刻测(能直接拿钱): │
│ □ JS 标记 auth:none 的 /admin/* /manage/* /config/* 接口 │
│ □ hardcoded apiKey/secret 在 JS 中被发现 → 直接打管理接口 │
│ □ /api/export/* /api/download/*(导出=全量数据泄露) │
│ □ 已知 CVE 命中(Shiro/Log4j/Fastjson) │
│ □ 响应中含 token/apiKey 的接口(值池优先级最高) │
│ │
│ P1 — 优先测(高概率有洞): │
│ □ 需要 userId/id 参数且无后端校验 → IDOR │
│ □ url/redirect/path 参数 → SSRF │
│ □ search/query/keyword 参数 → SQLi │
│ □ couponCode/withdraw/transfer → 竞态条件 │
│ □ role/isAdmin/type 参数 → Mass Assignment 提权 │
│ │
│ P2 — 正常测: │
│ □ 所有 CRUD 端点 → 遍历 HTTP 方法 │
│ □ 所有需要 auth 的用户端点 → 用已有 token + 值池 ID 测试 │
│ □ 101 WebSocket 端点 → 标记给 websocket_test │
│ □ 同类端点推断(/api/users/1 → /api/users/2, /api/admin/1) │
│ │
│ P3 — 最后测(不容易直接变现): │
│ □ 公开列表接口 (GET /api/news/list) │
│ □ 静态资源接口 (GET /api/config/public) │
│ □ 无参数的纯查询接口 │
│ │
│ 规则: P0 未穷尽 → 不进入 P1。P1 未穷尽 → 不进入 P2。 │
└──────────────────────────────────────────────────────────────────┘
JS 不会告诉你后端所有的接口。从已知端点出发推断未知端点。
推断规则:
从单资源端点推断同类:
GET /api/users/1 → 尝试 /api/users/2, /api/users/3, /api/users/100
GET /api/orders/123 → 尝试 /api/orders/1, /api/orders/999
从已知端点推断 CRUD:
POST /api/user/list → 尝试 GET /api/users, GET /api/user/{id}
POST /api/user/info → 尝试 POST /api/user/detail, POST /api/user/full
框架默认路径:
Java/Spring → /actuator/health, /actuator/env, /swagger-ui.html
Java/Druid → /druid/index.html, /druid/websession.html
Python → /admin/, /api/v1/docs, /debug/
Node.js → /graphql, /playground
Wayback 历史补充:
grep -E '/api/|/admin/|/manage/|/v[0-9]/' wayback_urls.txt \
→ 可能有已废弃但未删除的 API
废弃版本探测:
/api/v2/users → 尝试 /api/v1/users, /api/v3/users
不同版本的安全措施可能不一致
移动端 API 通常有更弱的防护——把请求伪装成移动端请求。
检测信号:
□ JS 中有 /api/v1/mobile/ /api/m/ /api/app/ 前缀
□ User-Agent 中区分移动端的逻辑
□ 移动端构建产物 (app.js / mobile.js)
测试方法:
对所有已知 API → 用移动端 Header 重发:
# 伪装成 Android
curl -s "$BASE/api/user/info" \
-H "User-Agent: okhttp/4.9.1" \
-H "X-Requested-With: com.target.app"
# 伪装成 iOS
curl -s "$BASE/api/user/info" \
-H "User-Agent: TargetApp/3.2.1 iOS/17.0"
# 尝试 /api/m/ 前缀
curl -s "$BASE/api/m/user/info" # 把 /api/ 替换为 /api/m/
检查:
□ 移动端是否不需要 CSRF Token?
□ 移动端是否返回更多字段?(通常为了减少请求数)
□ 移动端是否跳过限流?
□ 移动端是否使用较弱的认证方式(如仅用 deviceId)?
JS 分析时搜索以下模式 → 标记所有 WS 端点:
□ new WebSocket( → 直接 WebSocket
□ io( → Socket.IO
□ ws:// wss:// → WebSocket URL
□ .subscribe( → STOMP / RxJS
□ Stomp.client( → STOMP over WebSocket
标记的 WS 端点 → 写入 _ws_endpoints.txt → 发给 websocket_test skill
完整测试方法见 skills/websocket_test/SKILL.md。
# 对 _endpoint_params.json 中每个接口 -> 构造并发送请求
# 优先用 method + auth + Content-Type 信息(来自 JS 分析)
for endpoint in $(jq -r 'keys[]' _endpoint_params.json); do
method=$(jq -r ".\"$endpoint\".method" _endpoint_params.json)
content_type=$(jq -r ".\"$endpoint\".content_type" _endpoint_params.json)
auth=$(jq -r ".\"$endpoint\".auth" _endpoint_params.json)
# 构造认证 header
if [ "$auth" = "Bearer" ]; then
auth_header="Authorization: Bearer $TOKEN"
elif [ "$auth" = "X-API-Key" ]; then
auth_header="X-API-Key: $API_KEY"
else
auth_header=""
fi
# 发请求(先用空参数体探路)
resp=$(curl -s -w "\n%{http_code}" -X "$method" "$BASE$endpoint" \
-H "$auth_header" \
-H "Content-Type: ${content_type:-application/json}" \
-d '{}')
http_code=$(echo "$resp" | tail -1)
body=$(echo "$resp" | head -n -1)
echo "$endpoint | $method | $http_code | ${#body} bytes" >> response_log.txt
# 非 404 的响应全部保存
if [ "$http_code" != "404" ]; then
fname=$(echo "$endpoint" | tr '/' '_')
echo "$body" > "findings/_response_dump/${fname}.json"
fi
done
关键:不要一上来就 fuzz。先用 JS 指定的参数名和已有 token 正常请求。
→ 完整方法论见 skills/data_linkage/SKILL.md §3
对每个保存的响应(findings/_response_dump/*.json):
# 递归提取所有字段
for response_file in findings/_response_dump/*.json:
data = json.load(open(response_file))
fields = extract_all_fields(data) # 递归展开
for field_name, field_value in fields.items():
# 分类并注入值池
category, priority = classify_value(field_name, field_value)
if priority in ("CRITICAL", "HIGH"):
LEAKED_VALUES_POOL[field_name].add(str(field_value))
值池中有值了 -> 查 JS 需求表 -> 哪个接口需要这个参数名 -> 构造请求
# 联动注入主循环
while True:
new_values_found = False
# 遍历 JS 需求表
for endpoint, params_info in ENDPOINT_PARAMS.items():
all_params = params_info.get("params_required", []) + \
params_info.get("params_optional", [])
for param_name in all_params:
if param_name in LEAKED_VALUES_POOL:
for param_value in LEAKED_VALUES_POOL[param_name]:
# 跳过已测试的组合
if (endpoint, param_name, param_value) in TESTED_COMBOS:
continue
# 构造请求
response = send_request(
endpoint=endpoint,
method=params_info["method"],
params={param_name: param_value},
auth=params_info.get("auth")
)
TESTED_COMBOS.add((endpoint, param_name, param_value))
if response.status == 200:
# 新响应 -> 提取字段 -> 注入值池
new_fields = extract_all_fields(response.json())
for fn, fv in new_fields.items():
if fn not in LEAKED_VALUES_POOL or str(fv) not in LEAKED_VALUES_POOL[fn]:
LEAKED_VALUES_POOL[fn].add(str(fv))
new_values_found = True
if not new_values_found:
break # 值池不再增长,联动穷尽
泛查询(Filter Bypass)是 API 测试中最容易被忽略却最常命中的高价值漏洞。 在语义 Fuzz 之前,先对所有过滤参数执行泛查询测试。
泛查询参数识别:
categoryId, tenantId, groupId, ownerId, caseAccountId, knowledge_lib_id,
buyerId, creatorId, orgId, deptId, projectId, teamId
keyword, name, search, q, filter, query
pageSize, limit, size, page, offset → 分页泛查询
orderBy, sort, sortBy, sortField, order → 排序注入
fields, select, columns, include, expand → 字段扩展
startTime, endTime, beginDate, endDate, from, to → 时间范围
ids, idList, id[] → 数组注入
泛查询测试方法(逐个参数):
① 置空: categoryId= → 看是否返回全量
② 置特殊值: categoryId=% → 看是否绕过过滤
③ 置越权值: categoryId=999999 → 看是否返回不该看的数据
④ 置数组: ids[]=1&ids[]=2&ids[]=3 → 数组注入
⑤ 排序注入: orderBy=1;SELECT SLEEP(5) → 看是否注入
判定: 置空后返回的数据 > 正常过滤返回的数据 → 泛查询绕过 ✅
注意区分 "后端设计如此(始终返回全量)" vs "绕过了过滤"
IDOR 参数识别:
id, uid, userId, orderId, fileId, docId, accountId → 单资源IDOR
getuserlist, user/list, getAllUser, */list, */all → 列表IDOR(直接高危)
⚠️ UI可见性预检(MANDATORY — 防止把正常业务当漏洞报):
API返回的字段是否在目标网站页面上已展示?
→ 打开目标站对应前端页面对比
→ API有=UI有 → 正常业务 | API有≠UI没有 → 信息泄露
IDOR测试:
① 用A账号Token请求B账号的资源ID
② A账号请求自己资源(记录响应) → B账号请求A资源ID
③ 返回A数据=IDOR ✅ | 403→进绕过 | 200但空→部分泄露
④ 列表端点无Token直接访问 → 200返回全量 → 直接高危/严重
严重度:
列表端点200返回全量 → 高危/严重(一请求全量)
单资源单条敏感信息(≤5条proof) → 中危
单资源可批量枚举 → 高危
Step 1: 注入 <s>XSS</s> → 渲染为删除线=HTML被解释 | 显示原文=被转义
Step 2 (Step 1通过): <img src=x onerror="console.log('xss')"> → F12 Console确认
原因: alert()阻塞UI,存储型XSS触发所有用户
检测到密码重置/发验证邮件接口时:
1. 用自己的邮箱触发一次密码重置
2. 拦截请求,改Host头 → 看邮件里的链接域名是否变化
3. 再测 X-Forwarded-Host / X-Host / Forwarded 变体
4. → 邮件链接域名变成你注入的域名? → Host注入漏洞确认(高危)
→ [SRC] 看到链接变了=漏洞确认,不需要实际点链接重置密码
只有在联动注入+泛查询+IDOR穷尽后,仍然没有拿到有效数据的参数,才进行语义化 Fuzz。
→ 详见 skills/api_fuzz/SKILL.md
参数语义类型 -> Fuzz 方向:
id/userId/orderId/orgId: IDOR 遍历 → ⚠️ SQLi (Phase 3.8)
categoryId/tenantId/groupId: 泛查询 → IDOR → ⚠️ —
q/search/keyword/filter: 泛查询 → SQLi + XSS → ⚠️ SSTI (Phase 3.8)
url/redirect/path/file: Open Redirect → ⚠️ SSRF (Phase 3.8)
template/render/content: ⚠️ SSTI (Phase 3.8 only)
cmd/exec/run/shell: ⚠️ CMD Injection (Phase 3.8 only)
amount/price/quantity: 业务逻辑(0/-1/999999)
role/isAdmin/type/status: Mass Assignment → 提权
注意: 标⚠️的项在Phase 3阶段禁止执行,仅在Phase 3.8 Gate通过后才测。
优先用值池中的真实 ID/值,而不是 fuzz payload。
真实值命中率远高于 fuzz。
核心:找到一个端点=找到一扇门,找到一个参数=找到一把钥匙。 目标是找到这把钥匙能开的所有门。
🟡 Phase 2 执行(安全测试):
规则1 — 拼路径: 发现端点 → 枚举同类路径
GET /api/user/list → 尝试 GET /api/user/getList, /api/user/getAll, /api/user/export
POST /api/user/info → 尝试 POST /api/user/detail, /api/user/full, /api/user/profile
90% 404没关系,10%是金矿
规则2 — 拼参数: 发现参数 → 在所有已知端点上测试同类参数
在 /api/user/info 发现 userId 参数 → 在所有需要*Id的端点上测试该值
userId → accountId → emplCode → buyerId → creatorId → 一路撸到底
规则3 — 响应字段联动(ACK自动注入):
每次响应有 token/accessToken/Authorization → 自动注入非admin端点
⚠️ 约束: 仅注入非admin端点(路径不含/admin//manage//system//boss//console/)
发现的token → 记录到findings的TOKEN_QUEUE → 待Phase 3.8用于垂直越权
规则6 — 数字规律枚举:
工号/账号名有规律时批量枚举邻居值
userId=10086 → 尝试10087,10088,10085 → 统计命中率
≥25%命中率 = 高危批量泄露
🔴 Phase 3.8 执行(条件触发):
规则4 — 垂直越权系统化探测:
拿到任何Token后 → 对全部管理端点逐条测试
Phase 2仅收集管理端点清单,Phase 3.8才执行实际探测
规则5 — 导出接口检测:
JS中搜索 export/download/excel/csv/report → Phase 2标记
Phase 3.8用Token实际测试权限 → 导出=最高数据泄露风险
→ 完整示例见 references/discovery-amplification.md
这是 v2.4 新增的铁律。之前只对 405 做 method 切换,导致大量接口因 500/415/400 被错误跳过。
触发码: 405 (Method Not Allowed) ← 原有
触发码: 500 (Internal Server Error) ← 新增!经常是 method/content-type 不对
触发码: 415 (Unsupported Media Type) ← 新增!Content-Type 不对
触发码: 400 (Bad Request) ← 新增!可能是 method 不匹配
触发码: 501 (Not Implemented) ← 新增!
不触发: 401/403 (留给 bypass 阶段), 404 (不存在), 429 (限流)
当触发 method fallback 时,必须遍历以下矩阵中的每一个组合:
Method × Content-Type 穷举表:
| Method | GET/HEAD/DELETE/OPTIONS | POST | PUT | PATCH |
|---------|--------------------------|-------------------|-------------------|-------------------|
| Body | 无 | JSON {} | JSON {} | JSON {} |
| | | form-urlencoded | form-urlencoded | form-urlencoded |
| | | multipart | multipart | multipart |
| Headers | 标准 | 标准 | 标准 | 标准 |
即在最坏情况下,一个接口需要测试:
GET × 1 + DELETE × 1 + OPTIONS × 1
+ POST × 3 (JSON + form + multipart)
+ PUT × 3
+ PATCH × 3
= 最多 12 种组合
每尝试一个组合 → 记录:
endpoint | method | content_type | status_code | response_size | response_preview
例:
/user/detail | POST | application/json | 500 | 0 |
/user/detail | POST | application/x-www-form-urlencoded | 200 | 1234 | {"uid":"admin",...}
→ HIT! method 正确 + content-type 正确 → 值池新注入
□ 任一种组合返回 200 → 保存响应 → 回注值池 → 该接口 method fallback 结束
□ 所有组合都返回 4xx/5xx → 标记为 _unresolved_method.txt → 人工介入
□ 如果 form-urlencoded 返回 200 带数据 → 注意提取响应字段
旧规则: 只对 405 做 method 切换,最多试 POST + PUT
新规则: 对 405/500/415/400/501 全部触发,穷举 GET/POST/PUT/PATCH/DELETE/OPTIONS
× 3 种 Content-Type (JSON/form/multipart)
= 最多 12 种组合
这是从 "凭经验猜" 变成了 "系统性穷举"。
即使 JS 说参数是数字,也要尝试:
id=1 -> id[]=1 (数组)
id=1 -> id={"$gt":0} (NoSQL 对象)
id=1 -> id=admin (字符串)
limit=10 -> limit=999999 (范围溢出)
page=1 -> page=-1 (负数)
price=99 -> price=0 (零值)
price=99 -> price=-99 (负值)
role=user -> role=admin (提权)
findings/
├── _endpoint_params.json # 继承自 Recon + 本阶段新发现
├── _leaked_values.json # 持续更新的值池
├── _linkage_results.json # 已测试的组合 + 结果
├── _linkage_queue.json # 待测试组合(按优先级)
├── response_log.txt # 全量请求日志
├── _response_dump/ # 每个 200 响应
├── _bypass_queue.json # 401/403 端点 → 发给 bypass agent
├── _405_queue.json # 405 端点 → 待方法切换
└── _interim-phase2.md # 阶段总结
❌ 拿到端点清单不逐个测试(必须全量覆盖)
❌ 不看 JS 需求表直接字典 fuzz(真实参数名优先)
❌ 不挖掘响应体(只看状态码)
❌ 值池更新后不检查新的联动机会
❌ 发现一个洞就停
❌ 不保存 200 响应体(每个响应都可能在下一次联动中有用)
❌ 不限流导致 IP 被 WAF 封(封了就什么都测不了了)