- Dificultad:
easy - Tiempo aprox.
~3h - Datos Iniciales:
10.10.11.116
Nmap Scan#
Tras realizar un escaneo nmap completo, se encuentran los siguientes puertos abiertos:
| |
Puertos abiertos:
- 22/TCP (SSH): Poco que hacer de momento
- 80/TCP (HTTP): El principal vector de entrada
- 4566,8080/TCP (HTTP): Ninguno de los dos parece funcionar
80 TCP#
Al entrar a la página encontramos un formulario de Login para registrarse con un nombre de usuario y un país:

Probamos a registrar un usuario mientras vemos las solicitudes y respuestas HTTP en Burpsuite. Al registrar un usuario tienen lugar 4 mensajes:
- Al pulsar
Join Nowcomo usernameusuarioy paísPhilippinesvemos que se realiza la siguiente solicitud:
| |
Aquí vemos que simplemente mandamos como parámetros username y country nuestros datos.
- El servidor nos responde con una cookie
userque contiene nuestra info:
| |
- Nuestro navegador ahora manda otra nueva solicitud hacia
/account.phpusando la cookieuserdada.
| |
- El servidor nos responde, mostrándonos una lista de usuarios que han seleccionado nuestro mismo país:

Second Order SQLi#
Ahora que sabemos lo que hace la página, podemos intuir que el query que tiene lugar por detrás tiene la forma:
| |
Se trata de un Second Order SQL injection. Si usamos user=paco1 y como país, en lugar de Philippines, mandamos USA' UNION SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA-- -, el query quedará:
| |
Miramos el output:

El proceso para ejecutar comandos de MySQL a partir de ahora será:
- Reenviar mensaje inicial (1) con el siguiente formato de parámetros:
| |
- Tomar la cookie devuelta por el servidor, p.ej:
| |
- Reenviar tercer mensaje con la cookie nueva y ver el output
En algunos payloads se va viendo cómo cambio el username (y el país antes del
' UNIONla inyección) cada vez que registro un nuevo usuario (paco1, paco2, Jorge, etc.). Aunque esto no es necesario y los nombres pueden reutilizarse y registrarse varias veces con países diferentes, lo hago para tener el output de cada comando de enumeración aislado del resto de los anteriores.
Enumeración de MySQL#
Probamos a ver qué usuarios hay en la base de datos, recibimos lo siguiente:
| |
Usuarios: uhc, root, mysql, mariadb.sys
Para ver el usuario en uso:
| |
Respuesta: uhc@localhost
Para ver sus permisos de R/W, mandamos el siguente contenido en el campo country:
| |
Y el servidor nos devuelve “Y”, lo que indica que uhc tiene privilegios R/W.
R/W, Foothold inicial#
Como tenemos 3 sitios web abiertos en el mismo servidor (puertos 80, 4566, 8080), podemos probar a escribir un Reverse Shell al directorio por defecto de nginx y Apache de Ubuntu, que es para ambos /var/www/html y tratar de acceder desde alguno de los 3.
Tratamos de escribir lo siguiente a /var/www/html/shell.php:
| |
Para que no haya errores de parseo o sintaxis por las comillas, lo codificamos en hexadecimal:
| |
Y probamos a hacer curl a script.php con un handler abierto:
| |
| |
PrivEsc#
Ejecutamos linPEAS, que nos muestra algunas cosas relevantes:
- Estamos dentro de un Docker container, tenemos
/usr/bin/nsenterpara intentar escapar, aunque tras probarlo no funciona. - Archivo
/var/www/html/config.phpcon contraseña deuhcen MySQL:
| |
- nginx no está presente en el container, pero sabemos que hay 2 puertos con nginx en escucha, por lo que nginx probablemente esté ejecutándose en el host OS.
- Puertos en escucha (destaca el 35801):
| |
Pruebo a hacer SSH a root o uhc usando uhc-9qual-global-pw para intentar del container en caso de que se estuviesen reutilizando contraseñas para cuentas fuera del entorno de Docker, pero no funciona.
Más adelante, pruebo a hacer su root dentro del container con la misma contraseña:
| |
Y tenemos root, aunque dentro del container, pero dado que la root flag está dentro del propio container, damos la máquina por concluida.
Post-Root: Código fuente#
Aunque ya hemos completado la máquina, podemos intentar entender qué la hacía vulnerable.
Si volvemos a /var/www/html, podemos ver el código fuente de account.php:
| |
En las primeras líneas, el código trata la cookie user estrictamente como datos, por lo que no hay fallo de seguridad ahí:
| |
Aquí el código php manda directamente "SELECT username, country FROM registration WHERE userhash = ?" a MySQL, y MySQL toma ? como un placeholder para una cadena de texto literal.
Cuando el usuario manda su username, PHP manda el username a MySQL, y como MySQL sabe que lo que le faltaba por llegar es una cadena de texto literal, lo toma como un simple string, y no hay lugar para inyección de comandos, aunque ponga UNION, ' o cualquier otra cosa.
En las siguientes líneas ya podemos ver la vulnerabilidad. Cuando tiene que sacar todos los usuarios de un país específico, lo hace de forma directa, y da por hecho que, dado que ya tiene country guardado en la DB, no hay vulnerabilidad:
| |
Este query es prácticamente el mismo que habíamos imaginado antes, al construir nuestro payload malicioso para la SQLi
Aquí, si antes habíamos puesto algo que pudiese tomarse como comando en el campo country, ahora se convierte en comando realmente. Esto viene principalmente de la asunción del programador de que, si los datos estaban ya en la base de datos, era seguro usarlos sin filtros más adelante.




