PDF с нормальным оформлением во вложении. Извините, но у меня нет ни времени, ни желания еще тут редактировать и вылизывать всё. Мы, с коллегой, итак потратили три дня на причесывание статьи
***markdown
# Пишем распределенную высоконагруженную систему брутфорса корпоративных VPN
Автор: Snow
Специально для: XSS.PRO
---
# Глава 1. Введение
## 1.1. Предыстория
С момента начала продаж моего Дампера выяснилось, что наиболее популярным источником корпоративных VPN является TM Checker. Продукт, безусловно, качественный и свою репутацию имеющий вполне заслуженно. Но при этом обладающий одним существенным недостатком – отсутствие единой панели управления делает его довольно неудобным в использовании. При наличии десятка-другого запущенных его копий начинается сущий кошмар.
Залить базы адресов, логинов и паролей, прокси и при этом не запутаться. Плюс необходимость постоянного мониторинга результатов и ручная отправка их в дампер – удовольствие ниже среднего.
Дампер, к слову, до недавнего времени обладал тем же недостатком, в довесок к которому идет нетривиальная настройка серверов и сложность обновления. То есть приходилось руками загружать впнки для проверки и вручную же собирать результаты. У меня давно чесались руки довести всё до ума и сделать единую панель управления, с автоматическим развертыванием софта на серверах, загрузкой данных для проверки и выгрузкой результатов. Что в итоге и было сделано. Но речь сейчас не об этом.
Я ни в коем случае не хочу охаивать проверенный временем и десятками, если не сотнями пользователей продукт. Просто констатирую факты, основанные на общении с реальными пользователями.
Ну и постепенно вынашивалась идея создания собственного комплексного высоконагруженного брута как полноценной распределенной системы. В итоге, примерно после года неторопливой разработки и тестов «в поле» парой человек получился довольно интересный и, что самое главное, – достаточно эффективный продукт. Историей создания которого я и хочу поделиться.
### 1.1.1. Анализ текущего состояния рынка корпоративных доступов
Если коротко, то спрос явно превышает предложение. Желающих купить доступы полно. Продавцов тоже хватает. Только вот качественный материал становится достать все сложнее. Под качественным материалом в данном случае понимаем доступы со следующими характеристиками:
1. Топ гео – США (юсу хотят все, много и почти всё), Канада, Австралия, Европа — в общем так называемые страны TIER 1.
2. Revenue – хотя бы от 10кк за США, остальные от 20-50кк.
3. Сфера деятельности – в особом почете юристы и страховщики, следом идут айтишники, финансисты и имя им легион.
В той или иной степени, постоянно общаясь с покупателями, я вижу растущую проблему дефицита качественного материала, а некоторые мои знакомые уже даже начали снижать свои требования к сетям. Это сугубо личное наблюдение, так что буду рад, если вы поделитесь своими наблюдениями.
## 1.2. Историческая справка
Способов получения корпоративных доступов существует довольно много. Все они обладают своими преимуществами и недостатками, порогом входа, окупаемостью и многими другими характеристиками, которые в текущем контексте можно опустить. Вот некоторые из них:
Сегодня мы остановимся на последнем – классическом брутфорсе корпоративных VPN (Fortinet, Cisco, SonicWall, WatchGuard...).
Брут VPN систем – штука известная с давних пор, брутили так-то всегда, но особую популярность этот метод приобрел в пандемийные времена, когда в мире нарисовался нехуевый такой трындец, вынудивший компании в срочном порядке переводить сотрудников на удаленку. Зачастую силами собственных IT-отделов, где заебанные вусмерть сисадмины (зачастую самоучки) с помощью гугла и какой-то матери пытались хоть как-то обеспечить работоспособность вверенной инфраструктуры.
Некоторые особо ушлые товарищи умудрялись даже продавать курсы по бруту посредством Metasploit за, всего лишь, если мне память не изменяет, 10к зелени.
Это, в свою очередь, породило золотую лихорадку среди мамкиных хацкеров и прочих скрипт-киддис. Толпа ринулась брутить всё, что плохо лежит, благо особого ума для этого не требовалось, Metasploit давал хорошие результаты. А если у тебя были python скрипты, берущие вводные данные из одного файла и складывающие результаты работы в другой – ты был уже гением инженерной мысли.
Однако недолго музыка играла, недолго фраер танцевал. Компании, а также поставщики корпоративных VPN – наученные горьким опытом локов, утечек, повлекших за собой огромные убытки, принялись перестраиваться и стараться не только оперативно реагировать на угрозы, но принимать превентивные меры. В частности, внедрять двуфакторную аутентификацию, выводить пользователей VPN из домена, блокировать запросы с необычных геолокаций... В общем золотые времена, когда любой индус мог в считанные часы набрутить себе доступов, канули в лету. Сейчас наступает время серьезных инженерных решений, требующих высокого уровня квалификации разработчиков и творческого подхода.
## 1.3. От скриптов к системе – по заветам Ильича
Итак, мы хотим свой брут.
Круто. А как его сделать? Есть, конечно, готовые решения, но многие ли из них могут работать под высокой нагрузкой, а также легко масштабируемы? Боюсь, нам придется сочинять очередную козу на лисапеде. Причем коза должна быть максимально тюнингована.

Рис. 1.1: Та самая Коза на лисапеде
А именно удовлетворять следующим требованиям:
1. Решать задачи централизованного хранения данных – адреса, пары логин-пароль, а также прогресс. Будет очень обидно потерять результаты брута, даже за пару дней. Это критично для сохранения данных в случае абуза серверов или сбоев. Да и без этого будет очень тяжело работать с парой-тройкой десятков беспрерывно работающих инстансов брута.
2. Полная автоматизация и интеграция всех этапов. Если коротко, то тут у нас:
* сканирование сетей — берем старый добрый масскан и запускаем его искать открытые порты. Только масскан не сам по себе – мы будем интегрировать его в систему;
* определение протоколов – мало найти открытый порт, надо еще понять что там на нем висит, определить вариацию API. Задача тоже далеко не тривиальная;
* непосредственно брут – брут протоколов, за небольшими исключениями, реализуется не сложно, но нам то нужно сделать весь флоу беспрерывным, с выбором кредов и всем прочим;
* дамп результатов – поскольку у нас уже имеется проверенный временем дампер, то мы просто обязаны вкорячить его в систему, дабы получить на выходе волшебную кнопку «Сделать заебись» – единый конвейер, который сам сканирует, сам анализирует найденное, определяет протокол, хоть как-то фильтрует ханипоты, брутит и снимает минимальный дамп, позволяющий определить хотя бы приблизительную ценность доступа.
3. Оптимизация ресурсов – еще мы хотим хоть сколько-то эффективно использовать ресурсы. Не, конечно можно пойти путем тупого увеличения вычислительной мощности кластера. Как сейчас поступают игроделы – тормозит игра – нахуй оптимизацию, еще не хватало биться за каждый байт оперативки и такт процессора – пусть юзер покупает более мощное железо. Но, поскольку, бесконечное вертикальное масштабирование попросту невозможно, да и стоит такое удовольствие недешево – придется включать голову и думать как таки выжать из доступных ресурсов максимальный КПД и минимизировать простой серверов.
Забегая вперед скажу, что проблем добавляет и ужесточение регуляции в некоторых гео. Например, простой поиск сетей с IP-адресов США уже становится нетривиальной задачей из-за быстрых абузов.
Итак, чтобы конкурировать с тысячами индусов, использующих публичные скрипты и словари вида
Следовательно, наше преимущество должно строиться на комплексном подходе, недоступном для массового «скрипт-кидди» и «вайб-кодера». А посему мы имеем:
### 1.3.1. Новые требования к качеству
1. Расширение списка протоколов и постоянный поиск нестандартных точек входа. Особенно хорошо, если публичный веб протокола имеет защиту, так больше шансов, что мамкины вайбкодеры ее не пробили и осталось больше нетронутых сеток.
2. Гео-специфичность. Использование баз кредов и прокси, релевантных конкретному региону атаки. Нет смысла брутить США по азиатским базам, как и наоборот.
3. Непрерывность. Система должна работать 24/7, пересканируя уже известные диапазоны и новые порты, прогоняя живые впны по новым кредам, проверяя, не ожили ли мертвые, ну и т.д.
4. Масштабируемость архитектуры, способной обрабатывать крайне большое число сеток с минимальным участием оператора.
Ну и путем несложных размышлений получаем, что только создание высоконагруженной, распределенной и детально проработанной системы позволит получить стабильный поток валидных корпоративных доступов в текущих реалиях.
---
# Глава 2. Архитектура
### 2.0.1. Вместо предисловия
Это сейчас, я типа умный, буду описывать уже имеющуюся архитектуру, скромно умалчивая о количестве мертвых нервных клеток, путешествий по граблям и прочими интересностями, сопровождающими разработку подобных систем. По возможности буду пояснять причины принятия того или иного архитектурного решения.
## 2.1. Требования к архитектуре
Разрабатывая архитектуру подобной системы, нам нужно обратить внимание сразу на несколько важных деталей:
## 2.2. Выбор архитектуры
Про архитектуру у меня тут было несколько статей. В рамках небольших проектов придерживаться принципов «Чистой архитектуры» довольно просто. Тут же сильно другая ситуация. Построение распределенной системы требует ответственного подхода, потом переделывать будет очень дорого. На выбор у нас: классический монолит, его мы сразу отбросим, т.к. он не позволит перераспределять мощности на разные этапы (скан, чек, брут, дамп) на лету и без костылей, а изоляция по ГЕО некоторых этапов может стать весьма нетривиальной. Кроме того, подобная архитектура очень сильно затруднит интеграцию элементов системы, написанных на разных стеках. А забегая вперед, скажу, что масскан, необходимый для первого этапа, написан на Си. Да-да, на старом добром чистом Си. И у меня, честно говоря, не было даже желания пытаться вкорячить туда C++. Точнее оно было, но быстро пропало, поскольку сама интеграция свелась к запросу диапазонов адресов/портов и отправке результатов. Ну и по мелочи – мониторинг нагрузки на процессор и память. Все это прекрасно решается через
Дампер – написан на питоне. Принцип интеграции в систему такой же. Взяли задачу и отправили результат. А вот ядро должно уметь общаться с ними всеми.
Использование же чистой микросервисной архитектуры, как по учебнику, обернется огромной стоимостью поддержки инфраструктуры — нам понадобится целая команда админов, чтобы все это поддерживать и обслуживать. Опять же, забегая вперед, работу некоторых сервисов под высокой нагрузкой оказалось очень сложно реализовать через прокси, в связи с этим нам пришлось размещать части нашей инфраструктуры в разных географических локациях. Так что пришлось искать компромиссный вариант.
Учитывая вышеизложенные сложности, нам пришлось прибегнуть к архитектуре, которую на модных конференциях принято с издёвкой называть «Распределенный монолит».
Если говорить простыми словами, то у нас будет единая база данных и куча независимых сервисов, обращающихся к ней по мере необходимости.
Фактически, в корпоративной разработке для высоконагруженных распределенных систем такой подход принято считать антипаттерном. Почему? Да потому что единая БД, в которую ломятся все, кому не лень автоматически становится узким местом. И при экспоненциальном росте нагрузки она просто ляжет к хуям. Но, я подозреваю, что у большинства читателей нет армии личных девопсов, чтобы содержать полноценную микросервисную систему. Да и масштаб у нас навряд ли будет сравним с масштабами того же Amazon‘a, так что мы можем спокойно пойти на этот шаг.
Раз уж мы определились с нашей архитектурой, настало время повнимательнее посмотреть на элементы, из которых она будет состоять.
## 2.3 Основные элементы системы
### 2.3.1. База данных
С единой БД определились. Мук выбора конкретной СУБД не было. Берем старый добрый PostgreSQL, и, как говорят в Одессе, – не делаем никому голову. В идеале бы прикрутить какой-нибудь Redis для использования в качестве in-memory cache, как это делают в суровом Ынтырпрайзе. Это позволило бы значительно повысить скорости выдачи новых задач и сохранения промежуточного результата брута. Но у нас один сисадмин на все случаи жизни и его надо беречь, холить и лелеять. К тому же Redis требует дополнительных ресурсов. Так что мы решили на первое время обойтись без него, и, по итогу, он даже не понадобился. Для этого опять пришлось обращаться к старине Дейту, вспоминать про нормализацию и прочие оптимизации SQL-запросов. В общем было весело.
Теперь пришло время выяснить что же мы в этой самой БД будем хранить.
1. Логины и пароли (в просторечии креды) – ну тут, как-бы, очевидно – раз у нас брут, да еще с претензией на эффективность, то без этого никуда. Причем нам нужны как юзернеймы и пароли отдельно, так и пары кредов, все это распихиваем по отдельным табличкам, объединением в группы для удобства эксплуатации и отдельно храним связку группы с каждым впном, что бы всегда точно знать что с чем брутилось, и все это с учетом гео.
2. Все, что нужно для masscan’а – тут у нас тоже свои особенности. Рассмотрим именно в контексте работы с БД. Масскан обращается через оркестратор к базе и запрашивает список подсетей и портов для сканирования, которые надо выделить в отдельные таблички, дальше надо сделать связку подсетей и портов, и на ее основе сделать запись в таблицу задач для масскана, что бы трекать статус задачи и в случае абуза масскана отправить ее на повторный скан. Конечно же при этом не забывая о привязке к гео. В процессе сканирования найденные адреса аккумулируются и отправляются в базу чанками по $N$ штук, таким образом мы до завершения скана уже можем начинать проверять найденные адреса.
3. Это делает сервис проверки адресов или просто сервис "чека", который берет данные из таблички, куда складываются найденные в масскане адреса, и выясняет "кто-кто в теремочке живет?", а так же базово фильтрует хонипоты. Он же может еще и домены отработать, там достаточно сложная система, о которой в деталях позже. Найденные впны сохраняются отдельно, это намеренная денормализация базы, ибо впнов у нас будет на несколько порядков меньше, чем адресов для чека.
4. Ну и дальше по цепочке - непосредственно брут, дамп, и прочие штуки, там прокси надо хранить, отдельно хранить задачи для повторного чека померших впнов, отдельно расширенный результат дампа, таски там на это все, статусы и комментарии для веб панели по каждому впн, ну и т.д. Просто скажем, что у нас там еще куча таблиц и довольно таки сложная структура БД.
С БД определились.
### 2.3.2. Основные компоненты системы. Базовое описание.
Дальше, разумно будет выделить следующие сервисы:
## 2.4. Выбор стека для бекэнда
Так, ну вроде бы наброски архитектуры сделаны. Матчасть изучена на уровне достаточном для старта разработки. Осталось определиться с языком программирования и стеком используемых технологий.
Казалось бы – чего тут сложного? С++ наиболее очевидный выбор. Особенно для задач брута. Преимущества описывать не буду – они итак всем хорошо известны. Работай я над проектом в одиночку – я бы даже не думал. Однако, работал над проектом я на пару с коллегой, так что пришлось остановиться на стеке, знакомом обоим из нас, корпоративной классике – Java со Spring Boot. По производительности значительно уступает C++ или какому-нибудь Go, но, как показала практика, производительности нам хватило, а вот выбранный стек позволил реализовать систему сильно быстрее. К тому же, при необходимости, наша архитектура позволила бы переписать конкретные сильно чувствительные к нагрузке сервисы на те же C++ без изменения флоу системы, но это не понадобилось.
Что тут сказать? Могу со всей ответственностью заявить следующее: можете смело дать в морду тому, кто скажет, что перейти с Плюсов на жабу – нехуй делать ваще.
Мол если на плюсах писал всю жизнь, то и с жабой справишься на изи вообще. Да я даже на джаве писал, и не мало, но то было давно и без Spring. На уровне хелловорлдов проблем нет, общего у языков много. Да и у экосистемы жабы есть плюшки, которые я бы с удовольствием перетащил в плюсы. Например, написал перед определением класса
Ты ничего не решаешь. Если в плюсах мне понадобится добавить, например, работу с Json, но я не хочу тянуть в проект boost, то существует полно Header-only реализаций всяких ништяков. Просто добавил заголовочный файл и радуйся. В жабе со спрингом – хрен тебе по всей морде. Ты должен попросить
Еще рассматривали Go, но поскольку от него плевались оба, то он был фактически сразу отброшен, хотя по перформансу можно было бы немного выиграть.
Python и Node.js помрут от планируемой нагрузки, поэтому их тоже отбросили сразу.
В итоге:
На этом небольшое лирическое вступление окончено и пришло время более детально познакомиться с внутренним миром продукта.
---
# Глава 3. Общая система и WorkFlow
Теперь рассмотрим процесс брута в целом и его отдельные этапы. В нашем случае они выглядят следующим образом:
## 3.1. Этап 1: Сканирование
Начинаем мы, как вы можете догадаться, со сканирования диапазонов адресов и портов. Конечно, можно по старинке арендовать готовые абузоустойчивые сервера под масскан и ручками заливать в него диапазоны IP-адресов и порты. Все недостатки такого подхода уже озвучены выше. Так что это не наш случай.
Мы же пишем Продукт для автоматизации всего и вся. Тут можно обозначить следующие требования к сервису:
1. Непрерывность – диапазоны адресов и портов должны сканироваться постоянно. На одном сервере, доказано методом научного тыка, мы можем запускать 3-4 инстанса масскана. Некоторые сервера живут до недели. Некоторые дохнут только успев начать работать. Из этого следует, что для поддержки непрерывности мы должны уметь работать с постоянно отваливающимися серверами, а делается это достаточно просто:
* вносим в код масскана доработки, позволяющие обращаться к серверу и получать задачу на сканирование диапазонов адресов и портов, а также отправлять промежуточные результаты оркестратору для последующей записи в базу;
* в отдельный поток выносим функцию мониторинга состояния сервера. Данная функция по таймеру будет отправлять оркестратору примерно следующую информацию:
* IP-address, с которого идет сканирование;
* Прогресс выполнения текущей задачи в процентах;
* Загрузку памяти и процессора в процентах;
* Количество полученных и выполненных задач;
* Ожидаемое время до завершения задачи;
* Число взятых в работу и успешно завершенных задач.
2. Для деплоя пишем простенький баш-скрипт, которому отдаем на вход список серверов для масскана, с кредами, туда же передаем токен авторизации, количество инстансов на сервер и адрес панели, чтобы наш масскан знал куда ему стучаться за задачами, и регион, в котором он запущен. Всё.
3. Надежное хранение результатов – из коробки masscan пишет результаты в файл на том же сервере, где запущен. Если сервер умрет – данные умрут вместе с ним. Это не есть гуд. Поэтому по мере накопления результатов мы отправляем их на сервер. Это позволяет в случае сбоя потерять только малую часть просканированных адресов. В случае, если задача была выполнена не полностью, мы всегда можем ее перезапустить на новом инстансе.
4. Привязка к заданному региону – реализуем ее, чтобы оркестратор не отдал серверу в какой-нить неметчине американские диапазоны. Таким образом каждый инстанс будет сканить диапы только своего гео.
Рис. 3.1: Упрощенная схема работы Masscan
## 3.2. Этап 2: Проверка (Чек)
Отлично, масскан работает и шлет нам первые адреса. Прежде чем их отдавать на брут – необходимо их проверить на наличие интересующих нас протоколов VPN-ов, и хотя бы частично отфильтровать хонипоты.
Всего получилось выделить несколько этапов проверки:
1. Определение протокола VPN – для этого достаточно просто сделать HTTPS-запрос на найденный адрес (по найденному порту, разумеется), после чего по ключевым словам определяем, страница какого VPN-сервиса тут висит.
2. Сканирование открытых портов – возможна ситуация, когда по какой-то причине мы пропустили этап скана и залили сразу IP-адрес, а также проверить сабдомены, если у нас вместо IP передан домен или URL. Будет сильно медленнее, чем массканом, так что много портов просканить не выйдет.
3. Базовая проверка на honeypot.
4. Контроль геолокации – очевидно, что запросы лучше всего отправлять с того же региона, где находится целевой адрес.
Там будет еще много различных приколов, поэтому было принято вынести всё это хозяйство в отдельный сервис.
### 3.2.1. Балансировка нагрузки
И тут у нас возникает проблема — мы не знаем, на каком железе будут запущены сервисы чека, оно может быть сильнее, слабее... Суть в том, что мы никак не можем узнать, сколько параллельных воркеров, проверяющих адреса, конкретное железо выдержит, так что придется опять сочинять козу на лисапеде.
Решение получилось простое и элегантное – мы можем получить текущий уровень нагрузки ЦПУ сервера и на его основе динамически определять оптимальное число потоков. В таком случае мы можем выставить каждому отдельному сервису в конфигурации границы по числу потоков (например, от 300 до 2000), и дальше по алгоритму система сбалансирует общее число воркеров. Выглядеть это будет примерно так: делаем воркер в отдельном потоке, который будет по таймеру проверять текущую нагрузку на проц, и если она не достигает верхней заданной границы, то увеличиваем число потоков, если превышает ее, то уменьшаем. Детали процесса будут ниже.
### 3.2.2. Обход блокировок
С нагрузкой разобрались. Покупаем десяток-другой серверов и погнали. Смотрим в панель – а туда уже полетели первые результаты проверки. День-два все нормально, а потом хуяк-хуяк, и на наши сервера начинают лететь абузы.
В теории все легально — мы просто сканим GET-запросами URL-ы, но на практике в некоторых регионах, например, в США, с этим возникнут проблемы. Хоть мы и формально только делаем GET-запросы, фактически это малоебущий фактор, и наши сервера автоматом будут лететь абузы.
В Европе с этим все просто — взяли абузоустойчивый сервер и вперед, а вот в Штатах придется попотеть.
Первое, что приходит на ум, — это прокси. Но, как говорится, есть один нюанс: объем трафика, который будут генерировать наши сервисы чека, будет настолько большой, что прокси начнут в нем захлебываться. Дело тут будет даже не сколько в объеме трафика, а сколько в том, что мы будем слать запросы только на потенциально живые адреса, а значит очень большое количество запросов будет обрывать сокет, терять пакеты, падать по таймауту, да и вообще там будет твориться всякая дичь.
В качестве альтернативы можно поднять VPN, но там тоже много нюансов, о чем будет расписано чуть ниже.
Для того, чтобы описать все приключения и подводные камни, возникшие на нашем пути понадобится отдельная серия статей. Поэтому мы это опустим и представим, что на некоторых адресах мы нашли интересующие нас протоколы. Что с ними делать дальше? Очевидно, начинать брут.
Далее предлагаю читателю либо погрузиться в детали реализации сервиса проверки адресов (чека). Либо же сразу перейти к описанию процесса брута и деталям его реализации. Я долго думал как лучше все организовать. Изначально планировалась серия статей, и это должна была быть вторая статья в серии. Но, увы, такой роскоши пока позволить себе не могу. Поэтому решил объединить описание сервисов и детали их реализации в отдельные разделы, дабы не растекаться мыслью по древу.
## 3.3. Внутренности сервиса чека
Углубимся, пожалуй, во внутрянку сервисов чека. Его основная задача — проверка найденных массканом адресов (ну или чек залитых корпоративных доменов). Тут у нас встает дилемма: хотим мы точность или высокую нагрузку? Не знаю, как у дорогого читателя, но при постановке данной задачи у меня в голову сразу приходит «гениальная» идея делать дабл-чек всех адресов через пару прокси от разных вендоров, что бы точно ничего не упустить.
На первый взгляд звучит красиво, но только до момента, как мы начнем на эти несчастные прокси лить хотя бы 200–300 RPS с каждого из 20 одновременно запущенных сервисов
. Конечно, мы можем увеличивать число проксей, но учитывая немаленькую стоимость приватных проксей (особенно в некоторых ГЕО), и, наверное, самое главное, учитывая то, что многие запросы не будут корректно завершаться удаленным сервером (даже несмотря на
Забегая вперед, как показывает опыт брута: если бы наши проверяемые адреса нормально отвечали на запросы, то прокси бы прекрасно справились с этой ношей. Но, к сожалению, если мы хотим чекать реально много, да еще и под нужными ГЕО, нам придется повозиться с костылями.
Вообще, отказ от прокси — это очень неприятное решение. Конечно, мы не делаем ничего противозаконного, как минимум в этом сервисе
, но в некоторых ГЕО (кхм... USA... кхм) это никого особо не ебет, и абузы будут лететь только в путь. Можно чекать с абузоустойчивых серверов в Европке, и в целом это работает, но выход сеток с США из-за геоблока, конечно, будет чуть ниже. Хотя по началу мы так и делали. В общем нам остается либо смириться и брать абузоустойчивые сервера в нужном ГЕО, либо осчастлививать наших админов прекрасной и интересной задачей (все же любят интересные задачи) – прокидыванием не шифрованного туннеля с сервера чека на другой, внешний сервер, который будет нам служить как гейтвей. А потом, после абуза этого гейтвея, его нужно сразу менять на следующий. В общем — сказка, а не работа. Ну и конечно, в случае обрыва соединения сразу останавливаем чек, что бы не было ложно-негативного результата.
И да, не рекомендую тратить свое время на использование для этих целей классических VPN — они шифруют трафик, и им, похоже, тоже плохеет от неадекватных ответов с некоторых адресов. У нас дело доходило до того, что некоторые сервера, где был запущен VPN, приходилось ребутать через интерфейс хостера, а на их ЦПУ серверные гномики жарили яичницу.
И этот заеб нужен исключительно ради юсы, и то, что бы получить процентов 10-20 геоблоченых адресов, остальные будут спокойно чекаться с европейских айпи, а брут у нас будет через прокси.
### 3.3.1. Структура чекера
Но вернемся обратно. Наш сервис чека будет иметь простую структуру:
1. Очередь с адресами на чек.
2. Очередь с впнами для проверки их состояния (сюда будут попадать те, которые сервисы брута пометили как мертвые).
3. Воркер, запрашивающий адреса, если адресов в очереди мало, и отправляющий результаты в оркестратор.
4. Динамическое количество воркеров(консумеров), которые берут эти адреса в работу.
5. Воркер, осуществляющий сбор метрик текущего состояния для веб панели.
Число консумеров будем определять динамически, но в заданном диапазоне, т.к. мы можем раскатывать наши сервисы на разные сервера, у которых будет разный конфиг, и угадывать точно нужное число параллельных консумеров весьма проблематично.
Сделать это можно достаточно просто: берем минимальное и максимальное значение диапазона (скажем, 300 и 1500 потоков) и стартуем с минимального числа. Дальше раз в заданное время смотрим, как изменилась нагрузка на ЦПУ. Если ЦПУ нагружен менее чем на условные 97%, то увеличиваем число потоков; если же ЦПУ перегружен, то понижаем.
Естественно, обрубать работающий консумер прямо во время отработки адреса идея херовая (можно потерять прогресс обработки адреса), так что наш балансер нагрузки просто меняет целевое число потоков. А вот сам консумер перед тем, как взять новую задачу, смотрит на текущее число запущенных консумеров, смотрит на целевое, и если текущее число больше целевого, то он самоустраняется. Добавлять консумеры, ясное дело, можно в любой момент без каких-либо проблем.
Отдельно скажу про интеграцию любых сервисов с оркестратором: очевидно, необходимо закрывать все эндпоинты за кредами/токеном, чтобы не было возможности вытащить из базы оркестратора никакую информацию.
### 3.3.2. Логика проверки
Теперь вернемся к чеку. Вот у нас на фоне происходит балансировка числа параллельных консумеров, воркер запрашивает и получает порцию адресов на чек по своему ГЕО из оркестратора, кладет их в очередь, после чего наши консумеры набрасываются на эту очередь и растаскивают адреса на чек.
Но как проводить сам чек?
Во-первых, при старте нужно запросить включенные протоколы из оркестратора. Мало ли, мы хотим искать только что-то конкретное. Дальше по каждому полученному протоколу нужно сформировать типовые пути, например
Вообще, ожидаемое поведение от сервера VPN — это редирект на такой путь, но если вдруг его не будет, надо прочекать их отдельно.
А вот если наш адрес — это не IP, а корп. домен, то на каждый путь не помешало бы сформировать вариант со всеми сабдоменами, характерными для включенных протоколов (например
Подобная проверка всех путей, портов и сабдоменов очень сильно замедляет чек, потому что вместо одного запроса и, возможно, редиректа нужно сделать больше десятка запросов с путями и сабдоменами (в случае корп. домена) каждого протокола, если редирект не произошел или мы не нашли то, что нам интересно. Ясен пень подобное поведение должно быть опциональным и отключаемым, если вдруг у нас не хватает ресурсов и мы хотим чекать URL-ы быстрее.
Дальшe мы делаем обычный GET-запрос на адрес и смотрим по ключевым словам, есть ли там что-то интересное. Если мы что-то нашли, то тут же надо проверить, не на ханипот ли мы нарвались.
Если хоник сделан, например, чисто под циску, то мы хрен как это разгадаем, но такое бывает крайне редко. Как правило, хоники запускают одновременно на кучу всего, и чтобы зацепить подобные чекеры, в них суется огромное количество ключевых слов, не относящихся к нашему протоколу. Как вы могли догадаться, мы просто собираем гигантский список ключевых слов, которые часто встречаются в подобных хониках, но не встречаются на странице логина нашего протокола, и делаем дополнительную проверку в HTML, в куках, возможно, делаем пару запросов по другим path. Если есть пересечение — помечаем адрес как хоник, больше мы его трогать не будем. И чуть не забыл еще один важный нюанс: некоторые VPN могут быть выключены или иметь аутентификацию через внешние сервисы, о чем они великодушно нам могут сообщить на главной странице. Так что это тоже нужно сохранить в панель, чтобы мы не долбились в закрытую дверь.
### 3.3.3. Обработка результатов
Но представим, что мы нашли нормальную веб-панель VPN, убедились, что это не хоник (хотя бы на первый взгляд). Что дальше?
Не будет же каждый поток спамить в оркестратор найденными протоколами, да и пометить адрес как пустой тоже надо, если на нем нифига не было. Когда наших потоков будет пара тысяч, то с таким флоу даже пара сервисов смогут положить наш оркестратор на лопатки. Значит, нам нужна еще одна очередь, куда мы будем совать прочеканные адреса с их статусом, и, если повезло, найденным протоколом. А дальше, как мы засунули туда результат чека адреса, нам нужен отдельный воркер, который раз в $N$ минут будет вытаскивать эти адреса и батчем отправлять их в панель. В целом, ничего сложного.
Сделаем еще одну необязательную, но очень удобную фичу. Работая с системой через веб-панель, очень хотелось бы видеть текущий статус и метрики по каждому запущенному сервису. Так что добавим в наш чекер еще один воркер, который будет отправлять в оркестратор раз в заданный промежуток времени такую инфу, как: количество адресов в очереди, общее число потоков, RPS за минуту, среднее время реквеста, загрузку ЦПУ и RAM, таймстемп, ну и что-нибудь еще. В целом, отправлять можно все, что мы хотели бы видеть в веб-панели. Дальше оркестратор сможет корректно отрисовать текущий статус нашего сервиса, и если он перестанет выходить на связь, то мы хотя бы это увидим.
Не забудем еще о завершении работы нашего сервиса. Иногда нужно либо его перезапустить, либо установить обновленную версию, а значит текущий инстанс надо бы выключить. На этот случай внутри самого сервиса будет разумно перехватить завершение работы и отправить в панель все завершенные и незавершенные задачи, чтобы они не потерялись (конечно же, если перехватить завершение удастся).
Рис. 3.2: Упрощенная схема работы сервиса чека
Рис. 3.3: Схема сервиса чека вместе с массканом
## 3.4. Этап 3: Брутфорс
Для брута нам нужны наборы логинов и паролей, как отдельно, так и парами, здесь и далее – креды, по которым мы будем работать.
Допустим у нас есть множество логинов и множество паролей. Для каждого конкретного VPN мы хотим перебрать всевозможные комбинации – так да, это опять Декартово произведение множеств. Остаются еще пары кредов, например
Некоторые креды будут только под конкретные регионы — нет никакого смысла гонять топ немецких паролей по Италии или Штатам.
Ну и понятное дело мы хотим запоминать, какие креды с какими VPN-ами мы уже проверяли.
Для решения этой задачи организовали креды в группы, например суем туда 2 тысячи самых типовых имен для гео и какие-нибудь вариации "Welcome!1" паролей, и отмечаем в базе, какие VPN с какими группами кредов уже проверялись.
Также создадим отдельные сервисы брута, и туда будет передаваться найденный VPN и группа кредов, которую мы с ним проверяем.
Таким образом, мы сильно упростим поиск еще не просканенных кредов под конкретный VPN:
Это простое решение целиком решает геморрой с менеджментом кредов для десятков одновременно запущенных сервисов брута и сотен тысяч VPN-ов. Добавим сюда еще менеджмент числа потоков, завязанный на загрузку ЦПУ (аналогично сервисам чека), и не забываем, что брутить надо из того Региона, к которому относится целевая VPN, а значит и прокси нужно использовать из этого же Региона.
Для упрощения процесса сделали возможность залить все прокси с нужными Регионами в оркестратор и делегировать все остальное сервисам брута. Оставив себе только необходимость загружать новые прокси время от времени.
Часть VPN-ов начнет отваливаться во время брута. Почему? Да хрен его знает, товарищ майор. Может померли от передоза от нагрузки или по естественным причинам. А может их включают только в рабочее время – такое иногда встречается. В общем сразу мы его выбрасывать на свалку истории не будем, а отправим на перепроверку. Вдруг он оживет.
Было бы неплохо через оркестратор отправлять их обратно в чек, чтобы проверить, была ли это временная проблема или они окончательно умерли. Для сервиса брута еще важно имплементировать поддержку прокси. К счастью, таких проблем с мусорными адресами, как в сервисах чека, у нас тут нет, так что и изворачиваться не придется. Все что нужно сделать, это просто подтягивать актуальные прокси из оркестратора раз в $N$ минут, проверять их на жизнеспособность, ну и в случае смерти отсылать мертвые прокси обратно в оркестратор, чтобы они были удалены из выдачи.
Сами прокси мы можем выдавать воркерам по Round Robin, ну или просто рандомно — разницы особой нет. Главное, что каждый запрос с новой вариацией кредов будет лететь на VPN с нового IP-адреса, что предотвратит блэклист наших IP целевыми VPN-ами.
### 3.4.1. Внутренности сервиса брута
Берясь за сами сервисы брута, может показаться, будто тут у нас будет все то же самое: балансируем потоки, вытаскиваем VPN из оркестратора, суем в очередь, консумеры их начинают дербанить ну и так далее. Общего много, но есть и небольшие нюансы.
Во-первых, далеко не все протоколы можно брутить просто по вебу. Иногда туда вкорячивают защиту, и просто так сделать POST-запрос с кредами уже не получится. Выхода тут два:
1. Вкорячивать веб-драйвер, что сожрет буквально тонну ресурсов.
2. Или же реверсить клиенты и смотреть, как у них в потрохах идет аутентификация.
Очевидно, что мы пойдем по второму пути, но и тут не всегда все гладко. Некоторые хитрецы в клиент буквально встраивают веб-браузер, так что вытащить оттуда какой-то внутренний API не выйдет. Тут остается только веб-драйвер, что я оставлю для ценителей особого рода извращений, ибо с веб-драйвером нормального RPS у нас точно не будет. Но на большинстве протоколов все будет гораздо проще, да настолько, что даже обезьянка с GPT справится.
#### 3.4.1.1. Логика очередей и прокси
Есть еще одна важная деталь, на которую нам надо обратить внимание: лучше всего один и тот же VPN не долбить без остановки, а сделать кулдаун-период (минуту-две) и закидывать этот VPN обратно в очередь. Таким образом, при насыщении очереди достаточным количеством VPN-ов, брут будет идти без остановки, но вот один конкретный VPN будет проверяться только, как минимум, раз в пару минут.
Добавим к этому пару тысяч прокси, которые будут выдергиваться для каждого реквеста в случайном порядке, и получим ничем не примечательную активность: раз в пару минут с какого-то айпишника из ГЕО VPN прилетает запрос на аутентификацию, причем практически каждый раз с нового.
Естественно, эти прокси нужно также сгружать с оркестратора, их надо проверять на валидность как на старте сервиса, так и раз минут в 10. Еще обязательно на все запросы нужен ретрай, чтобы в случае отвала одного прокси, запрос повторно делался с другого. Сам же прокси, который отвалился, можно поместить в отдельный пул, в котором специальный воркер будет проверять такие прокси на валидность раз в минуту. Помершие же прокси мы можем возвращать обратно в оркестратор, чтобы он их соответствующим образом пометил и никаким сервисам больше не давал. Так мы интегрируем проверку наших проксей прямо в сервис брута, который по ходу дела будет избавляться от почивших проксей и подтягивать с оркестратора новые, тем самым обеспечивая беспрерывность процесса.
#### 3.4.1.2. Процесс брута по шагам
Если кратко описать процесс, то он получается следующим:
1. Сервис брута на запуске запрашивает прокси, потом их все проверяет.
2. Как только он их проверит, запускаются воркеры:
* Воркер для повторной проверки прокси (раз в 10 мин).
* Воркер, который будет пулить прокси с оркестратора и отправлять ему мертвые.
* Воркер, выдергивающий VPN-ы с оркестратора.
* Воркер, отсылающий в оркестратор результат.
3. Поднимаются консумеры с балансировкой потоков (все как в чекере).
4. Консумеры берут VPN-ы, при необходимости выдергивают домен аутентификации (
В момент, когда консумер взял VPN, ему для начала брута нужно взять с оркестратора креды, которые будут по этому VPN гоняться. В оркестратор креды заливаются группами, и оркестратор самостоятельно подбирает группу кредов с максимальным приоритетом, которая еще не гонялась по конкретному VPN. И так по каждому.
Соответственно, вместе с VPN на брут передается и ID этой группы. Когда этот VPN берется в работу, консумер пытается найти группу кредов с таким ID в локальной мапе. Если ее там нет, то он один раз запрашивает эту группу с панели и кладет в мапу. В дальнейшем для остальных VPN-ов, которые будут поступать на брут с этой группой кредов, запросы в оркестратор уже делать будет не надо, т.к. группа будет храниться в памяти брута.
Сделав несколько запросов (ну или один, в зависимости от настроек), консумер возвращает VPN в очередь и берет следующий. Возвращает он его со временем, когда данный VPN будет доступен для следующей итерации. Например, «текущее время + 2 минуты» — это можно настроить для каждого протокола отдельно.
Если же консумер достал VPN, который еще не готов к работе, то он просто кладет его обратно, ждет секунду (это охренеть как важный момент, без него консумеры будут как бешеные крутиться по очереди, сжирая весь ресурс ЦПУ), и берет следующий. И так, пока не найдет готовый к новой итерации брута VPN.
Кроме этого, он подаст сигнал воркеру, чтобы тот запросил еще дополнительные VPN-ы с панели. Вообще, такое будет происходить только на старте брута. Как только он наберет рабочее число VPN-ов, он стабилизируется. Будет несколько десятков VPN-ов, ожидающих в очереди и доступных для брута. Это сразу будет видно в метриках, которые будут выгружаться на веб-панель.
Рис. 3.4: Упрощенная схема сервиса брута
Рис. 3.5: Схема, включающая в себя масскан, чек и брут
## 3.5 Этап 4: Дамп и пост-эксплуатация
Двигаясь дальше, когда мы получим первый результат, встает вопрос — хватаем ли мы эту сетку руками, или же сбрученных VPN-ов настолько много, что рук просто не хватит?
Если сеток падает достаточно много, но доменных аккаунтов там единицы, то прежде чем сажать «масленка» разгребать их за миску риса, мы можем попробовать подключить дамп.
На рынке уже есть готовые решения
, и мы можем адаптировать одно из них, благо наша почти «микросервисная» архитектура это позволяет. Все, что нам нужно, — это адаптировать такой сервис для работы с оркестратором.
1. Сервис запрашивает сбрученный VPN на дамп тех протоколов, с которыми он умеет работать.
2. Мы ему такой выдаем, меняя у выданного VPN, естественно, статус на какой-нибудь «в дампе».
3. После чего принимаем результаты. Часть результатов, типа имени домена или того, является ли аккаунт доменным, мы сохраняем в базу. Остальное же загружаем архивом.
При желании можно будет получить подробный отчет о сетке и даже базовую проверку на самые ходовые уязвимости, типа Zerologon, в зависимости от реализации дампера. В целом, тут даже можно какой-нибудь фреймворк для автопентеста на основе LLM прикрутить, было бы желание.
А вот дальше начинаются всякие интересные фишки. Например, если аккаунт не доменный, то мы можем собрать имена юзеров из Kerberos и попробовать побрутить по ним. Для этого нам нужно будет собрать побольше паролей и выделить их в специальную группу, которая будет применяться только для работы с подобным повторным брутом.
Вообще, в бруте VPN-ов есть серьезная проблема — нужно угадать не только пароль, но и логин, что безумно увеличивает число комбинаций для перебора. Если мы вытащили хотя бы часть имен из Kerberos, то мы можем побрутить по этим именам, на порядки повышая наши шансы успешного брута и, самое главное, получения доменного аккаунта в сети. Не забывайте: мы брутим VPN, а не какой-то DC — локаута пользователей в AD не будет, да и никакие средства мониторинга внутренней сети тревогу от такого бить не начнут, мы же не внутри брутим
Бывают еще ситуации, когда мы заходим в сеть, а там ничего. Вот буквально пустая подсеть, в которой ничего не просканивается, да и других подсетей нет. Это может быть связано с тем, что некоторых пользователей, например
## 3.6 Управление системой
Мы тут обсудили довольно объемный флоу, и будет нелишним добавить в UI оркестратора еще и какой-никакой менеджмент VPN-ов: сбрученных, сдампленных, да вообще любых. Просто чтобы можно было ими удобно управлять, когда со временем их там будет пара тысяч: ставить им статусы (например, «в работе» или «продан»), оставлять комментарии, например ссылки на Zoom, ну и так далее.
Рис. 3.6: Схема интеграция дампера без деталей.
Еще неплохо бы на вебе мониторить текущее состояние сервисов чека, масскана, брута: загрузка ЦПУ, RPS и всякое такое, чтобы вовремя видеть проблемы с перформансом или откисшие прокси. Во время работы такого массивного флоу что-то обязательно пойдет не так: какие-то сервера будут абузиться, а за какие-то просто оплата просрочится.
Так что нам нужен механизм восстановления системы в условиях периодически откисающих серверов, у которых будут оставаться таски в работе. Тут решение тоже достаточно простое: если у нас упало несколько серверов брута, то гасим сервера (в них бы еще добавить механизм возвращения задач на шатдауне, но и без этого можно обойтись), и, как только сервера уходят в оффлайн, просто сбрасываем статус всем таскам со статусом «в работе». Очевидно, что для такого флоу сервисы брута должны будут постоянно отсылать в оркестратор свое текущее состояние, чтобы в случае перезапуска можно было бы не начинать брут сначала, а продолжить его с момента остановки. Реализуется это тоже парой эндпоинтов.
---
# Глава 4. Итог
Рис. 4.1: Вот такая система у нас получилась по итогу. В схеме упущены многие детали и части флоу для упрощения восприятия.
Хочется сказать, что рассмотрев проблему брута с инженерной точки зрения, мы можем адаптироваться к меняющемуся рельефу рынка корп. доступов и выйти на объем брута, попросту невозможный без специальных технических решений.
Система, подобная описанной выше, способна осуществлять беспрерывный брут сразу по нескольким ГЕО, работая с наборами кредов, характерных именно для этих ГЕО, начиная с этапа скана и заканчивая дампом сбрученных сеток, автоматизируя весь пайплайн. Очевидно, что брут завайбкодеными кустарными скриптами (или, прости господи, MSF-ом) рано или поздно полностью уступит рынок сложным высоконагруженным системам.
Немного скринов с панели добавлю
```
***markdown
# Пишем распределенную высоконагруженную систему брутфорса корпоративных VPN
Автор: Snow
Специально для: XSS.PRO
---
# Глава 1. Введение
## 1.1. Предыстория
С момента начала продаж моего Дампера выяснилось, что наиболее популярным источником корпоративных VPN является TM Checker. Продукт, безусловно, качественный и свою репутацию имеющий вполне заслуженно. Но при этом обладающий одним существенным недостатком – отсутствие единой панели управления делает его довольно неудобным в использовании. При наличии десятка-другого запущенных его копий начинается сущий кошмар.
Залить базы адресов, логинов и паролей, прокси и при этом не запутаться. Плюс необходимость постоянного мониторинга результатов и ручная отправка их в дампер – удовольствие ниже среднего.
Дампер, к слову, до недавнего времени обладал тем же недостатком, в довесок к которому идет нетривиальная настройка серверов и сложность обновления. То есть приходилось руками загружать впнки для проверки и вручную же собирать результаты. У меня давно чесались руки довести всё до ума и сделать единую панель управления, с автоматическим развертыванием софта на серверах, загрузкой данных для проверки и выгрузкой результатов. Что в итоге и было сделано. Но речь сейчас не об этом.
Я ни в коем случае не хочу охаивать проверенный временем и десятками, если не сотнями пользователей продукт. Просто констатирую факты, основанные на общении с реальными пользователями.
Ну и постепенно вынашивалась идея создания собственного комплексного высоконагруженного брута как полноценной распределенной системы. В итоге, примерно после года неторопливой разработки и тестов «в поле» парой человек получился довольно интересный и, что самое главное, – достаточно эффективный продукт. Историей создания которого я и хочу поделиться.
### 1.1.1. Анализ текущего состояния рынка корпоративных доступов
Если коротко, то спрос явно превышает предложение. Желающих купить доступы полно. Продавцов тоже хватает. Только вот качественный материал становится достать все сложнее. Под качественным материалом в данном случае понимаем доступы со следующими характеристиками:
1. Топ гео – США (юсу хотят все, много и почти всё), Канада, Австралия, Европа — в общем так называемые страны TIER 1.
2. Revenue – хотя бы от 10кк за США, остальные от 20-50кк.
3. Сфера деятельности – в особом почете юристы и страховщики, следом идут айтишники, финансисты и имя им легион.
В той или иной степени, постоянно общаясь с покупателями, я вижу растущую проблему дефицита качественного материала, а некоторые мои знакомые уже даже начали снижать свои требования к сетям. Это сугубо личное наблюдение, так что буду рад, если вы поделитесь своими наблюдениями.
## 1.2. Историческая справка
Способов получения корпоративных доступов существует довольно много. Все они обладают своими преимуществами и недостатками, порогом входа, окупаемостью и многими другими характеристиками, которые в текущем контексте можно опустить. Вот некоторые из них:
- Стилеры;
- Фишинг;
- Эксплуатация уязвимостей — RCE и прочие 0day/n-day, реализация которых далеко не тривиальна, а за некоторые из них готовы платить круглые суммы.
- Brute force — атаки методом грубой силы, так называемый брутфорс.
Сегодня мы остановимся на последнем – классическом брутфорсе корпоративных VPN (Fortinet, Cisco, SonicWall, WatchGuard...).
Брут VPN систем – штука известная с давних пор, брутили так-то всегда, но особую популярность этот метод приобрел в пандемийные времена, когда в мире нарисовался нехуевый такой трындец, вынудивший компании в срочном порядке переводить сотрудников на удаленку. Зачастую силами собственных IT-отделов, где заебанные вусмерть сисадмины (зачастую самоучки) с помощью гугла и какой-то матери пытались хоть как-то обеспечить работоспособность вверенной инфраструктуры.
Некоторые особо ушлые товарищи умудрялись даже продавать курсы по бруту посредством Metasploit за, всего лишь, если мне память не изменяет, 10к зелени.
Это, в свою очередь, породило золотую лихорадку среди мамкиных хацкеров и прочих скрипт-киддис. Толпа ринулась брутить всё, что плохо лежит, благо особого ума для этого не требовалось, Metasploit давал хорошие результаты. А если у тебя были python скрипты, берущие вводные данные из одного файла и складывающие результаты работы в другой – ты был уже гением инженерной мысли.
Однако недолго музыка играла, недолго фраер танцевал. Компании, а также поставщики корпоративных VPN – наученные горьким опытом локов, утечек, повлекших за собой огромные убытки, принялись перестраиваться и стараться не только оперативно реагировать на угрозы, но принимать превентивные меры. В частности, внедрять двуфакторную аутентификацию, выводить пользователей VPN из домена, блокировать запросы с необычных геолокаций... В общем золотые времена, когда любой индус мог в считанные часы набрутить себе доступов, канули в лету. Сейчас наступает время серьезных инженерных решений, требующих высокого уровня квалификации разработчиков и творческого подхода.
## 1.3. От скриптов к системе – по заветам Ильича
Итак, мы хотим свой брут.
Круто. А как его сделать? Есть, конечно, готовые решения, но многие ли из них могут работать под высокой нагрузкой, а также легко масштабируемы? Боюсь, нам придется сочинять очередную козу на лисапеде. Причем коза должна быть максимально тюнингована.

Рис. 1.1: Та самая Коза на лисапеде
А именно удовлетворять следующим требованиям:
1. Решать задачи централизованного хранения данных – адреса, пары логин-пароль, а также прогресс. Будет очень обидно потерять результаты брута, даже за пару дней. Это критично для сохранения данных в случае абуза серверов или сбоев. Да и без этого будет очень тяжело работать с парой-тройкой десятков беспрерывно работающих инстансов брута.
2. Полная автоматизация и интеграция всех этапов. Если коротко, то тут у нас:
* сканирование сетей — берем старый добрый масскан и запускаем его искать открытые порты. Только масскан не сам по себе – мы будем интегрировать его в систему;
* определение протоколов – мало найти открытый порт, надо еще понять что там на нем висит, определить вариацию API. Задача тоже далеко не тривиальная;
* непосредственно брут – брут протоколов, за небольшими исключениями, реализуется не сложно, но нам то нужно сделать весь флоу беспрерывным, с выбором кредов и всем прочим;
* дамп результатов – поскольку у нас уже имеется проверенный временем дампер, то мы просто обязаны вкорячить его в систему, дабы получить на выходе волшебную кнопку «Сделать заебись» – единый конвейер, который сам сканирует, сам анализирует найденное, определяет протокол, хоть как-то фильтрует ханипоты, брутит и снимает минимальный дамп, позволяющий определить хотя бы приблизительную ценность доступа.
3. Оптимизация ресурсов – еще мы хотим хоть сколько-то эффективно использовать ресурсы. Не, конечно можно пойти путем тупого увеличения вычислительной мощности кластера. Как сейчас поступают игроделы – тормозит игра – нахуй оптимизацию, еще не хватало биться за каждый байт оперативки и такт процессора – пусть юзер покупает более мощное железо. Но, поскольку, бесконечное вертикальное масштабирование попросту невозможно, да и стоит такое удовольствие недешево – придется включать голову и думать как таки выжать из доступных ресурсов максимальный КПД и минимизировать простой серверов.
Забегая вперед скажу, что проблем добавляет и ужесточение регуляции в некоторых гео. Например, простой поиск сетей с IP-адресов США уже становится нетривиальной задачей из-за быстрых абузов.
Итак, чтобы конкурировать с тысячами индусов, использующих публичные скрипты и словари вида
guest:guest, нужно выходить на качественно иной уровень. Всё, что лежало на поверхности, давно собрано. Ожидать выдающихся результатов, делая то же, что и все — бессмысленно. Тем более, что сейчас мир погрузился в безумное поклонение нейронкам и каждый первый мамкин хацкер может наваять скрипт для атаки за пару промптов.Следовательно, наше преимущество должно строиться на комплексном подходе, недоступном для массового «скрипт-кидди» и «вайб-кодера». А посему мы имеем:
### 1.3.1. Новые требования к качеству
1. Расширение списка протоколов и постоянный поиск нестандартных точек входа. Особенно хорошо, если публичный веб протокола имеет защиту, так больше шансов, что мамкины вайбкодеры ее не пробили и осталось больше нетронутых сеток.
2. Гео-специфичность. Использование баз кредов и прокси, релевантных конкретному региону атаки. Нет смысла брутить США по азиатским базам, как и наоборот.
3. Непрерывность. Система должна работать 24/7, пересканируя уже известные диапазоны и новые порты, прогоняя живые впны по новым кредам, проверяя, не ожили ли мертвые, ну и т.д.
4. Масштабируемость архитектуры, способной обрабатывать крайне большое число сеток с минимальным участием оператора.
Ну и путем несложных размышлений получаем, что только создание высоконагруженной, распределенной и детально проработанной системы позволит получить стабильный поток валидных корпоративных доступов в текущих реалиях.
---
# Глава 2. Архитектура
### 2.0.1. Вместо предисловия
Это сейчас, я типа умный, буду описывать уже имеющуюся архитектуру, скромно умалчивая о количестве мертвых нервных клеток, путешествий по граблям и прочими интересностями, сопровождающими разработку подобных систем. По возможности буду пояснять причины принятия того или иного архитектурного решения.
## 2.1. Требования к архитектуре
Разрабатывая архитектуру подобной системы, нам нужно обратить внимание сразу на несколько важных деталей:
- Система должна иметь возможность работать изолированно по конкретным ГЕО, т.к. без этого результаты брута заметно ухудшаются. Для этого необходимо встроить изоляцию по ГЕО в каждый этап. Тут надо пояснить, что изначально этого разделения не было. Оно пришло постепенно, после того как была набита куча шишек в попытках повысить КПД системы.
- Части системы, работающие во внешнем контуре, имеют риск словить абуз или попасть в блек-лист IP-адресов. Понимание этого момента было изначально, поэтому сразу закладывались на то, что некоторые сервера необходимо будет оперативно менять прямо по ходу работы без перезапуска. Почему прокси не решат все проблемы расскажу отдельно.
- Система должна быть масштабируемой, то есть предоставлять возможность по ходу брута наращивать мощности, причем именно по необходимым направлениям. Например, если ботлнек наступает на мощностях брута, то наращивать мощности только на брут, а если на чеке адресов при недогруженном бруте — то только на чек.
## 2.2. Выбор архитектуры
Про архитектуру у меня тут было несколько статей. В рамках небольших проектов придерживаться принципов «Чистой архитектуры» довольно просто. Тут же сильно другая ситуация. Построение распределенной системы требует ответственного подхода, потом переделывать будет очень дорого. На выбор у нас: классический монолит, его мы сразу отбросим, т.к. он не позволит перераспределять мощности на разные этапы (скан, чек, брут, дамп) на лету и без костылей, а изоляция по ГЕО некоторых этапов может стать весьма нетривиальной. Кроме того, подобная архитектура очень сильно затруднит интеграцию элементов системы, написанных на разных стеках. А забегая вперед, скажу, что масскан, необходимый для первого этапа, написан на Си. Да-да, на старом добром чистом Си. И у меня, честно говоря, не было даже желания пытаться вкорячить туда C++. Точнее оно было, но быстро пропало, поскольку сама интеграция свелась к запросу диапазонов адресов/портов и отправке результатов. Ну и по мелочи – мониторинг нагрузки на процессор и память. Все это прекрасно решается через
libcurl / cJSON.Дампер – написан на питоне. Принцип интеграции в систему такой же. Взяли задачу и отправили результат. А вот ядро должно уметь общаться с ними всеми.
Использование же чистой микросервисной архитектуры, как по учебнику, обернется огромной стоимостью поддержки инфраструктуры — нам понадобится целая команда админов, чтобы все это поддерживать и обслуживать. Опять же, забегая вперед, работу некоторых сервисов под высокой нагрузкой оказалось очень сложно реализовать через прокси, в связи с этим нам пришлось размещать части нашей инфраструктуры в разных географических локациях. Так что пришлось искать компромиссный вариант.
Учитывая вышеизложенные сложности, нам пришлось прибегнуть к архитектуре, которую на модных конференциях принято с издёвкой называть «Распределенный монолит».
Если говорить простыми словами, то у нас будет единая база данных и куча независимых сервисов, обращающихся к ней по мере необходимости.
Фактически, в корпоративной разработке для высоконагруженных распределенных систем такой подход принято считать антипаттерном. Почему? Да потому что единая БД, в которую ломятся все, кому не лень автоматически становится узким местом. И при экспоненциальном росте нагрузки она просто ляжет к хуям. Но, я подозреваю, что у большинства читателей нет армии личных девопсов, чтобы содержать полноценную микросервисную систему. Да и масштаб у нас навряд ли будет сравним с масштабами того же Amazon‘a, так что мы можем спокойно пойти на этот шаг.
Раз уж мы определились с нашей архитектурой, настало время повнимательнее посмотреть на элементы, из которых она будет состоять.
## 2.3 Основные элементы системы
### 2.3.1. База данных
С единой БД определились. Мук выбора конкретной СУБД не было. Берем старый добрый PostgreSQL, и, как говорят в Одессе, – не делаем никому голову. В идеале бы прикрутить какой-нибудь Redis для использования в качестве in-memory cache, как это делают в суровом Ынтырпрайзе. Это позволило бы значительно повысить скорости выдачи новых задач и сохранения промежуточного результата брута. Но у нас один сисадмин на все случаи жизни и его надо беречь, холить и лелеять. К тому же Redis требует дополнительных ресурсов. Так что мы решили на первое время обойтись без него, и, по итогу, он даже не понадобился. Для этого опять пришлось обращаться к старине Дейту, вспоминать про нормализацию и прочие оптимизации SQL-запросов. В общем было весело.
Теперь пришло время выяснить что же мы в этой самой БД будем хранить.
1. Логины и пароли (в просторечии креды) – ну тут, как-бы, очевидно – раз у нас брут, да еще с претензией на эффективность, то без этого никуда. Причем нам нужны как юзернеймы и пароли отдельно, так и пары кредов, все это распихиваем по отдельным табличкам, объединением в группы для удобства эксплуатации и отдельно храним связку группы с каждым впном, что бы всегда точно знать что с чем брутилось, и все это с учетом гео.
2. Все, что нужно для masscan’а – тут у нас тоже свои особенности. Рассмотрим именно в контексте работы с БД. Масскан обращается через оркестратор к базе и запрашивает список подсетей и портов для сканирования, которые надо выделить в отдельные таблички, дальше надо сделать связку подсетей и портов, и на ее основе сделать запись в таблицу задач для масскана, что бы трекать статус задачи и в случае абуза масскана отправить ее на повторный скан. Конечно же при этом не забывая о привязке к гео. В процессе сканирования найденные адреса аккумулируются и отправляются в базу чанками по $N$ штук, таким образом мы до завершения скана уже можем начинать проверять найденные адреса.
3. Это делает сервис проверки адресов или просто сервис "чека", который берет данные из таблички, куда складываются найденные в масскане адреса, и выясняет "кто-кто в теремочке живет?", а так же базово фильтрует хонипоты. Он же может еще и домены отработать, там достаточно сложная система, о которой в деталях позже. Найденные впны сохраняются отдельно, это намеренная денормализация базы, ибо впнов у нас будет на несколько порядков меньше, чем адресов для чека.
4. Ну и дальше по цепочке - непосредственно брут, дамп, и прочие штуки, там прокси надо хранить, отдельно хранить задачи для повторного чека померших впнов, отдельно расширенный результат дампа, таски там на это все, статусы и комментарии для веб панели по каждому впн, ну и т.д. Просто скажем, что у нас там еще куча таблиц и довольно таки сложная структура БД.
С БД определились.
### 2.3.2. Основные компоненты системы. Базовое описание.
Дальше, разумно будет выделить следующие сервисы:
- Masscan – о нем я уже писал выше. Особенности реализации будут ниже.
- Сервис проверки найденных адресов, a.k.a. чекер – для поиска на них VPN-протоколов и базовой фильтрации ханипотов, а так же повторной проверки мертвых впнов.
- Непосредственно брут.
- Дамп уже сбрученных VPN – согласитесь, что получение первичной информации о сети «из коробки» – очень удобная штука. Поэтому мы интегрировали дампер в процесс. Кроме того дампер позволяет нам имплементировать очень интересные сценарии повторного брута, если юзер не в домене, о чем позже.
- Оркестратор – для того чтобы у нас не получился классический мартышкин квартет придется писать сервис-оркестратор для управления всеми этими штуками. Именно он и будет осуществлять управление всеми сервисами, координировать их взаимодействие как друг с другом, так и с базой данных. Еще нам понадобится веб-панель для управления всей системой и ее бекенд мы тоже налепим на оркестратор.
## 2.4. Выбор стека для бекэнда
Так, ну вроде бы наброски архитектуры сделаны. Матчасть изучена на уровне достаточном для старта разработки. Осталось определиться с языком программирования и стеком используемых технологий.
Казалось бы – чего тут сложного? С++ наиболее очевидный выбор. Особенно для задач брута. Преимущества описывать не буду – они итак всем хорошо известны. Работай я над проектом в одиночку – я бы даже не думал. Однако, работал над проектом я на пару с коллегой, так что пришлось остановиться на стеке, знакомом обоим из нас, корпоративной классике – Java со Spring Boot. По производительности значительно уступает C++ или какому-нибудь Go, но, как показала практика, производительности нам хватило, а вот выбранный стек позволил реализовать систему сильно быстрее. К тому же, при необходимости, наша архитектура позволила бы переписать конкретные сильно чувствительные к нагрузке сервисы на те же C++ без изменения флоу системы, но это не понадобилось.
Что тут сказать? Могу со всей ответственностью заявить следующее: можете смело дать в морду тому, кто скажет, что перейти с Плюсов на жабу – нехуй делать ваще.
Мол если на плюсах писал всю жизнь, то и с жабой справишься на изи вообще. Да я даже на джаве писал, и не мало, но то было давно и без Spring. На уровне хелловорлдов проблем нет, общего у языков много. Да и у экосистемы жабы есть плюшки, которые я бы с удовольствием перетащил в плюсы. Например, написал перед определением класса
@getter, @setter и не нужно засирать код очевидными вещами. Встроенные билдеры из коробки – прямо вау. Много еще чего. НО!!! После плюсов ты чувствуешь, что у тебя отобрали рули. Если проводить аналогию, то это как со старого доброго УАЗика, который ты тюннинговал много лет и только эмблема напоминает о его происхождении ты пересел в сраную Теслу, у которой и руль то – больше для того, чтобы кожаный мешок мог ощущать себя типа водителем. Возможно, нечто подобное испытывает пилот истребителя, которого с СУ-57 пересадили на Арбуз (Aerobus 321 или другой), где 99% работы выполняет автопилот и тебе остается только следить за приборами, да пиздеть со вторым пилотом чтобы не заснуть.Ты ничего не решаешь. Если в плюсах мне понадобится добавить, например, работу с Json, но я не хочу тянуть в проект boost, то существует полно Header-only реализаций всяких ништяков. Просто добавил заголовочный файл и радуйся. В жабе со спрингом – хрен тебе по всей морде. Ты должен попросить
maven/gradle подтянуть нужные зависимости. Она это сделает. Ну подумаешь пару гигов говна вытянет из сети, но всё будет работать. Зато если ты уже набил себе кучу шишек путешествуя по этим граблям, то процесс разработки заметно ускоряется. В Spring так вообще, особенно поначалу, всё казалось черной магией. Проводя аналогию с той же Теслой – это ты осилил их мануал на тысячи страниц и знаешь где в этом ебучем планшете на колесах искать нужную фичу. Тем не менее – это больше субъективное мнение. Втянувшись в процесс я понял, что жабу нужно уметь правильно готовить, а уж всяких вкусностей в ней пруд пруди.Еще рассматривали Go, но поскольку от него плевались оба, то он был фактически сразу отброшен, хотя по перформансу можно было бы немного выиграть.
Python и Node.js помрут от планируемой нагрузки, поэтому их тоже отбросили сразу.
В итоге:
- Core – Java + Spring Boot Framework.
- Masscan service – Plain C.
- Dumper – Python.
- Остальные сервисы – Java + Spring Boot Framework.
На этом небольшое лирическое вступление окончено и пришло время более детально познакомиться с внутренним миром продукта.
---
# Глава 3. Общая система и WorkFlow
Теперь рассмотрим процесс брута в целом и его отдельные этапы. В нашем случае они выглядят следующим образом:
## 3.1. Этап 1: Сканирование
Начинаем мы, как вы можете догадаться, со сканирования диапазонов адресов и портов. Конечно, можно по старинке арендовать готовые абузоустойчивые сервера под масскан и ручками заливать в него диапазоны IP-адресов и порты. Все недостатки такого подхода уже озвучены выше. Так что это не наш случай.
Мы же пишем Продукт для автоматизации всего и вся. Тут можно обозначить следующие требования к сервису:
1. Непрерывность – диапазоны адресов и портов должны сканироваться постоянно. На одном сервере, доказано методом научного тыка, мы можем запускать 3-4 инстанса масскана. Некоторые сервера живут до недели. Некоторые дохнут только успев начать работать. Из этого следует, что для поддержки непрерывности мы должны уметь работать с постоянно отваливающимися серверами, а делается это достаточно просто:
* вносим в код масскана доработки, позволяющие обращаться к серверу и получать задачу на сканирование диапазонов адресов и портов, а также отправлять промежуточные результаты оркестратору для последующей записи в базу;
* в отдельный поток выносим функцию мониторинга состояния сервера. Данная функция по таймеру будет отправлять оркестратору примерно следующую информацию:
* IP-address, с которого идет сканирование;
* Прогресс выполнения текущей задачи в процентах;
* Загрузку памяти и процессора в процентах;
* Количество полученных и выполненных задач;
* Ожидаемое время до завершения задачи;
* Число взятых в работу и успешно завершенных задач.
2. Для деплоя пишем простенький баш-скрипт, которому отдаем на вход список серверов для масскана, с кредами, туда же передаем токен авторизации, количество инстансов на сервер и адрес панели, чтобы наш масскан знал куда ему стучаться за задачами, и регион, в котором он запущен. Всё.
3. Надежное хранение результатов – из коробки masscan пишет результаты в файл на том же сервере, где запущен. Если сервер умрет – данные умрут вместе с ним. Это не есть гуд. Поэтому по мере накопления результатов мы отправляем их на сервер. Это позволяет в случае сбоя потерять только малую часть просканированных адресов. В случае, если задача была выполнена не полностью, мы всегда можем ее перезапустить на новом инстансе.
4. Привязка к заданному региону – реализуем ее, чтобы оркестратор не отдал серверу в какой-нить неметчине американские диапазоны. Таким образом каждый инстанс будет сканить диапы только своего гео.
Рис. 3.1: Упрощенная схема работы Masscan
## 3.2. Этап 2: Проверка (Чек)
Отлично, масскан работает и шлет нам первые адреса. Прежде чем их отдавать на брут – необходимо их проверить на наличие интересующих нас протоколов VPN-ов, и хотя бы частично отфильтровать хонипоты.
Всего получилось выделить несколько этапов проверки:
1. Определение протокола VPN – для этого достаточно просто сделать HTTPS-запрос на найденный адрес (по найденному порту, разумеется), после чего по ключевым словам определяем, страница какого VPN-сервиса тут висит.
2. Сканирование открытых портов – возможна ситуация, когда по какой-то причине мы пропустили этап скана и залили сразу IP-адрес, а также проверить сабдомены, если у нас вместо IP передан домен или URL. Будет сильно медленнее, чем массканом, так что много портов просканить не выйдет.
3. Базовая проверка на honeypot.
4. Контроль геолокации – очевидно, что запросы лучше всего отправлять с того же региона, где находится целевой адрес.
Там будет еще много различных приколов, поэтому было принято вынести всё это хозяйство в отдельный сервис.
### 3.2.1. Балансировка нагрузки
И тут у нас возникает проблема — мы не знаем, на каком железе будут запущены сервисы чека, оно может быть сильнее, слабее... Суть в том, что мы никак не можем узнать, сколько параллельных воркеров, проверяющих адреса, конкретное железо выдержит, так что придется опять сочинять козу на лисапеде.
Решение получилось простое и элегантное – мы можем получить текущий уровень нагрузки ЦПУ сервера и на его основе динамически определять оптимальное число потоков. В таком случае мы можем выставить каждому отдельному сервису в конфигурации границы по числу потоков (например, от 300 до 2000), и дальше по алгоритму система сбалансирует общее число воркеров. Выглядеть это будет примерно так: делаем воркер в отдельном потоке, который будет по таймеру проверять текущую нагрузку на проц, и если она не достигает верхней заданной границы, то увеличиваем число потоков, если превышает ее, то уменьшаем. Детали процесса будут ниже.
### 3.2.2. Обход блокировок
С нагрузкой разобрались. Покупаем десяток-другой серверов и погнали. Смотрим в панель – а туда уже полетели первые результаты проверки. День-два все нормально, а потом хуяк-хуяк, и на наши сервера начинают лететь абузы.
В теории все легально — мы просто сканим GET-запросами URL-ы, но на практике в некоторых регионах, например, в США, с этим возникнут проблемы. Хоть мы и формально только делаем GET-запросы, фактически это малоебущий фактор, и наши сервера автоматом будут лететь абузы.
В Европе с этим все просто — взяли абузоустойчивый сервер и вперед, а вот в Штатах придется попотеть.
Первое, что приходит на ум, — это прокси. Но, как говорится, есть один нюанс: объем трафика, который будут генерировать наши сервисы чека, будет настолько большой, что прокси начнут в нем захлебываться. Дело тут будет даже не сколько в объеме трафика, а сколько в том, что мы будем слать запросы только на потенциально живые адреса, а значит очень большое количество запросов будет обрывать сокет, терять пакеты, падать по таймауту, да и вообще там будет твориться всякая дичь.
В качестве альтернативы можно поднять VPN, но там тоже много нюансов, о чем будет расписано чуть ниже.
Для того, чтобы описать все приключения и подводные камни, возникшие на нашем пути понадобится отдельная серия статей. Поэтому мы это опустим и представим, что на некоторых адресах мы нашли интересующие нас протоколы. Что с ними делать дальше? Очевидно, начинать брут.
Далее предлагаю читателю либо погрузиться в детали реализации сервиса проверки адресов (чека). Либо же сразу перейти к описанию процесса брута и деталям его реализации. Я долго думал как лучше все организовать. Изначально планировалась серия статей, и это должна была быть вторая статья в серии. Но, увы, такой роскоши пока позволить себе не могу. Поэтому решил объединить описание сервисов и детали их реализации в отдельные разделы, дабы не растекаться мыслью по древу.
## 3.3. Внутренности сервиса чека
Углубимся, пожалуй, во внутрянку сервисов чека. Его основная задача — проверка найденных массканом адресов (ну или чек залитых корпоративных доменов). Тут у нас встает дилемма: хотим мы точность или высокую нагрузку? Не знаю, как у дорогого читателя, но при постановке данной задачи у меня в голову сразу приходит «гениальная» идея делать дабл-чек всех адресов через пару прокси от разных вендоров, что бы точно ничего не упустить.
На первый взгляд звучит красиво, но только до момента, как мы начнем на эти несчастные прокси лить хотя бы 200–300 RPS с каждого из 20 одновременно запущенных сервисов
ssl-hello, вкоряченный в масскан), наши прокси под такой нагрузкой начнут натурально, одна за одной, уходить на раннюю пенсию по инвалидности.Забегая вперед, как показывает опыт брута: если бы наши проверяемые адреса нормально отвечали на запросы, то прокси бы прекрасно справились с этой ношей. Но, к сожалению, если мы хотим чекать реально много, да еще и под нужными ГЕО, нам придется повозиться с костылями.
Вообще, отказ от прокси — это очень неприятное решение. Конечно, мы не делаем ничего противозаконного, как минимум в этом сервисе
И да, не рекомендую тратить свое время на использование для этих целей классических VPN — они шифруют трафик, и им, похоже, тоже плохеет от неадекватных ответов с некоторых адресов. У нас дело доходило до того, что некоторые сервера, где был запущен VPN, приходилось ребутать через интерфейс хостера, а на их ЦПУ серверные гномики жарили яичницу.
И этот заеб нужен исключительно ради юсы, и то, что бы получить процентов 10-20 геоблоченых адресов, остальные будут спокойно чекаться с европейских айпи, а брут у нас будет через прокси.
### 3.3.1. Структура чекера
Но вернемся обратно. Наш сервис чека будет иметь простую структуру:
1. Очередь с адресами на чек.
2. Очередь с впнами для проверки их состояния (сюда будут попадать те, которые сервисы брута пометили как мертвые).
3. Воркер, запрашивающий адреса, если адресов в очереди мало, и отправляющий результаты в оркестратор.
4. Динамическое количество воркеров(консумеров), которые берут эти адреса в работу.
5. Воркер, осуществляющий сбор метрик текущего состояния для веб панели.
Число консумеров будем определять динамически, но в заданном диапазоне, т.к. мы можем раскатывать наши сервисы на разные сервера, у которых будет разный конфиг, и угадывать точно нужное число параллельных консумеров весьма проблематично.
Сделать это можно достаточно просто: берем минимальное и максимальное значение диапазона (скажем, 300 и 1500 потоков) и стартуем с минимального числа. Дальше раз в заданное время смотрим, как изменилась нагрузка на ЦПУ. Если ЦПУ нагружен менее чем на условные 97%, то увеличиваем число потоков; если же ЦПУ перегружен, то понижаем.
Естественно, обрубать работающий консумер прямо во время отработки адреса идея херовая (можно потерять прогресс обработки адреса), так что наш балансер нагрузки просто меняет целевое число потоков. А вот сам консумер перед тем, как взять новую задачу, смотрит на текущее число запущенных консумеров, смотрит на целевое, и если текущее число больше целевого, то он самоустраняется. Добавлять консумеры, ясное дело, можно в любой момент без каких-либо проблем.
Отдельно скажу про интеграцию любых сервисов с оркестратором: очевидно, необходимо закрывать все эндпоинты за кредами/токеном, чтобы не было возможности вытащить из базы оркестратора никакую информацию.
### 3.3.2. Логика проверки
Теперь вернемся к чеку. Вот у нас на фоне происходит балансировка числа параллельных консумеров, воркер запрашивает и получает порцию адресов на чек по своему ГЕО из оркестратора, кладет их в очередь, после чего наши консумеры набрасываются на эту очередь и растаскивают адреса на чек.
Но как проводить сам чек?
Во-первых, при старте нужно запросить включенные протоколы из оркестратора. Мало ли, мы хотим искать только что-то конкретное. Дальше по каждому полученному протоколу нужно сформировать типовые пути, например
remote/login?lang=en или remote/login.Вообще, ожидаемое поведение от сервера VPN — это редирект на такой путь, но если вдруг его не будет, надо прочекать их отдельно.
А вот если наш адрес — это не IP, а корп. домен, то на каждый путь не помешало бы сформировать вариант со всеми сабдоменами, характерными для включенных протоколов (например
vpn2.corpdomain.com/somepath). Если же наш адрес — это IP, и у этого IP не задан порт (т.е. мы смотрим на дефолтный 443, но он явно не указан), надо бы еще и посканить порты на этом IP. Так же эффективно, как массканом, мы тут не пройдемся, но пару десятков самых популярных портов все же можно глянуть. Разумеется, что в нормальном флоу все адреса с масскана будут идти уже с портами, но мы же не ограничены только насканенными адресами, можем залить и ручками.Подобная проверка всех путей, портов и сабдоменов очень сильно замедляет чек, потому что вместо одного запроса и, возможно, редиректа нужно сделать больше десятка запросов с путями и сабдоменами (в случае корп. домена) каждого протокола, если редирект не произошел или мы не нашли то, что нам интересно. Ясен пень подобное поведение должно быть опциональным и отключаемым, если вдруг у нас не хватает ресурсов и мы хотим чекать URL-ы быстрее.
Дальшe мы делаем обычный GET-запрос на адрес и смотрим по ключевым словам, есть ли там что-то интересное. Если мы что-то нашли, то тут же надо проверить, не на ханипот ли мы нарвались.
Если хоник сделан, например, чисто под циску, то мы хрен как это разгадаем, но такое бывает крайне редко. Как правило, хоники запускают одновременно на кучу всего, и чтобы зацепить подобные чекеры, в них суется огромное количество ключевых слов, не относящихся к нашему протоколу. Как вы могли догадаться, мы просто собираем гигантский список ключевых слов, которые часто встречаются в подобных хониках, но не встречаются на странице логина нашего протокола, и делаем дополнительную проверку в HTML, в куках, возможно, делаем пару запросов по другим path. Если есть пересечение — помечаем адрес как хоник, больше мы его трогать не будем. И чуть не забыл еще один важный нюанс: некоторые VPN могут быть выключены или иметь аутентификацию через внешние сервисы, о чем они великодушно нам могут сообщить на главной странице. Так что это тоже нужно сохранить в панель, чтобы мы не долбились в закрытую дверь.
### 3.3.3. Обработка результатов
Но представим, что мы нашли нормальную веб-панель VPN, убедились, что это не хоник (хотя бы на первый взгляд). Что дальше?
Не будет же каждый поток спамить в оркестратор найденными протоколами, да и пометить адрес как пустой тоже надо, если на нем нифига не было. Когда наших потоков будет пара тысяч, то с таким флоу даже пара сервисов смогут положить наш оркестратор на лопатки. Значит, нам нужна еще одна очередь, куда мы будем совать прочеканные адреса с их статусом, и, если повезло, найденным протоколом. А дальше, как мы засунули туда результат чека адреса, нам нужен отдельный воркер, который раз в $N$ минут будет вытаскивать эти адреса и батчем отправлять их в панель. В целом, ничего сложного.
Сделаем еще одну необязательную, но очень удобную фичу. Работая с системой через веб-панель, очень хотелось бы видеть текущий статус и метрики по каждому запущенному сервису. Так что добавим в наш чекер еще один воркер, который будет отправлять в оркестратор раз в заданный промежуток времени такую инфу, как: количество адресов в очереди, общее число потоков, RPS за минуту, среднее время реквеста, загрузку ЦПУ и RAM, таймстемп, ну и что-нибудь еще. В целом, отправлять можно все, что мы хотели бы видеть в веб-панели. Дальше оркестратор сможет корректно отрисовать текущий статус нашего сервиса, и если он перестанет выходить на связь, то мы хотя бы это увидим.
Не забудем еще о завершении работы нашего сервиса. Иногда нужно либо его перезапустить, либо установить обновленную версию, а значит текущий инстанс надо бы выключить. На этот случай внутри самого сервиса будет разумно перехватить завершение работы и отправить в панель все завершенные и незавершенные задачи, чтобы они не потерялись (конечно же, если перехватить завершение удастся).
Рис. 3.2: Упрощенная схема работы сервиса чека
Рис. 3.3: Схема сервиса чека вместе с массканом
## 3.4. Этап 3: Брутфорс
Для брута нам нужны наборы логинов и паролей, как отдельно, так и парами, здесь и далее – креды, по которым мы будем работать.
Допустим у нас есть множество логинов и множество паролей. Для каждого конкретного VPN мы хотим перебрать всевозможные комбинации – так да, это опять Декартово произведение множеств. Остаются еще пары кредов, например
itsupport:itsupport!1. В общем все те, что нам нужно проверять парами, не делая пересечения с другими кредами.Некоторые креды будут только под конкретные регионы — нет никакого смысла гонять топ немецких паролей по Италии или Штатам.
Ну и понятное дело мы хотим запоминать, какие креды с какими VPN-ами мы уже проверяли.
Для решения этой задачи организовали креды в группы, например суем туда 2 тысячи самых типовых имен для гео и какие-нибудь вариации "Welcome!1" паролей, и отмечаем в базе, какие VPN с какими группами кредов уже проверялись.
Также создадим отдельные сервисы брута, и туда будет передаваться найденный VPN и группа кредов, которую мы с ним проверяем.
Таким образом, мы сильно упростим поиск еще не просканенных кредов под конкретный VPN:
- У нас в базе уже есть VPN.
- Мы добавляем туда сущность «группа кредов» с или без привязки к региону.
- В отдельных табличках привязываем к группе имена, пароли и пары кредов.
- Делаем отдельную табличку для связи many-to-many между VPN-ами и группами кредов, добавляем туда статус (например, «не брутилось», «в бруте» и т.д.).
Это простое решение целиком решает геморрой с менеджментом кредов для десятков одновременно запущенных сервисов брута и сотен тысяч VPN-ов. Добавим сюда еще менеджмент числа потоков, завязанный на загрузку ЦПУ (аналогично сервисам чека), и не забываем, что брутить надо из того Региона, к которому относится целевая VPN, а значит и прокси нужно использовать из этого же Региона.
Для упрощения процесса сделали возможность залить все прокси с нужными Регионами в оркестратор и делегировать все остальное сервисам брута. Оставив себе только необходимость загружать новые прокси время от времени.
Часть VPN-ов начнет отваливаться во время брута. Почему? Да хрен его знает, товарищ майор. Может померли от передоза от нагрузки или по естественным причинам. А может их включают только в рабочее время – такое иногда встречается. В общем сразу мы его выбрасывать на свалку истории не будем, а отправим на перепроверку. Вдруг он оживет.
Было бы неплохо через оркестратор отправлять их обратно в чек, чтобы проверить, была ли это временная проблема или они окончательно умерли. Для сервиса брута еще важно имплементировать поддержку прокси. К счастью, таких проблем с мусорными адресами, как в сервисах чека, у нас тут нет, так что и изворачиваться не придется. Все что нужно сделать, это просто подтягивать актуальные прокси из оркестратора раз в $N$ минут, проверять их на жизнеспособность, ну и в случае смерти отсылать мертвые прокси обратно в оркестратор, чтобы они были удалены из выдачи.
Сами прокси мы можем выдавать воркерам по Round Robin, ну или просто рандомно — разницы особой нет. Главное, что каждый запрос с новой вариацией кредов будет лететь на VPN с нового IP-адреса, что предотвратит блэклист наших IP целевыми VPN-ами.
### 3.4.1. Внутренности сервиса брута
Берясь за сами сервисы брута, может показаться, будто тут у нас будет все то же самое: балансируем потоки, вытаскиваем VPN из оркестратора, суем в очередь, консумеры их начинают дербанить ну и так далее. Общего много, но есть и небольшие нюансы.
Во-первых, далеко не все протоколы можно брутить просто по вебу. Иногда туда вкорячивают защиту, и просто так сделать POST-запрос с кредами уже не получится. Выхода тут два:
1. Вкорячивать веб-драйвер, что сожрет буквально тонну ресурсов.
2. Или же реверсить клиенты и смотреть, как у них в потрохах идет аутентификация.
Очевидно, что мы пойдем по второму пути, но и тут не всегда все гладко. Некоторые хитрецы в клиент буквально встраивают веб-браузер, так что вытащить оттуда какой-то внутренний API не выйдет. Тут остается только веб-драйвер, что я оставлю для ценителей особого рода извращений, ибо с веб-драйвером нормального RPS у нас точно не будет. Но на большинстве протоколов все будет гораздо проще, да настолько, что даже обезьянка с GPT справится.
#### 3.4.1.1. Логика очередей и прокси
Есть еще одна важная деталь, на которую нам надо обратить внимание: лучше всего один и тот же VPN не долбить без остановки, а сделать кулдаун-период (минуту-две) и закидывать этот VPN обратно в очередь. Таким образом, при насыщении очереди достаточным количеством VPN-ов, брут будет идти без остановки, но вот один конкретный VPN будет проверяться только, как минимум, раз в пару минут.
Добавим к этому пару тысяч прокси, которые будут выдергиваться для каждого реквеста в случайном порядке, и получим ничем не примечательную активность: раз в пару минут с какого-то айпишника из ГЕО VPN прилетает запрос на аутентификацию, причем практически каждый раз с нового.
Естественно, эти прокси нужно также сгружать с оркестратора, их надо проверять на валидность как на старте сервиса, так и раз минут в 10. Еще обязательно на все запросы нужен ретрай, чтобы в случае отвала одного прокси, запрос повторно делался с другого. Сам же прокси, который отвалился, можно поместить в отдельный пул, в котором специальный воркер будет проверять такие прокси на валидность раз в минуту. Помершие же прокси мы можем возвращать обратно в оркестратор, чтобы он их соответствующим образом пометил и никаким сервисам больше не давал. Так мы интегрируем проверку наших проксей прямо в сервис брута, который по ходу дела будет избавляться от почивших проксей и подтягивать с оркестратора новые, тем самым обеспечивая беспрерывность процесса.
#### 3.4.1.2. Процесс брута по шагам
Если кратко описать процесс, то он получается следующим:
1. Сервис брута на запуске запрашивает прокси, потом их все проверяет.
2. Как только он их проверит, запускаются воркеры:
* Воркер для повторной проверки прокси (раз в 10 мин).
* Воркер, который будет пулить прокси с оркестратора и отправлять ему мертвые.
* Воркер, выдергивающий VPN-ы с оркестратора.
* Воркер, отсылающий в оркестратор результат.
3. Поднимаются консумеры с балансировкой потоков (все как в чекере).
4. Консумеры берут VPN-ы, при необходимости выдергивают домен аутентификации (
LocalDomain, например) с веба VPN-а, сохраняют его и начинают брутить. Можно делать пару-тройку запросов подряд, можно только один.В момент, когда консумер взял VPN, ему для начала брута нужно взять с оркестратора креды, которые будут по этому VPN гоняться. В оркестратор креды заливаются группами, и оркестратор самостоятельно подбирает группу кредов с максимальным приоритетом, которая еще не гонялась по конкретному VPN. И так по каждому.
Соответственно, вместе с VPN на брут передается и ID этой группы. Когда этот VPN берется в работу, консумер пытается найти группу кредов с таким ID в локальной мапе. Если ее там нет, то он один раз запрашивает эту группу с панели и кладет в мапу. В дальнейшем для остальных VPN-ов, которые будут поступать на брут с этой группой кредов, запросы в оркестратор уже делать будет не надо, т.к. группа будет храниться в памяти брута.
Сделав несколько запросов (ну или один, в зависимости от настроек), консумер возвращает VPN в очередь и берет следующий. Возвращает он его со временем, когда данный VPN будет доступен для следующей итерации. Например, «текущее время + 2 минуты» — это можно настроить для каждого протокола отдельно.
Если же консумер достал VPN, который еще не готов к работе, то он просто кладет его обратно, ждет секунду (это охренеть как важный момент, без него консумеры будут как бешеные крутиться по очереди, сжирая весь ресурс ЦПУ), и берет следующий. И так, пока не найдет готовый к новой итерации брута VPN.
Кроме этого, он подаст сигнал воркеру, чтобы тот запросил еще дополнительные VPN-ы с панели. Вообще, такое будет происходить только на старте брута. Как только он наберет рабочее число VPN-ов, он стабилизируется. Будет несколько десятков VPN-ов, ожидающих в очереди и доступных для брута. Это сразу будет видно в метриках, которые будут выгружаться на веб-панель.
Рис. 3.4: Упрощенная схема сервиса брута
Рис. 3.5: Схема, включающая в себя масскан, чек и брут
## 3.5 Этап 4: Дамп и пост-эксплуатация
Двигаясь дальше, когда мы получим первый результат, встает вопрос — хватаем ли мы эту сетку руками, или же сбрученных VPN-ов настолько много, что рук просто не хватит?
Если сеток падает достаточно много, но доменных аккаунтов там единицы, то прежде чем сажать «масленка» разгребать их за миску риса, мы можем попробовать подключить дамп.
На рынке уже есть готовые решения
1. Сервис запрашивает сбрученный VPN на дамп тех протоколов, с которыми он умеет работать.
2. Мы ему такой выдаем, меняя у выданного VPN, естественно, статус на какой-нибудь «в дампе».
3. После чего принимаем результаты. Часть результатов, типа имени домена или того, является ли аккаунт доменным, мы сохраняем в базу. Остальное же загружаем архивом.
При желании можно будет получить подробный отчет о сетке и даже базовую проверку на самые ходовые уязвимости, типа Zerologon, в зависимости от реализации дампера. В целом, тут даже можно какой-нибудь фреймворк для автопентеста на основе LLM прикрутить, было бы желание.
А вот дальше начинаются всякие интересные фишки. Например, если аккаунт не доменный, то мы можем собрать имена юзеров из Kerberos и попробовать побрутить по ним. Для этого нам нужно будет собрать побольше паролей и выделить их в специальную группу, которая будет применяться только для работы с подобным повторным брутом.
Вообще, в бруте VPN-ов есть серьезная проблема — нужно угадать не только пароль, но и логин, что безумно увеличивает число комбинаций для перебора. Если мы вытащили хотя бы часть имен из Kerberos, то мы можем побрутить по этим именам, на порядки повышая наши шансы успешного брута и, самое главное, получения доменного аккаунта в сети. Не забывайте: мы брутим VPN, а не какой-то DC — локаута пользователей в AD не будет, да и никакие средства мониторинга внутренней сети тревогу от такого бить не начнут, мы же не внутри брутим
Бывают еще ситуации, когда мы заходим в сеть, а там ничего. Вот буквально пустая подсеть, в которой ничего не просканивается, да и других подсетей нет. Это может быть связано с тем, что некоторых пользователей, например
test и подобных, кидают в «песочницу», где им ничего не доступно. В таком кейсе мы можем попробовать сбрутить данный VPN с другими кредами, и будет очень удобно, если наш дампер сам направит подобный VPN на дальнейший брут.## 3.6 Управление системой
Мы тут обсудили довольно объемный флоу, и будет нелишним добавить в UI оркестратора еще и какой-никакой менеджмент VPN-ов: сбрученных, сдампленных, да вообще любых. Просто чтобы можно было ими удобно управлять, когда со временем их там будет пара тысяч: ставить им статусы (например, «в работе» или «продан»), оставлять комментарии, например ссылки на Zoom, ну и так далее.
Рис. 3.6: Схема интеграция дампера без деталей.
Еще неплохо бы на вебе мониторить текущее состояние сервисов чека, масскана, брута: загрузка ЦПУ, RPS и всякое такое, чтобы вовремя видеть проблемы с перформансом или откисшие прокси. Во время работы такого массивного флоу что-то обязательно пойдет не так: какие-то сервера будут абузиться, а за какие-то просто оплата просрочится.
Так что нам нужен механизм восстановления системы в условиях периодически откисающих серверов, у которых будут оставаться таски в работе. Тут решение тоже достаточно простое: если у нас упало несколько серверов брута, то гасим сервера (в них бы еще добавить механизм возвращения задач на шатдауне, но и без этого можно обойтись), и, как только сервера уходят в оффлайн, просто сбрасываем статус всем таскам со статусом «в работе». Очевидно, что для такого флоу сервисы брута должны будут постоянно отсылать в оркестратор свое текущее состояние, чтобы в случае перезапуска можно было бы не начинать брут сначала, а продолжить его с момента остановки. Реализуется это тоже парой эндпоинтов.
---
# Глава 4. Итог
Рис. 4.1: Вот такая система у нас получилась по итогу. В схеме упущены многие детали и части флоу для упрощения восприятия.
Хочется сказать, что рассмотрев проблему брута с инженерной точки зрения, мы можем адаптироваться к меняющемуся рельефу рынка корп. доступов и выйти на объем брута, попросту невозможный без специальных технических решений.
Система, подобная описанной выше, способна осуществлять беспрерывный брут сразу по нескольким ГЕО, работая с наборами кредов, характерных именно для этих ГЕО, начиная с этапа скана и заканчивая дампом сбрученных сеток, автоматизируя весь пайплайн. Очевидно, что брут завайбкодеными кустарными скриптами (или, прости господи, MSF-ом) рано или поздно полностью уступит рынок сложным высоконагруженным системам.
Немного скринов с панели добавлю
```
Вложения
Последнее редактирование:
