• XSS.stack #1 – первый литературный журнал от юзеров форума

Поиграй с Рансомом

yashechka

Генератор контента.Фанат Ильфака и Рикардо Нарвахи
Эксперт
Регистрация
24.11.2012
Сообщения
2 344
Реакции
3 563
Кампании PLAY Ransomware (он же PlayCrypt) активны как минимум с середины июля 2022 года. На данный момент на VirusTotal загружено до пяти заметок о выкупе. В середине августа 2022 года было объявлено о первом публичном деле о программе-вымогателе PLAY, когда журналист обнаружил, что аргентинская судебная система Кордовы стала их жертвой.

Известно, что операторы используют обычные тактики охоты на крупную дичь (BGH), такие как SystemBC RAT для персистентности и Cobalt Strike для тактики посткомпрометации. Также известно, что они используют пользовательские сценарии PowerShell и AdFind для перечисления, WinPEAS для повышения привилегий и RDP или SMB для бокового перемещения внутри целевой сети.

Группа добавляет ".play" к зашифрованным файлам, а ее примечание о выкупе включает только слово "PLAY" и адрес электронной почты для связи с злоумышленниками. Известно, что хакеры извлекают файлы с помощью WinSCP, но не известно, что у них есть сайт утечки данных Tor, как во многих других кампаниях вымогателей BGH.

Огромное спасибо человеку Уиллу Томасу за эту информацию!

Обзор

Это мой анализ PLAY Ransomware. Я сосредоточусь исключительно на его функциях антианализа и шифрования. Есть несколько других функций, таких как внедрение DLL и работа в сети, которые не будут рассматриваться в этом анализе.

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

Вредоносное ПО использует универсальную гибридную криптосистему RSA-AES для шифрования файлов. Скорость выполнения PLAY довольно средняя, поскольку она использует алгоритм обхода в глубину для итерации по файловой системе. Несмотря на запуск отдельного потока для шифрования каждого файла, такой рекурсивный обход значительно снижает его производительность.

IOCS

Анализируемый образец представляет собой 32-битный исполняемый файл Windows.

MD5: 223eff1610b432a1f1aa06c60bd7b9a6

SHA256: 006ae41910887f0811a3ba2868ef9576bbd265216554850112319af878f06e55

Сэмпл: MalwareBazaar

1662368085099.png



Записка с требованием выкупа

Содержимое примечания о выкупе по умолчанию хранится в виде закодированной строки в исполняемом файле PLAY, которая содержит строку "PLAY", а также адрес электронной почты, по которому жертва может связаться с злоумышленником.

Имя файла PLAY с требованием выкупа — "ReadMe.txt".

1662368105795.png


Антианализ

Антианализ: возвратно-ориентированное программирование


Открыв исполняемый файл в IDA, мы видим, что большая часть ассемблерного кода не имеет смысла и не слишком осмысленна. Пример можно увидеть из функции WinMain, где нет четкого оператора return с мусорными байтами, всплывающими среди корректного кода.

1662368145050.png


Как показано в дизассемблированном коде выше, поток управления в WinMain вызывает sub_4142F5, и по возвращении извлекается edi, и мы сталкиваемся с байтами мусора по адресу 0x4142F2. В результате IDA не может правильно декомпилировать этот код.

1662368156146.png


Изучая sub_4142F5, мы видим, что значение, хранящееся в указателе стека, немедленно добавляется к 0x35 перед выполнением инструкции retn.

Мы знаем, что инструкция call в основном содержит две атомарные инструкции, одна из которых помещает адрес следующей инструкции (после инструкции call) в стек, а другая выполняет переход к вызываемой подпрограмме. Когда код входит в sub_4142F5, адрес возврата (в данном случае это 0x4142F1) сохраняется в указателе стека поверх стека.

Подпрограмма добавляет к этому 0x35, изменяя адрес возврата на 0x414326, и возвращает для перехода на него.

Зная это, мы можем прокрутить вниз и попытаться разобрать байты по адресу 0x414326, чтобы получить следующую часть кода WinMain.

1662368176781.png


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

Мы также можем быстро увидеть, что по адресу 0x41433A находится еще одна инструкция вызова, за которой следуют несколько мусорных байтов. Это означает, что обфускация происходит несколько раз в коде.

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

Чтобы отсканировать весь этот запутанный код, я использую 3 разных (но очень похожих) регулярных выражения в IDAPython, чтобы найти и исправить их. Вы можете найти мой скрипт исправления здесь (https://github.com/cdong1012/IDAPython-Malware-Scripts/blob/master/PLAY/script.py).

После исправления код WinMain выглядит примерно так.

1662368203004.png


Теперь мы успешно расшифровали код, получили осмысленную инструкцию вызова sub_415110 и правильный оператор возврата в декомпилированном коде!

Антианализ: мусорный код


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

1662368224912.png


1662368268521.png


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

Единственное решение, которое у меня есть для этого, — мысленно игнорировать их во время анализа.

Анти-анализ: хеширование API


Подобно большинству современных программ-вымогателей, PLAY скрывает свой вызов API с помощью хеширования имени API. Функция разрешения API принимает целевой хэш и адрес DLL.

Она просматривает таблицу экспорта DLL, чтобы получить имя экспорта. Для каждого имени API вредоносная программа вызывает sub_40F580 с именем в качестве параметра и добавляет к результату 0x4E986790 для формирования окончательного хэша. Этот хэш сравнивается с целевым хешем, и если они совпадают, возвращается адрес API.

1662368295338.png


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

1662368310559.png


Отсюда я разработал сценарий IDAPython для автоматического разрешения всех API-интерфейсов, которые использует вредоносное ПО, которые вы можете найти здесь (https://github.com/cdong1012/IDAPython-Malware-Scripts/blob/master/PLAY/API_resolve.py).

1662368324007.png


Антианализ: шифрование строк


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

1662368336626.png


Статический анализ кода


Аргументы командной строки

PLAY может работать как с аргументами командной строки, так и без них.

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

1662368359993.png


1662368373284.png


Инициализация криптографии

Перед шифрованием PLAY инициализирует и извлекает провайдеров криптографических алгоритмов.

Во-первых, он вызывает BCryptOpenAlgorithmProvider для загрузки и инициализации поставщика CNG для генерации случайных чисел, а BCryptImportKeyPair — для импорта его жестко запрограммированного открытого ключа RSA.

1662368386998.png


Затем вредоносное ПО вызывает VirtualAlloc, чтобы выделить буфер для хранения 128 файловых структур, используемых для шифрования файлов. Размер структуры составляет 0x48 байт, а ее содержимое указано ниже.

1662368398874.png


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

1662368419413.png


Затем вредоносное ПО устанавливает дескрипторы поставщика RNG и AES, а также дескриптор открытого ключа RSA в структуру. Позже они будут использоваться для генерации случайного ключа AES и IV для шифрования файлов.

1662368429478.png


Проверка существующих дисков


Прежде чем перебирать все диски для шифрования, PLAY перечисляет все тома в системе жертвы, вызывая FindFirstVolumeW и FindNextVolumeW. Если том не является дисководом для компакт-дисков или электронным диском, вредоносная программа вызывает GetVolumePathNamesForVolumeNameW для получения списка букв дисков и путей к подключенным папкам для указанного тома.

Если этот список пуст, что означает, что том не смонтирован ни в какую папку, PLAY вызывает GetDiskFreeSpaceExW, чтобы проверить, больше ли свободного места на томе 0x40000000 байт. Если это так, вредоносная программа вызывает SetVolumeMountPointW, чтобы попытаться подключить том к пути к диску.

1662368450068.png


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

1662368461438.png


Используя тот же прием для перебора всех возможных имен дисков, PLAY вызывает GetDriveTypeW для проверки типа каждого диска.

Это позволяет избежать шифрования привода CD-ROM или RAM-диска. Если это удаленный диск, вредоносное ПО вызывает WNetGetUniversalNameW, чтобы получить универсальное имя сетевого диска.

1662368472146.png


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

1662368484813.png


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

Рекурсивный обход


Чтобы начать обход дисков, PLAY выполняет итерацию по приведенному выше списку имен дисков и порождает поток с CreateThread для обхода каждого диска в системе.

1662368503012.png



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

1662368514891.png


1662368522357.png


1662368530731.png


Чтобы начать перечисление, вредоносное ПО вызывает FindFirstFileW и FindNextFileW для перечисления вложенных папок и файлов. Она специально проверяет, чтобы избежать обработки путей к текущему и родительскому каталогу "." а также "..".

1662368542003.png


Если обнаруженный файл является каталогом, вредоносное ПО проверяет, не шифрует ли каталог "Windows".
После этого он объединяет имя подкаталога с текущим путем поиска файла и рекурсивно проходит через подкаталог, вызывая для него функцию обхода.

1662368552556.png


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

1662368564154.png


Если его имя/расширение есть в списке ниже или если его размер меньше 6, PLAY не шифрует его.

1662368577985.png


1662368595962.png


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

1662368607526.png


Заполнение файловой структуры


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

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

1662368625399.png


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

Как только структура найдена, вредоносное ПО устанавливает в поле initialized_flag значение 1, а в поле имени файла — имя целевого файла. Он также заполняет другие поля, такие как размер файла, флаг большого файла и дескриптор файла.

1662368638557.png

1662368645510.png


Шифрование дочернего потока


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

Если файл не классифицируется как большой, вредоносное ПО вычисляет, сколько кусков нужно зашифровать, в зависимости от размера файла. Количество зашифрованных фрагментов равно 2, если размер файла меньше или равен 0x3ffffff 3 байта, если размер файла меньше или равен 0x27fffffff байт и больше 0x3ffffffff байт, и 0, если размер файла равен 0x280000000. Если размер файла больше 0x280000000 байт, количество зашифрованных чанков равно 5.

1662368660541.png


1662368668145.png


Режим цепочки по умолчанию установлен на AES-GCM. Однако, если размер файла превышает зашифрованный размер более чем в 4025 раз (это размер фрагмента 0x100000, умноженный на количество фрагментов), для режима цепочки устанавливается значение AES-CBC.

Это связано с тем, что AES-GCM имеет худшую производительность по сравнению с AES-CBC. Согласно этому сообщению, AES-GCM является более безопасным шифром, чем AES-CBC, потому что AES-CBC работает путем обработки XOR каждого блока с предыдущим блоком и не может быть записан параллельно.

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

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

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

1662368691127.png


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

Если последние 0x428 байт читаются успешно, вредоносная программа затем проверяет, равен ли хэш xxHash32 заголовка маркера нижнего колонтитула хвосту маркера нижнего колонтитула. Если это так, то подтверждается, что нижний колонтитул файла действителен, и файл уже зашифрован.

Если это не так, PLAY проверяет каждое DWORD в заголовке маркера нижнего колонтитула и сравнивает его с жестко закодированными значениями в файловой структуре. Это делается для проверки того, не зашифрован ли нижний колонтитул файла, записан ли нижний колонтитул файла, но не зашифрован, или файл уже зашифрован.

1662368712256.png


1662368721831.png


Шифрование файлов


Чтобы зашифровать файл с нуля, PLAY сначала генерирует ключ AES для шифрования файла. Он вызывает BCryptGenRandom для создания случайного буфера размером 0x20 байт.

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

Затем вызывается BCryptGenerateSymmetricKey для случайно сгенерированного 0x20-байтового буфера для создания дескриптора ключа AES.

1662368876227.png


1662368881812.png


1662368888532.png


Затем, чтобы сохранить ключ AES в структуре нижнего колонтитула файла, PLAY вызывает BCryptExportKey для экспорта ключа AES в большой двоичный объект ключа размером 0x230 байт. Он также вызывает BCryptGenRandom для случайного создания IV размером 0x10 байт и добавляет его после большого двоичного объекта ключа.

1662368897790.png


1662368904174.png


Затем он вызывает BCryptEncrypt для шифрования экспортированного большого двоичного объекта ключа и IV с помощью дескриптора открытого ключа RSA и записывает зашифрованные выходные данные в буфер размером 0x400 байт. Затем этот буфер копируется в поле encoding_symboltic_key структуры нижнего колонтитула файла.

1662368913766.png


Затем PLAY заполняет другие поля нижнего колонтитула файла, такие как footer_marker_head, footer_marker_tail, small_file_flag и large_file_flag существующей информацией из файловой структуры. Размер фрагмента по умолчанию также установлен на 0x100000 байт.

1662368926061.png


Как только нижний колонтитул файла полностью заполнен, вредоносное ПО вызывает SetFilePointerEx, чтобы переместить указатель файла в конец файла, и вызывает WriteFile, чтобы записать туда структуру.

1662368935980.png


Если размер файла превышает 0x500000 байт, PLAY шифрует только первый и последний фрагмент в файле.

1662368948104.png


1662368958136.png


Функция шифрования состоит из вызова ReadFile для чтения фрагмента данных в буфере файловой структуры, вызова BCryptEncrypt для шифрования файла с использованием дескриптора ключа AES и сгенерированного IV. После завершения шифрования вредоносная программа вызывает WriteFile для записи зашифрованного вывода в файл, а также индекс зашифрованного фрагмента в нижнем колонтитуле файла. Потенциально это используется для отслеживания того, сколько фрагментов было зашифровано в случае повреждения или прерывания.
 
1662369015988.png


1662369022568.png


1662369029411.png



Если размер файла меньше, чем размер фрагмента по умолчанию 0x100000 байт, вредоносное ПО шифрует весь файл.

1662369053914.png


Если размер файла находится где-то между 0x100000 и 0x500000, вредоносное ПО шифрует его фрагментами по 0x100000 байт, пока не достигнет конца файла.

1662369065340.png


Наконец, после того как файл зашифрован, вредоносная программа меняет свое расширение на .PLAY, вызывая MoveFileW.

1662369074625.png


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

1662369084530.png


Референс​

https://www.bleepingcomputer[.]com/...ary-of-c-rdoba-hit-by-play-ransomware-attack/

 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх