NOW Briefing
nginx 문자열 표현식 평가 과정의 힙 버퍼 오버플로우 취약점 (CVE-2026-93479)
■ 개요
nginx는 웹 서버와 리버스 프록시(클라이언트 요청을 뒤쪽 서버로 중계하는 중간 서버)로 널리 쓰이는 오픈소스 소프트웨어입니다. 2026년 10월 14일 공개된 1.31.7과 1.30.6은 다섯 건의 취약점을 수정한 보안 릴리스이며, 이 가운데 하나가 변수가 포함된 문자열 표현식을 평가하는 과정에서 힙 버퍼 오버플로우(할당된 메모리 영역 밖으로 데이터를 쓰는 오류)가 발생하는 CVE-2026-93479입니다.
F5가 등록한 CVE 레코드 기준으로, 미인증 원격 공격자가 조작된 HTTP 요청을 보내 워커 프로세스(실제 요청을 처리하는 nginx 하위 프로세스)에서 힙 버퍼 오버플로우를 일으키고 서비스 거부를 유발할 수 있습니다. 같은 문서는 ASLR이 비활성화됐거나 공격자가 이를 우회할 수 있는 시스템에서는 코드 실행으로 이어질 가능성도 있다고 설명합니다. 다만 영향 범위는 트래픽을 처리하는 데이터 플레인에 한정되며, 관리·설정을 담당하는 제어 플레인 노출은 없다고 명시합니다.
취약 버전은 Open Source 1.31.2~1.31.6과 1.30.0~1.30.5, NGINX Plus R33~R36 및 37.0.0~37.0.5.1 미만·37.1.0~37.1.0.1 미만입니다. F5는 CVSS v3.1 8.1점과 CVSS v4.0 9.2점을 부여했지만 nginx.org 보안 권고 목록은 같은 항목의 심각도를 Medium으로 표기해 출처 간 표기가 엇갈립니다. 심각도 표기가 갈리더라도 패치 적용을 우선해야 합니다.
■ 요약
| 항목 | 내용 |
|---|---|
| CVE ID | CVE-2026-93479 (문자열 표현식 평가 중 힙 버퍼 오버플로우) |
| CVSS 점수 | • CVSS v3.1 8.1 • CVSS v4.0 9.2 |
| 취약점 유형 | CWE-122, 힙 기반 버퍼 오버플로우 |
| 영향/위험 | • 미인증 원격 공격자가 조작된 HTTP 요청으로 워커 프로세스 손상·서비스 거부 유발 가능 • ASLR이 비활성화됐거나 우회 가능한 시스템에서는 코드 실행 가능성 있음(벤더 기준) • 데이터 플레인에 한정되며 제어 플레인 노출은 없음(벤더 기준) • 접근 로그 경로에서는 널 바이트 1개 초과 쓰기로 영향이 제한됨(패치 커밋 기준) |
| 취약 버전 | • NGINX Open Source 1.31.2~1.31.6 • NGINX Open Source 1.30.0~1.30.5 • NGINX Plus R33~R36 • NGINX Plus 37.0.0~37.0.5.1 미만, 37.1.0~37.1.0.1 미만 |
| 패치 버전 | • NGINX Open Source 1.31.7, 1.30.6 • NGINX Plus 37.0.5.1, 37.1.0.1 |
■ 취약 설정 조건과 영향
벤더 기준으로 정규식 매칭을 사용하는 map 지시어(요청 값에 따라 변수 값을 결정하는 nginx 설정 블록)가 있어야 하고, root 지시어나 접근 로그 형식이 그 map의 출력 변수보다 정규식 캡처 변수(정규식 괄호로 잡아낸 부분 문자열이 담기는 $1~$9 형태의 변수)를 먼저 참조해야 합니다. 벤더는 문자열 표현식에서 캐시되지 않는 변수를 쓰는 경우에도 특정 조건에서 같은 결과가 만들어질 수 있다고 덧붙였습니다.
기본 설정만으로 자동 성립하는 조건은 아니며, CVE 레코드도 공격자가 통제할 수 없는 조건이 함께 갖춰져야 악용이 가능하다고 명시합니다. 반대로 조건이 성립하는 서버라면 로그인이나 사전 권한 없이 요청만으로 도달할 수 있으므로, 설정 점검을 패치 적용과 같은 우선순위로 다뤄야 합니다. 벤더 확인 기준으로 현재까지 공개된 PoC 코드나 실제 공격에 악용된 사례는 확인되지 않았습니다.
■ 버퍼 계산이 어긋나는 원리
nginx는 변수가 포함된 문자열을 두 번에 걸쳐 처리합니다. 먼저 길이 계산 코드를 돌려 필요한 바이트 수를 구하고 버퍼를 할당한 다음, 값 생성 코드를 돌려 그 버퍼에 실제 데이터를 씁니다. 이때 호출자는 뒤에 덧붙일 공간을 미리 확보하기 위해 예약 영역 크기를 len 인자로 넘깁니다. 패치 전에는 이 예약 영역 크기가 길이 누적 변수 n의 시작값으로 들어갔기 때문에, 쓰기 단계의 경계가 예약 영역까지 포함하도록 잡혀 데이터가 예약 영역으로 밀려들어갈 수 있었습니다. 커밋 설명에 따르면 이후 도입된 문자열 길이 재계산과 맞물려 최종 문자열 길이가 할당된 버퍼 크기보다 커질 수 있었고, 최대 초과 폭은 예약 영역 크기만큼입니다.
아래는 수정 커밋 6aa4b3fd의 src/http/ngx_http_script.c 변경분으로, 길이 누적값과 예약 영역을 분리하는 부분입니다.
@@ -645,16 +645,16 @@ ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,
e.request = r;
e.flushed = 1;
- n = len;
+ n = 0;
while (*(uintptr_t *) e.ip) {
lcode = *(ngx_http_script_len_code_pt *) e.ip;
n += lcode(&e);
}
- value->len = n;
- value->data = ngx_pnalloc(r->pool, n);
+ value->len = n + len;
+ value->data = ngx_pnalloc(r->pool, n + len);
if (value->data == NULL) {
return NULL;
}
패치 전에도 n이 len부터 누적됐으므로 확보되는 전체 버퍼 크기는 전후가 같습니다. 다만 n이 더 이상 예약 영역을 포함하지 않으므로 쓰기 경계가 좁아집니다. 다음은 같은 커밋 시점의 ngx_http_script_run()에서 버퍼를 할당하고 쓰기 엔진을 초기화하는 부분으로, 경계 변수 e.end는 예약 영역을 제외한 n까지만 허용합니다.
value->len = n + len;
value->data = ngx_pnalloc(r->pool, n + len);
if (value->data == NULL) {
return NULL;
}
e.ip = code_values;
e.pos = value->data;
e.end = value->data + n;
커밋 메시지는 영향받는 지점을 HTTP의 변수 기반 문서 루트 계산과 HTTP·Stream의 접근 로그로 꼽고, 접근 로그의 경우 널 바이트 1개를 범위 밖에 쓰는 수준으로 영향이 제한된다고 설명합니다. nginx는 src/stream/ngx_stream_script.c의 ngx_stream_script_run()에도 같은 수정을 적용했습니다.
커밋 자체에 CVE 번호는 적혀 있지 않습니다. 다만 보고자 표기(Matt Suiche of Tolmo Inc.)가 CVE 레코드 크레딧에 포함되고 수정 내용이 레코드 설명과 일치합니다. 1.31.6 태그의 소스에는 n = len;이 그대로 남아 있어, 해당 수정이 1.31.7에 처음 반영된 것으로 확인됩니다. CVE 레코드 크레딧에는 Matt Suiche와 함께 Mateusz Gierblinski도 명시돼 있습니다.
■ 탐지 및 점검
• nginx -v로 실행 중인 버전을 확인해 Open Source 1.31.2~1.31.6 또는 1.30.0~1.30.5에 해당하는지 점검합니다. NGINX Plus는 R33~R36과 37.0.0~37.0.5.1 미만·37.1.0~37.1.0.1 미만이 대상입니다.
• nginx -T로 실행 중인 설정 전체를 출력한 뒤, 정규식을 사용하는 map 블록이 있는지, 그리고 root나 log_format이 $1~$9 형태의 번호 캡처 변수를 참조하는지 확인합니다.
• stream 블록의 접근 로그 처리 코드에도 같은 수정이 적용됐으므로 해당 설정을 함께 점검합니다.
• 오류 로그에서 워커 프로세스가 시그널로 종료되고 재시작된 기록을 확인하고, 같은 시각의 접근 로그에서 반복적으로 들어온 비정상 요청 경로를 대조합니다. 다만 워커 재시작 기록 하나만으로는 이 취약점의 악용을 확정할 수 없으므로, 취약 버전·취약 설정 조합이 함께 확인되는 경우에만 추가 점검 대상으로 다룹니다.
✅ CHECK
버전과 설정 중 하나만 보고 판단하지 말고 두 조건을 함께 확인해야 합니다.
해당 설정 조합이 없더라도 문자열 표현식에서 캐시되지 않는 변수를 쓰는 경우에는 특정 조건에서 같은 결과가 발생할 수 있습니다.
■ 대응 방안
• NGINX Open Source는 1.31.7 또는 1.30.6 이상으로, NGINX Plus는 37.1.0.1 또는 37.0.5.1 이상으로 업그레이드합니다.
• R36 이하의 NGINX Plus를 운영 중이라면 수정판이 제공되는 지원 릴리스로 올려야 합니다. CVE 레코드는 기술 지원이 종료된(EoTS) 버전은 평가 대상이 아니라고 명시합니다.
• 즉시 업그레이드가 어렵다면 벤더가 제시한 완화 조치에 따라 번호 캡처 대신 명명 캡처를 사용하고, 그 사용 범위를 정규식이 매칭되는 블록 안으로 제한합니다.
• 같은 릴리스에서 alias·rewrite 조합 버퍼 오버플로우, HTTP/3 요청 본문 주입, SSI 모듈과 비동기 파일 입출력(AIO) 조합의 해제된 메모리 재사용(use-after-free), TLS 세션 티켓 키 교체 시 세션 위조가 함께 수정됐으므로 개별 수정이 아닌 릴리스 단위로 적용합니다.
💡 TIP
완화 조치는 설정 변경이므로 include로 불러오는 하위 설정 파일까지 모두 확인해야 누락이 없습니다.
변경 후에는 nginx -t로 문법을 검증하고 재적용합니다.
[참고 자료]
• nginx 2026년 변경 이력 — 1.31.7 / 1.30.6 릴리스
• nginx 수정 커밋 — Script: reserved memory overrun protection in ngx_http_script_run()
• 수정 커밋 시점의 src/http/ngx_http_script.c 원본
• 수정 커밋 시점의 src/stream/ngx_stream_script.c 원본

