
비공개 Nginx Rift ASLR 랩, 익스플로잇 체인 및 데모 녹화
CVE-2026-42945에 대한 RCE 개념 증명(PoC)으로, 2008년에 도입된 NGINX ngx_http_rewrite_module의 치명적인 힙 버퍼 오버플로입니다. 이 버그는 rewrite 및 set 지시문을 사용하는 서버에 대해 인증 없이 원격 코드 실행을 가능하게 합니다.
이 포크는 NGINX 오버플로를 일반적인 동일 호스트 LFI/임의 파일 읽기 프리미티브와 결합한 ASLR 우회 체인으로 기존 PoC를 확장합니다. 파일 읽기 프리미티브는 nginx 워커 맵, libc, 그리고 라이브 /proc/<worker>/mem을 복구한 다음, 원격으로 system() 주소와 사용 가능한 힙 대상을 도출하는 데 사용됩니다.
이 랩의 초기 버전은 의도적으로 nginx 워커를 크래시시켜 서비스가 코어 덤프를 쓰도록 만든 다음, 파일 읽기 프리미티브를 통해 해당 코어 덤프를 가져와 파싱하여 힙 대상을 포함한 ASLR 민감 프로세스 상태를 복구했습니다. 이 저장소에서 coreless는 단지 "읽을 수 있는 크래시 코어 덤프가 없다"는 뜻의 약칭일 뿐입니다. 현재 기본 경로는 크래시 코어 의존성을 라이브 procfs 메모리 읽기로 대체하며, 보존된 레거시 core-guided 경로는 여전히 생성된 워커 코어 덤프를 사용합니다.
이 취약점은 다른 세 가지 메모리 손상 문제(CVE-2026-42946, CVE-2026-40701, CVE-2026-42934)와 함께 NGINX 소스를 온보딩(등록)하는 단 한 번의 클릭으로 depthfirst의 보안 분석 시스템에 의해 자율적으로 발견되었습니다.
자신의 코드에서도 이런 문제를 찾고 싶으신가요? **https://depthfirst.com/open-defense**에서 동일한 시스템을 사용해 보세요.
NGINX 스크립트 엔진은 2-pass 프로세스를 사용합니다. 먼저 필요한 버퍼 크기를 계산한 다음 데이터를 복사합니다. is_args 플래그는 rewrite 대체 문자열에 ?가 포함될 때 메인 엔진에 설정되지만, 길이 계산 패스는 새로 0으로 초기화된 하위 엔진에서 실행됩니다. 즉:
is_args = 0을 확인 → 원시 캡처 길이를 반환합니다.is_args = 1을 확인 → NGX_ESCAPE_ARGS와 함께 ngx_escape_uri를 호출하여 이스케이프 가능한 각 바이트를 3바이트로 확장합니다.복사는 공격자가 제어하는 URI 데이터로 크기가 부족한 힙 버퍼를 오버플로합니다. 익스플로잇은 요청 간 힙 펭슈이를 사용하여 인접한 ngx_pool_t의 cleanup 포인터를 손상시키고(POST 본문을 통해 스프레이됨, URI 바이트는 null 바이트를 포함할 수 없으므로), 이를 가짜 ngx_pool_cleanup_s로 리디렉션하여 풀 파괴 시 system()을 호출합니다.
이 버그에 대한 자세한 내용은 기술 문서에서 확인하세요.
| 제품 | 영향 버전 | 수정 버전 |
|---|---|---|
| NGINX Open Source | 0.6.27 – 1.30.0 | 1.31.0, 1.30.1 |
| NGINX Plus | R32 – R36 | R36 P4, R35 P2, R32 P6 |
공급업체 전체 권고문: https://my.f5.com/manage/s/article/K000160932



이 포크는 원래 공개 PoC를 그대로 유지하면서, 보다 현실적인 질문에 초점을 맞춘 두 번째 연구 트랙을 추가합니다:
ASLR이 활성화된 실제 x86_64 Linux VM에서 하드코딩된 Docker/랩 오프셋에 의존하지 않고 이 버그를 악용할 수 있을까요?
이 연구 포크에서의 답변은 가능하지만, 중요한 제약이 있습니다. 작동하는 체인은 ASLR을 비활성화하지 않으며 원래의 하드코딩된 힙/libc 주소를 사용하지 않습니다. 대신 동일 포트 HTTP 접근 가능 프리미티브를 통해 런타임 상태를 도출한 다음, 원격으로 획득한 공개 데이터에서 최종 힙 대상을 선택합니다.
이제 두 가지 ASLR 지원 익스플로잇 트랙이 있으며, coreless 경로가 현재 최고의 PoC로 취급됩니다:
nginx_rifter.py: 깔끔하고 자체 포함된 평가 및 통합 익스플로잇 진입점입니다. 기본 익스플로잇 방식은 이제 coreless /proc/<nginx-worker>/mem 체인입니다.nginx_rifter_core_v2_1.py: 보존된 레거시 core-guided 버전의 nginx_rifter.py입니다. 이전에 VM에서 테스트된 크래시 코어 연구 경로를 재현하는 데 유용하지만 더 이상 선호되는 PoC는 아닙니다.tools/proc_mem_coreless_exploit.py: 초기의 독립형 coreless 연구 하네스입니다. 그 논리는 nginx_rifter.py에 병합되었으며, 이 도구는 원시 실험 재생을 위해 남아 있습니다.대상 토폴로지는 의도적으로 동일 포트입니다:
/api/.../lfi.php?file=.../phpinfo.php현재 coreless proc-mem 경로는 다음의 높은 수준의 단계를 수행합니다:
/proc/<pid>/maps 및 매핑된 libc 파일을 읽습니다.system() 주소를 계산합니다./proc/<worker>/mem에서 매핑된 범위를 읽습니다.레거시 core-guided 경로는 유사한 기본 주소 도출을 수행한 다음, 의도적으로 워커를 크래시시키고, LFI를 통해 생성된 코어 파일을 읽고, 스프레이된 가짜 cleanup 슬롯에 대해 해당 코어를 마이닝합니다. 이는 유용한 연구 교두보였지만, 기본 배포에서는 흔하지 않은 코어 덤프 정책과 파일시스템 권한에 의존합니다.
이것은 원래의 결정적 Docker 데모와 동일하지 않습니다. x86_64 VM 경로는 일반적인 Linux ASLR을 활성화 상태로 두고 실행할 때마다 프로세스별 주소를 다시 계산합니다. Docker coreless 경로도 ASLR을 활성화 상태로 두고 비정상적인 읽기 가능 코어 요구 사항을 제거하지만, 대상 클래스에 대해 검증해야 하는 procfs 권한 동작에 의존합니다.
이 포크는 통제된 연구 랩입니다. ASLR 지원 체인은 보편적인 프로덕션 가정이 아닌 강력한 조건에 의존합니다:
/proc/<pid>/maps, 매핑된 libc 및 큰 매핑 오프셋에서 /proc/<pid>/mem을 읽을 수 있어야 합니다./proc/<pid>/maps, 매핑된 libc 및 생성된 워커 코어를 읽을 수 있어야 합니다.phpinfo()와 /proc/<pid>/maps는 PIE/libc 기본 주소를 복구하기에 충분하지만, 이 익스플로잇에 필요한 정확한 힙 객체/윈도우를 복구하기에는 그 자체로 충분하지 않습니다. 이전 체인은 최종 공개를 위해 읽을 수 있는 크래시 코어를 사용했습니다. 현재 기본 체인은 대신 /proc/<worker>/mem을 사용하는데, 이는 코어 덤프 정책을 변경하지 않고 라이브 워커 메모리를 노출하므로 동일 UID 배포에서 실제 임의 파일 읽기 결과에 더 가깝습니다.
남아 있는 중요한 한계:
/proc/<pid>/mem은 ptrace 게이트가 적용됩니다. Docker 랩과 공식 nginx:stable 이미지 모델에 대한 동일 UID 검사에서는 작동했지만, 기본 procfs 보호 하에서는 다른 UID 앱 프로세스는 실패해야 합니다.현재 깔끔한 진입점은 nginx_rifter.py입니다. 이는 HTTP로 접근 가능한 로컬 파일 읽기 프리미티브가 있는 알려진 취약한 nginx 배포를 승인된 테스터가 평가하는 방식에 더 가깝게 설계된 평가 우선 도구입니다.
초기 데모 러너와 비교하여 nginx_rifter.py는 여러 방식으로 워크플로를 개선합니다:
--exploit가 명시적으로 제공되지 않는 한 크래시 익스플로잇을 실행하지 않습니다.HOST:PORT로 제공되며, 파일 읽기 프리미티브는 --file-read-template을 통해 모듈화됩니다./proc/self/status, /proc/self/maps, 동일 UID 워커 procfs 도달 가능성을 포함하여 LFI 프리미티브에 의존하기 전에 이를 프로파일링합니다.system(), 빌드 ID, 바이너리 해시, OS 세부 정보, proc-mem/core 설정을 발견합니다.rewrite + set 경로 후보에 플래그를 지정합니다.nginx_rifter.py에 통합되어 있습니다. 기본 방식은 coreless proc-mem입니다.현재 nginx_rifter.py는 자체 포함되어 있습니다. 평가 또는 익스플로잇을 위해 더 이상 이전 데모 PoC 버전이나 tools/proc_mem_coreless_exploit.py를 import하거나 셸 아웃하지 않습니다.
이전 core-guided nginx_rifter.py 구현은 nginx_rifter_core_v2_1.py로 보존됩니다.
최신 artifacts/nginx_rifter_v3_coreless_exploit_20260519.gif는 병합된 v3 nginx_rifter.py coreless 익스플로잇 경로를 보여줍니다. demo4.gif는 이전 올인원 core-guided 도구의 평가 및 명시적 익스플로잇 흐름을 보여줍니다. 이전 nginx-aslr-demo.gif는 원래 ASLR 지원 익스플로잇 데모로 남아 있습니다.
Ubuntu 24.04.3 LTS에서 테스트되었습니다.
원래 ASLR 비활성화 Docker 재현:
./setup.sh — 컨테이너를 빌드합니다.docker compose -f env/docker-compose.yml up — 취약한 NGINX 서버를 시작합니다.python3 poc.py --shell — 셸을 획득합니다.로컬 Docker 재현 흐름은 LAB.md를 참조하세요.
레거시 ASLR 지원 VM core-guided 체인:
./nginx_rifter_core_v2_1.py --target <target-host>:19321 --exploit --cmd id --fast
평가 우선 v3 도구:
./nginx_rifter.py --target <target-host>:19321
nginx_rifter.py는 현재 실제 환경 지향 평가자이자 통합 PoC 진입점입니다. 기본 모드는 크래시 익스플로잇 경로를 실행하지 않습니다. HTTP 파일 읽기 프리미티브를 프로파일링하고, 범위 및 바이너리 읽기를 확인하고, OS/nginx/libc를 핑거프린팅하고, nginx 워커와 ASLR 관련 맵을 발견하고, 동일 UID /proc/<worker>/mem 읽기 가능성을 테스트하고, pid/cmdline/config 읽기를 통해 nginx 구성 경로 복구를 시도하고, 취약한 rewrite + set 경로 후보에 플래그를 지정하고, 현재 coreless 체인에 대한 실행 가능성 매트릭스를 출력합니다.
현재 nginx_rifter.py는 자체 포함되어 있습니다. 평가 또는 익스플로잇을 위해 더 이상 이전 데모 PoC 버전이나 독립형 proc-mem 연구 하네스를 import하거나 셸 아웃하지 않습니다.
사용자 정의 LFI/다운로드 형태의 경우:
./nginx_rifter.py --target <target-host>:19321 \
--file-read-template 'http://{host}:{port}/download?path={path_url}{range_query}'
익스플로잇 실행은 명시적입니다:
./nginx_rifter.py --target <target-host>:19321 --exploit --cmd id --fast
# Discovery-only exploit smoke test, no spray/probe
./nginx_rifter.py --target <target-host>:19321 --exploit --derive-only --cmd id
기본 익스플로잇 방식은 coreless proc-mem입니다. 다음 옵션은 coreless Docker 증명에 가장 안정적이었기 때문에 이미 기본값으로 선택되어 있습니다:
--exploit-method proc-mem
--target-len 6
--max-region 268435456
레거시 읽기 가능 코어 모드는 비교를 위해 계속 사용할 수 있지만, 해당 이전 경로를 재현하는 데는 버전이 붙은 스크립트가 더 명확합니다:
./nginx_rifter_core_v2_1.py --target <target-host>:19321 --exploit --cmd id --fast
레거시 녹화 친화적 터미널 데모:
./demo_ctf_exploit_v1_9.py --host <target-host>:19321 --cmd id --clear
demo_ctf_exploit_v1_9.py는 core-guided 랩 경로를 위한 이전 운영자용 러너입니다. 현재 선호되는 PoC 진입점은 nginx_rifter.py입니다.
기본 파일 읽기 프리미티브는 이 포크의 PHP 경로입니다:
/lfi.php?file=<path>&offset=<n>&length=<n>
다른 알려진 취약한 CTF 앱 또는 테스트 플랫폼의 경우 파일 읽기 벡터는 모듈식입니다:
./nginx_rifter.py --target <target-host>:19321 --exploit --cmd id \
--target-profile generic \
--file-read-template 'http://{host}:{port}/download?path={path_url}{range_query}'
템플릿은 {host}, {port}, {path_url}, {offset}, {length} 및 {range_query}를 지원합니다. generic 프로필은 이 포크의 랩 특정 nginx 구성 어설션을 건너뛰지만, 기본 익스플로잇에는 여전히 동일한 기본 기능이 필요합니다: 읽을 수 있는 nginx 워커 /proc 맵, 읽을 수 있는 libc, 읽을 수 있는 /proc/<worker>/mem. phpinfo()는 선택 사항입니다. 비활성화하려면 --phpinfo-path ''를 사용하세요.
현실성 주의: LFI/파일 읽기 버그 클래스와 동일 호스트 nginx/PHP-FPM 배포 모델은 현실적입니다. proc-mem 체인은 워커 코어 덤프를 활성화하거나 읽을 필요가 없기 때문에 이전 크래시 코어 체인보다 더 현실적입니다. 그래도 보편적인 기본 프로덕션 가정은 아닙니다. 동일 UID 프로세스 레이아웃, procfs/Yama 정책, 컨테이너 네임스페이스 설정, 파일 읽기 프리미티브의 품질이 /proc/<worker>/mem 도달 가능 여부를 결정합니다.
LFI 미사용 연구 프로브:
python3 tools/non_lfi_leak_probe.py --target <target-host>:19321
python3 tools/non_lfi_active_response_probe.py --target <target-host>:19321
이들은 익스플로잇 진입점이 아닌 부정적 연구 프로브입니다. LFI, phpinfo, procfs, 코어, 디버거 접근 또는 하드코딩된 라이브 ASLR 베이스 없이 패시브 반사 싱크와 초기 지연 응답 오버리드 형태를 테스트합니다.
추가 랩 노트 및 실행 로그는 docs/ 아래에 있으며, 특히:
docs/CTF_PLAN.mddocs/CTF_FINDINGS.mddocs/CTF_TESTS.mddocs/CTF_EXPERIMENT_LOG.mddocs/DEMO_POC_IMPROVEMENTS.mddocs/KNOWN_LAYOUT_PATTERNS.mddocs/VAGRANT_ESXI.mddocs/CORELESS_ASLR_PLAN.mddocs/CORELESS_ASLR_FINDINGS.mddocs/NON_LFI_ASLR_BYPASS_LOG.mddocs/DEMO_RECORDING_WORKFLOW.md