In this post, I will demonstrate how to make the tool of your choice undetectable and bypass security products like classic Antivirus (AV) and Endpoint Detection and Response (EDR) systems.
As security professionals, penetrationtesters and red teamers, we often face challenges in ensuring our tools remain undetected by various security measures. The goal is to find solutions that require minimal manual effort and can be quickly applied to a wide range of tools, including those that are not open-source. This guide is intended for educational purposes only, and I encourage ethical use of the techniques discussed here.
In the first step we concentrate on bypassing static detection. The AV solutions provide signatures for known malicious code or programs that are recognized as harmful. These can contain a hash check as well as use certain code parts, strings or the like as a basis for detection. In order to avoid this detection, we have to know the signatures in detail in order to remove or obfuscate them from the binary. In addition, different AV solutions work with different signatures, which would mean that we may be able to create a bypass for a solution, but another product will still recognize the file as malicious. But since we originally said we wanted a general solution, we use a simple technique. We take the complete binary code and encrypt it.
In order to completely encrypt a PE file we have to use a secure encryption algorithm like AES. Below we have a short Python code that accomplishes this for our purpose
https://github.com/ZERODETECTION/BypassAV/blob/main/pe_2_aes.py
We're testing our initial PE file at Virustotal and the encrypted version.
As we can see, no AV scanner can now decrypt the encrypted code and therefore it remains undetected for a static scan. but how can we execute the encrypted code?
We could decrypt the PE file and put it back on the disk, but that would have the same effect because the on-access scan would check the file and recognize the original code as malicious.
Another solution would be that we don't take the PE file itself, but convert the PE file into shellcode. Shellcode can be executed directly from memory and does not need to be copied to disk as a file.
For the conversion we use the great tool donut. Donut converts the PE file into position-independent shellcode.
Position-Independent Code (PIC) shellcode is designed to execute regardless of where it's loaded in memory. This is particularly useful for environments like modern operating systems, which use address space layout randomization (ASLR).
https://github.com/TheWover/donut
We are not only pass the PE file but also the parameters to be executed to donut.
As we can see, this is also encrypted by Donut, but multiply AV products are still able to recognize it.
To get around this, we simply use our own encryption algorithm.
Now we have our position independent shellcode AES encrypted. To execute our shellcode we need to build a loader.
To execute the shellcode we create a simple C program that opens the shellcode, copies it into memory, decrypts it and then executes it in memory. This way we avoid the fact that the code is lying unencrypted on the hard drive. There is still a residual risk that the AV scanner will scan the memory and detect the malicious code there.
https://github.com/ZERODETECTION/BypassAV/blob/main/loader.cpp
We have now written a very simple loader, which executes it with the functions VirtualAlloc, AESDecrypt, RtlMoveMemory and a direct jump to the shellcode. We should note that we have marked a memory area as RWX executable for executing the shellcode. For some AV products, this is a crucial signal that potentially malicious code could be executed here.
We should also note at this point that our loader loads the shellcode from a file and has not integrated it. This has advantages and disadvantages. It would also be possible to embed the shellcode into the executeable without much effort.
In order to reduce the detection rates even further, we rename the decryption routine and also change the parameters.
With this modification we can achieve that the Microsoft Defender, which has a high prevalence, no longer recognizes the loader as harmful. Even the AI based static detection of Sentinel One also no longer recognizes this sample with this modification.
In order to ensure a complete bypass of all products, we could now make further changes. but that would end in a trial and error principle. It is now important to check what the dynamic detection looks like. Currently we have only tested the bypass for dynamic detection.
Now it's time to test the dynamic execution. We run the loader on a VM with Microsoft Defender and check whether the shellcode is also executed accordingly or whether the AV Scanner recognizes the execution or stops it. Most AV scanners have cloud and sandbox functions, which means that the samples are also checked on runtime without user interaction.
In order to prevent our sample from being uploaded, we always turn off the cloud upload. We should also avoid checking with Virustotal if we don't want our loader to be recognized by AV solutions at short notice.
As you can see, we can now execute this while bypassing the AV solution without modifying the PE file.
We have therefore achieved our goal, we have taken a known malware / dual-use tool and successfully executed it undetected on an AV-protected system without any individual adjustment. The method shown here should also be adaptable with other programs, pentest tools or even malware.
When using zerodetection and the Implant Builder, these techniques are also used, but you can choose between over 50 templates with a wide variety of techniques and tactics. In addition, the entire process is completely automated.
https://www.zerodetection.net/demonstration.html
Even if the execution worked at first glance and was not recognized or blocked, it cannot be ruled out that, depending on the activity, the AV scanner may recognize the software. This can be done through behaivor detection as well as based on telemetry data or artifacts in memory can also be recognized.
It makes sense to clean the memory or memory areas after execution in order to avoid leaving any residue behind that will be detected when the memory is scanned later.