Machine / Machines

DevHub

Completion state: COMPLETE. DevHub exposed a static nginx site on port 80 and MCPJam Inspector v1.4.2 on port 6274. The MCPJam Inspector /api/mcp/connect endpoint accepted unauthenticated stdio MCP server configs, allowing command execution as mcp-dev. Local...

DocumentedPublished 2025-12-21Sanitized local writeup

Scenario

DevHub attack path

Completion state: COMPLETE. DevHub exposed a static nginx site on port 80 and MCPJam Inspector v1.4.2 on port 6274. The MCPJam Inspector /api/mcp/connect endpoint accepted unauthenticated stdio MCP server configs, allowing command execution as mcp-dev. Local...

Objective

Machine walkthrough focused on Machines evidence, validation, and reusable operator lessons.

DevHub sanitized attack graph

Walkthrough flow

01

Confirmed services and versions:

02

Hostnames and artifacts:

03

Primary vector:

04

Backup vector:

05

Cross-service connection:

Source coverage

Moderate source coverage

Status: partial. This article is generated from 2 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.

74% coverage
Evidence verdict

Moderate confidence: the page is useful for review, but it should be treated as partial because the available source material is thinner or less narrative-complete.

  • <TARGET>-DevHub/walkthrough.md
  • HTB/<TARGET>-DevHub/notes.md

Technical Walkthrough

DevHub Walkthrough

Raw flags and reusable secrets are stored only under loot/ and are intentionally not printed here.

Summary

Completion state: COMPLETE.

DevHub exposed a static nginx site on port 80 and MCPJam Inspector v1.4.2 on port 6274. The MCPJam Inspector /api/mcp/connect endpoint accepted unauthenticated stdio MCP server configs, allowing command execution as mcp-dev. Local enumeration found an analyst-owned Jupyter Lab service and a root-run OpsMCP service. Jupyter provided lateral execution as analyst; OpsMCP exposed a hidden authenticated admin dump function that returned root SSH material.

Recon

  • nmap/initial.txt: SSH 22 and HTTP 80.
  • nmap/allports.txt: additional TCP 6274.
  • nmap/6274-version.txt: HTTP-like service serving MCPJam Inspector.
  • enum/http80_ip_headers.txt: IP requests to port 80 return a 302 redirect to http://devhub.htb/.
  • enum/http6274_api_strings.txt: frontend references /api/mcp/connect and other MCP API routes.
  • enum/proc_interesting.txt: confirms @mcpjam/inspector@1.4.2 during post-foothold enumeration.

Foothold

  1. Confirmed /api/mcp/connect accepted JSON with serverConfig and serverId.
  2. Sent a harmless stdio config executing a callback command and observed the callback in enum/http_callback_after_stdio_proof.txt.
  3. Started a listener on the Pwnbox and triggered a reverse shell using the same endpoint.
  4. Baseline output as mcp-dev was saved in enum/foothold_*.

User path

  1. ss -lntup showed local-only Jupyter on <TARGET>:8888 and OpsMCP on <TARGET>:5000.
  2. Process arguments showed Jupyter running as analyst with a token; token saved only to loot/jupyter-token.txt.
  3. Used the Jupyter API to create a Python kernel and execute commands as analyst.
  4. Captured /home/analyst/user.txt to loot/user.txt; format verified as 32-character hex.

Root path

  1. As analyst, read /home/analyst/.opsmcp_key and /opt/opsmcp/server.py.
  2. Source review showed hidden tool ops._admin_dump on root-run Flask service <TARGET>:5000, gated by X-API-Key.
  3. Called /tools/call with name=ops._admin_dump, arguments.target=ssh_keys, and arguments.confirm=true.
  4. Saved root SSH material under loot/, extracted it to loot/root_id_rsa, then authenticated to SSH as root.
  5. Captured /root/root.txt to loot/root.txt; format verified as 32-character hex.

Evidence

  • Notes: notes.md
  • Foothold: enum/http_callback_after_stdio_proof.txt, enum/devhub_shell.log, enum/foothold_*
  • Lateral: enum/jupyter_, enum/analyst_
  • Privilege escalation: enum/opsmcp_source_redacted_range.txt, enum/root_ssh_test.txt, enum/root_baseline.txt
  • Flags and secrets: loot/ only

Source-Backed Dossier

The sections below are merged from companion Markdown notes for the same case. They are rendered after sanitization so the article stays precise without publishing raw flags, credentials, or target-specific secrets.

Notes

Scope

FieldValue
PlatformHack The Box / simulated lab
TargetDevHub
DifficultyMedium
OSLinux
Active target IP<TARGET>
Hostname/domaindevhub.htb
Pwnbox<TARGET>
Attacker/VPN IP<TARGET>
Local workspace<local workspace><TARGET>-DevHub
Pwnbox workspace~/htb/<TARGET>-DevHub
Started2026-06-06T00:54:22Z

Evidence Ledger

Time UTCPhaseCommand/ActionOutput fileFindingConfidenceNext action
2026-06-06T00:54:22Zsetuphtbctl inittarget-state.jsonWorkspace initialized by deterministic harness.HighValidate route and start baseline recon.

Synthesis

Current completion state: COMPLETE.

Pre-exploitation synthesis — 2026-06-06T01:18:00Z

  1. Confirmed services and versions:

- 22/tcp: OpenSSH 8.9p1 Ubuntu 3ubuntu0.15.

- 80/tcp: nginx 1.18.0, static DevHub - Internal Development Platform page.

- 6274/tcp: HTTP service serving MCPJam Inspector; nmap cannot name the service, but the frontend title, icon, and JS routes confirm MCPJam Inspector behavior.

  1. Hostnames and artifacts:

- <TARGET> redirects to http://devhub.htb/ on port 80.

- Pwnbox /etc/hosts has <TARGET> devhub.htb.

- Port 6274 frontend bundle references /api/mcp/connect plus multiple /api/mcp/* endpoints.

  1. Primary vector:

- Validate and attempt the suspected MCPJam Inspector unauthenticated /api/mcp/connect vector because the endpoint exists, accepts POST JSON, and the app can connect to attacker-controlled MCP server configuration.

  1. Backup vector:

- Continue port 80 web enumeration if MCPJam exploitation fails; current quick-win checks show robots.txt and .git/HEAD return 404, and the visible page appears static.

  1. Cross-service connection:

- Port 80 mainly exposes hostname/service hints; port 6274 is the active development tooling surface and likely initial access path.

Raw flags and reusable secrets must be stored only under loot/.

Foothold and Privilege Escalation Evidence — 2026-06-06T01:33:00Z

Current completion state: COMPLETE.

Validated pre-research intel

ItemResultEvidence
Port 80 redirects to devhub.htbMATCHEDenum/http80_ip_headers.txt shows 302 with Location: http://devhub.htb/.
Port 6274 is MCPJam InspectorMATCHEDnmap/6274-version.txt and enum/http6274_index.js; frontend title is MCPJam Inspector.
MCPJam Inspector version v1.4.2 / vulnerable lineMATCHEDProcess enumeration shows npx @mcpjam/inspector@1.4.2; saved in enum/proc_interesting.txt.
/api/mcp/connect unauthenticated RCE with attacker-controlled MCP server configMATCHEDBenign callback proof saved in enum/http6274_connect_stdio_proof_response.json and enum/http_callback_after_stdio_proof.txt.

Exploitation path

  1. POSTed a stdio MCP server config to http://devhub.htb:6274/api/mcp/connect and confirmed command execution as mcp-dev via callback to the Pwnbox.
  2. Triggered a reverse shell to <TARGET>:4444; shell evidence in enum/reverse_shell_initial_capture.txt and enum/devhub_shell.log.
  3. Ran required foothold baseline commands as mcp-dev; outputs saved in enum/foothold_*.
  4. Found local-only Jupyter Lab on <TARGET>:8888 running as analyst; token saved only to loot/jupyter-token.txt.
  5. Used the Jupyter REST/WebSocket API to run a Python kernel as analyst; analyst outputs saved in enum/analyst_*.
  6. Captured user.txt from /home/analyst/user.txt; raw flag saved only to loot/user.txt and verified as 32-character hex.

Privilege escalation path

  1. As analyst, read /home/analyst/.opsmcp_key and /opt/opsmcp/server.py; secret material saved only to loot/.
  2. Source review showed root-run OpsMCP on <TARGET>:5000 with hidden tool ops._admin_dump; redacted source evidence in enum/opsmcp_source_redacted_range.txt.
  3. Authenticated to OpsMCP with the analyst API key and called ops._admin_dump with target ssh_keys, retrieving root SSH material into loot/opsmcp-emergency-ssh_keys.json.
  4. Extracted the root key to loot/root_id_rsa, SSHed as root, and saved root baseline in enum/root_baseline.txt.
  5. Captured root.txt from /root/root.txt; raw flag saved only to loot/root.txt and verified as 32-character hex.

Key evidence files

  • Recon: nmap/initial.txt, nmap/allports.txt, nmap/6274-version.txt.
  • Web/API: enum/http80_ip_headers.txt, enum/http6274_api_strings.txt, enum/http6274_fetch_contexts.txt.
  • Foothold: enum/http_callback_after_stdio_proof.txt, enum/reverse_shell_initial_capture.txt, enum/foothold_*.
  • Lateral/privesc: enum/jupyter_, enum/analyst_, enum/opsmcp_source_redacted_range.txt, enum/root_baseline.txt.
  • Flags/secrets: loot/ only.