Как мы написали свой EASM, потому что ни один из готовых не подошёл

Привет, Хабр! Меня зовут Макар Роденко, я «белый хакер», пентестер и специалист по безопасности в облачном интеграторе OXYGEN Data Centers & Clouds. Мы давно заметили, что забытые активы — больное место многих компаний. А тут ещё и недавние новости про уязвимости в PHP, и другие громкие CVE спать спокойно не дают. Для наших клиентов мы пропилотили большинство сканеров уязвимостей на российском рынке, но ни один не подошёл идеально. И тогда за 1,5 года мы сделали свой.
Под катом разбираемся, как устроен наш EASM, как мы боремся с ложными срабатываниями и что оказалось самым сложным в разработке (и стоило нам немало бессонных ночей). Вэлком!
Зачем нужно сканировать внешний периметр?
Типичная история выглядит так: маркетинг на скорую руку поднял лендинг под акцию, разработчики открыли тестовый контур наружу, чтобы коллегам было удобнее проверять код. Никто не делал ничего плохого, но в инвентаризацию эти ресурсы не попали. Служба ИБ узнаёт о них постфактум, а нередко уже после инцидента. Классические инструменты тут не помогают: SIEM работает с известными источниками логов, NGFW фильтрует трафик через известные шлюзы. Актива, которого нет в реестре, они просто не видят.
В такой ситуации компания становится легкой добычей для злоумышленника. Зачем ломиться через бетонные заборы информационной безопасности, если можно зайти в приоткрытую дверь? Именно так рассуждают хакеры. Поэтому компании важно закрыть все подобные двери, а для начала — найти их с помощью EASM.
Что такое EASM
Немного матчасти. Уязвимые места во внешнем периметре находят с помощью решений класса EASM — External Attack Surface Management. Это отдельный инструмент, обычно для работы с ним достаточно браузера. Чтобы начать, просто добавляете свой домен. Сканирование происходит методом «чёрного ящика», то есть система не знает ничего про вашу компанию — как и злоумышленник. Сканер находит все вероятные уязвимости, приоритизирует их и показывает в удобном интерфейсе.
EASM это не пентест. Пентест делает человек, обычно раз в квартал или год, как правило, это очень глубокая работа. А EASM автоматически ловит новые активы при регулярном сканировании.
Слитые учетные записи, перс. данные, другую «серую» информацию EASM не найдёт — это не мониторинг даркнета. EASM работает только с вашей с собственной инфраструктурой — всем, что вы сами выставили в интернет.
Почему мы написали свой VISORA EASM?
На рынке много хороших решений. Информационная безопасность в нашей стране развивается очень быстро. Но в плане EASM нам не хватило глубины проверки, в каких-то решениях было слишком много шума от ложных срабатываний или не было гибкости под наши сценарии. Никто не обращался к вендорам для проверки уязвимостей в их ПО и ОС. Так мы и начали делать собственный сканер — VISORA, с блэкджеком и особым модулем верификации. Об этом дальше.
С чего всё начиналось: архитектура и принципы
Собрать такую систему можно двумя способами.
Первый — монолит: все функции собраны в одной программе. Она и сканирует, и хранит данные, и считает риски, и выводит интерфейс. Но у монолитов есть минус: чтобы сканировать быстрее, придется запускать копию всей системы вместе с интерфейсом и хранилищем, которым дополнительные ресурсы не нужны. А случайный сбой положит всю систему.
Второй — микросервисная архитектура. Каждый сервис отвечает за одну задачу: например, сканирует или хранит данные. В таком подходе для ускорения достаточно добавить дополнительные сервисы. Плюс отказоустойчивость: сбой одного из них не останавливает остальные.
Мы пошли вторым путём и разделили систему на части с четкими ролями. Для масштабирования отдельно вынесена сканирующая нода, и если нужно сканировать больше и быстрее, то мы просто добавляем новую ноду, и всё. Сканер у нас является как раз этой самой (внезапно) сканирующей нодой, которая:
получает задание — например, проверить домен,
запускает нужные инструменты,
приводит результат к единому виду,
отправляет обратно сервису, отвечающему за обработку результатов сканирования.
Сканер ничего не хранит и не решает, насколько находка опасна. Хранением, планированием и анализом занимается отдельный бэкенд.
Наш стек: бэкенд на FastAPI, брокер сообщений Apache Kafka, хранилище PostgreSQL. Используем стандартный OSS-набор для разведки и фаззинга.
В архитектуру VISORA мы заложили три принципа.
Во-первых, результат воспроизводим: повторный скан периметра даёт тот же результат, и расхождения означают реальные изменения, а не сбой сканера.
Во-вторых, никакой эксплуатации: сканер проверяет, есть ли уязвимость, но не пользуется ею и не обращается к данным клиента. Просто фиксирует факт.
В-третьих, никаких додумываний: если проверку не удалось выполнить, например инструмент завис, это фиксируется в логах.
Саму базу уязвимостей построили по принципу offline-first: сначала обновляем по API все базы данных и работаем с ними локально, так сканирование идёт быстрее. Внешние API служат только запасным вариантом.
Как понимаем, что найденная программа уязвима? Сверяемся с базами известных уязвимостей: NVD, российским БДУ ФСТЭК и дистрибутивными фидами (Debian, Ubuntu и других). Копии этих баз мы храним у себя и обновляем по расписанию.
Разведка боем, но без боя. Как мы находим активы

Что VISORA считает активом?
Домены и поддомены — это основа периметра; именно здесь чаще всего всплывают забытые ресурсы.
IP-адреса — в том числе те, что не привязаны напрямую к доменной зоне компании.
Порты и сервисы — то, что реально смотрит наружу.
Веб-приложения с учётом технологий, на которых они построены.
Облачные ресурсы — bucket'ы, инстансы и другие ресурсы, которые легко забыть выключить.
TLS-сертификаты — источник информации не только о шифровании, но и о реальной топологии сервисов (через SAN-записи можно найти связанные хосты).
Важный архитектурный момент: реестр самого клиента (внутренняя CMDB) не используется как источник данных для обнаружения. Наоборот — результат независимого сканирования сравнивается с этим реестром, и расхождение между ними как раз и является находкой.
А что если кто-то загрузит в сканер чужой домен и увидит все активы?
Такого не случится. Кроме домена или IP мы требуем подтверждение владения через DNS TXT или HTTP-токен. И только после верификации дальше всё работает автоматически: VISORA раскручивает связи от подтвержденного актива, находит смежные домены, поддомены и ресурсы.
Краткий процесс работы
Начинаем с разведки: надо понять, что вообще торчит наружу. Сначала ищем домены и поддомены и смотрим их резолв (то, на какие IP они ведут). Потом определяем ASN и сканируем порты. Смотрим, что на них реально отвечает.
Дальше выясняем, на чём всё это работает. Тут мы не полагаемся на один инструмент: несколько источников работают параллельно, причем для сайтов и для всего остального (почта, базы, SSH) набор инструментов разный. Универсального способа опознать и nginx, и какой-нибудь экзотический сервис нет, так что приходится комбинировать. Всё сводится в один список, где у каждого продукта есть CPE: название, вендор, версия в стандартном формате. По этому паспорту потом ищутся уязвимости.
Отдельно строим карту поверхности: эндпоинты, параметры, методы запросов, формы, OpenAPI- и GraphQL-спецификации собираются как переиспользуемый артефакт.
Для разных задач сканирование идет с разной частотой:
Задача | Частота | Почему так |
Поиск активов (пассивный) | каждые 20 минут | дешёво по нагрузке, новый актив видно почти сразу после появления |
Порты и уязвимости (активный) | раз в сутки | создаёт нагрузку на инфраструктуру клиента |
И в двух режимах:
Тип | Частота | Зачем |
Лёгкий режим | ежедневно | для быстрой актуализации периметра |
Глубокий режим | раз в неделю | для полного перебора скрытых параметров |
Главный босс — ложные срабатывания
Это, пожалуй, самая интересная инженерная часть. Классический подход большинства сканеров — определить сервис и его версию по баннеру и сопоставить это с базой CVE. Проблема в том, что это дает огромный процент нерелевантных находок: версия ПО может быть уязвима в общем списке CVE, но конкретная сборка, которую использует вендор дистрибутива, может быть пропатчена бэкпортом — и баннер об этом ничего не знает. Без модуля на типовом Debian/Ubuntu-хосте количество «мусорных» CVE может быть от половины до ~80%.
Мы создали модуль верификации, который работает с уязвимостями по двум взаимодополняющим стратегиям.
Сигнатурная
Версия сопоставляется с CPE, по нему ищутся CVE в NVD и БДУ ФСТЭК. Отдельный слой сверяет реальный статус пакета в конкретном дистрибутиве и выдаёт один из четырёх вердиктов:
подтверждено;
вероятно пропатчено;
статус дистрибутива неизвестен;
вероятно ложное срабатывание.
Пользователь при этом может сам уточнить версии ПО и ОС.
Дистрибутивная верификация CVE (tools/verification/):
компаратор версий dpkg на чистом Python, без зависимости от apt_pkg;
чекеры для Red Hat, Debian и Ubuntu;
четыре возможных вердикта;
определение релиза по ревизии пакета в дистрибутиве.
Активный веб-скан
Сначала собираем поверхность: архивные URL, краулинг, фаззинг и брутфорс скрытых параметров. Только после этого по ней прогоняются детекторы по категориям уязвимостей (включая OWASP Top 10):
XSS, SQLi, SSTI, LFI, RCE, XXE, SSRF;
мисконфиги и дефолтные панели;
утечки секретов в JS;
захват поддоменов;
TLS-аудит;
GraphQL;
CSRF в формах;
SPF/DMARC.
Найденные SQL-инъекции подтверждаются отдельным инструментом.
Потом происходит очистка выдачи от шума:
Дедупликация. Если несколько инструментов независимо нашли одну уязвимость, запись схлопывается в одну соответствующей пометкой.
Liveness-фильтр. Он отбрасывает «мёртвые» находки: битые URL, 404, редиректы на логин, которые формально совпали с сигнатурой.
В интерфейсе у каждой находки есть статус, уровень риска, источник и связанные данные. Подтверждённая проблема не лежит рядом с сомнительной без контекста.
Во-первых, это красиво. Наш фронтенд

Плохой интерфейс может убить даже самый функциональный сканер. Если им неудобно пользоваться, разбор результатов будет отнимать кучу времени. Поэтому мы особое внимание уделили UI/UX, над которым много колдовал мой коллега и разработчик Андрей Исупов.
Что у нас получилось:
Главный экран показывает сводку по активам, сервисам и уязвимостям, и с него можно провалиться в детали.
Домен или IP в карточке актива превращается в небольшую историю наблюдений: текущие свойства, связанные адреса, открытые порты, веб-приложения, уязвимости, результаты прошлых сканов и изменения во времени.
Единый слой запросов, построенный на React + TanStack Query. У списков, деталей и статистики разные ключи и время актуальности, а после изменения актива обновляются только затронутые запросы.
Вкладочная структура не дает разным типам данных конкурировать за внимание.
Фильтры, сортировка и пагинация сохраняются в URL, поэтому ссылку на конкретную выборку можно отправить коллеге или открыть после обновления страницы.
Карточка открывается поверх таблицы, при этом сохраняются исходный маршрут и позиция в истории браузера, и пользователь возвращается туда, где остановился.
Графики на дашборде и в карточках сделаны отдельными компонентами с общими форматтерами данных. Компонент получает нормализованный набор значений и отвечает только за представление: шкалы, подсказки, цвета и ресайз. Визуализация не смешивается с логикой запросов, поэтому её можно менять независимо от API.
Так пользователь не теряет нужное место в реестре, не повторяет путь к находке, легко копирует ссылки и в целом легко и удобно работает со сканером.

Наши сложности и что пришлось переделывать
Разработка VISORA EASM заняла почти 1,5 года. Одним из переломных моментов стала переработка собственного API — вместе с ним пришлось переписать и фронтенд-часть, чтобы обе стороны системы соответствовали новой архитектуре. Также мы дважды пересобрали дизайн: сначала сделали MVP, на котором проверяли структуру продукта, потом доводили до рабочего интерфейса: дорабатывали навигацию, таблицы, состояния ошибок и пустые результаты.
"Эксплуатируете? Нет, только показываем"
Решение не эксплуатирует и не устраняет уязвимости — только собирает, структурирует и показывает их в самом удобном виде, со всей историей и приоритизацией. Версионные RCE-уязвимости в известном ПО мы вообще не эксплуатируем, вывод делается из версии и дистрибутивного вердикта. Активная проверка веба — это детект по сигнатурам. Единственное исключение — подтверждение SQL-инъекций: мы шлём минимальные проверочные пробы (boolean- и time-based), чтобы отличить реальную уязвимость от ложного срабатывания, но без дампа данных, без изменения содержимого и без деструктива — это осознанная граница «серой зоны», за которую мы не заходим.
На основе полученных данных вы можете легко контролировать внешний периметр компании и закрыть все «двери» для хакеров — но это отдельная работа.
С устранением уязвимостей мы в OXYGEN, кстати, можем помочь: наш центр безопасности создан буквально для того, что делать вашу инфраструктуру надежнее и защищённее.
Продолжение следует…
Мы продолжаем развивать сканер, так что вы будущем появятся:
расширение покрытия по поиску уязвимостей;
углубление веб-скана: аутентифицированное сканирование, которое открывает доступ к классам уязвимостей, не видимым в черном ящике (IDOR, CSRF);
приоритизация находок по CVE, которые реально эксплуатируются «в дикой природе», а не просто присутствуют в базе;
структурированный инвентарь для популярных CMS;
AI для верификации находок, для поиска активов и аналитики.
А как у вас? Знаете ли вы, сколько забытых ресурсов сейчас «светится» в интернете от имени вашей компании? Проводите ли регулярное сканирование внешнего периметра или полагаетесь на периодические пентесты? Поделитесь мнением в комментариях, буду рад ответить на вопросы и дополнить, если что-то осталось за кадром.
Познакомиться с решением можно на нашем сайте. Есть 14 дней бесплатного доступа. И подписывайтесь на Telegram-канал OXYGEN, там мы регулярно делимся новостями из жизни ЦОДов, облаков и кибербеза.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.