한 줄 요약

rewrite 한 줄에 이름 없는 PCRE 캡처($1, $2)와 대체 문자열의 물음표(?)가 함께 들어가면, 비인증 공격자가 HTTP 요청 한 번으로 워커의 힙을 부순다. CVSS v4.0 9.2 Critical, 18년 전(2008년 NGINX 0.6.27)부터 조용히 있었던 결함이다.

F5와 depthfirst가 2026-05-13에 공동 공개했고, 별명은 NGINX Rift. 사흘 뒤인 5월 16일부터 VulnCheck canary가 실 익스플로이트 시도를 잡기 시작했다. 운영자는 즉시 패치 또는 임시 완화 둘 중 하나는 골라야 한다.

18년 묵은 결함이라는 의미

src/http/ngx_http_script.c에서 사는 결함이다. 2008년 NGINX 0.6.27에 처음 들어간 후 지금까지 모든 빌드가 영향권이다. 단일 함수 한 줄짜리 회귀가 아니라, 스크립트 엔진의 설계 단계에서 두 단계 호출 사이의 상태 누출이 그대로 굳어 버린 형태다.

지금까지 익숙했던 NGINX 취약점은 보통 모듈 단위였고 영향 버전 범위도 1~2년 정도였다. Rift는 0.6.27 ~ 1.30.0, NGINX Plus는 R32 ~ R36까지 한 줄로 묶인다. 같은 코드 경로를 그대로 가져다 쓴 fork와 다운스트림도 똑같이 영향을 받는다.

영향 받는 버전과 해결 버전

제품영향 받는 버전해결 버전
NGINX PlusR36 P4 미만R36 P4 이상
NGINX PlusR35 / R34 / R33지원 라인업으로 마이그레이션
NGINX PlusR32 P6 미만R32 P6 이상
NGINX Open Source1.30.1 미만 (≥ 0.6.27)1.30.1 (stable) / 1.31.0 (mainline)
NGINX Open Source0.9.7 이하지원 라인업으로 마이그레이션

R35, R34, R33은 별도 패치 빌드가 없다 — 지원 중인 라인(R36 P4 또는 R32 P6)으로 마이그레이션이 답이다. F5 advisory는 K000161019, CVE 메타데이터는 NVD 페이지에 있다.

발현 조건

세 가지가 한 자리에서 만나야 한다.

  1. rewrite 지시문이 있다.
  2. 그 뒤에 rewrite, if, set 중 하나가 따라온다.
  3. 대체 문자열에 이름 없는 PCRE 캡처($1, $2 …)와 물음표(?)가 같이 들어간다.

예를 들어 다음 같은 흔한 패턴이 그대로 함정이다.

location /old/ {
    # 취약: 이름 없는 $1 + 물음표 ?
    rewrite ^/old/(.*)$ /new/$1?source=legacy break;
    if ($http_user_agent ~ "bot") {
        return 403;
    }
}

이 자리에서 비인증 공격자가 특정 형태의 HTTP 요청을 한 번 보내면 워커의 힙에 경계 너머 쓰기가 일어난다. ASLR이 꺼져 있으면 임의 코드 실행이 가능해진다 — 워커 권한이므로 권한 상승은 그다음 단계지만, 컨테이너·구형 임베디드 환경에서는 그것만으로도 충분히 치명적이다.

버그 메커니즘 — 두 단계 스크립트 엔진의 상태 누출

NGINX의 스크립트 엔진은 같은 rewrite를 두 번 돈다. 길이 계산 패스(length pass)에서 출력 버퍼 크기를 정하고, 복사 패스(copy pass)에서 실제 값을 쓴다.

문제는 두 패스 사이에서 is_args 상태 플래그가 그대로 남는다는 점이다. 길이 계산 단계에서 물음표를 만나 is_args = 1로 켜진 플래그가 복사 단계의 다음 캡처 처리에 누출된다. 복사 단계의 ngx_escape_uri()는 켜진 플래그를 보고 인자(query string)용 이스케이프 규칙으로 더 많은 바이트를 써넣는다 — 길이 계산 단계에서는 다른 규칙으로 짧게 잡아 둔 그 버퍼에 말이다.

결과는 결정론적이다. heap chunk 경계 너머에 의도된 길이만큼 정확히 흘러넘친다.

flowchart TD
    REQ["악성 HTTP 요청"]
    REW["rewrite 지시문
(?, $1 포함)"] LEN["1단계: 길이 계산
is_args 플래그 ON"] LEAK["플래그 누출
(reset 누락)"] COPY["2단계: 복사 패스
다음 캡처 처리"] ESC["ngx_escape_uri
인자 규칙 적용"] BUF["힙 버퍼 경계 초과 쓰기"] DOS["워커 죽음 / 재시작"] RCE["ASLR 꺼지면 RCE"] REQ --> REW REW --> LEN LEN --> LEAK LEAK --> COPY COPY --> ESC ESC --> BUF BUF --> DOS BUF --> RCE

패치(1.30.1 / 1.31.0)는 두 패스 진입 시 is_args를 명시적으로 reset하는 한 줄이 핵심이다. 18년 동안 묻혀 있던 이유는 — if / set이 뒤따르는 rewrite 체인에서만 두 패스가 같은 캡처를 두 번 보고, 거기에 이름 없는 캡처와 물음표가 동시에 있어야 하기 때문이다. 운영 환경에서 충분히 흔한 패턴인데 fuzz 코퍼스가 거기에 닿지 않았다.

임시 완화 — named capture로 교체

패치를 당장 못 올리는 환경에서는 F5와 depthfirst 모두 같은 권고를 한다. 모든 rewrite에서 이름 없는 캡처를 이름 있는 캡처로 바꾼다.

# Before (취약)
rewrite ^/old/(.*)$ /new/$1?source=legacy break;

# After (안전)
rewrite ^/old/(?<path>.*)$ /new/$path?source=legacy break;

이름 있는 캡처는 NGINX 스크립트 엔진의 다른 코드 경로를 타고, 거기에는 같은 플래그 누출이 없다. 동일한 동작을 유지하면서 결함 경로만 회피한다.

운영 중인 모든 config 파일에서 이름 없는 캡처 패턴을 검색해 둘 자리만 골라낸다.

grep -rnE 'rewrite[^;]*\$[0-9]' /etc/nginx/

지금 해야 할 일

  1. 버전 확인. nginx -v로 정확한 빌드를 본다. NGINX Plus는 nginx-plus -V에서 R 라인업이 나온다.
  2. 노출도 평가. 위의 grep 한 줄로 이름 없는 캡처 + 물음표 패턴을 가진 rewrite가 있는지 본다. 없으면 시간을 벌 수 있다.
  3. 패치 적용. Open Source는 1.30.1 / 1.31.0, NGINX Plus는 R36 P4 / R32 P6. R33~R35는 지원 라인으로 마이그레이션.
  4. 즉시 패치 불가 시 named capture로 우회. 임시 패치 검증은 두 단계 — 정상 트래픽 회귀 테스트 + PoC 변형 요청 차단 확인.
  5. ASLR 확인. cat /proc/sys/kernel/randomize_va_space2인지 본다. 0이면 임의 코드 실행 위험이 같이 살아 있다.
  6. WAF·CDN 룰 확인. 앞단에 Cloudflare나 F5 BIG-IP 등이 있으면 벤더의 임시 시그니처가 이미 배포돼 있다 — Cloudflare는 5월 13일 당일에 managed rule을 풀었다.
  7. 로그 회수. error.log에서 워커 재시작 패턴(worker process ... exited on signal 11)을 5월 13일 이후 기간으로 잡고 본다. 평소보다 빈도가 높으면 정찰 시도 흔적일 수 있다.

실 익스플로이트 현황

PoC는 DepthFirstDisclosures/Nginx-Rift repo에 공개돼 있다. VulnCheck의 canary 시스템이 5월 16일부터 인터넷 노출 NGINX를 향한 실 익스플로이트 시도를 잡기 시작했다. 공개에서 실 공격까지 사흘이다. Ingress NGINX(K8s)로도 같은 결함이 그대로 전파된다 — HeroDevs의 분석이 클러스터 운영자 관점에서 가장 친절하다.

정리

항목
CVECVE-2026-42945 (NGINX Rift)
분류CWE-122 Heap-based Buffer Overflow
CVSSv4.0 9.2 Critical / v3.1 8.1 High
공개일2026-05-13
발견자depthfirst (with F5)
도입 시점2008년 NGINX 0.6.27
패치NGINX 1.30.1 / 1.31.0 · NGINX Plus R36 P4 / R32 P6
임시 완화이름 없는 PCRE 캡처를 이름 있는 캡처로 교체
실 익스플로이트2026-05-16부터 관측 (VulnCheck)

if · set이 따라붙는 rewrite 체인은 어디서나 자주 쓴다. 운영하는 NGINX가 한 대라도 외부에 노출돼 있고, rewrite config에 $1 같은 표현이 한 줄이라도 들어 있다면, 지금이 1.30.1 패치를 올릴 가장 좋은 순간이다.

참고