Создание уникального вредоносного ПО требует больших ресурсов, поэтому многие хакерские группировки используют в своих атаках массовое, часто публично доступное ВПО. Широкое использование неизбежно приводит к тому, что такой инструмент попадает на радары антивирусных компаний, а его эффективность снижается.
Для решения этой проблемы хакеры используют техники упаковки, шифрования и мутации кода. Такие техники часто реализуют отдельные инструменты — «крипторы» (crypters) или просто «пакеры». В этой статье на примере банковского трояна RTM мы рассмотрим, какие «пакеры» могут использовать злоумышленники и как эти «пакеры» осложняют обнаружение ВПО.
Полная версия данного исследования доступна по ссылке.
Packer-as-a-service
Хакерская группа, стоящая за распространением RTM, до конца 2020 года регулярно проводила массовые фишинговые рассылки с вредоносными вложениями. Этот процесс, по всей видимости, происходил автоматически.
Каждое такое вложение содержало существенно отличающиеся друг от друга файлы, при этом итоговая полезная нагрузка практически не менялась.
Подобная особенность — естественное следствие применения «крипторов». Первоначально группа, стоящая за RTM, использовала свой собственный уникальный «криптор», однако в течение 2020 года дважды его сменила.
При исследовании по-новому упакованных образцов нам удалось обнаружить множество другого ВПО, которое было защищено аналогичным образом. Пересечения с другими вредоносами с учетом автоматизации процесса упаковки, на наш взгляд, позволяют говорить об использовании злоумышленниками модели packer-as-a-service. В этой модели упаковка вредоносных файлов делегируется специальному сервису, которым управляет третья сторона. Доступ к таким сервисам часто продается на хакерских форумах.
Rex3Packer
Первое использование этого пакера группой RTM, которое нам удалось обнаружить, относится к ноябрю 2019 года. Активное же его применение, по нашим данным, приходится на период апрель—май 2020 года.
Нам не удалось связать этот упаковщик с каким-либо из ранее описанных публично, поэтому мы дали ему свое название по трем особенностям его устройства: наличию рекурсии (recursion), реверса битов (reverse) и рефлективной загрузки PE-файлов (reflection) — Rex3Packer.
Алгоритм распаковки
Общий алгоритм извлечения полезной нагрузки выглядит так:
С помощью VirtualAlloc выделяется заранее определенное количество памяти с правами на чтение, запись и исполнение.
В выделенный буфер копируется содержимое образа текущего процесса в памяти (в частности, секция .text).
Управление передается на функцию внутри буфера.
Вычисляется разница между положением одних и тех же данных в буфере и в образе PE-файла (разность между адресами в буфере и виртуальными адресами в образе). Эта разность заносится в регистр ebx. Все обращения к виртуальным адресам в коде проиндексированы содержимым этого регистра. За счет этого везде, где это необходимо, к адресам из PE-образа добавляется поправка, которая позволяет получить соответствующий адрес в буфере.
Выделяется еще один буфер под упакованные данные.
Через вызов VirtualProtect устанавливаются права RWX на весь регион памяти с образом PE-файла.
Упакованные данные копируются в свой буфер.
Происходит декодирование упакованных данных.
Регион памяти с образом PE заполняется нулевыми байтами.
Декодированные данные представляют собой исполняемый файл — PE-нагрузку. Эта полезная нагрузка рефлективно загружается на место исходного PE-образа, а управление передается на ее точку входа.
Отдельный интерес представляет специфический алгоритм декодирования упакованных данных. В данном случае некорректно говорить об упаковке как о сжатии: алгоритм устроен так, что размер упакованных данных всегда больше размера исходных.
Непосредственно упакованным данным предшествует заголовок размером 16 байт, который содержит 4 поля по 4 байта:
размер самого заголовка,
размер исходных данных (PE-нагрузки),
позиция в исходных данных (*), по которой происходит их разделение,
режим кодирования (1, 2, либо 4).
Декодирование выполняется следующим образом:
Внутри каждого байта выполняется реверс порядка битов (к примеру, 10011000 становится 00011001).
В зависимости от режима кодирования (1, 2, 4), данные разбиваются на блоки размером N = 9, 5, либо 3 байта соответственно. Результат декодирования блока — это (N – 1) байт (то есть 8, 4, или 2).
В первых N-1 байтах блока отсутствует часть битов: их значения всегда равны нулю. Чтобы восстановить оригинальные байты, с помощью масок вида 00000001, 00010001 или 01010101 из последнего байта блока извлекаются недостающие биты. При этом для каждого следующего байта маска сдвигается. То есть последний байт блока фактически составлен из объединенных логической операцией OR битов, которые извлечены из предыдущих байтов.
Например, в режиме 4 последний байт состоит из четных битов первого байта блока и нечетных битов второго байта блока. В результате возврата этих битов в первый и второй байты получается оригинальная последовательность из двух байт.
4. После операции по восстановлению битов во всех блоках полученные данные представляют собой исходный PE-файл, который был разделен по позиции (*) на две части. Эти части, в свою очередь, были переставлены между собой. Обратная перестановка с учетом значения (*) позволяет получить оригинальный файл.
Обфускация
Чтобы усложнить анализ кода, в пакере применяется различного рода запутывание:
В промежутках между исполнением полезных команд делаются вызовы различных функций WinAPI. Их результаты сохраняются, но не используются, а сами функции выбираются так, чтобы не влиять на работу программы.
Характерная особенность данного пакера — наличие циклов (не выполняющих полезных операций), которые реализуются через рекурсивную функцию.
Для дополнительного запутывания в исполняемый файл добавляется несколько десятков случайно сгенерированных функций. Они могут ссылаться друг на друга, но в процессе работы ни одна из них не получает управления.
Использование
Кроме экземпляров RTM, мы обнаружили использование Rex3Packer для упаковки различного ВПО, в основном из стран СНГ.
Семейство ВПО | SHA256 |
6e9c9b72d1bdb993184c7aa05d961e706a57b3becf151ca4f883a80a07fdd955 | |
8d44fdbedd0ec9ae59fad78bdb12d15d6903470eb1046b45c227193b233adda6 | |
3be91458baa365febafb6b33283b9e1d7e53291de9fec9d3050cd32d98b7a039 | |
9b6af2502547bbf9a64ccfb8889ee25566322da38e9e0ccb86b0e6131a67df1e | |
d1060835793f01d1e137ad92e4e38ef2596f20b26da3d12abcc8372158764a8f | |
18cc92453936d1267e790c489c419802403bb9544275b4a18f3472d2fe6f5dea |
Также мы отметили использование пакера для упаковки экземпляров ВПО из семейств Nemty, Pony, Amadey.
HellowinPacker
В мае 2020 группа RTM переключилась на использование нового упаковщика — HellowinPacker, который продолжала активно использовать до начала 2021 года. Ключевой особенностью этого пакера является два уровня мутации кода. Первый из них существенно меняет структуру кода распаковки, делая различные образцы не похожими друг на друга.
Второй уровень меняет лишь отдельные детали при неизменной в целом структуре кода. При этом изменения главным образом затрагивают ассемблерные инструкции и не влияющие на работу программы константы. В результате в декомпилированном виде код выглядит практически идентичным.
Так же, как и в случае с Rex3Packer, мы имеем дело с массовым использованием HellowinPacker для упаковки различного ВПО. При этом вредоносное ПО из одного семейства, как правило, имеет в упакованном виде одну и ту же структуру. Это можно пронаблюдать, по крайней мере, на протяжении некоторого времени — затем структура может измениться.
Алгоритм распаковки
Одни из первых действий, которые встречаются во всех упакованных файлах — это попытки открыть ключ реестра HKEY_CLASSES_ROOT\Interface\{b196b287-bab4-101a-b69c-00aa00341d07} (регистр символов в конкретном случае может отличаться) и запросить в нем значение по умолчанию (Default). От успешности этих операций в некоторых модификациях генерируемого кода зависит корректное продолжение работы программы.
GUID интерфейса в разных случаях также может отличаться.
Дальнейший код некоторым образом получает адрес, по которому располагается блок зашифрованных данных.
Этот блок начинается с четырехбайтного числа, которое хранит размер исходных данных (тех, которые будут получены после декодирования). Вызовом VirtualAlloc под расшифрованные данные выделяется блок памяти нужного размера с правами RWX. В выделенную память блоками по X байт копируются зашифрованные данные. При этом в оригинальном файле между этими блоками располагаются пропуски длиной Y байт.
Затем происходит процесс дешифровки блоками по 4 байта:
очередной блок интерпретируется как целое число (DWORD),
к его значению прибавляется индекс первого байта в блоке,
выполняется операция xor между полученным значением и суммой индекса и фиксированного ключа, числа Z.
Обфускация
Как и в случае с Rex3Packer, в экземплярах с HellowinPacker встречаются вызовы функций WinAPI, не относящихся к основной логике программы. Однако в данном случае они используются скорее как способ затруднить поведенческий анализ и детектирование в песочницах. В пользу этого предположения говорит то, что в большинстве случаев разнообразные функции вызываются подряд в самом начале программы.
Дополнительным эффектом от такого использования WinAPI становится невозможность детектирования по списку импортируемых функций и imphash.
При работе с различными числовыми значениями часто встречается некоторая арифметическая обфускация: необходимые константы представляются в виде суммы или разности других констант (в определенных случаях равной нулю). При этом для получения констант могут быть использованы и вызовы функций WinAPI, дающие предсказуемый результат (например, 0 в случае неудачи).
Использование
HellowinPacker существует по крайней мере с 2014 года. За это время он был использован в различном массовом вредоносном ПО. Вот лишь несколько примеров:
Семейство ВПО | SHA256 |
1e8b814a4bd850fc21690a66159a742bfcec212ccab3c3153a2c54c88c83ed9d | |
44ede6e1b9be1c013f13d82645f7a9cff7d92b267778f19b46aa5c1f7fa3c10b | |
f5dfbb67b582a58e86db314cc99924502d52ccc306a646da25f5f2529b7bff16 | |
54ff90a4b9d4f6bb2808476983c1a902d7d20fc0348a61c79ee2a9e123054cce | |
c2482679c665dbec35164aba7554000817139035dc12efc9e936790ca49e7854 |
Заключение
Пример с использованием крипторов позволяет проиллюстрировать разделение обязанностей в хакерской среде, особенно в сфере массового ВПО. Совершенно не связанные друг с другом хакеры могут заниматься разработкой вредоносной нагрузки, ее защитой от антивирусов («криптом») и доставкой конечному пользователю. При этом каждый элемент в этой цепочке может быть предоставлен как сервис. Такой подход снижает порог входа для технически неподготовленных киберпреступников: для проведения массовой атаки достаточно обладать лишь необходимой суммой денег на оплату всех сервисов.
Описанные нами упаковщики, конечно же, далеко не единственные на рынке. При этом они хорошо демонстрируют общие свойства подобных инструментов: в результате их работы получается исполняемый файл с обфусцированным полиморфным кодом распаковщика и шифрованной полезной нагрузкой. Мутации в коде и использование одних и тех же крипторов делают практически невозможным статическое детектирование полезной нагрузки. Однако, поскольку эта нагрузка так или иначе расшифровывается в память и начинает свою вредоносную деятельность, поведенческий анализ с использованием песочниц (таких, как PT Sandbox) позволяет обнаруживать ВПО и выносить точные вердикты даже для упакованных файлов. Следует также отметить, что упаковщики никак не влияют на взаимодействие вредоносов с управляющими серверами. Это дает возможность определять присутствие ВПО в сети, используя инструменты анализа трафика — такие, как PT Network Attack Discovery.
Автор: Алексей Захаров
X-Oleg
Отличная статья спасибо, познавательная, но хочу добавить пару моментов:
Мало кто это понимает, но если мы говорим про серьёзную малварь, не то-что делают скрипт-кидди, то цель крипторов/пакеров немного поменялась, если раньше крипторы использовались именно для обхода антивирусов, например как дело происходило лет наверное десять назад:
Брался уже детектируемый зверек, накрывался протектором и вау-круто, он не детектится...)))
Что происходит сейчас?
Да, статический детект и какую-то эмуляцию кода так можно сбить, но в момент запуска из-под думаю любого антивируса, пусть даже дефендера, что у нас будет происходить:
1. Все антивирусы способны выявить факт распаковки и запуска зверька в памяти.
2. Облако, да это действенное оружие против таких вирусов, а проводил тесты, даже суток не проходит, а иногда даже за пару часов, такого типа вирусы попадают в детект.
3. Детект по поведению так не обойти, но про это сказано в этой статье.)
Так зачем крипторы нужны сейчас?
Всё очень просто, для усложнения исследования и усложнения детекта уже чистых зверьков.
Вот пример, есть у вас чистый зверек, цель именно усложнить детект.
Для этого пакуется зверек, после распаковки в памяти, он может по максимому морфить свой код перед запуском.
Это всё усложнит детекты следующих образцов в облаке, более-того сам криптованный зверек не обязательно паковать в файл, можно например «спрятать» его в картинку и качать с какого-то легального обменника и т.д., что ещё более усложнит детекты чистого файла, т.к. каждый следующий образец будет почти уникальным.)
Если интересно подробнее, как-то делал статьи по крипторам и не только:https://xss.is/threads/37420/
Mur81
Сколько лет уже существую такие вещи как Software Restriction Policies и AppLocker, но корпоративный сегмент до сих пор приходит в ужас от вложенного в письмо заархивированного exe'шника.