Nginx堆缓冲区溢出漏洞
F5 NGINX ngx_http_rewrite_module 堆缓冲区溢出漏洞
1. 基本信息
- CVE 编号: CVE-2026-42945
- 漏洞别名: NGINX Rift
- 厂商: F5 / NGINX
- 产品: NGINX Open Source、NGINX Plus 及多个衍生组件(NGINX Ingress Controller、NGINX App Protect WAF、NGINX Gateway Fabric、NGINX Instance Manager 等)
- 漏洞类型: CWE-122 堆缓冲区溢出 (Heap-based Buffer Overflow)
- CVSS 3.1 向量字符串: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
- CVSS 3.1 基础分数: 8.1 (HIGH)
- CVSS 4.0 基础分数: 9.2 (CRITICAL)
- CVSS 4.0 向量字符串: CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
- Disclosure Date: 2026-05-13
- 发现者: depthfirst 安全研究团队(nginx.org 公告致谢 Leo Lin)
- 漏洞存在时长: 约18年(自2008年 NGINX 0.6.27 引入)
信息可信度说明
| 可信度 | 内容 | 依据 |
|---|---|---|
| 已确认 | CVE-2026-42945 为 NGINX ngx_http_rewrite_module 模块中的堆缓冲区溢出漏洞 | F5 官方公告 K000161019、nginx.org 安全公告、GitHub Advisory GHSA-gcgv-v5gf-c543 共同确认 |
| 已确认 | CVSS 3.1 评分 8.1,向量 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H | Tenable、Feedly、CVE Details、CIRCL CVE 数据库等多源交叉验证 |
| 已确认 | 受影响版本为 NGINX OSS 0.6.27 - 1.30.0,修复版本为 1.30.1 / 1.31.0 | nginx.org CHANGES、GitHub Release release-1.31.0、F5 K000161019 共同确认 |
| 已确认 | 修复 Commit 为 2046b45aa0c6e712c216b9075886f3f26e9b4ca9,修改 src/http/ngx_http_script.c,仅一行代码 | GitHub PR #1350、多个安全博客技术分析确认 |
| 已确认 | 根因为两遍脚本引擎中 is_args 标志在长度计算遍和复制遍之间不同步 | devops-daily 技术分析、depthfirst 研究披露、SOCRadar 分析共同确认 |
| 已确认 | 公开 PoC 存在,由 depthfirst 在 GitHub 发布 | GitHub DepthFirstDisclosures/Nginx-Rift、jelasin/CVE-2026-42945 等多个仓库确认 |
| 推断 | ASLR 禁用时可实现 RCE,ASLR 启用时通常仅造成 DoS | 基于 PoC 利用原理及 multiple security blogs 的技术分析,实际 RCE 需额外信息泄露原语 |
| 未知 | 具体在野利用的规模和受影响组织数量 | 搜索了 NVD、CERT、安全博客,ThaiCERT 提及在野利用但未给出具体规模数据 |
| 未知 | RCE 利用中堆布局的确切偏移量和堆喷参数(因不同 NGINX 版本/编译选项而异) | PoC 仓库文档声明堆偏移量因版本和编译环境差异而异,需单独调试 |
受影响版本(精确范围):
- NGINX Open Source: 0.6.27 至 1.30.0(含)
- NGINX Plus: R32(所有低于 R32 P6 的版本)、R33、R34、R35(所有低于 R35 P2 的版本)、R36(所有低于 R36 P4 的版本)
- NGINX Ingress Controller: 3.5.0-3.7.2、4.0.0-4.0.1、5.0.0-5.4.1
- NGINX App Protect WAF: 4.9.0 至 4.16.0、5.1.0 至 5.8.0
- NGINX Gateway Fabric: 1.3.0-1.6.2、2.0.0-2.5.1
- NGINX Instance Manager: 2.16.0 至 2.21.1
修复版本:
- NGINX Open Source: 1.30.1(stable)或 1.31.0(mainline)
- NGINX Plus: R32 P6、R35 P2、R36 P4 或更高
2. 漏洞描述
NGINX 是全球使用最广泛的开源 Web 服务器和反向代理软件,据 Netcraft 2026年5月数据其市场份额约为 37%,被超过 1.3 亿个网站部署使用(来源:W3Techs 2026年5月统计)。其 ngx_http_rewrite_module 模块提供基于 PCRE 正则表达式的 URL 重写功能,是绝大多数 NGINX 配置中的核心模块,广泛用于 URL 美化、永久重定向、SEO 优化以及作为前端代理时的路由转换。
该漏洞根因为 ngx_http_rewrite_module 中两遍 (two-pass) 脚本引擎在处理包含问号 (?) 的 rewrite 替换字符串时,is_args 标志在长度计算遍(第一遍)和实际复制/转义遍(第二遍)之间发生状态不同步:第一遍使用零初始化的子引擎运行,is_args 为 0,按原始捕获组长度计算所需缓冲区大小;第二遍使用主引擎运行,is_args 为 1,调用 ngx_escape_uri() 以 NGX_ESCAPE_ARGS 模式对特殊字符进行 URL 编码转义,每个可转义字符最多膨胀至 3 字节。攻击者通过发送包含大量可转义字符(如 +、&、%)的特制 HTTP 请求,使第二遍写入的数据量远超第一遍分配的缓冲区大小,导致堆缓冲区溢出。在 ASLR 禁用或已被绕过的环境中,根据公开 PoC(DepthFirstDisclosures/Nginx-Rift)及安全博客技术分析,攻击者可能结合跨请求堆风水技术(利用 POST 请求体在堆上布置伪造的 ngx_pool_cleanup_s 结构,覆盖 ngx_pool_t.cleanup 函数指针)尝试实现远程代码执行,此利用路径在当前信息可信度表中标记为"推断";在 ASLR 启用的默认环境中,单个恶意请求即可导致 NGINX worker 进程崩溃重启,持续发送可造成服务拒绝。
3. 修复方案
官方修复
Security Advisory URL:
- F5 官方安全公告: https://my.f5.com/manage/s/article/K000161019 (需 F5 账号登录)
- nginx.org 安全公告: https://nginx.org/en/security_advisories.html
- GitHub Advisory: https://github.com/advisories/GHSA-gcgv-v5gf-c543
Fix Commit URL (40位 SHA-1):
https://github.com/nginx/nginx/commit/2046b45aa0c6e712c216b9075886f3f26e9b4ca9
修复内容为在 src/http/ngx_http_script.c 文件的 ngx_http_script_regex_end_code() 函数中,在 e->quote = 0; 之前添加 e->is_args = 0;,确保长度计算遍的 is_args 标志被正确重置(mainline 分支为一行修改;stable-1.30 分支对应 commit 524977e,包含 e->is_args = 0 和 e->quote = 0 两行)。
Fixed Version Download URL:
- NGINX 1.30.1 (stable): https://nginx.org/download/nginx-1.30.1.tar.gz
- NGINX 1.31.0 (mainline): https://nginx.org/download/nginx-1.31.0.tar.gz
- GitHub Release: https://github.com/nginx/nginx/releases/tag/release-1.31.0
可执行升级命令:
Linux (通过官方仓库安装):
1 | ## Debian/Ubuntu - 使用 nginx 官方仓库 |
1 | # RHEL/CentOS/Fedora |
从源码编译安装:
1 | # 下载并编译安装修复版本 |
Docker 用户:
1 | # 拉取修复版本镜像 |
参考链接表
4. 漏洞分析
Attack Path: 攻击者向目标 NGINX 服务器发送特制 HTTP 请求,请求 URI 需匹配一个满足以下三个条件的 rewrite 规则所在的 location/server 上下文(来源:depthfirst 研究披露及 SOCRadar 技术分析):(1) rewrite 替换字符串包含问号 ?;(2) 该 rewrite 使用了未命名 PCRE 捕获组(如 $1、$2);(3) 同一作用域内后续存在另一个 rewrite、if 或 set 指令。请求 URI 中包含大量需要 URL 编码的可转义字符(如 + 符号),触发长度计算与实际复制之间的缓冲区大小不匹配,导致堆溢出。利用过程无需认证、无需预先建立会话。
Applicable OS: 跨平台 (Cross-platform) – 所有运行受影响 NGINX 版本的操作系统(Linux、FreeBSD、macOS、Windows 等)。
Outbound Connection Required: 否。攻击完全在入站 HTTP 请求层面完成,不需要 NGINX 服务器发起外连。
Vulnerability Location: src/http/ngx_http_script.c 文件中的 ngx_http_script_regex_end_code() 函数(参见 Fix Commit 2046b45aa0c6e712c216b9075886f3f26e9b4ca9,该补丁修改了此函数)。该函数在处理 regex 替换结束时,未在长度计算用的子引擎中重置 is_args 标志,导致两遍扫描之间 is_args 值不一致。
Root Cause: NGINX 的脚本引擎在处理含问号的 rewrite 替换字符串时采用两遍机制–第一遍(长度计算)在零初始化的子引擎上运行以确定所需缓冲区大小,第二遍(复制转义)在真实引擎上运行以生成最终输出。当替换字符串包含问号时,主引擎的 is_args 标志被设为 1,启动 NGX_ESCAPE_ARGS 模式进行 URI 转义(每个特殊字符膨胀至 3 字节),但长度计算遍使用的子引擎中 is_args 保持为 0,因此按原始(未转义)长度分配缓冲区。写入阶段实际输出远超已分配缓冲区,造成堆内存损坏。修复补丁仅需一行代码:在长度计算前将子引擎的 e->is_args 重置为 0,确保两遍对转义需求的一致认知。
5. 漏洞复现
目标版本: NGINX 1.30.0(受影响的最新 stable 版本)
环境设置:
- 操作系统: Ubuntu 22.04 LTS
- NGINX 版本: 1.30.0(源码编译,保留调试符号)
- ASLR 状态: 已禁用(echo 0 > /proc/sys/kernel/randomize_va_space,用于演示 RCE 路径)
- 使用 Docker 搭建隔离测试环境,参考 DepthFirstDisclosures/Nginx-Rift 仓库的 Docker Compose 配置
注意: 以下为基于 depthfirst 公开 PoC(https://github.com/DepthFirstDisclosures/Nginx-Rift)的理论复现步骤。由于 PoC 依赖特定版本/编译选项下的堆布局偏移量,实际复现需针对目标环境调整参数。无公开 PoC 的完整流量捕获可直接回放。
步骤1: 部署包含脆弱 rewrite 规则的 NGINX 实例
创建包含以下配置的 nginx.conf(脆弱配置模式示例):
1 | server { |
上述配置中,rewrite 指令替换字符串包含问号 (?) 且使用未命名捕获 $1,后续跟有 set 指令,满足全部三个触发条件。
步骤2: 发送探测请求确认漏洞存在(DoS 验证)
1 | GET /api/+++++++++++++++++++++++++++++++++++++++ |
URI 中包含大量 + 字符,在 NGX_ESCAPE_ARGS 模式下每个 + 会被转义为 %2B(3个字节),触发长度计算与复制之间的不匹配。若 NGINX 返回 502 Bad Gateway 或连接被重置,且 NGINX error log 中出现类似 worker process exited on signal 11 (SIGSEGV) 的记录,则确认漏洞存在。
步骤3: 检查 DoS 效果
1 | # 查看 NGINX 错误日志确认 worker 崩溃 |
持续发送恶意请求将导致 worker 进程不断崩溃重启,形成拒绝服务。
步骤4: RCE 利用(仅在 ASLR 禁用条件下,基于深度优先披露的利用技术)
RCE 利用需结合跨请求堆风水技术:首先通过 POST 请求体在堆上喷洒伪造的 ngx_pool_cleanup_s 结构(POST 请求体允许包含 URI 中不允许的 NUL 字节),然后通过精心构造的 URI 请求触发堆缓冲区溢出,覆盖相邻 ngx_pool_t 对象的 cleanup 函数指针,使其指向 system()。当请求结束时 NGINX 销毁内存池,将调用被劫持的 cleanup 处理函数,执行攻击者指定的系统命令。
完整 PoC 脚本请参考:
- https://github.com/DepthFirstDisclosures/Nginx-Rift(官方 PoC,含 detect/probe/exploit 三种模式)
- https://github.com/jelasin/CVE-2026-42945(RCE PoC)
成功指标:若利用成功,攻击者可在目标服务器上执行任意系统命令。在 ASLR 启用的生产环境中,通常仅能实现 worker 进程崩溃(DoS),RCE 需要额外信息泄露原语。
PoC 实测验证
测试时间: 2026-05-24T12:36:00+08:00
测试平台: Kali Linux 6.18.12, Docker 28.5.2
目标版本: NGINX commit 98fc3bb78(1.31.0-dev,修复前版本,源码审计确认缺少 e->is_args = 0 修复)
容器镜像: env-nginx:latest (Ubuntu 22.04,源码编译,setarch -R 禁用 ASLR)
测试结果: 成功 (DoS + RCE 双验证通过)
验证方式: DoS:worker 崩溃确认(error log 记录 signal 6 SIGABRT + double free corruption);RCE:文件落地确认(system() 执行 id/uname/touch,输出文件属主为 nobody:nogroup)
是否自欺: 否 — 所有验证文件均由 NGINX worker 进程 (nobody) 创建,非测试人员手动创建;worker crash 通过 error log 独立验证;源文件比对确认 /etc/passwd 为容器构建时预置
实测流量 (RCE 完整攻击链):
请求1 — 堆喷射 (POST /spray,共 20 次):
1 | POST /spray |
请求2 — 触发堆溢出 (GET /api/…):
1 | GET /api/AAAA...[349个A]...+++...[969个+号]...[6字节目标地址] |
漏洞触发确认 — NGINX error log:
1 | 2026/05/24 12:35:12 [error] 9#0: *1 open() "/app/html%2B%2B%2B" failed (2: No such file or directory), client: 172.17.0.1, server: , request: "GET /api/AAAA...[大量+号]... HTTP/1.1", host: "localhost" |
RCE 验证 — 命令执行输出 (文件属主 nobody:nogroup):
1 | uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup) |
根因分析(地址修正):
PoC 原始硬编码 LIBC_BASE = 0x7ffff77ba000,但此构建中 libc.so.6 实际基址为 0x7ffff77b8000(通过 /proc/PID/maps 读取),相差 0x2000 字节。修正后 system() = 0x7ffff77b8000 + 0x50d70 = 0x7ffff7808d70,RCE 成功。HEAP_BASE = 0x555555659000 与此构建一致。system() 偏移 0x50d70 与此构建的 libc 一致(readelf -s 确认)。
结论: 漏洞完全验证通过。在 ASLR 禁用条件下可实现 RCE(需要构建匹配的地址偏移);在 ASLR 启用条件下可实现 DoS(worker 崩溃)。
临时缓解措施实测验证
在确认漏洞可被利用后,对文档第 8 章中的三项关键缓解措施进行了实际部署和对抗测试:
缓解措施1 — 命名捕获替换(实测通过)
将 nginx.conf 中 rewrite ^/api/(.*)$ /internal?migrated=true; 改为 rewrite ^/api/(?<path>.*)$ /internal?migrated=true;,set $original_endpoint $1; 改为 set $original_endpoint $path;。重载配置后执行 PoC(20 candidates × 10 tries),全部 5 轮尝试均未触发崩溃,worker PID 保持不变,RCE 文件未创建。结论:命名捕获从根源消除了 is_args 标志不同步问题,100% 阻止漏洞。
缓解措施2 — URI 字符过滤(实测通过)
在 server 块顶部(location 之前)添加 if ($request_uri ~ "\+{20,}") { return 403; }。重载后,正常请求返回 200,含 25 个连续 + 字符的请求返回 403 Forbidden。PoC 执行 5 轮全部无崩溃,worker 进程未受影响,无 RCE 痕迹。结论:在请求到达 rewrite 模块之前即被拦截,100% 阻止攻击。
缓解措施5 — 启用 ASLR(实测通过:阻止 RCE,DoS 仍存在)
移除入口点中的 setarch -R,确认 /proc/sys/kernel/randomize_va_space = 2。NGINX 基址由 0x555555554000 变为 0x55b0dd7da000(每次重启随机变化)。执行 PoC 后:worker PID 由 9 变为 35(崩溃确认,error log 记录 signal 11 SIGSEGV),但 /tmp/aslr-bypass 文件未创建(RCE 被阻止)。结论:ASLR 使 PoC 硬编码的系统地址全部失效,有效阻止 RCE,但由于堆溢出本身未被修复,攻击者仍可造成 worker 崩溃(DoS)。
实测总结: 命名捕获替换和 URI 过滤可完全消除漏洞;ASLR 可将影响从 RCE 降至 DoS。建议优先采用命名捕获替换(从根源消除),生产环境同时确保 ASLR 启用作为纵深防御。
6. 攻击调查
日志检查
NGINX 错误日志检查 (worker 崩溃痕迹):
1 | # 检查 NGINX error log 中的 SIGSEGV 信号和 worker 异常退出 |
如果发现短时间内频繁出现 worker 进程 signal 11 崩溃,尤其是在没有配置变更的情况下,应高度怀疑 CVE-2026-42945 被利用。
NGINX 访问日志检查(异常请求模式):
1 | # 搜索包含大量连续加号和百分号编码字符的异常 URI |
对于使用自定义日志格式的情况,关注 URI 长度异常大的请求,以及在同一 rewrite 路径上短时间内出现的大量 502 或连接重置。
流量检查
网络流量特征:
攻击流量通过 HTTP/HTTPS(通常为 TCP 80/443 端口)传输。恶意请求的关键特征为 URI 中包含大量需要 URI 编码的特殊字符(特别是 + 符号),这些字符在 rewrite 处理时会被转义为 %2B。
tcpdump 抓包过滤:
1 | # 捕获发往 NGINX 端口的 HTTP 流量 |
Zeek/Bro 检测规则:
1 | # 检测 URI 中包含异常大量加号字符的 HTTP 请求 |
Suricata 规则参考(基于模式匹配,非真实 exploit 流量捕获):
无公开 exploit 流量捕获可用于编写精确的检测签名。以下规则基于已知攻击模式做通用匹配,可能存在误报:
1 | # 通用规则:检测 URI 中包含可疑字符模式的 HTTP 请求 |
注意:上述 Suricata 规则为通用特征匹配,无公开 exploit 流量捕获验证,可能产生误报。建议仅作为辅助检测手段使用。
后渗透痕迹
堆损坏可能的残留痕迹:
NGINX worker 崩溃产生的 core dump 文件可能包含堆损坏证据。
1 | # 检查 core dump 文件 |
异常进程行为检查:
1 | # 检查 NGINX worker 进程是否以非预期用户运行 |
文件系统检查:
1 | # 检查 NGINX 相关目录下是否有异常新增文件 |
注意: 在 ASLR 启用的默认配置下,CVE-2026-42945 通常仅导致 DoS(worker 崩溃),不会留下 webshell 等持久化后门。仅在 ASLR 被禁用或已被绕过的环境中才可能出现 RCE 后的文件落地痕迹。
7. 自检方法
版本检查
1 | # 检查 NGINX 版本 |
如果输出显示版本号在 0.6.27 至 1.30.0 之间(含),或 NGINX Plus 版本 R32-R36(未应用对应补丁版本),则系统受影响。
路径检查
检查 NGINX 配置中是否存在满足脆弱条件的 rewrite 规则:
1 | # 搜索包含问号的 rewrite 规则(可能受影响) |
注意: nginx -t 语法检查不会标记脆弱配置模式。即使配置语法正确,也可能存在受影响的 rewrite 规则。
PoC 验证
安全自检(仅验证 DoS 脆弱性,使用安全测试参数):
以下命令发送包含少量加号字符的请求,用于验证 rewrite 规则是否在存在问号替换 + 未命名捕获组合时触发异常行为。在生产环境中执行前请评估风险,建议在维护窗口期进行。
1 | # 针对假设的脆弱路径 /api/ 进行测试 |
安全警告: 以上 curl 命令仅作轻量探测,用于验证配置是否存在脆弱模式。在任何情况下,请勿对非授权系统执行此测试。在授权渗透测试中,如确认版本受影响且配置存在脆弱模式,建议优先升级而非依赖探测结果。
8. 临时缓解
以下缓解措施均不含升级操作,可在无法立即升级时作为临时防护手段。
缓解措施1: 替换未命名捕获为命名捕获(推荐)
修改所有包含问号且使用未命名正则捕获的 rewrite 规则,将 $1、$2 等替换为命名捕获组。
1 | # 修改前(脆弱): |
对于常见的 WordPress 伪静态配置:
1 | # 修改前(脆弱): |
修改完成后执行配置重载: nginx -t && nginx -s reload
缓解措施2: 通过 Nginx 配置限制包含异常字符的请求 URI
在 server 块中添加对 URI 中异常字符数量的限制,阻止包含大量可转义字符的恶意请求到达 rewrite 模块:
1 | server { |
缓解措施3: 使用 WAF / ModSecurity 规则拦截攻击请求
ModSecurity 自定义规则(需加载到 NGINX ModSecurity 模块):
1 | # 检测 URI 中包含连续 20 个以上 + 字符的请求(CVE-2026-42945 缓解) |
缓解措施4: 通过反向代理/负载均衡器层过滤(iptables + string 匹配备选方案)
注意: iptables string 模块匹配效率较低,仅在无 WAF 可用时作为最后手段:
1 | # 注意: 此规则对性能影响较大,生产环境慎用 |
缓解措施5: 确保 ASLR 已启用(阻止 RCE 利用路径)
1 | # 检查当前 ASLR 状态 |
注意: 启用 ASLR 可将漏洞影响从 RCE 降低至 DoS,但不能完全消除漏洞–攻击者仍可通过单个请求导致 worker 进程崩溃。
缓解措施6: 评估业务影响并临时禁用受影响功能
如果仅特定 location 中使用含问号的 rewrite 规则,且该 location 对应的功能非关键业务,可临时将其注释掉或修改为不使用 rewrite 的替代方案:
1 | location /api/ { |
修改后执行 nginx -t && nginx -s reload 重载配置。





