
Плохие вещи от плохих парней
Образец Hive, проанализированный и упомянутый в этом документе, был случайным образом выбран из этого списка, созданного @rivitna, которому я выражаю свою искреннюю благодарность. Артефакты доступны на платформе VirusTotal.
В этом документе в качестве эталона взят файл a0h2uih3d2.exe
MD5: 15CF5E0DA094ACDD751A513402A8C941
SHA-1: 72E15AC4473903C814E65E3C06F54EB0399580AA
SHA-256: 335D2E4A743D059955760ECF2EC25EE86D36AA60B096C9180E860C64EF78EE55
Чтобы получить представление о сложности этого вымогателя, пожалуйста, ознакомьтесь с этим анализом, опубликованным центром Microsoft Threat Intelligence Center (MSTIC).
Пожалуйста, внимательно прочитайте весь документ, прежде чем начинать экспериментировать с кодом!
В последние месяцы я направил большую часть своих усилий на изучение и обратную разработку алгоритма шифрования Hive v5. У меня была возможность сотрудничать с выдающимся аналитиком вредоносного ПО и реверс-инженером @rivitna, который в прошлом анализировал предыдущие версии Hive и публиковал код и PoC, касающиеся их механизмов шифрования. Он внёс немалый вклад в выявление компонентов, участвующих в операциях шифрования Hive v5, который, будучи написанным на RUST, стал более сложным для анализа. Я нашёл нечто общее с Babuk, ещё одним очень важным вымогателем, исходные коды которого были раскрыты в июне 2021 года:
Вымогатель Hive v5, работающий в системе жертвы, генерирует два cleartext-ключа, используя алгоритм, приведённый ниже, основанный на Windows API QueryPerformanceCounter и QueryPerformanceFrequency.
Пожалуйста, обратитесь к этой странице Microsoft для получения дополнительной информации об API QueryPerformanceCounter, а здесь — об API QueryPerformanceFrequency.
QueryPerformanceCounter — это очень точный счётчик времени. При вызове он возвращает время, прошедшее с момента последнего включения ПК.
QueryPerformanceFrequency возвращает значение (частоту) счётчика производительности. Оно имеет фиксированное значение 0x989680. Это означает, что значение QueryPerformanceCounter обновляется 0x989680 раз в секунду, то есть 10 000 000 раз.
Два cleartext-ключа имеют размер 0xCFFF00 байт и генерируются по одному, байт за байтом. Ниже приведён фрагмент, который позволяет создать массив размером 0xA00000 байт, составляющий наибольшую часть так называемого cleartext-ключа, которым Hive шифрует файлы на ПК жертвы.

Каждый байт ключа получается путём взятия значения регистра AL. Регистр EAX содержит результат функции 0044ADE0, переименованной в createByte, которая вычисляет разницу между текущим моментом времени и начальным значением семени (seed), рассчитанным при первом вызове функции 0044A850, переименованной в call_to_QueryPerformanceCounter.
Ниже приведён код на C++ для генерации cleartext-ключа:

Алгоритм очень прост, хотя внутри функции 0044ADE0 были вставлены инструкции, выполняющие избыточные операции и различные условные переходы, чтобы попытаться замедлить время выполнения кода во время генерации cleartext-ключа:

В папке HiveRansomwareV5_custom_keygen_PoC вы найдёте исходный код, восстановленный из проанализированного образца Hive v5. Это не оптимизированный код, подобный тому, что присутствует во вредоносном ПО, поскольку мне нужно было не упустить ни одной строки кода из скомпилированной версии.
В папке HiveRansomwareV5_custom_keygen_PoC-optimized вы найдёте оптимизированный код, полученный из вышеупомянутого исходного кода. В этой версии код читается гораздо легче, чем исходный, что позволяет понять реализуемую им функциональность.
Обе версии перед запуском необходимо настроить под вашего пользователя, чтобы сохранить сгенерированный cleartext-ключ на вашем рабочем столе.
Оба cleartext-ключа генерируются с использованием одного и того же алгоритма.
Cleartext-ключ состоит из 0xA00000 безопасно сгенерированных случайным образом байт. Затем первые 0x2FFF00 байт копируются в конец, создавая итоговый cleartext-ключ размером 0xCFFF00 байт.

Затем Hive использует два сгенерированных ключа для шифрования файлов, но прежде всего вымогатель Hive v5 шифрует сгенерированные ключи в собственную структуру (далее мы будем называть их keystream) и размещает их в корне каждого шифруемого диска с расширением .key. Например, если в вашей системе установлены оба диска C и D, зашифрованные keystream-файлы будут присутствовать в корне каждого диска.

Вымогатель Hive v5 использует сгенерированные cleartext-ключи для шифрования файлов с помощью инструкции XOR, так что мы имеем дело с очень быстрым симметричным шифрованием на современных процессорах x86/x64.
Вымогателю Hive v5 необходимо защитить сгенерированный cleartext-ключ, зашифровав его два раза; далее мы будем называть эти шифрования раундами. Для получения итогового keystream требуется два раунда шифрования.
Для этого на каждом раунде выполняются следующие шаги:
Шаг 3 гарантирует создание keystream, который может быть открыт двойной парой приватных ключей: теми, что Hive генерирует во время шифрования, и теми, что партнёр Hive сгенерировал, когда компилировал для нас вымогателя.

В конце этого описания становится очевидным один конкретный момент: cleartext-ключ, приватный ключ и nonce, используемые в обоих раундах шифрования, генерируются одной и той же функцией, описанной выше (0044ADE0, она же createByte). Функция 0044ADE0 обусловлена временем, которое CPU затрачивает на выполнение кода, вызываемого внутри цикла for.
Взглянув на рисунок выше, на котором выделена структура keystream после двух раундов шифрования, становится очевидно, что у нас есть свободный доступ только к nonce (иначе партнёры Hive не знали бы, как расшифровать файлы).
Итак, давайте сосредоточимся на NONCE, длина которого составляет 24 байта:
NONCE: 40 A4 08 6C D0 D0 34 98 FC 60 C4 28 8C F0 F0 54 B8 1C 80 E4 48 AC AC 10
Разница между одним байтом nonce и следующим (по абсолютному значению) представляет собой время, прошедшее между одной итерацией и следующей. С этим определением мы вводим понятие отпечатка (fingerprint).
NONCE FINGERPRINT: 64 9c 64 64 00 9c 64 64 9c 64 9c 64 64 00 9c 64 9c 64 64 9c 64 00 9c
Если мы проанализируем полученные значения, то обнаружим, что время выполнения кода почти идентично, с небольшими отклонениями, обусловленными главным образом технологией используемого процессора (для своих тестов я использовал процессор i7 10-го поколения и i5 5-го поколения; на других системах этот отпечаток может отличаться).
Эта находка очень важна, если учесть, что nonce генерируется той же функцией, которая генерирует cleartext-ключ и, прежде всего, приватный ключ. Поскольку упомянутые значения также будут следовать этому принципу, т.е. разница между отдельными байтами nonce предсказуема, то и значения приватного ключа и cleartext-ключа также будут предсказуемы.
Однако анализ показал, что генерация массива из 0xA00000 байт в надежде получить те же исходные байты cleartext-ключа, вычисленного Hive, очень сложна: изменения нагрузки на CPU и память влияют на скорость выполнения кода, и часто исходный cleartext-ключ, вычисленный PE-файлом Hive, отличается (пусть даже на несколько байт) от ключа, вычисленного нами.
Мы используем этот отпечаток nonce для сравнения с отпечатком, полученным при генерации возможного словаря длиной 0xA00000 байт (это число было установлено эмпирически: после серии тестов было замечено, что статистически в этом количестве байт содержатся два приватных ключа по 32 байта каждый, необходимых в двух раундах шифрования). Если отпечаток nonce содержится в отпечатке словаря, мы нашли правильный словарь для начала брутфорса обоих приватных ключей.
На этом дело не заканчивается, поскольку из динамического анализа, проведённого применительно к генерации nonce, cleartext-ключа, а также приватного ключа, было установлено, что первый байт имеет среднюю дистанцию, отличную от второго байта, по сравнению со всеми остальными байтами, которые имеют почти однородные значения. Давайте рассмотрим подробнее:

Как вы можете видеть, значения отпечатка после первого байта претерпевают минимальные изменения, т.е. абсолютная дистанция между первым и вторым байтом приватного ключа в большинстве случаев имеет значения, выходящие за пределы значений, присутствующих в остальной части отпечатка.
Вероятно, это связано с некоторыми алгоритмами оптимизации в CPU, которые ускоряют выполнение кода после первой итерации в цикле for.
Предлагаемый код считывает nonce каждого раунда шифрования keystream, определяет его отпечаток и генерирует список возможных словарей ключей, которые содержат возможный приватный ключ.
Чтобы решить проблему, связанную с первым байтом отпечатка, который всегда отличается от остальной части ключа, я решил сделать следующее:
Когда два публичных ключа совпадут, мы найдём приватный ключ, которым был зашифрован второй (последний) раунд шифрования. Повторив описанные выше операции ещё раз, мы получим приватный ключ для расшифровки первого раунда зашифрованного keystream и, наконец, извлечём исходный cleartext-ключ.
В папке HiveRansomwareV5-keystream_decryptor вы найдёте файл решения (sln) для VS 2017 и доработанную библиотеку monocypher. Программа позволяет выбрать, какую операцию выполнить.

Опцию «1» следует выбирать первой, поскольку она позволяет создать словарь байтов, «сшитых по мерке» для процессора вашего ПК, поэтому её следует выполнять на зашифрованной машине, так как гораздо более вероятно, что вы получите значения, равные тем, которые присутствуют в её keystream.

Или, как вариант, если первый вариант не работает, создайте собственный словарь, запустив вредоносное ПО в отладчике (на том же уже заражённом ПК) до конца генерации cleartext-ключа (сразу за пределами цикла for), и сохраните содержимое памяти, содержащей cleartext-ключ. В этом случае вы можете проверить свой словарь с помощью опции «3»:

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

Чтобы правильно выполнить функцию, реализованную в опции 2, необходимо извлечь публичный ключ. Поскольку не все образцы Hive одинаковы, создать универсальный экстрактор публичного ключа не так-то просто. Однако один из известных рабочих способов — установить точку останова в этой части дизассемблированного кода после создания nonce. На изображении ниже публичный ключ передаётся в функцию 44E3D8 для получения общего ключа с помощью curve25519. Это единственное место, где публичный ключ раскрывается на протяжении всего выполнения вредоносного ПО.

Пожалуйста, будьте внимательны при работе с параметрами Visual Studio. Вывод программы, например сгенерированные байты словаря, может измениться, если вы переключитесь с Debug на Release и наоборот.
Если вы хотите ускорить процедуру перебора, вы можете изменить значение dictionary_dimension в коде, но имейте в виду, что уменьшение размера словаря также может снизить шансы найти приватные ключи.
Кроме того, если вы решите использовать массив байтов cleartext, полученный путём запуска Hive и дампа его из памяти, не забудьте указать размер вашего дампа в переменной dictionary_dimension.
Для тестирования инструмента расшифровки я предоставил несколько файлов в папке dummy_data_PoC:
Удачи!
https://github.com/rivitna/Malware/blob/main/Hive/Hive_samples.txt
https://www.virustotal.com/gui/file/335d2e4a743d059955760ecf2ec25ee86d36aa60b096c9180e860c64ef78ee55
https://www.microsoft.com/security/blog/2022/07/05/hive-ransomware-gets-upgrades-in-rust/
https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
https://monocypher.org/manual/x25519
https://monocypher.org/manual/advanced/poly1305