
Одного пропущенного вызова функции при регистрации маршрута было достаточно, чтобы превратить интерфейс MCP в шлюз для неаутентифицированного удалённого выполнения кода (RCE).
Неразрушающий инструмент обнаружения для обхода аутентификации MCP в nginx-ui, известного публично как MCPwn. Позволяет за два HTTP-запроса без аутентификации получить полный контроль над любым экземпляром nginx-ui, работающим на уязвимой версии.
| Поле | Значение |
|---|---|
| CVE ID | CVE-2026-33032 |
| Псевдоним | MCPwn |
| CVSS 3.1 | 9.8 / Критический (AV:N / AC:L / PR:N / UI:N / S:U / C:H/I:H/A:H) |
| CWE | CWE-306 Отсутствие аутентификации для критической функции |
| Затронутый продукт | nginx-ui (github.com/0xjacky/nginx-ui) |
| Затронутые версии | Все версии до 2.3.4 |
| Исправленная версия | 2.3.4 |
| Статус эксплуатации | Активная эксплуатация в дикой природе (Picus Security, Recorded Future) |
| Доступность в Shodan | ~2 689 экземпляров, доступных из интернета |
| Дата публикации | 2026-04-15 |
nginx-ui предоставляет интерфейс Model Context Protocol (MCP), который позволяет аутентифицированному оператору управлять разрушительными инструментами, такими как редактирование конфигурации nginx, команды перезапуска и операции резервного копирования, через JSON-RPC.
Конечная точка /mcp защищена промежуточным ПО AuthRequired(), в то время как
её парная конечная точка /mcp_message, которая фактически принимает вызовы
инструментов, была развёрнута без этого промежуточного ПО. Любой клиент, который
может получить доступ к веб-интерфейсу по сети, может:
/mcp и получить новый
sessionID без предъявления каких-либо учётных данных.sessionID для вызова любого зарегистрированного MCP-инструмента,
отправив POST-запрос к /mcp_message, по-прежнему без аутентификации.Атакующий получает полный контроль над процессом nginx: редактирует серверные блоки для перенаправления трафика, извлекает закрытые ключи TLS через чтение путей, перезагружает или останавливает nginx, а также внедряет постоянные бэкдоры.
// уязвимая версия (до 2.3.4)
r.GET("/mcp", AuthRequired(), mcpHandler)
r.POST("/mcp_message", mcpMessageHandler) // отсутствует промежуточное ПО
// исправлено в 2.3.4
r.GET("/mcp", AuthRequired(), mcpHandler)
r.POST("/mcp_message", AuthRequired(), mcpMessageHandler)
Одного пропущенного вызова функции при регистрации маршрута было достаточно, чтобы превратить интерфейс MCP в шлюз неаутентифицированного RCE.
Атакующий nginx-ui (<2.3.4)
| 1) GET /mcp (без аутентификации) |
| --------------------------------------> |
| 2) event: endpoint data: /mcp_message?sessionID=XYZ
| <-------------------------------------- |
| 3) POST /mcp_message?sessionID=XYZ |
| тело: JSON-RPC call_tool "nginx_reload" или
| "edit_config {...}" |
| --------------------------------------> |
| 4) 200 OK; инструмент выполнен с полными привилегиями
| <-------------------------------------- |
| MCP-инструмент | Эффект на сервер nginx |
|---|---|
edit_config | Перезапись произвольных серверных блоков, вставка апстрима атакующего |
reload_nginx | Применение конфигурации атакующего без ручных действий |
stop_nginx | Отказ в обслуживании |
read_file | Экфильтрация закрытых ключей TLS, учётных данных из смонтированных путей |
create_cert | Перевыпуск TLS-сертификатов под контролем атакующего |
list_backups / download | Выгрузка полной резервной копии nginx-ui, включая node_secret и хэши |
Этот репозиторий намеренно избегает любых разрушительных действий. Детектор
использует только методы MCP, доступные для чтения (tools/list). Логика:
> Шаг 1 Определение nginx-ui через
> GET / (HTML-заголовок, JS-бандлы)
> GET /api/settings (JSON со ссылками на ключи nginx-ui)
> Извлечение версии через заголовок / регулярное выражение в теле
> Шаг 2 Открытие SSE-потока к /mcp
> Извлечение первого sessionID из события "endpoint"
> Шаг 3 POST /mcp_message?sessionID=<id>
> Тело: {"jsonrpc":"2.0","method":"tools/list","params":{}}
> Без заголовка Authorization
> Шаг 4 Уязвим, если статус 200 и тело содержит манифест инструментов
> Исправлен, если статус 401 / 403 / 404
> Неопределённо в остальных случаях
Метод JSON-RPC tools/list доступен только для чтения. Он перечисляет, какие
инструменты известны MCP-серверу, но не вызывает ни один из них. Скрипт
отказывается отправлять любые другие методы и никогда не формирует полезные
нагрузки для разрушительных инструментов.
git clone https://github.com/your-org/CVE-2026-33032-detector.git
cd CVE-2026-33032-detector
python3 --version # 3.9 или новее
# Сторонние пакеты не требуются.
Для компонента Nmap NSE скопируйте nginx-ui-mcpwn.nse в локальный
каталог скриптов и обновите базу данных скриптов:
cp nginx-ui-mcpwn.nse /usr/share/nmap/scripts/
sudo nmap --script-updatedb
# Одиночная цель
python3 detect_nginx_ui_mcpwn.py --target https://nginx-ui.internal
# Массовое сканирование из файла
python3 detect_nginx_ui_mcpwn.py --targets targets.txt --workers 20
# Вывод в формате JSON (NDJSON), подходящий для передачи в jq
python3 detect_nginx_ui_mcpwn.py --target 10.0.0.5:9000 --json | jq .
Справочник по параметрам командной строки: