NGINX CVE-2026-42533:regex map 覆盖 r->captures,两遍求值把 buffer 长度算错
影响范围
官方公告条目为 "Buffer overflow when using map and regex",Severity: major。
- CVE:CVE-2026-42533
- Not vulnerable:1.31.3+ / 1.30.4+
- Vulnerable:0.9.6 – 1.31.2
- 漏洞自 2011 年 3 月 NGINX 0.9.6 起潜伏,约 15 年
- 影响 NGINX Open Source、NGINX Plus、NGINX Ingress Controller
- CVSS v4.0 9.2 Critical / v3.1 8.1
- 修复版本:Open Source 1.30.4(stable)/ 1.31.3(mainline);NGINX Plus 37.0.3.1 或 R37 及以上
- 发行版另有独立补丁包
根因:LEN/VALUE 两遍求值 + 共享的 r->captures
nginx 的脚本/复杂值求值引擎对含变量的字符串走两遍(two-pass):
- LEN pass:逐个 token 量长度,据此从 request pool 分配一块 buffer;
- VALUE pass:把实际数据 memcpy 进去。
这个设计的前提是变量的值在两次 pass 之间不变。但 numbered capture($1..$9)保存在每请求共享的 r->captures 数组里,本身就是可变状态。
当求值过程中遇到一个基于正则的 map 指令,例如:
map $http_x_overflow $overflow_gadget {
"~^(.+)$" $1;
}
它会跑一次新的 PCRE 匹配,直接覆盖 r->captures,并把 r->captures_data 指向 map 的输入串(请求头 / 查询串 / 请求体)。ngx_http_complex_value() 和 ngx_http_map_find() 都不会保存/恢复 captures 状态。结果是:LEN 量的是旧捕获,VALUE 写的是被覆盖后的新捕获,缓冲区按一个大小分配、却按另一个大小填充。
两个方向都可用
方向由 location 捕获与 map 输入的相对大小决定,攻击者可控:
- INNER > OUTER(覆盖后更大):LEN 低估 buffer,VALUE 越界写,构成堆缓冲区溢出。可覆盖相邻的
ngx_pool_cleanup_t(handler 函数指针 / data / next),连接池销毁时调用handler(data)获得控制流。 - OUTER > INNER(覆盖后更小):LEN 高估 buffer,VALUE 只写入少量字节,剩下的未初始化堆内存(复用自之前分配,含 libc / heap 指针)随响应回吐,构成信息泄露,可绕过 ASLR。
两个原语串起来可实现 pre-auth RCE,PoC 在 Ubuntu 24.04 上报告 10/10 可靠。
触发三条件
- 某条 location / server_name / rewrite / if 用正则在同一次请求里产生
$1/$2捕获; - 正则 map 变量在同一条被求值的 buffer 中出现在捕获引用之后;
- 捕获引用和 map 变量不必在同一条指令,同一 location 块里相邻的不同指令即可。
受影响的落点指令:proxy_set_header、proxy_method、proxy_pass、fastcgi_param、uwsgi_param、scgi_param、grpc_set_header、return、add_header、rewrite、set、root、alias、access_log 等。HTTP 和 stream 两个模块都受影响。
不止一处:9 个源文件 13 个调用点
两遍 LEN/VALUE 模式不止出现在 ngx_http_complex_value,至少分布在 9 个源文件的 13 个独立调用点。每个调用点都有自己的 LEN 循环、分配和 VALUE 循环,都通过 copy_capture_len_code / copy_capture_code 读捕获,且都不保存/恢复 r->captures。研究者用 AddressSanitizer 构建 nginx 1.30.1,确认 13 处全部可触发。
修复补丁思路
nginx PR #1561,7 个 commit、17 个文件。做法不是简单在两次 pass 之间快照 r->captures,而是让 VALUE pass 本身无法越界:
- script engine 结构体新增
end指针,指向已分配 buffer 边界; - 在写操作码(
copy_code、copy_var_code、copy_capture_code及其ngx_escape_uri分支)前插入ngx_http_script_check_length()守卫; - 一旦要写过
end,置e->status=500,把e->ip跳向ngx_http_script_exit,并记录"no buffer space in script copy",调用方以 HTTP 500 中止,而不是破坏 pool 堆; - 信息泄露方向改为按实际写入字节数(
e->pos - e->buf.data)计算返回长度,而不是用被夸大的 LEN 测量值,未初始化的尾部不再回吐。
临时缓解:把 numbered capture 换成 named capture
无法立即升级时,把 maps 里的 numbered capture 改成 named capture:
# 改前
map $http_x_overflow $overflow_gadget {
"~^(.+)$" $1;
}
# 改后
map $http_x_overflow $overflow_gadget {
"~^(?.+)$" $val;
}
named capture 走 r->variables 索引,不依赖也不修改全局 r->captures。
注意研究者提到的同名变体:若 map 里用了与 location 正则同名的捕获,仍可能覆盖变量条目导致同样的 LEN/VALUE 失配,所以改 named 后要确认没有重名。
其他可做的:临时打破配置组合,避免同一请求路径上叠加「正则捕获 + regex map 输出 + 动态输出/转发」;用 WAF / 限流降低特制请求到达 worker 的概率;监控 worker 异常退出,保留 core dump 与访问日志。
自查
nginx -v 看版本;检查 conf 里是否同时存在正则捕获变量、regex map 输出变量,以及 return / add_header / proxy_set_header 等动态输出转发指令。命中组合的优先升级,否则先按上面的缓解处理。
参考链接
- nginx 官方安全公告:https://nginx.org/en/security_advisories.html
- F5 通告 K000162097:https://my.f5.com/manage/s/article/K000162097
- 根因分析(cyberstan):https://cyberstan.co.uk/nginx-rce/
- PoC 仓库:https://github.com/imbas007/CVE-2026-42533