Предисловие: все названия компаний, имена разработчиков и реальные IP-адреса изменены. Технические детали и векторы атак — настоящие.
Недавно мне поступила задача: провести внешний black-box пентест клиентской панели управления. Входных данных — минимум: только URL. Ни IP-диапазонов, ни схемы сети, ни описания архитектуры.
Звучит как типичная история, но в процессе я наткнулся на такие грабли, что решил поделиться методологией. Статья будет полезна и начинающим пентестерам, и админам, которые хотят понять, как их инфраструктуру видят из интернета.
Этап 1: Разведка
Пассивный сбор
Начал я с банального — посмотрел, что отвечает на запросы.
curl -skI https://target-panel.example.ru
Сервер ответил:
HTTP/2 200 Server: nginx Via: 1.1 Caddy Set-Cookie: XSRF-TOKEN=... Set-Cookie: panel_session=... X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff
Уже интересно. Видно, что за веб-сервером стоит Caddy в качестве обратного прокси, а сам апстрим — nginx. Cookie вида XSRF-TOKEN и название panel_session намекают на Laravel.
Дальше — WAF-детекция:
wafw00f https://target-panel.example.ru
Результат: WAF не обнаружен. Это хороший знак для тестирования.
Активное сканирование портов
nmap -p- --min-rate=5000 -T4 -Pn -sS target-panel.example.ru
Результат меня удивил:
PORT STATE SERVICE 80/tcp open http 443/tcp open https 65533 ports filtered
Из 65535 портов открыто только два. Это отличный показатель — межсетевой экран настроен грамотно. Большинство современных API-приложений так и работают: 80 → 443 редирект, 443 → приложение.
Проверил и UDP — все порты фильтруются. Сеть под защитой.
SSL/TLS
Беглый анализ TLS показал, что с этим всё хорошо:
TLS 1.2: ECDHE-ECDSA-AES128-GCM-SHA256 (класс A) TLS 1.2: ECDHE-ECDSA-AES256-GCM-SHA384 (класс A) TLS 1.3: TLS_AKE_WITH_AES_128_GCM_SHA256 (класс A) TLS 1.3: TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (класс A)
Сертификат Let’s Encrypt на EC-ключе P-256, Heartbleed/POODLE/CCS Injection — всё чисто. Отлично, идём дальше.
Этап 2: Анализ веб-приложения
Детект технологий
Перенаправил браузер на страницу /login — встречает форма аутентификации с заголовком «Логин — Client Panel».
Посмотрел исходный код страницы. Сразу бросается в глаза:
<meta name="csrf-token" content="..."> <script src="https://cdn.tailwindcss.com"></script> <script defer src="/js/alpine.min.js"></script> <link rel="manifest" href="/manifest.json">
Плюс внизу страницы — PWA-регистрация:
navigator.serviceWorker.register("/sw.js");
И служебный скрипт alpine:init с функцией отправки AJAX-запросов:
async sendRequestWithHandleResponse({ url, method = 'GET', body = null, headers = {} }) { const assignedHeaders = { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest', ...headers }; ... }
Технологический стек на этом этапе:
|
Компонент |
Технология |
|
Бэкенд |
Laravel (PHP) |
|
API-защита |
Laravel Sanctum |
|
Фронтенд |
Tailwind CSS + Alpine.js |
|
Web-сервер |
nginx + Caddy |
|
PWA |
Service Worker + Manifest |
Форма логина
Сама форма содержала четыре поля:
|
Поле |
Тип |
Плейсхолдер |
|
Аккаунт |
text |
account-name |
|
Почта |
|
|
|
Пароль |
password |
•••••••• |
|
Запомнить меня |
checkbox |
— |
Мне показалось необычным, что требуется одновременно и account, и email. Возможно, account — это идентификатор клиента в биллинговой системе, а email — привязанная почта.
Этап 3: Обнаружение уязвимостей
3.1. Режим отладки на продакшене
Самая громкая находка. Я проверил, что будет, если отправить неподдерживаемый HTTP-метод на API-эндпоинт:
curl -sk -X POST -H "Accept: application/json" https://target-panel.example.ru/api/user
И получил полный stack trace:
{ "message": "The POST method is not supported for route api/user. Supported methods: GET, HEAD.", "exception": "Symfony\\Component\\HttpKernel\\Exception\\MethodNotAllowedHttpException", "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/AbstractRouteCollection.php", "line": 131, "trace": [ { "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/AbstractRouteCollection.php", "line": 116, "function": "methodNotAllowed" }, { "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php", "line": 56, "function": "getAllowedMethods" }, { "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php", "line": 802, "function": "methodNotAllowed" } ] }
Тут же утекли:
-
полный путь на сервере:
/var/www/html/; -
версии фреймворка (пути vendor);
-
внутренняя структура роутинга;
-
три полных stack trace кадра.
Это классический APP_DEBUG=true. Казалось бы, сотни статей об этом написаны, а воз и ныне там. Ставлю CVSS 6.5 — при условии, что через debug не утекают APP_KEY и DB-credentials (в данном случае утекли только пути и архитектура). Если утекает APP_KEY, через deserialization возможна RCE, и тогда оценка поднимается до 7.5–9.0.
3.2. Cookie XSRF-TOKEN без HttpOnly
Посмотрел внимательнее на заголовки Set-Cookie:
Set-Cookie: XSRF-TOKEN=eyJpdiI6...; expires=...; Max-Age=7200; path=/; secure; samesite=lax Set-Cookie: panel_session=eyJpdiI6...; expires=...; Max-Age=7200; path=/; secure; httponly; samesite=lax
Видите разницу? Session cookie — httponly, XSRF-TOKEN — нет. На сайте к тому же отсутствует CSP — любой инлайн-скрипт или скрипт с CDN выполняется без ограничений. Комбинация не-HttpOnly токена и отсутствия CSP серьёзно упрощает атаку: найдите XSS — и токен ваш. Ставлю CVSS 6.1.
3.3. CORS нараспашку
Проверил CORS-заголовки:
curl -sk -H "Origin: https://evil.ru" -H "Access-Control-Request-Method: GET" -X OPTIONS https://target-panel.example.ru/sanctum/csrf-cookie
Ответ:
access-control-allow-origin: *
Wildcard CORS на эндпоинтах аутентификации. Да, с учётом того, что Access-Control-Allow-Credentials не выставлен, украсть сессию через браузер не выйдет. Но само наличие * позволяет любому сайту делать предварительные запросы и собирать информацию об API. CVSS 5.0.
3.4. Отсутствие HSTS
Несмотря на корректный редирект с 80-го порта (308 Permanent Redirect), заголовка Strict-Transport-Security не было. При первой загрузке через HTTP пользователь теоретически уязвим к SSL-stripping. CVSS 5.9.
3.5. Админка и API
Заметил по пути ещё пару эндпоинтов:
/admin → 401 (Unauthorized) — панель администратора /api/login → GET/HEAD разрешены, POST — 405 /api/user → защищён middleware /sanctum/csrf-cookie → 204 (Sanctum CSRF)
Факт, что /admin отдаёт 401 в JSON, а не редирект на страницу логина, подсказывает, что это SPA-подобная архитектура: Angular/React/Vue на фронте, API на бэке.
Этап 4: Поддомены — золотая жила
Самое интересное началось, когда я перешёл к поддоменам. Использовал три источника:
-
Certificate Transparency logs (crt.sh)
-
Пассивное перечисление (Subfinder)
-
DNS-запросы
Результат превзошёл ожидания — более 30 поддоменов:
target-panel.example.ru ← клиентская панель (наша цель) dev1-panel.example.ru ← ещё один dev-стенд dev2-panel.example.ru ← и ещё один api.example.ru ← API gpt-api.control-panel.example.ru ← GPT API (!) passport.example.ru ← аутентификация billing.example.ru ← биллинг admin.billing.example.ru ← админка биллинга zabbix.control-panel.example.ru ← ZABBIX nc.control-panel.example.ru ← Nextcloud jitsi.example.ru ← Jitsi Meet exchange.control-panel.example.ru ← MS Exchange mail.example.ru ← почта b24.example.ru ← Bitrix24? ai.example.ru ← сервис с AI
Что из этого доступно?
Я быстро проверил, какие из них отвечают:
zabbix.control-panel.example.ru — 200 OK «Zabbix docker: Zabbix» ← открыт! jitsi.example.ru — 200 OK «Jitsi Meet» ← открыт! nc.control-panel.example.ru — 200 OK «Login – Nextcloud» ← страница входа exchange.control-panel.example.ru — 502 ← упал с ошибкой name-panel.example.ru — 302 ← ещё один dev-стенд api.example.ru — timeout ← не отвечает
Zabbix в открытом доступе без ограничения по IP — это, пожалуй, вторая по критичности находка после APP_DEBUG. Zabbix — лакомый кусок: через него можно узнать версии ОС, имена хостов, метрики производительности, а в старых версиях — получить RCE.
Jitsi Meet тоже открыт полностью — любой может создать комнату и общаться, используя инфраструктуру компании.
Этап 5: Промежуточные итоги
Матрица найденных уязвимостей
|
# |
Уязвимость |
CVSS |
Приоритет |
|
1 |
Laravel APP_DEBUG=true (stack trace наружу) |
6.5 |
Высокий |
|
2 |
Zabbix в открытом доступе |
7.5 |
Высокий |
|
3 |
CORS Wildcard Origin |
5.0 |
Средний |
|
4 |
Cookie XSRF-TOKEN без HttpOnly (сочетание с отсутствием CSP) |
6.1 |
Средний |
|
5 |
Отсутствие HSTS |
5.9 |
Средний |
|
6 |
Server-заголовок (nginx) |
— |
Инфо |
|
7 |
Jitsi, Nextcloud наружу |
5.0 |
Средний |
Почему это произошло?
Судя по именам поддоменов (dev1, dev2, имена разработчиков), целевой сервер — среда разработки или стейджинг, не предназначенная для публичного доступа. Но она висела в открытом интернете, и Certificate Transparency logs исправно индексировали каждый поддомен.
Это классическая ситуация: DevOps-команда подняла стенд, забыла закрыть его VPN’ом или basic auth, и он оказался в общем доступе.
Этап 6: Что делать, если нашли такое у себя
Я составил список рекомендаций в порядке приоритета.
Критическое
-
APP_DEBUG → false — проверьте .env на всех серверах. Laravel, Django, Rails — неважно. Режим отладки на продакшене недопустим.
-
Закрыть Zabbix и админки — за VPN, IP-белый список, или хотя бы basic auth.
В ближайшую неделю
-
CORS — * допустим только для публичных API. Для всего остального — явный список origins.
-
HttpOnly на все cookie — особенно на токены.
-
HSTS и CSP — без них современный сайт как дверь без замка.
В плановом порядке
-
Аудит поддоменов — ct logs покажут всё, что вы забыли закрыть.
-
Включить WAF, если провайдер предоставляет.
-
Удалить неиспользуемые dev-стенды — каждый поддомен увеличивает поверхность атаки.
Заключение
Эта история про то, как одна маленькая ошибка (невыключенный debug) и один забытый сервис (Zabbix наружу) могут рассказать о компании больше, чем ей хотелось бы.
Каждый поддомен, прибитый гвоздями к публичному DNS, — это дверь. Не забывайте их закрывать.
Все данные анонимизированы. Технические детали атак соответствуют реальным.
Предоставленная информация о методах пентеста предназначена для повышения квалификации специалистов по безопасности и не может служить руководством к противоправным действиям. Распространение или использование этих данных во вредоносных целях противоречит законодательству РФ и преследуется по закону.
ссылка на оригинал статьи https://habr.com/ru/articles/1043478/