githubEditar

HackTheBox - Validation

Writeup de la máquina Validation de HackTheBox

  • 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:

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 d8:f5:ef:d2:d3:f9:8d:ad:c6:cf:24:85:94:26:ef:7a (RSA)
|   256 46:3d:6b:cb:a8:19:eb:6a:d0:68:86:94:86:73:e1:72 (ECDSA)
|_  256 70:32:d7:e3:77:c1:4a:cf:47:2a:de:e5:08:7a:f8:7a (ED25519)
80/tcp   open  http    Apache httpd 2.4.48 ((Debian)) #-> CVE-2021-40438?
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
|_http-server-header: Apache/2.4.48 (Debian)
4566/tcp open  http    nginx #-> 403 Forbidden para todo (gobuster no muestra nada)
|_http-title: 403 Forbidden
8080/tcp open  http    nginx #-> Bad Gateway para todo
|_http-title: 502 Bad Gateway
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
# Nada en UDP

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:

  1. Al pulsar Join Now como username usuario y país Philippines vemos que se realiza la siguiente solicitud:

Aquí vemos que simplemente mandamos como parámetros username y country nuestros datos.

  1. El servidor nos responde con una cookie user que contiene nuestra info:

  1. Nuestro navegador ahora manda otra nueva solicitud hacia /account.php usando la cookie user dada.

  1. 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á:

  1. Reenviar mensaje inicial (1) con el siguiente formato de parámetros:

  1. Tomar la cookie devuelta por el servidor, p.ej:

  1. 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 ' UNION la 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/nsenter para intentar escapar, aunque tras probarlo no funciona.

  • Archivo /var/www/html/config.php con contraseña de uhc en 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 dentro del container. Dado que el flag root está dentro del 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.

Última actualización