Announcement

👇Official Account👇

Welcome to join the group & private message

Article first/tail QR code

Skip to content

NGINX CVE-2026-42533 深度拆解:15 年预认证 RCE,map 指令两遍评估捕获污染攻击链

引言:一个存在了 15 年的设计级漏洞

2026 年 7 月 15 日,F5 Networks 发布了 NGINX 安全公告 K000162097,修复了 CVE-2026-42533——一个影响 NGINX Open Source 和 NGINX Plus 的堆缓冲区溢出漏洞。这不是一个普通的溢出:它自 2011 年 3 月 21 日(NGINX 0.9.6 引入 map 指令正则支持)就存在于代码中,横跨约 15 年、数十个版本。

更关键的是,这个漏洞的利用条件极为宽松:无需认证、无需客户端证书、无需用户交互,攻击者只需发送一条精心构造的 HTTP 请求即可触发。在 ASLR 被禁用或可绕过的情况下,它可以实现可靠的预认证远程代码执行。

CVSS 3.1 评分 8.1(High),CVSS 4.0 评分 9.2(Critical)。CISA 已将其纳入 SSVC 评估,技术影响评级为 "total"。

一、漏洞本质:两遍评估模型的共享状态污染

1.1 NGINX 脚本引擎的两遍评估

NGINX 的配置文件允许在变量表达式中引用正则捕获组(如 $1$2)和 map 指令的输出变量。当 NGINX 需要求解一个包含这些引用的复杂表达式时,它在 ngx_http_complex_value()src/http/ngx_http_script.c)中使用两遍评估模型

┌──────────────────────────────────────────────────────┐
│           NGINX 两遍评估模型 (Two-Pass Evaluation)     │
├──────────────────────────────────────────────────────┤
│                                                      │
│  第一遍 (LEN Pass):                                   │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐          │
│  │ 遍历操作码 │ -> │ 求和各段  │ -> │ 得到总长度 │          │
│  │ 序列     │    │ 长度     │    │ = malloc │          │
│  └─────────┘    └─────────┘    └────┬────┘          │
│                                     │                │
│                    ┌────────────────┘                │
│                    ▼                                 │
│  分配缓冲区: buf = ngx_palloc(pool, len)             │
│                    │                                 │
│                    ▼                                 │
│  第二遍 (VALUE Pass):                                 │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐          │
│  │ 遍历操作码 │ -> │ 填充数据  │ -> │ 写入 buf  │          │
│  │ 序列     │    │ 到缓冲区  │    │          │          │
│  └─────────┘    └─────────┘    └─────────┘          │
│                                                      │
│  问题: 两遍之间,共享状态 r->captures 可能被篡改!       │
└──────────────────────────────────────────────────────┘
  • 第一遍(LEN Pass):遍历操作码序列,累加每个变量引用的值长度,得到总长度 len
  • 分配缓冲区buf = ngx_palloc(pool, len),按第一遍测量的长度分配堆内存
  • 第二遍(VALUE Pass):再次遍历操作码序列,将实际数据写入 buf

这个设计的前提假设是:两遍评估之间,所有变量的值不会改变。但这个假设在引入 map 正则匹配后被打破了。

1.2 PCRE 捕获状态的共享与污染

NGINX 的正则匹配结果存储在请求结构体的 r->captures 数组中——这是一个共享的可变状态。当不同的正则操作在同一个请求处理流程中执行时,它们都读写同一个 r->captures

漏洞的根源在于:

c
// 简化的漏洞触发流程
// 表达式: "$1 ${map_var}"

// LEN Pass:
//   1. 解析 $1 → 读取 r->captures[0] → 得到原始捕获长度 L1
//   2. 解析 map_var → 触发 map 正则匹配 → 覆写 r->captures!
//   3. 此时 r->captures[0] 已被 map 的正则结果替换
//   总长度 = L1 + L_map (基于原始捕获计算)

// 分配: buf = malloc(L1 + L_map)

// VALUE Pass:
//   1. 解析 $1 → 读取 r->captures[0] → 得到被污染的捕获长度 L1'
//   2. L1' 可能远大于 L1!
//   3. 将 L1' 字节的数据写入 buf → 堆溢出!

当 map 指令的正则匹配在两个捕获引用之间执行时,map 的正则会覆盖 r->captures,导致第二遍评估时 $1 引用的不再是原始捕获的内容,而是 map 正则匹配后的新内容。如果新内容比原始内容长,VALUE Pass 写入的数据量就会超过 LEN Pass 测量的长度——堆缓冲区溢出

反之,如果新内容比原始内容短,缓冲区中会残留未初始化的堆数据——信息泄露,泄露的堆指针可以用于绕过 ASLR。

二、触发条件与影响范围

2.1 漏洞触发配置模式

漏洞触发的配置条件如下:

nginx
# 必须同时存在两个条件:

# 条件 1: 正则捕获源 (产生 $1, $2 等引用)
location ~ ^/api/(.+)$ {
    # $1 = "v1/users" 等

    # 条件 2: 正则 map 变量在同一表达式或后续指令中被评估
    # map 块定义 (在 http 块中)
    # map $request_uri $backend_pool {
    #     ~^/api/v1/  "v1_backend";
    #     ~^/api/v2/  "v2_backend";
    #     default     "default_backend";
    # }

    # 触发表达式: $1 在 map_var 之前被引用
    # 以下任何形式都可能触发:
    proxy_set_header X-Original-Path "$1 ${backend_pool}";
    # 或
    set $combined "$1-$backend_pool";
    # 或甚至在同一 location 块的不同指令中
}

# 注意: 不限于 location 正则
# server_name、rewrite、if 的正则捕获同样有效
# stream 模块也受影响

关键要点:

  • 正则捕获和正则 map 变量不需要在同一个指令中——同一 location 块中的不同指令也足够触发
  • HTTP 和 Stream 模块都受影响
  • 攻击者通过正常的 HTTP 请求字段(URI、头部、请求体)控制溢出内容和长度

2.2 影响版本

版本范围状态
NGINX 0.9.6 – 1.30.3 (stable)受影响
NGINX 1.31.2 及之前 (mainline)受影响
NGINX Plus R33–R36受影响
NGINX Plus R37.0.0.1–37.0.2.1受影响
NGINX 1.30.4 (stable)✅ 已修复
NGINX 1.31.3 (mainline)✅ 已修复
NGINX Plus R36 P7+✅ 已修复
NGINX Plus 37.0.3.1+✅ 已修复

重要提醒:近期的其他 NGINX CVE(CVE-2026-42945、CVE-2026-9256、CVE-2026-42055、CVE-2026-48142)不修复此漏洞。必须升级到上述指定版本。

2.3 攻击面评估

NGINX 部署在全球约 34% 的 Web 服务器上。最常见的反向代理和负载均衡器配置大量使用 map 指令处理请求头、URI 组件和查询参数——这些恰好是攻击者可控的输入源。

三、利用链构建:从信息泄露到预认证 RCE

3.1 攻击链概览

攻击者

  ├── 步骤 1: 信息泄露请求 (单条 GET)
  │   ├── 发送特制 HTTP 请求
  │   ├── map 正则触发, r->captures 被污染
  │   ├── VALUE Pass 写入不足 → 缓冲区残留堆数据
  │   └── 响应中泄露堆指针 → 绕过 ASLR

  ├── 步骤 2: 堆溢出请求 (单条 GET/POST)
  │   ├── 利用步骤 1 获得的堆地址
  │   ├── 构造请求使 map 正则捕获 > 原始捕获
  │   ├── VALUE Pass 写入超出 LEN Pass 分配的缓冲区
  │   └── 精确控制溢出内容和长度 → 覆盖关键堆元数据

  └── 结果: 预认证远程代码执行
      ├── 无需认证
      ├── 无需客户端证书
      ├── 无需用户交互
      └── 溢出长度无上限, 内容完全由攻击者控制

3.2 信息泄露原语

当 map 正则的捕获比原始捕获时,VALUE Pass 写入的数据少于 LEN Pass 分配的缓冲区大小。缓冲区中多出的部分包含未初始化的堆内存,其中可能包含堆指针。

python
# 信息泄露利用概念 (仅用于防御研究)
import requests
import struct

def leak_heap_address(target_url):
    """
    通过构造特定请求触发 r->captures 污染,
    使 VALUE Pass 写入不足, 响应中泄露堆指针
    """
    # 构造 URI 使原始正则捕获较长
    # map 正则匹配使 r->captures 被较短的内容覆盖
    # 响应中 $1 的值包含未初始化堆数据

    crafted_uri = "/api/AAAA...AAA"  # 长原始捕获
    headers = {"X-Trigger-Map": "short"}  # 触发短 map 匹配

    resp = requests.get(f"{target_url}{crafted_uri}", headers=headers)

    # 从响应中提取泄露的堆指针
    # 实际偏移取决于 NGINX 版本和编译配置
    leaked_bytes = resp.headers.get("X-Original-Path", "")

    # 解析泄露的地址 (示例, 实际偏移需调试)
    if len(leaked_bytes) > 8:
        heap_addr = struct.unpack("<Q", leaked_bytes[:8].encode())[0]
        return heap_addr

    return None

3.3 堆溢出原语

当 map 正则的捕获比原始捕获时,VALUE Pass 写入的数据超过 LEN Pass 分配的缓冲区大小,导致堆溢出。溢出的内容和长度完全由攻击者通过 HTTP 请求字段控制。

python
def trigger_overflow(target_url, heap_addr):
    """
    利用泄露的堆地址, 构造溢出请求
    覆盖 NGINX worker 的堆元数据实现控制流劫持
    """
    # 构造 URI 使原始正则捕获较短
    # map 正则匹配使 r->captures 被较长的内容覆盖
    # VALUE Pass 写入超过分配的缓冲区

    # 原始捕获: 短 (LEN Pass 据此分配小缓冲区)
    crafted_uri = "/api/X"

    # map 匹配: 长 (VALUE Pass 据此写入大量数据)
    headers = {
        "X-Trigger-Map": "A" * 4096,  # 长 map 捕获
    }

    # 溢出数据中嵌入 ROP 链或 shellcode 地址
    # 利用泄露的 heap_addr 精确定位目标
    payload = construct_payload(heap_addr)
    headers["X-Overflow-Payload"] = payload

    resp = requests.get(f"{target_url}{crafted_uri}", headers=headers)
    return resp.status_code

3.4 利用可靠性

根据安全研究人员的测试:

  • 信息泄露可在单次 GET 请求内完成 ASLR 绕过
  • 溢出触发可靠率 100%(10/10 次测试均成功)
  • 溢出长度无上限,内容完全由攻击者控制
  • 最终实现预认证远程代码执行,可完全接管 NGINX 服务器

四、防御与修复

4.1 立即升级

bash
# 检查当前 NGINX 版本
nginx -v

# 如果是 stable 分支, 升级到 1.30.4+
# 如果是 mainline 分支, 升级到 1.31.3+

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install nginx

# CentOS/RHEL
sudo yum update nginx

# Docker 部署
docker pull nginx:1.30.4
docker-compose up -d

4.2 配置审计

在无法立即升级时,审计配置中是否同时存在正则捕获和正则 map 变量的组合:

bash
#!/bin/bash
# CVE-2026-42533 配置审计脚本
# 检查是否存在漏洞触发条件

NGINX_CONF_DIR="/etc/nginx"
VULNERABLE=0

echo "=== CVE-2026-42533 配置审计 ==="

# 检查 map 指令是否使用正则匹配
MAP_REGEX=$(grep -rn "map.*~" "$NGINX_CONF_DIR" 2>/dev/null)
if [ -z "$MAP_REGEX" ]; then
    echo "[OK] 未发现正则 map 指令, 风险较低"
    exit 0
fi

echo "[!] 发现正则 map 指令:"
echo "$MAP_REGEX"
echo ""

# 检查是否存在正则捕获源
echo "检查正则捕获源..."
CAPTURE_SOURCES=$(grep -rnE "location\s+~|server_name\s+~|rewrite\s+.+\(|if\s*\(" "$NGINX_CONF_DIR" 2>/dev/null)

if [ -n "$CAPTURE_SOURCES" ]; then
    echo "[!] 发现正则捕获源 (可能产生 \$1, \$2 等引用):"
    echo "$CAPTURE_SOURCES"
    echo ""
    echo "[!!!] 高危: 正则 map + 正则捕获源同时存在!"
    echo "[!!!] 建议立即升级 NGINX 或重构配置"
    VULNERABLE=1
fi

# 检查 $1, $2 等引用是否与 map 变量在同一表达式中
if [ "$VULNERABLE" = "1" ]; then
    echo ""
    echo "=== 建议修复方案 ==="
    echo "1. 立即升级到 NGINX 1.30.4 (stable) 或 1.31.3 (mainline)"
    echo "2. 重构配置: 避免正则捕获与正则 map 变量出现在同一表达式"
    echo "3. 如 map 不需要正则, 改用精确匹配"
    echo "4. 使用配置扫描工具: https://github.com/0xCyberstan/CVE-2026-42533-Config-Scanner"
fi

4.3 使用社区配置扫描工具

安全研究员 0xCyberstan 发布了开源配置扫描器:

bash
# 安装并运行配置扫描器
git clone https://github.com/0xCyberstan/CVE-2026-42533-Config-Scanner.git
cd CVE-2026-42533-Config-Scanner

# 扫描 NGINX 配置
python3 scanner.py --config-dir /etc/nginx --json

# 输出示例:
# {
#   "vulnerable": true,
#   "findings": [
#     {
#       "file": "/etc/nginx/conf.d/api.conf",
#       "line": 15,
#       "pattern": "location ~ ^/api/(.+)$ + map $request_uri $pool { ~^/api/ }",
#       "risk": "high",
#       "recommendation": "Restructure to avoid regex capture + regex map in same evaluation"
#     }
#   ]
# }

该扫描器:

  • 支持 include 指令跟踪
  • 处理跨指令触发场景
  • 区分可利用的评估顺序与安全顺序
  • 支持 JSON 输出便于 CI/CD 集成

4.4 检测与监控

bash
# 监控 NGINX worker 异常崩溃 (可能的利用尝试)
# 在 error.log 中搜索 SIGSEGV
tail -f /var/log/nginx/error.log | grep "exited on signal 11"

# 设置告警: 短时间内多次 worker 重启
# /etc/fail2ban/jail.d/nginx-overflow.conf
[nginx-overflow]
enabled = true
filter = nginx-overflow
logpath = /var/log/nginx/error.log
maxretry = 3
findtime = 60
bantime = 3600

五、与其他 NGINX CVE 的关系

2026 年 7 月前后,NGINX 密集修复了多个安全漏洞。理解它们之间的关系有助于优先级排序:

CVE类型CVSS关系
CVE-2026-42533map 正则堆溢出8.1/9.2本文重点,15 年历史,预认证 RCE
CVE-2026-42945同引擎较弱漏洞7.5需要 ASLR 禁用才能利用,已被野外利用
CVE-2026-42055HTTP/2 RCE9.6不同组件,HTTP/2 模块
CVE-2026-42530HTTP/3 信息泄露5.3不同组件,HTTP/3 模块
CVE-2026-9256其他修复-不修复 42533
CVE-2026-48142其他修复-不修复 42533

关键区别:CVE-2026-42945 是同一脚本引擎中的较弱漏洞,但它需要 ASLR 被禁用才能可靠利用,且在 PoC 公开后迅速被野外利用。CVE-2026-42533 不需要 ASLR 被禁用——它的信息泄露原语可以在单次 GET 请求中绕过 ASLR。

六、根因分析:设计层面的教训

6.1 共享可变状态的陷阱

这个漏洞的本质是共享可变状态r->captures)在多次访问之间被意外修改。这是一个经典的设计级问题:

设计假设: 两遍评估之间, 变量值不变
实际行为: map 正则在两遍之间覆盖了 r->captures
结果: LEN Pass 和 VALUE Pass 对 $1 的理解不一致

NGINX 的脚本引擎在设计之初没有考虑到 map 指令的正则匹配会修改 r->captures——因为 map 指令的正则支持是在 0.9.6 版本才加入的(2011 年),而两遍评估模型在此之前就已存在。

6.2 修复方案:保存与恢复

正确的修复方式是在 map 正则匹配前保存 r->captures 状态,匹配后恢复:

c
// 修复后的伪代码
ngx_int_t ngx_http_complex_value(ngx_http_request_t *r,
    ngx_http_complex_value_t *val, ngx_str_t *value)
{
    // ... LEN Pass ...

    // 在 map 正则匹配前保存 captures
    ngx_int_t *saved_captures = NULL;
    if (r->captures) {
        saved_captures = ngx_palloc(r->pool, r->captures_elts * sizeof(ngx_int_t));
        ngx_memcpy(saved_captures, r->captures, r->captures_elts * sizeof(ngx_int_t));
    }

    // ... 执行 map 正则匹配 ...

    // 恢复 captures
    if (saved_captures) {
        ngx_memcpy(r->captures, saved_captures, r->captures_elts * sizeof(ngx_int_t));
    }

    // ... VALUE Pass ...
}

6.3 对安全工程师的启示

  1. 两遍评估是危险模式:任何 "先测量再填充" 的设计都必须保证两遍之间状态一致
  2. 共享可变状态需要显式管理r->captures 作为一个共享缓冲区,任何修改它的操作都应该有保存/恢复机制
  3. 功能叠加引入新攻击面:map 正则支持是在两遍评估模型之后加入的,这种功能叠加没有被充分审计
  4. 配置扫描不能替代升级:即使配置当前不触发漏洞,未来配置变更可能引入触发条件

七、时间线

时间事件
2011-03-21NGINX 0.9.6 引入 map 指令正则支持,漏洞引入
2026-07-15F5 发布安全公告 K000162097,CVE-2026-42533 公开
2026-07-15CISA 进行 SSVC 评估,技术影响评级 "total"
2026-07-15NGINX 1.30.4 (stable) 和 1.31.3 (mainline) 发布
2026-07-16NVD 发布 CVE 详情
2026-07-20配置扫描工具发布
截至本文PoC 未公开(研究员延迟发布以给用户修复时间)
待定PoC 公开后预计出现大规模扫描和利用

总结

CVE-2026-42533 是一个教科书级别的设计级漏洞:一个合理的架构假设(两遍评估之间状态不变)在功能演进(map 正则支持)后变得不成立,而这个不一致在 15 年里没有被任何人发现。

对于安全工程师和运维团队:

  • 立即升级到 NGINX 1.30.4+ 或 1.31.3+
  • 审计配置中是否同时存在正则捕获和正则 map 变量
  • 监控 worker 崩溃作为可能的利用尝试指标
  • 关注 CISA KEV:一旦 CVE-2026-42533 被加入,说明已确认野外利用

15 年,13 个调用点,一条 HTTP 请求就能打穿。这是 2026 年最需要紧急打补丁的 NGINX 漏洞之一。

参考资料

上次更新于: