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

Во вто­рой полови­не 2023 года коман­да Positive Technologies по рас­сле­дова­нию инци­ден­тов PT CSIRT (PT Expert Security Center) раз­бирала про­исшес­твие в инфраструк­туре одно­го из сво­их заказ­чиков. Спе­циалис­ты приш­ли к выводу, что за слу­чив­шимся сто­ят пред­ста­вите­ли хакер­ской груп­пиров­ки Twelve. Груп­па позици­они­рует себя как хак­тивис­тов полити­чес­кой нап­равлен­ности и выбира­ет себе цели сре­ди рос­сий­ских ком­паний и учрежде­ний. Счи­тает­ся, что эта груп­па тес­но свя­зана с груп­пиров­кой Shadow, сме­нив­шей наз­вание сна­чала на C0met, а с недав­них пор, веро­ятно, скры­вающей­ся под име­нем Darkstar.

Су­дя по схо­жему почер­ку и инс­тру­мен­тарию, эти две груп­пиров­ки явля­ются частью одной коман­ды и отве­чают за раз­ные нап­равле­ния. Обе исполь­зуют в ходе атак раз­личные вари­анты шиф­роваль­щиков, пре­иму­щес­твен­но семей­ства LockBit/Babuk. Но если хакеры из Shadow занима­ются вымога­тель­ством, наз­начая выкуп за ключ дешиф­ровки, то пред­ста­вите­ли Twelve прос­то стре­мят­ся нанес­ти мак­сималь­ный ущерб целевой инфраструк­туре, при этом пуб­ликуя в откры­том дос­тупе резуль­таты сво­ей работы, которые иног­да вклю­чают в себя выг­ружен­ные из инфраструк­туры чувс­тви­тель­ные дан­ные.

 

Инцидент

В ходе рас­сле­дова­ния инци­ден­та спе­циалис­ты обна­ружи­ли очень подоз­ритель­ный исполня­емый файл с име­нем twelve.exe. Его ана­лиз показал, что вмес­то при­выч­ного LockBit зло­умыш­ленни­ки исполь­зовали шиф­роваль­щик, отно­сящий­ся к семей­ству прог­рамм‑вымога­телей Ryuk / Chaos / Yashma / Obsidian ORB, который исполь­зовал­ся во мно­жес­тве вре­донос­ных кам­паний по все­му миру.

1,4 тысячи загруженных вредоносных семплов chaos ransomware на VirusTotal
1,4 тысячи заг­ружен­ных вре­донос­ных сем­плов chaos ransomware на VirusTotal

Су­дя по ряду приз­наков, для генера­ции вре­донос­ного фай­ла, ско­рее все­го, исполь­зовал­ся пуб­лично дос­тупный конс­трук­тор Chaos Ransomware Builder, который поз­воля­ет зло­умыш­ленни­кам кас­томизи­ровать вре­донос­ные прог­раммы‑шиф­роваль­щики. С его помощью, нап­ример, мож­но изме­нить имя про­цес­са, имя фай­ла с тре­бова­ниями, текст самих тре­бова­ний, задать спи­сок рас­ширений шиф­руемых фай­лов, сбил­дить дек­риптор и так далее.

Интерфейс Chaos Ransomware Builder
Ин­терфейс Chaos Ransomware Builder

Сам тро­ян написан на .NET, бла­года­ря чему мож­но изу­чить его фун­кции, нап­ример в DNSpy.

Список функций шифровальщика
Спи­сок фун­кций шиф­роваль­щика

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

 

Публичный RSA-ключ

Для каж­дого шиф­руемо­го фай­ла тро­ян генери­рует инди­виду­аль­ный AES-ключ:

string text = Program.CreatePassword(40); // Генерация ключа AES
if (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.TickCount. Важ­но отме­тить, что это спра­вед­ливо для при­ложе­ний .NET вер­сии 4, генера­ция ран­дома для вер­сий 5 и выше исполь­зует дру­гие началь­ные зна­чения.

Environment.TickCount — это количес­тво мил­лисекунд, про­шед­ших с момен­та запус­ка сис­темы. Таким обра­зом, мож­но пред­положить, что, зная зна­чение сида, мы в сос­тоянии «уга­дывать» слу­чай­ные зна­чения.

Про­верим наши пред­положе­ния. Возь­мем фун­кцию CreatePassword из вре­доно­са, добавим туда вывод текуще­го зна­чения Environment.TickCount и сге­нери­рован­ного пароля, а затем выведем получен­ные зна­чения в кон­соль:

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, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    1 Комментарий
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии