During an email phishing engagement, one of the attacks we typically use is a malicious Microsoft Office macro. For the unfamiliar, macros are bits of code embedded in Office documents. They are written in Visual Basic for Applications (VBA), and are intended to automate tasks within a document, such as scraping data from a website, formatting content, or performing calculations. While macros can obviously interact with the content of a document, they can also interact with other files on the computer as well as execute programs. This allows them to be abused.
Malicious macros are not a new attack, and most users are protected by multiple layers of defensive measures to stop malicious code from executing. For the victim to receive the document, it would typically need to pass through any email scanning and filters that are in place. Then, before the document can be opened it would have to survive any antivirus software running on the user’s computer. Finally, Microsoft Office will prompt to put a document in “editing mode” and again to “enable content”. A user would have to click through both warnings for a macro to run. Even at this point, good endpoint detection and response (EDR) software can still prevent malicious macro code from executing.
While that sounds like a lot of hurdles to clear, and it is, this attack is still successful time and time again. There are many ways to write a macro, and many ways to disguise malicious code as something more benign. This makes it hard for defensive measures that often favor signature-based detection to protect against all variants of this attack. Often if you can evade a particular defensive measure, you will make it past a few others.
For a typical spear phish, we will use a domain of our own with an email provider that doesn’t filter any content (such as GoDaddy). Sometimes however, it is more convenient to simply create some Gmail accounts, or perhaps we need to send a phishing email to someone using Gmail.
With over a billion active users, Gmail is the world’s most popular email provider. Gmail includes a virus scanner that prevents users from sending and downloading certain file types as well content that is considered malicious. There are a couple dozen file types that are outright prohibited, mainly types of scripts and executables.
Google also scans files types that are allowed to be attached and will block anything it considers to contain malicious content. Attaching a word doc is no issue, but attaching a malicious macro tends to give you one of these:
Let’s start with the example of a macro generated by the popular pen testing tool Cobalt Strike (https://www.cobaltstrike.com/). This macro contains a stager for a beacon in the form of shell code. When the macro is run, it will execute the shell code which will in turn download the code to establish a command and control channel and execute that. The biggest advantage to this macro is its ease of use: you click generate, paste it into a document, and you are done. The downside is that because it comes from a popular tool, it has a well-recognized signature. In addition, shell code is relatively easy to detect. Any decent antivirus program will immediately eat your document, including Windows Defender. 28/58 engines on VirusTotal detect this file.
Attack flow diagram 1
Interestingly, Gmail doesn’t complain about this macro, but with the high AV detection rate, this isn’t going to cut it.
Instead of executing shell code, we prefer to call an existing Windows program and use that to download and execute malicious code. Still using Cobalt Strike, we can create a “scripted web delivery” PowerShell payload. Cobalt Strike will provide a PowerShell one-liner for downloading and executing this payload which looks something like this:
We can use a WScript.Shell object in a macro to execute this command. The Document_Open() function will run when the file is opened (this example is using a Word document).
This macro gets past more AV programs including Windows Defender, but is still caught by several as well as Gmail’s virus filter. 19/57 engines on VirusTotal detect this file.
Attack flow diagram 2
The most questionable (aka detectable) part of the macro right now is the PowerShell one-liner. If we can remove that code from the document, it should be a stealthier attack. Instead of including it directly, let’s host that PowerShell command in a text file on the Cobalt Strike server and download it from there. The contents of Document_Open() then becomes:
We now get past Gmail’s virus filter undetected. 5/58 engines on VirusTotal detect this file.
Attack flow diagram 3
Macro v4 and Beyond
Instead of hosting the text file and PowerShell payload on the Cobalt Strike server (which a web proxy may not trust), these files could instead be uploaded to GitHub or another hosting solution that is likely to be allowed in any environment. For this method, a stageless PowerShell payload should be generated in Cobalt Strike so that only one payload file needs to be hosted and downloaded. This payload can be downloaded and executed with the same one-liner as above.
Attack flow diagram with trusted file hosting
Disguised PowerShell Payload
To disguise the PowerShell payload file from any web proxies, we can XOR each byte before hosting it, and then XOR again while we execute it. To any network inspection tools, it will not be identifiable as anything other than binary data. Again, a stageless PowerShell payload should be used for this method so that only one payload file needs to be hosted and downloaded. Here’s a simple PowerShell script to XOR the payload that will be hosted:
Assuming the output file is named “x” and hosted on the Cobalt Strike server, the one-liner (contents of the hosted text file) becomes:
Note the “0x6A” in both the script and the PowerShell one-liner. This can be any one byte value, but they need to match for the payload to be properly decoded.
Attack flow diagram with disguised PowerShell payload
To avoid executing our payload in sandboxes (and thereby being detected), we can ask the macro to send computer parameters with the request for the PowerShell one-liner. If the parameters are not as expected, we return an innocuous command instead of the PowerShell string. The last time we used this macro attack we knew the domains of the intended targets and could check against that. The first step is to send the user domain in the request for the command to run.
Instead of hosting a text file with the command we will use a PHP file to check the domain and return a command accordingly. You will need to use a webserver that supports PHP to host this file. For this example, let’s say our target domains are “SECURITY” and “RISK”. Our PHP file would be:
Unless the d parameter is set to “security” or “risk”, the file will only return “calc.exe”. The domain could instead be checked in the macro itself, but any manual inspection would immediately be able to uncover the payload since the check would be client-side.
Attack flow diagram with sandbox evasion
It is possible to combine these improvements for the ultimate stealthy macro. We would create a stageless PowerShell payload in Cobalt Strike and XOR that payload using the script above. Assuming we are still targeting the domains “SECURITY” and “RISK”, our PHP file would be:
Both the XORed payload (named “x” here) and the PHP file (named “a.php”) would be hosted on a trusted web server. The full macro function would then be:
Custom code is detected less
When many detection methods rely on signatures – whether that be a file hash, or a file containing certain strings – coding the same thing a different way is going to bypass some of those algorithms.
Get the malware out of the deliverable
Hosting the malicious code elsewhere greatly reduces the chance for tools to detect it. A full sandbox is needed to determine exactly what the macro is going to do. Even then, we can change our text file to dynamically serve different commands based on data from the requesting client in an attempt to evade sandboxes.
Implement an Endpoint Protection Solution
Much of this attack can be customized again and again to look somewhat different. The place where it will always look the same is when Word, Excel or another Office application launches another process. A good endpoint detection and response tool can stop that process from being started from an Office document.
Disclosure to Google Regarding Gmail Virus Filter Bypass
We reported our findings about bypassing Gmail’s virus filter to Google on July 18, 2017. They initially responded that they were looking into it, but ultimately decided “not to track it as a security bug”.