加载中

等一下,英子马上到

文章背景图

Nginx CVE‑2026‑42945:附解决方案

2026-06-13
0
-
- 分钟
|

一、自查

影响范围:NGINX Open Source 0.6.27 ~ 1.30.0,官方修复边界是 1.30.1+ 或 1.31.0+。

先自查:nginx -v,再扫配置里有没有 rewrite + $1/$2 + ?,后面还接 rewrite/if/set。

要不要升级:只要版本低于修复边界,或者命中上面的危险配置,先升级。

有没有中招:生产上如果已经出现 worker 异常退出、core dumped、无故重启,先按受影响处理;不确定就去隔离环境验证。

脚本覆盖:Debian/Ubuntu、RHEL/CentOS/Rocky/Alma/Oracle、Amazon Linux、SLES、Alpine;Fedora、Arch、openSUSE 这类不在 nginx.org 官方二进制源支持列表里的系统,脚本会停下来让你手动确认。

自查脚本

nginx -v

sudo nginx -T > /tmp/nginx-all.conf 2>/tmp/nginx-test.log

rg -n 'rewrite\s+.*\$\d+.*\?' /tmp/nginx-all.conf

修复步骤

  1. 先备份 /etc/nginx

  2. 根据系统类型安装基础依赖。

  3. 添加 nginx 官方 mainline 源和签名 key。

  4. 用当前系统的包管理器安装 nginx。

nginx -vnginx -t 通过后再重启。

一键修复脚本

#!/bin/sh
set -eu

NGINX_KEY_URL="https://nginx.org/keys/nginx_signing.key"
NGINX_APK_KEY_URL="https://nginx.org/keys/nginx_signing.rsa.pub"

if [ "$(id -u)" -ne 0 ]; then
  echo "请用 root 运行:sudo sh $0"
  exit 1
fi

if [ ! -r /etc/os-release ]; then
  echo "无法读取 /etc/os-release,先手动确认发行版"
  exit 1
fi

. /etc/os-release
BACKUP_DIR="/root/nginx-backup-$(date +%F-%H%M%S)"
mkdir -p "$BACKUP_DIR"
if [ -d /etc/nginx ]; then
  cp -a /etc/nginx "$BACKUP_DIR/"
fi

restart_nginx() {
  if command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then
    systemctl restart nginx
    return
  fi
  if command -v rc-service >/dev/null 2>&1; then
    rc-service nginx restart || rc-service nginx start
    return
  fi
  if command -v service >/dev/null 2>&1; then
    service nginx restart || service nginx start
    return
  fi
  nginx -s reload 2>/dev/null || nginx
}

version_ge() {
  awk -v v="$1" -v min="$2" '
    BEGIN {
      split(v, a, "."); split(min, b, ".");
      for (i = 1; i <= 3; i++) {
        a[i] += 0; b[i] += 0;
        if (a[i] > b[i]) exit 0;
        if (a[i] < b[i]) exit 1;
      }
      exit 0;
    }'
}

verify_nginx() {
  nginx -t
  restart_nginx
  nginx -v
  INSTALLED="$(nginx -v 2>&1 | sed -n 's#.*nginx/##p' | sed 's/[^0-9.].*$//')"
  if version_ge "$INSTALLED" "1.30.1"; then
    echo "版本检查通过:nginx/$INSTALLED"
  else
    echo "版本仍低于 1.30.1,请检查包来源或发行版 backport 状态:nginx/$INSTALLED"
    exit 1
  fi
}

setup_apt() {
  OS="$1"
  KEYRING_PACKAGE="$2"
  CODENAME="${3:-${VERSION_CODENAME:-}}"
  if [ -z "$CODENAME" ] && command -v lsb_release >/dev/null 2>&1; then
    CODENAME="$(lsb_release -cs)"
  fi
  if [ -z "$CODENAME" ]; then
    echo "无法识别发行版代号"
    exit 1
  fi

  apt-get update
  apt-get install -y curl gnupg ca-certificates lsb-release "$KEYRING_PACKAGE"
  install -d -m 0755 /usr/share/keyrings
  curl -fsSL "$NGINX_KEY_URL" | gpg --dearmor >/usr/share/keyrings/nginx-archive-keyring.gpg
  chmod 0644 /usr/share/keyrings/nginx-archive-keyring.gpg

  cat >/etc/apt/sources.list.d/nginx.list <<EOF
deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://nginx.org/packages/mainline/${OS} ${CODENAME} nginx
EOF

  cat >/etc/apt/preferences.d/99nginx <<'EOF'
Package: *
Pin: origin nginx.org
Pin: release o=nginx
Pin-Priority: 900
EOF

  apt-get update
  apt-cache policy nginx
  apt-get install -y nginx
}

setup_rhel_family() {
  PM="yum"
  command -v dnf >/dev/null 2>&1 && PM="dnf"
  "$PM" install -y yum-utils ca-certificates curl

  cat >/etc/yum.repos.d/nginx.repo <<'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=https://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=https://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF

  "$PM" module disable -y nginx >/dev/null 2>&1 || true
  "$PM" install -y nginx
}

setup_amazon() {
  PM="yum"
  command -v dnf >/dev/null 2>&1 && PM="dnf"
  "$PM" install -y yum-utils ca-certificates curl

  if [ "${VERSION_ID:-}" = "2023" ]; then
    MAINLINE_URL='https://nginx.org/packages/mainline/amzn/2023/$basearch/'
    STABLE_URL='https://nginx.org/packages/amzn/2023/$basearch/'
  else
    MAINLINE_URL='https://nginx.org/packages/mainline/amzn2/$releasever/$basearch/'
    STABLE_URL='https://nginx.org/packages/amzn2/$releasever/$basearch/'
  fi

  cat >/etc/yum.repos.d/nginx.repo <<EOF
[nginx-stable]
name=nginx stable repo
baseurl=${STABLE_URL}
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
priority=9

[nginx-mainline]
name=nginx mainline repo
baseurl=${MAINLINE_URL}
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
priority=9
EOF

  "$PM" install -y nginx
}

setup_sles() {
  zypper --non-interactive install curl ca-certificates gpg2
  curl -fsSL -o /tmp/nginx_signing.key "$NGINX_KEY_URL"
  rpmkeys --import /tmp/nginx_signing.key
  zypper --non-interactive removerepo nginx-mainline >/dev/null 2>&1 || true
  zypper --non-interactive addrepo --gpgcheck --type yum --refresh --check \
    'https://nginx.org/packages/mainline/sles/$releasever_major' nginx-mainline
  zypper --non-interactive refresh
  zypper --non-interactive install nginx
}

setup_alpine() {
  apk add --no-cache openssl curl ca-certificates
  ALPINE_VER="$(grep -o '^[0-9]\+\.[0-9]\+' /etc/alpine-release)"
  REPO="@nginx https://nginx.org/packages/mainline/alpine/v${ALPINE_VER}/main"
  grep -qxF "$REPO" /etc/apk/repositories || echo "$REPO" >>/etc/apk/repositories
  curl -fsSL -o /etc/apk/keys/nginx_signing.rsa.pub "$NGINX_APK_KEY_URL"
  apk update
  apk add nginx@nginx
}

case "${ID:-}" in
  debian)
    setup_apt "debian" "debian-archive-keyring" "${1:-}"
    ;;
  ubuntu)
    setup_apt "ubuntu" "ubuntu-keyring" "${1:-}"
    ;;
  rhel|centos|rocky|almalinux|ol|oracle)
    setup_rhel_family
    ;;
  amzn)
    setup_amazon
    ;;
  sles|sled|sles_sap)
    setup_sles
    ;;
  alpine)
    setup_alpine
    ;;
  fedora|arch|manjaro|opensuse-leap|opensuse-tumbleweed|opensuse)
    echo "当前系统 ${ID:-unknown} 不在 nginx.org 官方二进制源支持列表里。"
    echo "请用系统仓库升级后手动确认 nginx -v 是否 >= 1.30.1,或改用官方 Docker 镜像。"
    exit 1
    ;;
  *)
    echo "暂未识别系统 ID=${ID:-unknown},请按 nginx 官方文档手动配置 mainline 源。"
    exit 1
    ;;
esac

verify_nginx

if command -v apt-cache >/dev/null 2>&1; then
  apt-cache policy nginx || true
elif command -v rpm >/dev/null 2>&1; then
  rpm -qi nginx || true
elif command -v apk >/dev/null 2>&1; then
  apk info -v nginx || true
fi

echo "完成,备份目录:$BACKUP_DIR"

Nginx 漏洞编号是 CVE-2026-42945,也有人叫它 NGINX Rift。

它的问题不在控制台,也不在某个后台接口,而是在请求处理路径里的 ngx_http_rewrite_module。公网请求只要能打到命中的 rewrite 配置,就有机会触发 worker 进程里的堆缓冲区溢出。NVD 里 F5 CNA 给的是 CVSS v4.0 9.2,v3.1 是 8.1;nginx.org 的安全公告页把它列成 medium,但受影响版本范围写得很直接:0.6.27 到 1.30.0,修复边界是 1.30.1+ 或 1.31.0+。

触发点不是所有 rewrite,而是这个组合

这次漏洞卡在一个很具体的配置模式上。

按 NVD 的描述,满足下面几件事才危险:

使用 ngx_http_rewrite_module 里的 rewrite 指令。

正则里用了未命名 PCRE 捕获,比如 $1、$2。

替换字符串里带了 ?。

这个 rewrite 后面又跟着 rewrite、if 或 set 指令。

大概长这样:

server {
    listen 8080;

    location / {
        rewrite ^/users/([0-9]+)/profile/(.*)$ /profile.php?id=$1&tab=$2 last;
        set $rewrite_marker 1;

        return 200 "ok\n";
    }
}

$1、$2 和替换目标里的 ?。正常看它只是把路径改写到 PHP 参数里,这类配置在老项目、网关、CMS 迁移规则里并不少见。

Nginx 脚本引擎会先算目标 buffer 需要多长,再把内容 copy 进去。DepthFirst 的分析里提到,当替换字符串里出现 ? 时,主执行引擎会进入 args escape 相关状态;但长度计算那一轮用的是一个新的子引擎,它看到的状态不一致。于是长度按原始 capture 算,真正 copy 时又按 query args 规则转义,某些字符会膨胀,最后写出分配好的 heap buffer。

公开 PoC 已经有了,甚至有 RCE 复现仓库。这里我不贴直接拿去打 shell 的命令,没必要。对大多数维护自己服务器的人来说,第一步应该是确认配置是否命中触发模式,而不是拿 payload 去怼生产。

最后

这次 CVE-2026-42945 给我的感觉是,Nginx 这种老牌基础设施也不是“稳定到不用看”的东西。

危险的地方不只是“18 年老洞”,而是触发点藏在非常日常的 rewrite 配置里。你可能不是特意用了什么高级功能,只是多年前为了兼容旧 URL 写了几行 $1、$2,然后这个入口一直挂在公网。

建议:

先用 nginx -T 扫配置,找 $1/$2 加 ? 的 rewrite。

有危险配置就先改成命名捕获。

Debian 用户不要只看 1.26.3 这个版本号,要看安全跟踪状态或包来源。

能升级就直接上 nginx 官方修复版本,稳定线至少 1.30.1+,主线就是 1.31.x。

参考:

NGINX 官方安全公告:https://nginx.org/en/security_advisories.html

NGINX 官方发布信息:https://nginx.org/

NGINX Linux 包安装文档:https://nginx.org/en/linux_packages.html

NVD CVE-2026-42945:https://nvd.nist.gov/vuln/detail/CVE-2026-42945

DepthFirst NGINX Rift 分析:https://depthfirst.com/research/nginx-rift-achieving-nginx-rce-via-an-18-year-old-vulnerability

Debian Security Tracker:https://security-tracker.debian.org/tracker/CVE-2026-42945

评论交流

文章目录