mcpField
H
hunt-spa-api

hunt-spa-api

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

关于 hunt-spa-api

hunt-spa-api 是面向安全测试人员的技能,用于从单页应用的 JavaScript 包中发现 API 主机和路由,并检查未认证访问。它强调先建立受保护端点对照、仅以必要证据验证问题。

主要功能

  1. 01提取 JavaScript 包中的 API 主机与路由
  2. 02查找延迟加载的代码块
  3. 03对照检查未认证端点
  4. 04验证访问控制差异并限制取证范围

适用场景

  1. 01检查登录页面背后的 API 是否独立验证身份
  2. 02从 SPA 代码中梳理未公开的 API 路由
  3. 03验证不同路由组是否存在认证缺失

安装

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

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

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

详细介绍

何时使用此技能

适用于以下情况:

  • 目标主机返回一个很小的 HTML 外壳,以及大型 /static/js/*.js 或 /_next/static/* 捆绑文件(React/Vue/Angular/Next/Svelte SPA)
  • 你发现名为 console、app、dashboard、portal、admin、panel、manage、internal 的子域名
  • 侦察发现任何 *api*、*-api*、api.* 主机
  • 登录页面受 OAuth/SSO 限制(前端的身份验证机制并不能说明 API 是否强制身份验证)

核心要点:SPA 是后端 API 的客户端,它会向任何查看源代码的人提供该 API 的完整映射——包括主机、路由,有时还有密钥。 登录页面受到保护,并不能说明其背后的 API 是否会检查令牌。身份验证通常在 网关/登录 处实施,却可能在 API 的某个 路由组 中缺失。

不要因为“应用需要登录”就跳过这一步——这恰恰是它最有用的时候。


操作步骤(5 步)

1. 获取外壳并列出所有捆绑文件
curl -s https://console.target.com/ -o index.html
# React/CRA:
grep -oE '/static/js/[^"]+\.js' index.html
# Next.js:
grep -oE '/_next/static/[^"]+\.js' index.html
# generic:
grep -oiE 'src="[^"]+\.js[^"]*"' index.html

下载每个捆绑文件(文件可能有数 MB,这没关系,里面都是路由数据):

mkdir bundles
for j in $(grep -oE '/static/js/[^"]+\.js' index.html | sort -u); do
  curl -s "https://console.target.com$j" -o "bundles/$(echo "$j"|tr '/' '_')"
done
2. 从捆绑文件中提取 API 主机、路由和密钥
B=bundles/*.js
# Backend API hosts (incl. dev/beta/staging variants — often weaker auth)
grep -ohiE 'https://[a-z0-9.-]*(api|console|backend|service)[a-z0-9.-]*\.target\.com[a-z0-9/_-]*' $B | sort -u
# Versioned API base paths
grep -ohiE '/api/v[0-9]+/?' $B | sort -u
# Route literals — minified bundles store routes as STRING segments, not full URLs.
# Grep for quoted "resource/action" strings:
grep -ohiE '"[a-z0-9_-]+/[a-z0-9_/-]+"' $B | tr -d '"' \
  | grep -iE '(login|user|account|order|billing|invoice|payment|deal|report|token|otp|password|reset|admin|profile|auth|upload|export|role|permission|dashboard|wallet|finance|sales)' | sort -u
# Secrets (validate before trusting — most AIza keys are Maps/analytics, not Auth)
grep -ohiE '(AIza[0-9A-Za-z_-]{35}|AKIA[0-9A-Z]{16}|sk_live_[0-9A-Za-z]+|eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}|apiKey["'"'"']?\s*[:=]\s*["'"'"'][^"'"'"']+)' $B | sort -u

注意:压缩器会将路由存储为拼接后的字符串片段(例如 "account/payment/list"),而不是完整的 /api/v2/... URL,因此简单搜索 /api/v* 不会有结果。请搜索资源名路由字符串,并自行补上基址。

懒加载(异步)分块——不要只检查 HTML 中引用的捆绑文件。 CRA/webpack SPA 在 index.html 中只引用 runtime+main+vendor;带编号的异步路由分块名称位于 main.js 的分块映射中,并在运行时加载,因此只搜索外壳中 /static/js/*.js 会漏掉非即时加载分块中的路由。

grep -oE '[0-9]+:"[a-f0-9]+"' bundles/*main*.js        # {chunkId:"hash"} pairs
# reconstruct /static/js/<id>.<hash>.chunk.js, download each, re-run route/host/secret harvest
# asset-manifest.json (if present) lists them all -> hunt-source-leak
3. 建立对照组——找到一个确实受限的端点

在判定存在漏洞之前,向一个你预期受到保护的端点发送未经身份验证的请求,并记录正确拒绝请求时的响应:

curl -s -X POST https://api.target.com/api/users -H 'Content-Type: application/json' -d '{}'
# secure → {"error":"Missing or invalid authorization header"} or HTTP 401

这就是你的对照。相邻 API(例如第二个 API 主机,或同一主机上的另一个路由组)是理想的对照对象——它们使用相同技术栈,因此不同的响应意味着真实的授权缺失,而非行为差异。

4. 对每个路由组进行未经身份验证的测试,使用两种方法

对于发现的每条路由,在不带 Authorization 标头的情况下发送请求,并与对照组比较:

for r in <routes>; do
  curl -s -o /tmp/r -w "[%{http_code}] $r\n" -X POST -H 'Content-Type: application/json' -d '{}' "https://api.target.com/api/v2/$r"
done

判断方式:

  • 401/"Missing authorization" → 受限(符合预期)。继续。
  • 200 且包含数据 → 未经身份验证的数据泄露。发现漏洞。
  • 400 "field X is mandatory" → 路由在未经身份验证检查的情况下处理了请求,并进入业务逻辑验证 → 身份验证绕过;提供最少量字段以确认。
  • 200 + 详细的数据库/堆栈错误(例如 PROCEDURE db_x.sp_y does not exist)→ 未经身份验证即访问到数据层;同时表明存在 SQLi 攻击面。
  • 名为 is_admin/is_internal/requested_by/role_id/account_type 的必填字段 → 授权取决于客户端提供的参数——设置权限标记即可自行提升权限。属于严重级别。
5. 扩展验证并证明(尽量少做)
  • 一个端点返回的 ID(account_id、order_id、deal_id)是其他端点使用的键——这些 ID 能证明整个路由器都可访问,而不只是单条路由。
  • 测试 dev-/beta-/staging- API 变体——它们通常身份验证更弱或已禁用。
  • 检查响应标头:Access-Control-Allow-Origin: * 会加剧问题(任何 Web 来源都能从受害者的浏览器读取响应)。
  • **只进行满足最低证明要求的测试。**少量记录(或 totalCount)就能确认检查缺失。不要枚举整张表——参见 redteam-mindset 的数据最小化边界。漏洞在于缺少身份验证,而非数据量。

“SSO 登录背后的 API”真正意味着什么

一种常见且危险的架构:

  • console.target.com(SPA)→ 登录使用 Entra/Okta/Google OAuth(看起来无懈可击)。
  • api.target.com(后端)→ 有些路由组会验证 Bearer 令牌,有些路由组却忘了加中间件。

如果 API 没有在每条路由上独立验证令牌,那么前端登录就只是演戏。无论登录界面锁得多严,都要直接、不带凭据地测试 API。


反模式

  • “应用要求登录,所以 API 一定受到了保护。” 不对——直接以未认证状态测试 API。这正是重点。
  • “压缩后的包,没法阅读。” 你不需要阅读它——用 grep 搜索主机名/路由/机密。只需 5 分钟。
  • “/api/v1/foo 返回了 404,所以这里没有 API。” 不对。可能是基础路径或请求方法不对。试试 /api/、/api/v2/、用 POST 而不是 GET,以及从压缩包中找到的精确路由字符串(Express 的 404 会回显路径——可用它来校准)。
  • “找到了 Aiza 密钥 → 严重机密。” 先验证——大多数是 Maps/分析服务密钥(identitytoolkit 返回 CONFIGURATION_NOT_FOUND 表示未启用 Auth)。不要夸大结论。
  • 拿到 200 响应后就转储整个数据集。 在 PoC 阶段就停下。(redteam-mindset。)
  • 把创建账户/写入端点当作“证据”。 读取端点就足以证明存在认证缺口,无需创建状态。绝不要为了“演示”而 POST 到 create/signup/upload——这属于破坏性写入,需要针对每项操作获得明确授权。

相关技能与链路

  • hunt-api-misconfig — API 映射完成后,针对每条路由运行更全面的错误配置检查矩阵(方法篡改、批量赋值、JWT alg 混淆)。
  • hunt-idor — account_id/order_id 等参数可直接用于对已发现路由进行 IDOR 测试。
  • hunt-source-leak — 源映射(*.js.map)可还原原始源代码,以便更深入地提取路由/机密;使用的是同样的采集技巧。
  • hunt-nextjs — 对于 Next.js 目标,在此基础上叠加中间件绕过(x-middleware-subrequest)和 /_next/data 路由测试。
  • redteam-mindset — 数据最小化原则决定了第 5 步的边界:证明检查缺失即可,不要导出整张表。
  • recon-scope-triage — 测试前确认 API 主机确实属于目标(不要攻击同名第三方的 API)。

评论

支持 Markdown