Кампании 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
Записка с требованием выкупа
Содержимое примечания о выкупе по умолчанию хранится в виде закодированной строки в исполняемом файле PLAY, которая содержит строку "PLAY", а также адрес электронной почты, по которому жертва может связаться с злоумышленником.
Имя файла PLAY с требованием выкупа — "ReadMe.txt".
Антианализ
Антианализ: возвратно-ориентированное программирование
Открыв исполняемый файл в IDA, мы видим, что большая часть ассемблерного кода не имеет смысла и не слишком осмысленна. Пример можно увидеть из функции WinMain, где нет четкого оператора return с мусорными байтами, всплывающими среди корректного кода.
Как показано в дизассемблированном коде выше, поток управления в WinMain вызывает sub_4142F5, и по возвращении извлекается edi, и мы сталкиваемся с байтами мусора по адресу 0x4142F2. В результате IDA не может правильно декомпилировать этот код.
Изучая sub_4142F5, мы видим, что значение, хранящееся в указателе стека, немедленно добавляется к 0x35 перед выполнением инструкции retn.
Мы знаем, что инструкция call в основном содержит две атомарные инструкции, одна из которых помещает адрес следующей инструкции (после инструкции call) в стек, а другая выполняет переход к вызываемой подпрограмме. Когда код входит в sub_4142F5, адрес возврата (в данном случае это 0x4142F1) сохраняется в указателе стека поверх стека.
Подпрограмма добавляет к этому 0x35, изменяя адрес возврата на 0x414326, и возвращает для перехода на него.
Зная это, мы можем прокрутить вниз и попытаться разобрать байты по адресу 0x414326, чтобы получить следующую часть кода WinMain.
Используя возвратно ориентированное программирование, чтобы отвлечь обычный поток управления программой, PLAY может обойти большую часть статического анализа посредством дизассемблирования и декомпиляции IDA.
Мы также можем быстро увидеть, что по адресу 0x41433A находится еще одна инструкция вызова, за которой следуют несколько мусорных байтов. Это означает, что обфускация происходит несколько раз в коде.
Мой подход к этому состоял в том, чтобы программно исправить все эти инструкции вызова. Простой патч, используемый в моем анализе, вычисляет переход (значение, добавляемое к адресу возврата) и заменяет инструкцию вызова на инструкцию перехода к целевому адресу.
Чтобы отсканировать весь этот запутанный код, я использую 3 разных (но очень похожих) регулярных выражения в IDAPython, чтобы найти и исправить их. Вы можете найти мой скрипт исправления здесь (https://github.com/cdong1012/IDAPython-Malware-Scripts/blob/master/PLAY/script.py).
После исправления код WinMain выглядит примерно так.
Теперь мы успешно расшифровали код, получили осмысленную инструкцию вызова sub_415110 и правильный оператор возврата в декомпилированном коде!
Антианализ: мусорный код
Помимо обфускации потока управления, PLAY также засоряет свой код случайными инструкциями перемещения, которые не вносят вклад в основную функциональность программы.
Из-за этого декомпилированный код выглядит намного более беспорядочным, и исправить все эти ошибки непросто, так как действующий код обычно находится между этим мусорным кодом. Исправление путем перепрыгивания через них иногда приводило к поломке самой программы.
Единственное решение, которое у меня есть для этого, — мысленно игнорировать их во время анализа.
Анти-анализ: хеширование API
Подобно большинству современных программ-вымогателей, PLAY скрывает свой вызов API с помощью хеширования имени API. Функция разрешения API принимает целевой хэш и адрес DLL.
Она просматривает таблицу экспорта DLL, чтобы получить имя экспорта. Для каждого имени API вредоносная программа вызывает sub_40F580 с именем в качестве параметра и добавляет к результату 0x4E986790 для формирования окончательного хэша. Этот хэш сравнивается с целевым хешем, и если они совпадают, возвращается адрес API.
Как показано ниже, хэш-функция содержит множество уникальных констант, что позволяет нам быстро найти, что это xxHash32. При этом мы знаем, что полный алгоритм хеширования — это xxHash32 с начальным значением 1, а результат добавляется к 0x4E986790.
Отсюда я разработал сценарий IDAPython для автоматического разрешения всех API-интерфейсов, которые использует вредоносное ПО, которые вы можете найти здесь (https://github.com/cdong1012/IDAPython-Malware-Scripts/blob/master/PLAY/API_resolve.py).
Антианализ: шифрование строк
Наиболее важные строки в PLAY закодированы в памяти. Алгоритм декодирования не кажется слишком ясным, поэтому я просто динамически просматривал их. Школа сейчас надирает мне задницу, поэтому у меня нет времени.
Статический анализ кода
Аргументы командной строки
PLAY может работать как с аргументами командной строки, так и без них.
Ниже приведен список аргументов, которые могут быть предоставлены оператором.
Инициализация криптографии
Перед шифрованием PLAY инициализирует и извлекает провайдеров криптографических алгоритмов.
Во-первых, он вызывает BCryptOpenAlgorithmProvider для загрузки и инициализации поставщика CNG для генерации случайных чисел, а BCryptImportKeyPair — для импорта его жестко запрограммированного открытого ключа RSA.
Затем вредоносное ПО вызывает VirtualAlloc, чтобы выделить буфер для хранения 128 файловых структур, используемых для шифрования файлов. Размер структуры составляет 0x48 байт, а ее содержимое указано ниже.
PLAY перебирает этот глобальный список структур и заполняет поля каждой структуры. Во-первых, он устанавливает для маркеров зашифрованных файлов в структуре следующие жестко запрограммированные значения, которые позже будут записаны в конец каждого зашифрованного файла.
Затем вредоносное ПО устанавливает дескрипторы поставщика RNG и AES, а также дескриптор открытого ключа RSA в структуру. Позже они будут использоваться для генерации случайного ключа AES и IV для шифрования файлов.
Проверка существующих дисков
Прежде чем перебирать все диски для шифрования, PLAY перечисляет все тома в системе жертвы, вызывая FindFirstVolumeW и FindNextVolumeW. Если том не является дисководом для компакт-дисков или электронным диском, вредоносная программа вызывает GetVolumePathNamesForVolumeNameW для получения списка букв дисков и путей к подключенным папкам для указанного тома.
Если этот список пуст, что означает, что том не смонтирован ни в какую папку, PLAY вызывает GetDiskFreeSpaceExW, чтобы проверить, больше ли свободного места на томе 0x40000000 байт. Если это так, вредоносная программа вызывает SetVolumeMountPointW, чтобы попытаться подключить том к пути к диску.
Для каждого монтируемого тома PLAY перебирает все символы, чтобы найти имя диска, которое можно вызвать SetVolumeMountPointW для монтирования тома.
Используя тот же прием для перебора всех возможных имен дисков, PLAY вызывает GetDriveTypeW для проверки типа каждого диска.
Это позволяет избежать шифрования привода CD-ROM или RAM-диска. Если это удаленный диск, вредоносное ПО вызывает WNetGetUniversalNameW, чтобы получить универсальное имя сетевого диска.
В качестве конечного пути к диску для шифрования задается универсальное имя сетевого диска или имя подключения, в зависимости от того, какое из них существует.
Если диск является обычным диском, его имя остается прежним. Каждому допустимому диску добавляется его имя в список имен дисков, которые необходимо обойти и зашифровать.
Рекурсивный обход
Чтобы начать обход дисков, PLAY выполняет итерацию по приведенному выше списку имен дисков и порождает поток с CreateThread для обхода каждого диска в системе.
Перед обработкой диска вредоносное ПО извлекает следующее содержимое примечания о выкупе, прежде чем поместить его в папку на диске. Это единственное место, где записка с требованием выкупа падает в каждую папку, как другие программы-вымогатели.
Чтобы начать перечисление, вредоносное ПО вызывает FindFirstFileW и FindNextFileW для перечисления вложенных папок и файлов. Она специально проверяет, чтобы избежать обработки путей к текущему и родительскому каталогу "." а также "..".
Если обнаруженный файл является каталогом, вредоносное ПО проверяет, не шифрует ли каталог "Windows".
После этого он объединяет имя подкаталога с текущим путем поиска файла и рекурсивно проходит через подкаталог, вызывая для него функцию обхода.
Если обнаруженный файл является обычным файлом, вредоносное ПО проверяет его имя, а также размер, чтобы убедиться, что он действителен для шифрования.
Если его имя/расширение есть в списке ниже или если его размер меньше 6, PLAY не шифрует его.
PLAY также выполняет дополнительную проверку, чтобы увидеть, является ли расширение файла типичным для больших файлов, чтобы позже определить его тип шифрования. Файл классифицируется как большой, если его расширение находится в списке ниже.
Заполнение файловой структуры
Для каждого файла, подлежащего шифрованию, PLAY сначала заполняет файловую структуру соответствующими данными о файле.
Во-первых, он начинает перебирать список глобальных файловых структур, чтобы проверить, есть ли доступная структура для обработки файла.
Если в глобальном списке нет доступной структуры, PLAY вызывает Sleep, чтобы перевести поток в спящий режим, и выполняет повторную проверку, пока не найдет ее.
Как только структура найдена, вредоносное ПО устанавливает в поле initialized_flag значение 1, а в поле имени файла — имя целевого файла. Он также заполняет другие поля, такие как размер файла, флаг большого файла и дескриптор файла.
Шифрование дочернего потока
После заполнения файловой структуры для определенного файла PLAY запускает поток, чтобы начать шифрование файла.
Если файл не классифицируется как большой, вредоносное ПО вычисляет, сколько кусков нужно зашифровать, в зависимости от размера файла. Количество зашифрованных фрагментов равно 2, если размер файла меньше или равен 0x3ffffff 3 байта, если размер файла меньше или равен 0x27fffffff байт и больше 0x3ffffffff байт, и 0, если размер файла равен 0x280000000. Если размер файла больше 0x280000000 байт, количество зашифрованных чанков равно 5.
Режим цепочки по умолчанию установлен на AES-GCM. Однако, если размер файла превышает зашифрованный размер более чем в 4025 раз (это размер фрагмента 0x100000, умноженный на количество фрагментов), для режима цепочки устанавливается значение AES-CBC.
Это связано с тем, что AES-GCM имеет худшую производительность по сравнению с AES-CBC. Согласно этому сообщению, AES-GCM является более безопасным шифром, чем AES-CBC, потому что AES-CBC работает путем обработки XOR каждого блока с предыдущим блоком и не может быть записан параллельно.
Это влияет на производительность из-за сложной математики, требующей последовательного шифрования.
Для шифрования файлов PLAY теперь представляет новую структуру, представляющую содержимое нижнего колонтитула файла, которое записывается в каждый зашифрованный файл.
Мне понадобилась целая вечность, чтобы полностью понять и разрешить поля этой структуры, что напомнило мне, что я, вероятно, просто запутался в анализе вредоносного ПО.
Сначала PLAY считывает 0x428 байт в конце файла, чтобы проверить нижний колонтитул файла. Если размер файла меньше 0x428 байт, файл гарантированно не будет зашифрован, поэтому вредоносная программа немедленно переходит к его шифрованию.
Если последние 0x428 байт читаются успешно, вредоносная программа затем проверяет, равен ли хэш xxHash32 заголовка маркера нижнего колонтитула хвосту маркера нижнего колонтитула. Если это так, то подтверждается, что нижний колонтитул файла действителен, и файл уже зашифрован.
Если это не так, PLAY проверяет каждое DWORD в заголовке маркера нижнего колонтитула и сравнивает его с жестко закодированными значениями в файловой структуре. Это делается для проверки того, не зашифрован ли нижний колонтитул файла, записан ли нижний колонтитул файла, но не зашифрован, или файл уже зашифрован.
Шифрование файлов
Чтобы зашифровать файл с нуля, PLAY сначала генерирует ключ AES для шифрования файла. Он вызывает BCryptGenRandom для создания случайного буфера размером 0x20 байт.
В зависимости от режима цепочки, указанного в файловой структуре, вредоносная программа вызывает BCryptSetProperty, чтобы правильно установить цепочку для своего дескриптора поставщика AES.
Затем вызывается BCryptGenerateSymmetricKey для случайно сгенерированного 0x20-байтового буфера для создания дескриптора ключа AES.
Затем, чтобы сохранить ключ AES в структуре нижнего колонтитула файла, PLAY вызывает BCryptExportKey для экспорта ключа AES в большой двоичный объект ключа размером 0x230 байт. Он также вызывает BCryptGenRandom для случайного создания IV размером 0x10 байт и добавляет его после большого двоичного объекта ключа.
Затем он вызывает BCryptEncrypt для шифрования экспортированного большого двоичного объекта ключа и IV с помощью дескриптора открытого ключа RSA и записывает зашифрованные выходные данные в буфер размером 0x400 байт. Затем этот буфер копируется в поле encoding_symboltic_key структуры нижнего колонтитула файла.
Затем PLAY заполняет другие поля нижнего колонтитула файла, такие как footer_marker_head, footer_marker_tail, small_file_flag и large_file_flag существующей информацией из файловой структуры. Размер фрагмента по умолчанию также установлен на 0x100000 байт.
Как только нижний колонтитул файла полностью заполнен, вредоносное ПО вызывает SetFilePointerEx, чтобы переместить указатель файла в конец файла, и вызывает WriteFile, чтобы записать туда структуру.
Если размер файла превышает 0x500000 байт, PLAY шифрует только первый и последний фрагмент в файле.
Функция шифрования состоит из вызова ReadFile для чтения фрагмента данных в буфере файловой структуры, вызова BCryptEncrypt для шифрования файла с использованием дескриптора ключа AES и сгенерированного IV. После завершения шифрования вредоносная программа вызывает WriteFile для записи зашифрованного вывода в файл, а также индекс зашифрованного фрагмента в нижнем колонтитуле файла. Потенциально это используется для отслеживания того, сколько фрагментов было зашифровано в случае повреждения или прерывания.
Известно, что операторы используют обычные тактики охоты на крупную дичь (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
Записка с требованием выкупа
Содержимое примечания о выкупе по умолчанию хранится в виде закодированной строки в исполняемом файле PLAY, которая содержит строку "PLAY", а также адрес электронной почты, по которому жертва может связаться с злоумышленником.
Имя файла PLAY с требованием выкупа — "ReadMe.txt".
Антианализ
Антианализ: возвратно-ориентированное программирование
Открыв исполняемый файл в IDA, мы видим, что большая часть ассемблерного кода не имеет смысла и не слишком осмысленна. Пример можно увидеть из функции WinMain, где нет четкого оператора return с мусорными байтами, всплывающими среди корректного кода.
Как показано в дизассемблированном коде выше, поток управления в WinMain вызывает sub_4142F5, и по возвращении извлекается edi, и мы сталкиваемся с байтами мусора по адресу 0x4142F2. В результате IDA не может правильно декомпилировать этот код.
Изучая sub_4142F5, мы видим, что значение, хранящееся в указателе стека, немедленно добавляется к 0x35 перед выполнением инструкции retn.
Мы знаем, что инструкция call в основном содержит две атомарные инструкции, одна из которых помещает адрес следующей инструкции (после инструкции call) в стек, а другая выполняет переход к вызываемой подпрограмме. Когда код входит в sub_4142F5, адрес возврата (в данном случае это 0x4142F1) сохраняется в указателе стека поверх стека.
Подпрограмма добавляет к этому 0x35, изменяя адрес возврата на 0x414326, и возвращает для перехода на него.
Зная это, мы можем прокрутить вниз и попытаться разобрать байты по адресу 0x414326, чтобы получить следующую часть кода WinMain.
Используя возвратно ориентированное программирование, чтобы отвлечь обычный поток управления программой, PLAY может обойти большую часть статического анализа посредством дизассемблирования и декомпиляции IDA.
Мы также можем быстро увидеть, что по адресу 0x41433A находится еще одна инструкция вызова, за которой следуют несколько мусорных байтов. Это означает, что обфускация происходит несколько раз в коде.
Мой подход к этому состоял в том, чтобы программно исправить все эти инструкции вызова. Простой патч, используемый в моем анализе, вычисляет переход (значение, добавляемое к адресу возврата) и заменяет инструкцию вызова на инструкцию перехода к целевому адресу.
Чтобы отсканировать весь этот запутанный код, я использую 3 разных (но очень похожих) регулярных выражения в IDAPython, чтобы найти и исправить их. Вы можете найти мой скрипт исправления здесь (https://github.com/cdong1012/IDAPython-Malware-Scripts/blob/master/PLAY/script.py).
После исправления код WinMain выглядит примерно так.
Теперь мы успешно расшифровали код, получили осмысленную инструкцию вызова sub_415110 и правильный оператор возврата в декомпилированном коде!
Антианализ: мусорный код
Помимо обфускации потока управления, PLAY также засоряет свой код случайными инструкциями перемещения, которые не вносят вклад в основную функциональность программы.
Из-за этого декомпилированный код выглядит намного более беспорядочным, и исправить все эти ошибки непросто, так как действующий код обычно находится между этим мусорным кодом. Исправление путем перепрыгивания через них иногда приводило к поломке самой программы.
Единственное решение, которое у меня есть для этого, — мысленно игнорировать их во время анализа.
Анти-анализ: хеширование API
Подобно большинству современных программ-вымогателей, PLAY скрывает свой вызов API с помощью хеширования имени API. Функция разрешения API принимает целевой хэш и адрес DLL.
Она просматривает таблицу экспорта DLL, чтобы получить имя экспорта. Для каждого имени API вредоносная программа вызывает sub_40F580 с именем в качестве параметра и добавляет к результату 0x4E986790 для формирования окончательного хэша. Этот хэш сравнивается с целевым хешем, и если они совпадают, возвращается адрес API.
Как показано ниже, хэш-функция содержит множество уникальных констант, что позволяет нам быстро найти, что это xxHash32. При этом мы знаем, что полный алгоритм хеширования — это xxHash32 с начальным значением 1, а результат добавляется к 0x4E986790.
Отсюда я разработал сценарий IDAPython для автоматического разрешения всех API-интерфейсов, которые использует вредоносное ПО, которые вы можете найти здесь (https://github.com/cdong1012/IDAPython-Malware-Scripts/blob/master/PLAY/API_resolve.py).
Антианализ: шифрование строк
Наиболее важные строки в PLAY закодированы в памяти. Алгоритм декодирования не кажется слишком ясным, поэтому я просто динамически просматривал их. Школа сейчас надирает мне задницу, поэтому у меня нет времени.
Статический анализ кода
Аргументы командной строки
PLAY может работать как с аргументами командной строки, так и без них.
Ниже приведен список аргументов, которые могут быть предоставлены оператором.
Инициализация криптографии
Перед шифрованием PLAY инициализирует и извлекает провайдеров криптографических алгоритмов.
Во-первых, он вызывает BCryptOpenAlgorithmProvider для загрузки и инициализации поставщика CNG для генерации случайных чисел, а BCryptImportKeyPair — для импорта его жестко запрограммированного открытого ключа RSA.
Затем вредоносное ПО вызывает VirtualAlloc, чтобы выделить буфер для хранения 128 файловых структур, используемых для шифрования файлов. Размер структуры составляет 0x48 байт, а ее содержимое указано ниже.
PLAY перебирает этот глобальный список структур и заполняет поля каждой структуры. Во-первых, он устанавливает для маркеров зашифрованных файлов в структуре следующие жестко запрограммированные значения, которые позже будут записаны в конец каждого зашифрованного файла.
Затем вредоносное ПО устанавливает дескрипторы поставщика RNG и AES, а также дескриптор открытого ключа RSA в структуру. Позже они будут использоваться для генерации случайного ключа AES и IV для шифрования файлов.
Проверка существующих дисков
Прежде чем перебирать все диски для шифрования, PLAY перечисляет все тома в системе жертвы, вызывая FindFirstVolumeW и FindNextVolumeW. Если том не является дисководом для компакт-дисков или электронным диском, вредоносная программа вызывает GetVolumePathNamesForVolumeNameW для получения списка букв дисков и путей к подключенным папкам для указанного тома.
Если этот список пуст, что означает, что том не смонтирован ни в какую папку, PLAY вызывает GetDiskFreeSpaceExW, чтобы проверить, больше ли свободного места на томе 0x40000000 байт. Если это так, вредоносная программа вызывает SetVolumeMountPointW, чтобы попытаться подключить том к пути к диску.
Для каждого монтируемого тома PLAY перебирает все символы, чтобы найти имя диска, которое можно вызвать SetVolumeMountPointW для монтирования тома.
Используя тот же прием для перебора всех возможных имен дисков, PLAY вызывает GetDriveTypeW для проверки типа каждого диска.
Это позволяет избежать шифрования привода CD-ROM или RAM-диска. Если это удаленный диск, вредоносное ПО вызывает WNetGetUniversalNameW, чтобы получить универсальное имя сетевого диска.
В качестве конечного пути к диску для шифрования задается универсальное имя сетевого диска или имя подключения, в зависимости от того, какое из них существует.
Если диск является обычным диском, его имя остается прежним. Каждому допустимому диску добавляется его имя в список имен дисков, которые необходимо обойти и зашифровать.
Рекурсивный обход
Чтобы начать обход дисков, PLAY выполняет итерацию по приведенному выше списку имен дисков и порождает поток с CreateThread для обхода каждого диска в системе.
Перед обработкой диска вредоносное ПО извлекает следующее содержимое примечания о выкупе, прежде чем поместить его в папку на диске. Это единственное место, где записка с требованием выкупа падает в каждую папку, как другие программы-вымогатели.
Чтобы начать перечисление, вредоносное ПО вызывает FindFirstFileW и FindNextFileW для перечисления вложенных папок и файлов. Она специально проверяет, чтобы избежать обработки путей к текущему и родительскому каталогу "." а также "..".
Если обнаруженный файл является каталогом, вредоносное ПО проверяет, не шифрует ли каталог "Windows".
После этого он объединяет имя подкаталога с текущим путем поиска файла и рекурсивно проходит через подкаталог, вызывая для него функцию обхода.
Если обнаруженный файл является обычным файлом, вредоносное ПО проверяет его имя, а также размер, чтобы убедиться, что он действителен для шифрования.
Если его имя/расширение есть в списке ниже или если его размер меньше 6, PLAY не шифрует его.
PLAY также выполняет дополнительную проверку, чтобы увидеть, является ли расширение файла типичным для больших файлов, чтобы позже определить его тип шифрования. Файл классифицируется как большой, если его расширение находится в списке ниже.
Заполнение файловой структуры
Для каждого файла, подлежащего шифрованию, PLAY сначала заполняет файловую структуру соответствующими данными о файле.
Во-первых, он начинает перебирать список глобальных файловых структур, чтобы проверить, есть ли доступная структура для обработки файла.
Если в глобальном списке нет доступной структуры, PLAY вызывает Sleep, чтобы перевести поток в спящий режим, и выполняет повторную проверку, пока не найдет ее.
Как только структура найдена, вредоносное ПО устанавливает в поле initialized_flag значение 1, а в поле имени файла — имя целевого файла. Он также заполняет другие поля, такие как размер файла, флаг большого файла и дескриптор файла.
Шифрование дочернего потока
После заполнения файловой структуры для определенного файла PLAY запускает поток, чтобы начать шифрование файла.
Если файл не классифицируется как большой, вредоносное ПО вычисляет, сколько кусков нужно зашифровать, в зависимости от размера файла. Количество зашифрованных фрагментов равно 2, если размер файла меньше или равен 0x3ffffff 3 байта, если размер файла меньше или равен 0x27fffffff байт и больше 0x3ffffffff байт, и 0, если размер файла равен 0x280000000. Если размер файла больше 0x280000000 байт, количество зашифрованных чанков равно 5.
Режим цепочки по умолчанию установлен на AES-GCM. Однако, если размер файла превышает зашифрованный размер более чем в 4025 раз (это размер фрагмента 0x100000, умноженный на количество фрагментов), для режима цепочки устанавливается значение AES-CBC.
Это связано с тем, что AES-GCM имеет худшую производительность по сравнению с AES-CBC. Согласно этому сообщению, AES-GCM является более безопасным шифром, чем AES-CBC, потому что AES-CBC работает путем обработки XOR каждого блока с предыдущим блоком и не может быть записан параллельно.
Это влияет на производительность из-за сложной математики, требующей последовательного шифрования.
Для шифрования файлов PLAY теперь представляет новую структуру, представляющую содержимое нижнего колонтитула файла, которое записывается в каждый зашифрованный файл.
Мне понадобилась целая вечность, чтобы полностью понять и разрешить поля этой структуры, что напомнило мне, что я, вероятно, просто запутался в анализе вредоносного ПО.
Сначала PLAY считывает 0x428 байт в конце файла, чтобы проверить нижний колонтитул файла. Если размер файла меньше 0x428 байт, файл гарантированно не будет зашифрован, поэтому вредоносная программа немедленно переходит к его шифрованию.
Если последние 0x428 байт читаются успешно, вредоносная программа затем проверяет, равен ли хэш xxHash32 заголовка маркера нижнего колонтитула хвосту маркера нижнего колонтитула. Если это так, то подтверждается, что нижний колонтитул файла действителен, и файл уже зашифрован.
Если это не так, PLAY проверяет каждое DWORD в заголовке маркера нижнего колонтитула и сравнивает его с жестко закодированными значениями в файловой структуре. Это делается для проверки того, не зашифрован ли нижний колонтитул файла, записан ли нижний колонтитул файла, но не зашифрован, или файл уже зашифрован.
Шифрование файлов
Чтобы зашифровать файл с нуля, PLAY сначала генерирует ключ AES для шифрования файла. Он вызывает BCryptGenRandom для создания случайного буфера размером 0x20 байт.
В зависимости от режима цепочки, указанного в файловой структуре, вредоносная программа вызывает BCryptSetProperty, чтобы правильно установить цепочку для своего дескриптора поставщика AES.
Затем вызывается BCryptGenerateSymmetricKey для случайно сгенерированного 0x20-байтового буфера для создания дескриптора ключа AES.
Затем, чтобы сохранить ключ AES в структуре нижнего колонтитула файла, PLAY вызывает BCryptExportKey для экспорта ключа AES в большой двоичный объект ключа размером 0x230 байт. Он также вызывает BCryptGenRandom для случайного создания IV размером 0x10 байт и добавляет его после большого двоичного объекта ключа.
Затем он вызывает BCryptEncrypt для шифрования экспортированного большого двоичного объекта ключа и IV с помощью дескриптора открытого ключа RSA и записывает зашифрованные выходные данные в буфер размером 0x400 байт. Затем этот буфер копируется в поле encoding_symboltic_key структуры нижнего колонтитула файла.
Затем PLAY заполняет другие поля нижнего колонтитула файла, такие как footer_marker_head, footer_marker_tail, small_file_flag и large_file_flag существующей информацией из файловой структуры. Размер фрагмента по умолчанию также установлен на 0x100000 байт.
Как только нижний колонтитул файла полностью заполнен, вредоносное ПО вызывает SetFilePointerEx, чтобы переместить указатель файла в конец файла, и вызывает WriteFile, чтобы записать туда структуру.
Если размер файла превышает 0x500000 байт, PLAY шифрует только первый и последний фрагмент в файле.
Функция шифрования состоит из вызова ReadFile для чтения фрагмента данных в буфере файловой структуры, вызова BCryptEncrypt для шифрования файла с использованием дескриптора ключа AES и сгенерированного IV. После завершения шифрования вредоносная программа вызывает WriteFile для записи зашифрованного вывода в файл, а также индекс зашифрованного фрагмента в нижнем колонтитуле файла. Потенциально это используется для отслеживания того, сколько фрагментов было зашифровано в случае повреждения или прерывания.