home.. presentations..

Hiding in Plain Sight: A Survey of EDR Evasion Techniques

edr windows malware red

Transcript:

Hiding in Plain Sight A Survey of EDR Evasion Techniques

What is this talk? I will walk you through the steps that I took to evade almost every EDR on the market

I will explain every step and every technique so you can use them in your own malware

ToC EDR vs AV Bypassing AMSI Memory Patching … Bypassing ETW Payload Encryption API Hooking Imports & Import Obfuscation API Hashing Direct Syscalls Indirect Syscalls Stack Spoofing Heuristics Sleep Obfuscation API Hammering Compiler choice

What is an EDR? EDR: Endpoint Detection & Response Tool that does real-time monitoring on a host (endpoint) to find threats Often does so by hooking potentially suspicious API functions May be in user-space or kernel-space

EDR vs AV AV: Antivirus Typically is entirely signature-based This means it is best at finding known threats Not new ones

How can we bypass modern EDR solutions? Try to chain as many steps as possible If there is something we can’t bypass, try to add an extra separate step that will allow us to bypass

If each step is independent from the other, this can be used to make modular malware that is dependent on the EDR solution we are attempting to bypass

First: Bypassing AV through AMSI AMSI: Antimalware Scan Interface API that allows any tool to do signature-based static analysis on scripts. Used by PowerShell, cmd.exe, Windows Defender, and sometimes external AntiVirus Loaded from amsi.dll

Key API Functions: AmsiInitialize AmsiOpenSession AmsiScanBuffer AmsiScanString

Using AMSI Example

Bypassing AMSI - Memory Patching

AmsiScanBuffer Analysis What’s happening here? Function Parameters are being checked If they are invalid, set eax to 0x80070057 (E_INVALIDARG) If the parameters are invalid, don’t scan the buffer

What does this mean? If we can make AmsiScanBuffer think the params are always invalid, we can make it so it never scans the buffer

Patching AmsiScanBuffer pt.1

Patching AmsiScanBuffer pt.2

Patching AmsiScanBuffer pt.3 (Obfuscation)

Without AMSI Bypass

With AMSI Bypass Loading Bypass

Other AMSI Bypass Methods: Fly the Fail Flag PowerShell has an attribute for AMSI integration called amsiInitFailed If this is set to true, AMSI stops Fake amsi.dll Force PowerShell to load a patched version of amsi.dll No longer viable PowerShell will crash if proper interfaces aren’t available in amsi.dll … https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell

One problem Sadly, AMSI patch will get detected by any decent EDR Done through tamper-protection Sophos:

Alternative Languages: Golang This is a snippet of code in Go that uses golang.org/x/sys/windows and syscall Works exactly like the PowerShell version Sophos doesn’t detect it

Another Alternative: Python From BC-Security/IronSharpPack

  1. PowerShell AMSI Bypass run and detected by Sophos
  2. Running Golang AMSI Bypass
  3. Loading Invoke-Mimikatz into memory doesn’t get detected

Important: AV isn’t that good… AV does a great job at detecting publicly available malware Really bad at detecting custom malware.␋ If your goal is to get a simple reverse shell or C2, you can write both and you probably won’t be detected https://github.com/adamkadaban/C2 (C2 written in Go) https://github.com/BlWasp/rs-shell (Revshell written in Rust)␋ If you want to run somebody else’s tools (eg. Mimikatz), then you use an AMSI bypass

Bypassing ETW

Bypassing ETW via memory patching ETW: Event Tracing for Windows Kernel-level logging for Windows events Is used by many advanced EDRs We can bypass similar to how we bypassed AMSI

Get a handle to our own process Identify the addresses of functions linked to ETW (EtwEventWrite, NtTraceEvent, …) Patch each function with a ret

Now… Finally on to EDR Bypasses

Payload Encryption If we are trying to give our Malware as many steps as possible, we can start by having a dropper/loader that will take encrypted shellcode and execute it in-memory If encrypted properly, it won’t be picked up by signature-based detection Running in memory will prevent analysis that happens on disk. ␋ Important to note that some encryption can look suspicious: XOR is known to be used for malware and can often be brute-forced AES is similar. You can use your own AES library HTTPS can be good, as it is common

Imports

Imports are a big deal IAT: Import Address Table Simple AVs can look at imports to determine what a program might be doing␋ Many EDRs will also hook API functions (either in user-space or kernel-space) to detect when a function is called

How do EDRs work?

NTDLL.dll without API Hooks

NTDLL.dll without API Hooks

NTDLL.dll with API Hooks Hooked Not everything is hooked

All EDRs work differently They will look at one or more of: Imports Strings Functions called In user-space In kernel-space In what order they are called … Parameters in functions called …

IAT Obfuscation through API Hashing We can dynamically load functions like so:

IAT Obfuscation through API Hashing Okay, so we got rid of some of the imports, but we still have strings in the binary

IAT Obfuscation through API Hashing

We can fix that with function hashing: Key Terms: PEB: Process Environment Block Structure containing information about the process (including modules loaded) Export Table Table containing all exported function in a module␋ Obtain the modules loaded in the PEB Loop through the modules and check every exported function If hash(func_you_need) == hash(func_exported_in_module) That means you have the right function and you can use it!

What if an EDR is hooking API calls?

User-mode hooking https://redops.at/en/blog/direct-syscalls-vs-indirect-syscalls

Direct Syscalls Instead of calling windows API functions as they are defined in a module, we can just write it ourselves with custom assembly.

If we do this, we skip the API function entirely and go straight to the kernel (via the syscall)

User-mode hooking (with direct syscalls) https://redops.at/en/blog/direct-syscalls-vs-indirect-syscalls

Direct Syscalls: Tools Several tools will implement direct syscalls for you: Syswhispers2 Syswhispers3 Hell’s Gate Halo’s Gate

What if the EDR is hooking syscalls?

The problem with direct syscalls Many modern EDRs have recently been switching to kernel-mode hooking The expectation is that when syscalls for Windows API functions get to the kernel, they come from ntdll.dll If they come from MaliciousProgram.exe instead, we know something is wrong

If we can make syscalls from the memory of ntdll.dll, then we can mitigate detection

Indirect Syscalls: One minor change Direct Syscalls Indirect Syscalls

Indirect Syscalls: One minor change Direct Syscalls Indirect Syscalls Notice the missing ret!

Indirect Syscalls We use the ret that’s in ntdll.dll Think ROP

This technique can be automated with Hell’s Hall

Stack Spoofing Even if we change where something was called and where something returns to, the call stack is still available to an EDR (if it uses ETW) This call stack looks more legitimate, but still allows an EDR to see the program calling the function https://redops.at/en/blog/direct-syscalls-vs-indirect-syscalls

Stack Spoofing We control the call stack of the program, because we are running the program We can simply modify the stack to make it look like our indirect syscalls are coming from ntdll.dll instead of from our program Many ntdll.dll exports call each other, so this is completely normal Key Terms: RUNTIME_FUNCTION Structure that contains an entry on the function table (includes stack frame size) Locate the RUNTIME_FUNCTION of the stack frame we want to make Create a fake stack frame: Decrement RSP by stack size, set [RSP] to return address Call jmp [RBX] to jump to the function we want to call

SilentMoonWalk https://github.com/klezVirus/SilentMoonwalk

Tricking Heuristics

Sleep Obfuscation Beacons often sleep (either directly or indirectly) with NtDelayExecution or similar to prevent detection The problem is that these functions are suspicious when they have an unusual stack trace We can bypass detection here by making a “timer queue object”, which will hold timers that execute callbacks. This is a timer that expires at a specific time and then will call a function after that amount of time. Essentially sleep, but it never makes the offending syscall We can use the NtContinue callback to continue executing the thread with a specific context

Sleep Obfuscation To prevent detection while sleeping: Make memory writable Encrypt memory Sleep (CreateTimerQueueTimer) Decrypt memory Make memory unwritable␋ Example implementation: Ekko

API Hammering Certain API functions are rarely suspicious and must take up time File I/O functions are particularly expensive Thus, we can make code “sleep” by doing a lot of useless I/O functions

CreateFileW WriteFile ReadFile

Picking your compiler Mingw always seems to get hits on VirusTotal This includes the Go compiler Even for “Hello, World”

MSVC compiler doesn’t seem to impact detection

Zig as a drop-in compiler has given me very good results at getting past static analysis

Customizing Your Payload For signature-based detection (Windows Defender, …) All you need is AMSI bypass User-mode hooking Direct Syscalls Kernel-mode hooking Indirect Syscalls For EDRs that use ETW (Microsoft Defender ATP (MDATP), Elastic, FortiEDR) ETW Bypass OR Stack Spoofing + Indirect Syscalls

The Final Product AMSI Bypass ->

The Final Product AMSI Bypass -> Loader that download payload ->

The Final Product AMSI Bypass -> Loader that download payload -> API Hammering on loader ->

The Final Product AMSI Bypass -> Loader that download payload -> API Hammering on loader -> Shellcode payload sent over HTTPS ->

The Final Product AMSI Bypass -> Loader that download payload -> API Hammering on loader -> Shellcode payload sent over HTTPS -> Process injection w/ indirect syscalls+stack obfuscation ->

The Final Product AMSI Bypass -> Loader that download payload -> API Hammering on loader -> Shellcode payload sent over HTTPS -> Process injection w/ indirect syscalls+stack obfuscation -> Sleep Obfuscation for injected code

Demo!

© 2024 Adam Hassan