Next.js unstable缓存参数碰撞:Exploitarium前端框架服务端对象劫持PoC拆解
【免费下载链接】exploitariumA single archive of public exploit PoCs and vulnerability research writeups. At the time I post these, none have been reported. Feel free to report them yourself and take credit for the CVE if handed out lulz. Please do not abuse these. I do this so to allure people into the field, and I've always found this is the most efficient way.项目地址: https://gitcode.com/GitHub_Trending/ex/exploitarium
本文拆解 Exploitarium 安全研究仓库中的Next.js unstable_cache 缓存参数碰撞 PoC:当你把Request对象直接传给缓存函数时,缓存键会坍缩成同一个空对象,导致后来的用户直接拿到第一个用户的请求结果——一次典型的服务端"对象劫持"。
🔑 一句话理解:Next.js unstable_cache 缓存机制的坑
unstable_cache是 Next.js 官方提供的服务端数据缓存 API:同一个"缓存组"下,函数参数相同 → 命中同一条缓存。而缓存键的生成依赖对参数做 JSON 序列化。
问题就出在这里:三种 Web 原生对象序列化后全部是空对象——
JSON.stringify(request) // Request → "{}" JSON.stringify(searchParams) // URLSearchParams → "{}" JSON.stringify(formData) // FormData → "{}"但缓存回调内部依然能读取这些对象的实时内容(Cookie、查询参数、表单字段)。于是:
缓存键:所有请求都相同 ❌
计算结果:却取决于"第一个"请求的内容 ⚠️
不同用户的请求被折叠进同一条缓存条目,第二个用户拿到的是第一个用户的数据(first-writer wins,先写者赢)。
🧪 三种会碰撞的请求形态
PoC 生成了三条路由,覆盖全部三种对象参数:
| 路由 | 直接传入的缓存参数 | 回调里读取的实时内容 |
|---|---|---|
/api/request | Request对象 | Cookie 头、请求 URL |
/api/urlsearch | URLSearchParams | secret查询参数 |
/api/form | FormData | secret表单字段 |
验证逻辑很直观:同一个缓存组先发alice、再发bob两次请求,如果第二次响应仍是:
REQ_COOKIE=victim=alice JSON={}说明 bob 拿到的是 alice 的缓存结果——碰撞实锤。PoC 还会做反向验证(bob 先发、alice 后发),证明"谁先发请求,后来者就拿到谁的数据"。
🚀 PoC 一键复现步骤
环境要求:Node.js 20+、npm、可联网安装依赖(默认使用next@16.3.0-canary.70)。
git clone https://gitcode.com/GitHub_Trending/ex/exploitarium cd exploitarium/nextjs-unstable-cache-object-argument-collision node run_demo.mjs脚本全自动完成四步,见 run_demo.mjs:
- 📦 生成一个临时 Next.js 应用并完成
npm install+next build - 🚦 启动
next start,对三条路由各发 alice/bob 两组对照请求 - 🔄 停服再启(不删应用目录),用
charlie请求命中旧缓存组——验证缓存跨重启持久化在 Data Cache 中 - ✅ 全部断言通过后输出
confirmed=true并生成VULNERABILITY_CONFIRMED.marker
自定义端口或版本:
node run_demo.mjs --port 3561 --next-version 16.3.0-canary.70✅ 如何修复:先抽取原始值,再进缓存
PoC 内置了对照组(value control):在调用unstable_cache之前,先把 Cookie / 查询参数 / 表单字段提取成普通字符串再传入,结果 alice 和 bob 各自命中独立缓存条目,碰撞消失。
// ❌ 危险:直接传对象,缓存键坍缩为 {} unstable_cache(async (req) => { /* 读 req.headers */ }, ['group'], { revalidate: 3600 }) // ✅ 安全:先抽取原始值作为参数,缓存键各不相同 unstable_cache(async (cookie) => { /* 用 cookie 计算 */ }, ['group', cookie], { revalidate: 3600 })核心原则:进入unstable_cache的参数只能是可区分的原始值(字符串、数字),永远不要把Request、URLSearchParams、FormData这类对象直接当参数。
📁 相关文件导读
- PoC 说明文档:nextjs-unstable-cache-object-argument-collision/README.md
- 一键复现脚本(含路由生成与断言逻辑):run_demo.mjs
- 三条路由的生成逻辑:run_demo.mjs#L162-L263
- alice/bob 对照请求与重启验证:run_demo.mjs#L302-L346
- 碰撞判定断言:run_demo.mjs#L353-L369
⚠️ 结语
这个 PoC 演示了缓存框架中一个隐蔽却危险的范式:"键的视图"与"值的视图"不一致——序列化时看不到对象内容,执行时却能读到全部请求细节。它的影响面是敏感数据串号泄漏,且缓存会跨重启留存,隐蔽性更强。仓库作者声明这些 PoC 均为善意公开的研究成果,仅供学习,请勿用于任何滥用场景。
【免费下载链接】exploitariumA single archive of public exploit PoCs and vulnerability research writeups. At the time I post these, none have been reported. Feel free to report them yourself and take credit for the CVE if handed out lulz. Please do not abuse these. I do this so to allure people into the field, and I've always found this is the most efficient way.项目地址: https://gitcode.com/GitHub_Trending/ex/exploitarium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考