Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
NGINX-ngx_http_rewrite_module-heap-buffer-overflow-CVE-2026-9256 — CVE-2026-9256에 대한 개념 증명으로, NGINX의 ngx_http_rewrite_module의 힙 버퍼 오버플로입니다. 겹치는 PCRE 캡처 그룹이 있는 조작된 URI를 통해 작업자 충돌 및 서비스 거부를 시연합니다. 다단계 검증 및 keep-alive 프로빙을 포함합니다. | Kitploit
도구/GitHubGitHub/w5m1n9/nginx-ngx_http_rewrite_module-heap-buffer-overflow-cve-2026-9256
Vulnerability AnalysisExploitationWeb SecurityFuzzingPenetration Testing
GitHubw5m1n9/nginx-ngx_http_rewrite_module-heap-buffer-overflow-cve-2026-9256

NGINX-ngx_http_rewrite_module-heap-buffer-overflow-CVE-2026-9256

CVE-2026-9256에 대한 개념 증명으로, NGINX의 ngx_http_rewrite_module의 힙 버퍼 오버플로입니다. 겹치는 PCRE 캡처 그룹이 있는 조작된 URI를 통해 작업자 충돌 및 서비스 거부를 시연합니다. 다단계 검증 및 keep-alive 프로빙을 포함합니다.

저장소 보기
274개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-9256 NGINX ngx_http_rewrite_module PoC 유도 과정과 고찰

적용 범위: 로컬 실습 환경, 승인된 재현 환경, 취약점 검증 및 보호 규칙 분석에만 사용합니다. 승인되지 않은 대상에는 사용하지 마십시오. 본 문서의 PoC 아이디어는 원격에서 관찰 가능한 NGINX worker crash 동작만 검증하며, RCE, ASLR 우회 또는 안정적인 익스플로잇 체인을 포함하지 않습니다.

1. 취약점 배경

CVE-2026-9256은 NGINX ngx_http_rewrite_module의 힙 버퍼 오버플로우 취약점입니다. 취약점 트리거는 단순히 특정 고정 URI에 접근하는 것이 아니라, 특정 rewrite 설정 패턴에 의존합니다: rewrite 정규식에 서로 겹치는 PCRE 캡처 그룹이 존재하고, replacement 부분에서 $1, $2와 같은 여러 캡처 변수를 참조합니다.

공격자가 특수 URI를 구성하여 rewrite 로직이 관련 경로에 진입하면, NGINX가 캡처 내용을 처리하고 rewrite 결과를 연결하거나 URI/파라미터 이스케이프를 수행할 때 길이 계산과 실제 쓰기가 불일치하여 결국 worker 프로세스 힙 메모리가 손상될 수 있습니다.

따라서 이 취약점의 핵심은 /api라는 경로 자체가 아니라, 대상 NGINX 설정에 요청으로 적중 가능한 취약한 rewrite 규칙이 존재하는지 여부입니다. PoC에서 기본적으로 사용하는 /api는 현재 재현 환경의 예시 경로일 뿐이며, 실제 테스트 시에는 NGINX 설정에 존재하는, 겹치는 캡처 그룹을 포함하고 여러 캡처 변수를 참조하는 rewrite 규칙에 맞게 요청 경로를 조정해야 합니다.

현재 PoC의 목표는 worker crash / 서비스 거부(denial of service) 동작을 검증하는 것입니다. 정확한 힙 레이아웃을 구성하거나, 반환 주소나 함수 포인터를 덮어쓰거나, 원격 코드 실행을 증명하려고 시도하지 않습니다. 원격 측에서 관찰할 수 있는 안정적인 증거는 주로: 트리거 요청 연결이 비정상적으로 종료되고, 이후 NGINX 서비스가 응답을 재개하며, keep-alive 연결이 트리거 후 worker crash로 인해 중단되는 현상입니다.

2. 왜 HTTP 상태 코드만으로 판단할 수 없는가?

이 취약점이 트리거된 후 반드시 고정된 HTTP 500, 502 또는 400으로 나타나는 것은 아닙니다. 그 이유는 NGINX의 master-worker 모델에서 worker 프로세스가 충돌하면 master가 새로운 worker를 다시 띄우기 때문입니다. 공격자가 원격 측에서 보는 현상은 일반적으로 전체 서비스가 완전히 사용 불가능해지는 것이 아니라, 특정 연결이 갑자기 끊기거나, 읽기 타임아웃이 발생하거나, 연결이 리셋되고, 이후 다시 /에 접근하면 정상 응답을 받을 수 있는 상태입니다.

따라서 PoC는 단일 요청의 HTTP 상태 코드만으로 취약점 존재 여부를 판단할 수 없습니다. 긴 URI를 한 번 보내고 연결이 끊어지는 것을 보고 바로 "취약점 존재"라고 판단하면 오탐 위험이 높습니다. 연결 끊김은 네트워크 지터, 프록시 타임아웃, 요청이 중간 장비에 차단되거나, 백엔드 속도 제한 또는 서버 측에서 능동적으로 연결을 닫는 경우에도 발생할 수 있습니다.

따라서 PoC는 다단계 검증 방식으로 설계되어야 합니다:

  1. 먼저 대상이 살아있는지 확인합니다.
  2. 그런 다음 예시 rewrite 경로가 작동할 가능성이 있는지 확인합니다.
  3. 오버플로우 트리거 요청을 보내고 연결이 비정상적으로 종료되거나 타임아웃되는지 관찰합니다.
  4. 즉시 일반 요청을 보내 NGINX가 이미 응답을 재개했는지 확인합니다.
  5. keep-alive 방식을 통해 트리거 후 worker 연결이 안정적으로 끊어지는지 반복 검증합니다.

"트리거 연결 비정상 + 이후 서비스 복구 + keep-alive 다중 연결 끊김"이 동시에 나타날 때만 CVE-2026-9256 스타일의 worker crash 동작이 존재한다고 더 안정적으로 판단할 수 있습니다.

3. PoC 구성 아이디어

현재 PoC의 핵심 트리거 경로는 다음과 같습니다:

GET /api/++++++++++++++++++++++++++++++++... HTTP/1.1
Host: 127.0.0.1:19321

여기서 /api/는 현재 실습 환경에서 rewrite 규칙을 적중시키기 위한 예시 라우트이며, 뒤에 많은 수의 + 문자가 이어집니다. 기본 개수는 4096개입니다.

+를 선택한 이유는 주로 세 가지입니다.

첫째, +는 유효한 URI 문자이며, 일반 HTTP 클라이언트를 사용하여 전송할 때 공백, # 등의 문자처럼 잘리거나 강제로 변환되지 않는 경우가 많습니다. 따라서 현재 PoC는 일부 request-target 우회 취약점처럼 반드시 raw socket을 사용하여 비정상적인 요청 라인을 구성할 필요가 없습니다.

둘째, 많은 반복 문자는 rewrite 캡처 그룹이 충분히 긴 입력을 받도록 하여, 이후 replacement 연결 또는 이스케이프 처리의 출력 규모를 확대함으로써 길이 계산과 실제 쓰기 간의 불일치를 더 쉽게 트리거할 수 있습니다.

셋째, 반복되는 +로 구성된 페이로드는 구조가 간단하여 패킷 캡처, 로그, IDS 규칙에서 관찰하기 쉽고, 길이를 조정하여 임계값 테스트를 수행하기에도 편리합니다.

다만 +가 유일한 이론적 트리거 문자는 아닙니다. 실제 트리거 조건은 여전히 "취약한 rewrite 설정에 적중 + 입력이 관련 캡처 그룹에 진입 + rewrite 출력 처리로 힙 오버플로우 발생"입니다. 환경에 따라 트리거 라우트, 문자 유형, 길이 임계값을 조정해야 할 수 있습니다.

3.1 기타 트리거 가능 문자 설명

현재 PoC는 기본적으로 많은 수의 +를 트리거 문자로 선택하지만, 이것이 +만이 문제를 트리거할 수 있다는 의미는 아닙니다. +는 일반 PoC에 작성하기에 가장 적합한 문자일 뿐입니다. URI에서 상대적으로 안정적이며, 일반 HTTP 클라이언트로 전송하기 쉽고, 패킷 캡처 특징도 명확합니다.

취약점 원리상, NGINX rewrite 처리 과정에서 문자가 NGX_ESCAPE_ARGS 이스케이프 로직에 진입하고, 원래 1바이트에서 %XX 형태의 3바이트로 확장된다면, "길이 계산 값이 실제 쓰기 값보다 작은" 차이가 발생할 수 있습니다. 즉, 트리거 포인트는 본질적으로 + 자체가 아니라 "args 모드에서 이스케이프 가능한 문자가密集(밀집)하여 나타나는 것"입니다.

+ 외에도 이론적으로 고려해야 할 주요 문자는 다음과 같습니다:

공백: 0x20
#: 0x23
%: 0x25
&: 0x26
?: 0x3F
제어 문자: 0x00-0x1F
상위 바이트: 0x7F-0xFF

이 문자들이 관련 캡처에 진입하여 rewrite replacement에서 args 내용으로 이스케이프 처리되면 유사한 확장 효과가 발생합니다. 예:

+      -> %2B
&      -> %26
%      -> %25
#      -> %23
?      -> %3F
공백   -> %20

이러한 문자가 하나 나타날 때마다 이론적으로 1바이트에서 3바이트로 확장되어 실제 쓰기 길이가 2바이트 증가합니다. 입력에 이러한 문자가 많이 존재하면 실제 쓰기 길이가 이전에 잘못 계산된 버퍼 길이를 크게 초과하여 힙 버퍼 오버플로우를 더 쉽게 트리거할 수 있습니다.

그러나 PoC에서 각 문자의 사용 가능성은 완전히 동일하지 않습니다.

+가 가장 안정적입니다. 일반적으로 HTTP request-target에 직접 나타날 수 있으며, 브라우저나 명령줄 도구에 의해 잘리기 어렵고 URI의 path/query 구조를 자연스럽게 변경하지 않습니다. 따라서 현재 PoC는 4096개의 +를 기본 페이로드로 사용합니다.

&도 후보 문자가 될 수 있습니다. args 모드에서 %26으로 이스케이프되기 때문입니다. 그러나 셸에서 &는 백그라운드 실행 의미를 가지며, URL에서도 query 파라미터 구분자로 사용되는 경우가 많으므로 테스트 시 따옴표 처리와 위치에 주의해야 합니다. 그렇지 않으면 요청이 의도대로 전송되지 않을 수 있습니다.

%도 후보 문자가 될 수 있습니다. %25로 이스케이프되기 때문입니다. 그러나 % 자체는 URL 인코딩 접두사이므로 일부 클라이언트, 프록시 또는 프레임워크에서 %XX 시퀀스를 해석하려고 시도할 수 있습니다. 잘못 구성되면 대상이 원래 % 문자 대신 클라이언트가 전처리한 내용을 받을 수 있습니다.

?도 이론적으로 이스케이프 가능한 문자에 속하지만, HTTP request-target에서 path와 query를 구분합니다. 경로에 직접 배치하면 이후 내용이 query string으로 해석되어 rewrite 캡처 범위가 변경될 수 있습니다. 따라서 보충 테스트 문자로는 적합하지만 기본 PoC 주 문자로는 적합하지 않습니다.

#는 이론적으로 이스케이프를 트리거할 수 있지만, 브라우저는 #과 그 이후의 fragment를 서버로 전송하지 않습니다. 많은 고급 HTTP 클라이언트도 이를 인코딩하거나 자릅니다. 따라서 리터럴 #을 테스트하려면 일반적으로 raw socket, Burp Repeater 또는 원래 request-target을 유지할 수 있는 도구가 필요하며, 브라우저 주소 표시줄에 직접 의존할 수 없습니다.

공백 0x20도 이스케이프 문자에 속하지만, 일반 HTTP/1.1 요청 라인에서 공백 자체는 구분자이므로 request-target에 직접 넣으면 요청 라인 구조가 손상됩니다. 실제 테스트 시 %20으로 작성하면 서버 측 처리 단계에서 인코딩된 형태인지 디코딩된 공백인지는 구체적인 파싱 프로세스와 rewrite 위치에 따라 다릅니다. 따라서 공백은 원리 설명 및 보조 테스트에 더 적합하며 기본 페이로드로는 적합하지 않습니다.

0x00-0x1F 제어 문자와 0x7F-0xFF 상위 바이트도 이스케이프 범위에 포함되지만, 실제 HTTP 링크에서는 클라이언트, 프록시, WAF 또는 NGINX HTTP 파서에 의해 차단, 정규화 또는 거부될 가능성이 더 높습니다. 이들은 소스 코드 수준의 이스케이프 대상 설명에는 적합하지만 일반 PoC의 기본 트리거 문자로는 권장되지 않습니다.

따라서 현재 PoC가 +를 사용하는 이유는 취약점이 +로만 트리거될 수 있기 때문이 아니라, +가 세 가지 조건을 동시에 충족하기 때문입니다: args 이스케이프 확장을 트리거할 수 있고, 안정적으로 전송하기 쉬우며, URI 구조를 크게 변경하지 않습니다. 보호 규칙이나 트래픽 분석 시 연속된 +만 매칭해서는 안 되며, 긴 URI에 +, &, %, ?, # 등 이스케이프 가능한 문자가 많이 등장하는 고밀도 조합도 고려해야 합니다.

탐지 관점에서 더 합리적인 일반화는 다음과 같습니다:

/api/ 뒤에 많은 +가 나타남

보다는:

긴 URI에서 NGX_ESCAPE_ARGS 모드에서 %XX로 확장되는 특수 문자가 많이 나타남

++++만 탐지하면 현재 PoC의 기본 작성 방식만 커버할 수 있습니다. 공격자가 페이로드를 &&&&, %%%%, ????로 바꾸거나 +%&?#를 혼합하여 사용하면 단일 + 특징으로는 누락될 수 있습니다. 더 안정적인 탐지 방법은 URI 길이, 특수 문자 밀도, 연속 반복 횟수, rewrite 위험 경로 및 NGINX 서비스 노출 면을 종합적으로 판단하는 것입니다.

4. 대상 정규화 로직

PoC의 normalize_target 함수는 명령줄 입력을 처리하며, 세 가지 형식을 지원합니다:

python3 CVE-2026-9256-poc.py 127.0.0.1:19321
python3 CVE-2026-9256-poc.py 127.0.0.1 19321
python3 CVE-2026-9256-poc.py http://127.0.0.1:19321

사용자가 scheme 없이 host:port만 입력하면 스크립트가 자동으로 http://host:port로 보완합니다. 그런 다음 urllib.parse.urlparse를 사용하여 hostname과 port를 추출하고 base를 생성합니다. 예:

host = 127.0.0.1
port = 19321
base = http://127.0.0.1:19321

현재 구현은 주로 HTTP 평문 실습 환경을 대상으로 합니다. normalize_target이 https:// 형식을 허용하지만, 이후 keep-alive 탐지는 일반 TCP 소켓을 사용하며 TLS 계층을 포함하지 않으므로 HTTPS 시나리오에서는 crash probe가 정확하지 않습니다. HTTPS를 지원하려면 소켓에 ssl.wrap_socket 또는 ssl.create_default_context().wrap_socket()을 추가해야 합니다.

5. 활성 감지 및 rewrite 탐지

PoC는 먼저 check_alive(base)를 호출하여 루트 경로 /에 접근합니다:

GET /

대상이 임의의 HTTP 상태 코드를 반환하면 서비스가 기본적으로 살아있는 것으로 간주하고 계속 테스트합니다. 연결에 실패하면 바로 종료하여, 연결 불가능한 대상을 취약점 트리거 실패로 오판하지 않도록 합니다.

그런 다음 check_rewrite(base)를 호출하여 다음에 접근합니다:

GET /api/test

이 요청은 /api/*가 현재 실습 환경의 rewrite 로직에 적중할 가능성이 있는지 관찰하는 데 사용됩니다. 301, 302, 303, 307, 308 등 리다이렉트 상태 코드가 반환되면 rewrite 리다이렉트 동작이 뚜렷한 것이므로 PoC는 Location 헤더를 보조 증거로 출력합니다.

그러나 이 단계는 필수 성공 조건이 아닙니다. 일부 재현 구성에서는 /api/* 자체가 문제가 있는 rewrite 경로에 진입할 수 있으며, 일반 탐지 요청도 타임아웃되거나 예외적으로 처리될 수 있습니다. 따라서 스크립트는 정상적인 rewrite 응답을 받지 못하더라도 트리거 단계를 계속 진행합니다.

6. 오버플로우 트리거 요청 설계

트리거 함수는 send_trigger(base, plus_count=4096)이며, 핵심 로직은 다음과 같은 연결입니다:

payload = "/api/" + ("+" * plus_count)

기본 최종 요청 경로는 다음과 유사합니다:

/api/++++++++++++++++++++++++++++++++...   총 4096개의 +

그런 다음 requests.get(base + payload, timeout=10, allow_redirects=False)를 통해 요청을 전송합니다.

여기서 자동 리다이렉트를 비활성화하는 데는 두 가지 이유가 있습니다.

첫째, rewrite 자체가 리다이렉트를 반환할 수 있습니다. HTTP 클라이언트가 자동으로 리다이렉트를 따라가면 원래 트리거 요청과 이후 리다이렉트 요청이 혼합되어 첫 번째 요청에서 정확히 무슨 일이 일어났는지 판단하기 어려워집니다.

둘째, PoC는 트리거 단계의 연결 상태에 관심이 있을 뿐, 리다이렉트 후의 비즈니스 페이지에는 관심이 없습니다. 원래 응답을 유지하는 것이 분석에 더 유리합니다.

트리거 결과는 여러 범주로 나뉩니다:

ConnectionError가 포착되면 트리거 요청 중 연결이 비정상적으로 종료되었음을 의미하며, 이는 worker crash의 원격 징후일 수 있습니다.

ReadTimeout이 포착되면 요청 전송 후 오랫동안 정상 응답이 없음을 의미하며, worker가 멈추거나 충돌 전에 정상적으로 반환되지 않았거나, 네트워크 환경으로 인한 타임아웃일 수 있습니다.

도구 다운로드