
ASLR에 독립적인 nginx RCE 체인 PoC로, PoolSlip 힙 과다 읽기 누출(CVE-2026-9256)과 rift 오버플로(CVE-2026-42945)를 결합하여 기본 nginx에서 system()에 도달합니다.
최근 공개된 두 nginx rewrite-engine 버그를 결합하여 재고 공식 nginx:1.30.0 Docker 이미지
(Debian 13, glibc 2.41)에서 ASLR 독립적 원격 system() 을 실행하는 최초의 공개 체인 —
하드코딩된 주소 없음, nginx 재시작 없음, 새 워커당 약 90 % 성공률.
$args 힙 과다 읽기 → 라이브 libc + 힙 유출.is_args 힙 오버플로 (URL-안전 바이트만) → limit_conn 정리
포인터의 2바이트 부분 덮어쓰기 → ngx_destroy_pool이 이를 따라감 → system(cmd).둘 다 동일한 is_args 2-패스 불일치가 두 개의 싱크를 향한 것입니다. 전체 기술 문서: Writeup
공개 및 안전
- 두 CVE 모두 이미 패치됨이며 야생에서 활발히 악용 중입니다. nginx 1.30.2 (stable) 또는 1.31.1 (mainline)로 업그레이드하세요 —
1.30.1은 rift를 수정하지만 PoolSlip에는 여전히 취약하므로,1.30.2만이 둘 다 해결합니다. NGINX Plus: R36 P5 / R32 P7 / R37.0.1.1.- 이는 교육 및 승인된 테스트 전용으로 공개됩니다. 번들된 로컬 Docker 랩에 대해서만 실행됩니다 — 소유하지 않았거나 서면 허가를 받지 않은 인프라를 향해 실행하지 마세요.
| CVE | 별칭 | 취약 | 수정 (nginx OSS) |
|---|---|---|---|
| CVE-2026-42945 | rift | 0.6.27 – 1.30.0 | 1.30.1 / 1.31.0 |
| CVE-2026-9256 | PoolSlip | 0.1.17 – 1.30.1 + 1.31.0 | 1.30.2 / 1.31.1 |
| 파일 | 용도 |
|---|---|
nginx.conf | 그럴듯한 API 게이트웨이 설정 (대상 — limit_conn, v1→v2 마이그레이션 rewrite, 업로드 프록시, 성능 저하된 검색 페이지). 할당자 튜닝 없음. |
run.sh | 로컬 랩 구동: nginx.conf를 서비스하는 재고 nginx:1.30.0과 느린 업스트림을 호스트 :19322에 설정. |
exp_official.py | 전체 체인: PoolSlip 유출 → libc/힙 도출 → 스프레이 → 그루밍 → rift 부분 덮어쓰기 → system(). |
exp_official.py 실행용), curl, Linux x86-64.run.sh는 느린 업스트림을 위해 perl (재고 nginx 이미지에 이미 포함)을 사용하므로 추가 패키지, slow_backend 파일, 인터넷이 필요 없습니다. nginx 바이너리 자체는 재고 상태로 유지됩니다.WRITEUP.md의 gdb 세션을 따라가려면 docker exec로 접속하여 apt-get install gdb file binutils를 실행한 후 gef를 로드하세요.git clone https://github.com/y198nt/Nginx-chain-Rift-Poolslip
cd Nginx-chain-Rift-Poolslip
bash run.sh # stock nginx:1.30.0 on http://127.0.0.1:19322
python3 exp_official.py --cmd 'id > /tmp/rce_proof' # leak → overwrite → system()
docker exec nginx-rift-official cat /tmp/rce_proof # → uid=101(nginx) ...
exp_official.py는 일회성 (~새 워커당 약 90 %): 한 번 유출하고 한 번 발사합니다. 증명 파일이
비어 있다면 그루밍이 한 슬롯 어긋난 것이니 — 다시 실행하세요.
docker rm -f nginx-rift-official # tear the lab down
rewrite ^/search/((.*))$ /lookup?$1$2는 복사 패스가 r->args.len을
버퍼 너머로 설정하게 만듭니다; 성능 저하된 /search 페이지가 $args를 인접 힙에 반영합니다.
짧은 워밍업 후, libpcre-cluster 포인터 (libc 아래 고정 0xb0ad08)와 힙 포인터가 안정적인
오프셋에 자리잡습니다 → libc_base와 heap_base, 하드코딩된 것 없음.limit_conn 정리
포인터의 하위 2바이트만 덮어씁니다 — ASLR-무작위 상위 바이트는 그대로 따라감 →
ASLR 독립적.ngx_pool_cleanup_t{handler=&system, data=cmd, next=0}로
리다이렉트됩니다; 희생자를 닫으면 ngx_destroy_pool의 정리 순회가 트리거됨 →
system(cmd).전체 도출, gdb 세션, 막다른 길/전환점은 Writeup을 참조하세요.
?가 포함되고 PCRE 캡처 ($1/$2)를 참조하는 rewrite, 또는
^/x/((.*))$ → /y?$1$2와 같은 중첩 캡처 패턴을 통해서만 발동합니다. 설정에서 이를 감사하고
패치될 때까지 문제가 되는 rewrite/set을 재작업하세요.$args (또는 요청에서 파생된 변수를 그대로 되돌려 보내는 것)가 과다 읽기를
정보 유출로 바꾸는 원인입니다 — 요청 인자를 그대로 반영하지 마세요.%2B/+ 플러드 (GET /search/+++…), 긴 $args를 반영하는
성능 저하 페이지로 인한 503, 워커 SIGSEGV/재생성 폭풍.