Предисловие: все названия компаний, имена разработчиков и реальные 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

Почта

email

you@example.com

Пароль

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: Поддомены — золотая жила

Самое интересное началось, когда я перешёл к поддоменам. Использовал три источника:

  1. Certificate Transparency logs (crt.sh)

  2. Пассивное перечисление (Subfinder)

  3. 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: Что делать, если нашли такое у себя

Я составил список рекомендаций в порядке приоритета.

Критическое

  1. APP_DEBUG → false — проверьте .env на всех серверах. Laravel, Django, Rails — неважно. Режим отладки на продакшене недопустим.

  2. Закрыть Zabbix и админки — за VPN, IP-белый список, или хотя бы basic auth.

В ближайшую неделю

  1. CORS — * допустим только для публичных API. Для всего остального — явный список origins.

  2. HttpOnly на все cookie — особенно на токены.

  3. HSTS и CSP — без них современный сайт как дверь без замка.

В плановом порядке

  1. Аудит поддоменов — ct logs покажут всё, что вы забыли закрыть.

  2. Включить WAF, если провайдер предоставляет.

  3. Удалить неиспользуемые dev-стенды — каждый поддомен увеличивает поверхность атаки.

Заключение

Эта история про то, как одна маленькая ошибка (невыключенный debug) и один забытый сервис (Zabbix наружу) могут рассказать о компании больше, чем ей хотелось бы.

Каждый поддомен, прибитый гвоздями к публичному DNS, — это дверь. Не забывайте их закрывать.

Эта история — наглядная иллюстрация того, почему внешний периметр стоит проверять руками атакующего. Невыключенный debug, забытый dev-стенд, открытый наружу Zabbix — каждая из этих мелочей по отдельности кажется безобидной, но в сумме рисует чужому глазу подробную карту вашей инфраструктуры. Найти такие двери до того, как их найдёт кто-то другой, — ровно то, для чего нужен пентест.

Cloud4Y предлагает тестирование на проникновение (пентест) — комплексную проверку безопасности через имитацию действий злоумышленника. Мы не просто отдаём список CVE: ищем слабые места, подтверждаем каждую уязвимость эксплуатацией и формируем понятный отчёт со сценариями атак, оценкой бизнес-рисков и пошаговыми рекомендациями. Веб-приложения и API тестируем по методологии OWASP, инфраструктуру — по стандарту PTES, в форматах black-box или gray-box. Все работы — строго по договору и письменному разрешению, результаты под NDA.

А для новых клиентов с Хабра — скидка 20% по промокоду HABR20 на услуги Cloud4Y, подробности на странице акции.


Все данные анонимизированы. Технические детали атак соответствуют реальным.

Предоставленная информация о методах пентеста предназначена для повышения квалификации специалистов по безопасности и не может служить руководством к противоправным действиям. Распространение или использование этих данных во вредоносных целях противоречит законодательству РФ и преследуется по закону.

Комментарии (2)


  1. mitzury
    04.06.2026 11:42

    Заголовки в ответе можно подделать (наверное мало кто этим занимается), но все же верить наверное на 100% не стоит.

    А можете ли провести подобное тестирование стороннего проекта, в тему новой статьи подобного плана?


    1. Shaman_RSHU
      04.06.2026 11:42

      Сторонний проект - статья. Пентест должен проводиться после подписания необходимых бумаг. Хотя какой пентест, это просто маленький раздел из OWASP Web Security Testing Guide