Содержание статьи
Во второй половине 2023 года команда Positive Technologies по расследованию инцидентов PT CSIRT (PT Expert Security Center) разбирала происшествие в инфраструктуре одного из своих заказчиков. Специалисты пришли к выводу, что за случившимся стоят представители хакерской группировки Twelve. Группа позиционирует себя как хактивистов политической направленности и выбирает себе цели среди российских компаний и учреждений. Считается, что эта группа тесно связана с группировкой Shadow, сменившей название сначала на C0met, а с недавних пор, вероятно, скрывающейся под именем Darkstar.
Судя по схожему почерку и инструментарию, эти две группировки являются частью одной команды и отвечают за разные направления. Обе используют в ходе атак различные варианты шифровальщиков, преимущественно семейства LockBit/Babuk. Но если хакеры из Shadow занимаются вымогательством, назначая выкуп за ключ дешифровки, то представители Twelve просто стремятся нанести максимальный ущерб целевой инфраструктуре, при этом публикуя в открытом доступе результаты своей работы, которые иногда включают в себя выгруженные из инфраструктуры чувствительные данные.
Инцидент
В ходе расследования инцидента специалисты обнаружили очень подозрительный исполняемый файл с именем twelve.
. Его анализ показал, что вместо привычного LockBit злоумышленники использовали шифровальщик, относящийся к семейству программ‑вымогателей Ryuk / Chaos / Yashma / Obsidian ORB, который использовался во множестве вредоносных кампаний по всему миру.
Судя по ряду признаков, для генерации вредоносного файла, скорее всего, использовался публично доступный конструктор Chaos Ransomware Builder, который позволяет злоумышленникам кастомизировать вредоносные программы‑шифровальщики. С его помощью, например, можно изменить имя процесса, имя файла с требованиями, текст самих требований, задать список расширений шифруемых файлов, сбилдить декриптор и так далее.
Сам троян написан на .NET, благодаря чему можно изучить его функции, например в DNSpy.
Пробежавшись по основным функциям, исследователи выяснили, что этот энкодер шифрует файлы с использованием алгоритма AES, затем зашифровывает ключи AES встроенным открытым RSA-ключом и записывает их в конец зашифрованных файлов.
Публичный RSA-ключ
Для каждого шифруемого файла троян генерирует индивидуальный AES-ключ:
string text = Program.CreatePassword(40); // Генерация ключа AESif (fileInfo.Length < (long)((ulong)-1926258176)) // Проверка размера файла{ if (Program.checkDirContains(files[i])) { string keyRSA = Program.RSA_Encrypt(text, Program.rsaKey()); // Шифрование AES-ключа публичным RSA-ключом Program.AES_Encrypt(files[i], text, keyRSA); // Шифрование файлов }}
В первую очередь было интересно понять, каким образом генерируется ключ для AES. Функция генерации ключа выглядит следующим образом:
public static string CreatePassword(int length) { StringBuilder stringBuilder = new StringBuilder(); Random random = new Random(); while (0 < length--) { stringBuilder.Append("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*!=&?&/"[random.Next("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*!=&?&/".Length)]); } return stringBuilder.ToString(); }
Ключ генерируется отдельно для каждого файла на основе функции Random
, при этом Random
запускается без сида. Если в DNSpy перейти в объявление класса Random
, то мы увидим следующее.
То есть Random
, вызванный без сида, в качестве начального использует значение Environment.
. Важно отметить, что это справедливо для приложений .NET версии 4, генерация рандома для версий 5 и выше использует другие начальные значения.
Environment.
— это количество миллисекунд, прошедших с момента запуска системы. Таким образом, можно предположить, что, зная значение сида, мы в состоянии «угадывать» случайные значения.
Проверим наши предположения. Возьмем функцию CreatePassword
из вредоноса, добавим туда вывод текущего значения Environment.
и сгенерированного пароля, а затем выведем полученные значения в консоль:
using System;using System.Text;namespace test1{ class Program { static void Main(string[] args) { string s = CreatePassword(40); Console.WriteLine("Key: " + s); } public static string CreatePassword(int length) { StringBuilder stringBuilder = new StringBuilder(); Random random = new Random(); while (0 < length--) { stringBuilder.Append("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*!=&?&/"[random.Next("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*!=&?&/".Length)]); } Console.WriteLine("Environment.TickCount: " + Environment.TickCount); return stringBuilder.ToString(); }}}
В результате запуска программы получаем следующие значения:
C:\temp>1.exe
Environment.TickCount: 264152453
Key: &Cer88Tf8sErkcxYAgR5CvdawGij/JqSEVOln8m/
Теперь организуем добавление сида в функцию генерации ключа и передадим туда значения TickCount
из предыдущего запуска:
static void Main(string[] args) { int seed = 264152453; string s = CreatePassword(40,seed); Console.WriteLine("Key: " + s); } public static string CreatePassword(int length,int seed) { StringBuilder stringBuilder = new StringBuilder(); Random random = new Random(seed); while (0 < length--) { stringBuilder.Append("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*!=&?&/"[random.Next("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*!=&?&/".Length)]); } return stringBuilder.ToString(); }
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее