HTB: DevHub
Overview
| Attribute | Value |
|---|---|
| OS | Linux |
| Difficulty | Medium |
| Category | Web + Lateral Movement + PrivEsc |
| Key CVEs | CVE-2026-23744 |
| Techniques | RCE, API Abuse, Credential Exposure, WebSocket Manipulation |
DevHub is a medium-difficulty Linux machine centered around a developer toolchain: an MCPJam Inspector instance, a JupyterLab server, and an internal MCP operations service. The attack chain requires chaining three distinct vulnerabilities. An unauthenticated RCE in MCPJam Inspector, abuse of a JupyterLab API to achieve lateral movement, and credential exposure in an internal Flask service to escalate to root.
Reconnaissance
Port & Service Discovery
A standard nmap service scan reveals minimal surface — only SSH and HTTP are bound to external interfaces:
The web server announces itself as an internal development platform. However, the Webinterface on port 80 reveals three additional services running locally:
- MCPJam Inspector on port 6274 (externally exposed)
- JupyterLab on port 8888 (localhost only)
- OPSMCP Flask service on port 5000 (localhost only)
Confirming port 6274:
Identifying the Attack Surface
A search for MCPJam Inspector CVEs surfaces CVE-2026-23744 immediately: unauthenticated RCE via an exposed HTTP endpoint. The Inspector binds to `0.0.0.0` by default and performs no authentication on its `/api/mcp/connect` endpoint. The `command` and `args` fields in the JSON body are passed directly to the OS without sanitization.
Foothold: CVE-2026-23744 (MCPJam Inspector RCE)
Vulnerability Overview
The MCPJam Inspector endpoint expects a `serverConfig` object describing an MCP server to launch. It spawns the process directly. No token, no session, no validation.
Exploitation
Start a listener:
Send the payload via Burp Repeater (or curl):
The server returns a 500 (expected — bash is not an MCP server and closes immediately). The reverse shell connects regardless.
Shell Stabilization
Access to the `mcp-dev` user established.
Lateral Movement: JupyterLab API Abuse
Enumeration
Process listing reveals two services running as other users:
Key Finding: JupyterLab runs as `analyst` on localhost port 8888. The authentication token is visible in the process arguments in cleartext. Additionally, a Flask service (`server.py`) runs as `root` on port 5000 — the next target after gaining `analyst`.
Exploiting JupyterLab via REST API
`mcp-dev` cannot write to `/home/analyst`. Direct file placement is impossible. JupyterLab does not execute notebooks on session creation. Code must be sent to a kernel to execute in the analyst context.
The Jupyter REST API allows creating notebooks and spawning kernels. Executing code requires a WebSocket connection to the kernel’s `channels` endpoint. While no WebSocket library is available (`no websocket-client`, `wscat`, or `websocat`), Python’s stdlib `socket` module is sufficient for a manual WebSocket handshake.
Step 1: Spawn a Kernel
Note the returned kernel ID.
Step 2: Create a Notebook with SSH Key Injection
Using `urllib` (stdlib only):
Step 3: Create a Session Attached to Kernel
Note the new kernel ID from the response.
Step 4: Execute Code via WebSocket (Manual Handshake)
Connect to the kernel’s WebSocket channel and send a Jupyter messaging protocol `execute_request` frame:
The kernel responds with `status` messages — code has executed as `analyst`. SSH in:
Privilege Escalation: OPSMCP Credential Exposure
Source Code Analysis
The process list revealed `/opt/opsmcp/server.py` running as `root` on port 5000. The file is owned by `analyst` and readable.
The Flask application exposes a tool-calling API modeled after MCP. Visible tools include `ops.system_status` and `ops.list_services`. The API key is hardcoded:
One of the hidden tools (present in code, absent from `/tools/list`) is `ops._admin_dump`, which accepts a `target` parameter. With `target=ssh_keys` and `confirm=true` it reads and returns `/root/.ssh/id_rsa` directly.
Exploitation
Note: Piping directly into a file via Python avoids JSON escape corruption from terminal copy-paste.
#+END_SRC root@devhub:~# #+END_SRC
Root access obtained.
Detection & Lessons Learned
What Went Wrong (Defense Perspective)
MCPJam Inspector Default Binding: Development tool bound to `0.0.0.0` with no authentication (CVE-2026-23744). Should only be reachable on localhost, with explicit opt-in for external binding.
JupyterLab Token Exposure: Authentication tokens visible in process arguments via `ps aux`. Combined with an API allowing arbitrary kernel interactions, this is effectively unauthenticated code execution for any local user.
OPSMCP Hardcoded Credentials & Hidden Admin Tools: The service hardcodes credentials and exposes an admin credential dump endpoint with only a static API key for protection, while running as root. Hidden tools (present in code, absent from `/tools/list`) provide security through obscurity — no real protection once source access is obtained.
Mitigations for Similar Environments
- Development tools should NEVER bind to `0.0.0.0` by default
- Never pass secrets in command-line arguments (use config files or environment variables with restricted access)
- Implement proper authentication and authorization on all APIs
- Avoid hidden/undocumented endpoints — if it’s in code, it will be found
- Run services with minimal privilege (not as root unless absolutely necessary)
- Use secrets management systems (Vault, K8s Secrets) instead of hardcoding
Timeline
| Stage | Technique | Result |
|---|---|---|
| Recon | nmap, web interface analysis | MCPJam on 6274 |
| Foothold | CVE-2026-23744 RCE | mcp-dev shell |
| Lateral | Jupyter API + WebSocket | analyst access |
| PrivEsc | Hidden admin tool + hardcoded credentials | root access |
Tools & References
CVEs Referenced
- CVE-2026-23744: MCPJam Inspector Unauthenticated RCE
Tools Used
- nmap, gobuster
- Burp Suite (Repeater)
- netcat (reverse shell)
- curl, python3 stdlib (`urllib`, `socket`, `json`)
Detection Resources
- Jupyter Messaging Protocol (execute_request): Official Documentation
- MITRE ATT&CK: Exploit Public-Facing Application
- MITRE ATT&CK: Modify Authentication Process