Summary
- Introduction
- Tests scope
- APT macro analyses
- Observations
- Tests carried out:
- Bypassing CreateObject detection
- Crafting our easy custom undetected obfuscation
- Killing ViperMonkey
- Undetected code execution methods/functions
- Undetected methods/functions to get our malicious file
- Undetected autoexec sub events
- Results
- Suggestions for improvement
- Conclusions
- About the author
1.Introduction
One of the most common techniques for getting a foothold on a network client is based on Office files containing a malicious VBA macro. VBA macros are still widely used in a business context and, despite the mitigations offered by security vendors and Microsoft, it is still essential to detect evasion techniques in order to catch smart attackers.
On the attacker's side, there is plenty of public projects to setup, weaponize and obfuscate a macro while on the defenser's side there is a reasonable number of public projects, among the most famous projects to de-obfuscate and detect malicious VBA macro there are certainly olevba, MacroRaptor and ViperMonkey. At Certego, we also use these tools to perform automatic static analysis of VBA macro.
Today we would like to share a test carried out last week on the resilience of public logic offered by olevba, mraptor and vipermonkey. These tests were even more motivated by some autoexec sub functions identified in the wild that were not detected by our implementation of olevba.
All the tests carried out are aimed at encouraging the red teams to test new bypass techniques and improving the detection of olevba, mraptor and vipermonkey public projects.
2.Tests scope
- All tests were carried out on a fully patched Windows 10 client with last build and version.
- Macro based PoCs must work with medium integrity level.
- The tests carried out focus on the static analysis that can be performed by mraptor and olevba; in these tests we will consider out of scope the dynamic analysis from the point of view of a sandbox, an EDR and any ASR bypass.
- During the tests, we decided to include vipermonkey vba emulator in the scope.
- All the tests carried out start from the assumption that the latest versions of olevba, mraptor and vmonkey have been implemented out of the box.
- Common users are not always able to complete certain tasks keeping all ASR rules enabled, for this reason we consider disabled at least the following rules:
3.APT macro analyses
There are many ways to obfuscate a VBA macro, but how much do I have to obfuscate my macro to completely evade the analyses of a prepared blue team? It is important to know the weak points of a VBA obfuscation/evasion technique in order to detect it.
We started by learning from public examples of high-level attacks, then by analyzing through the tools mentioned above some noteworthy APT macro samples.
We decided to report the following three examples:
- 2019-06-06 gorodpavlodar.doc from APT28
APT28 is one of the groups with advanced TTPs.
VBA code:
olevba result:
2019-06-06 graphic.doc graphic.doc, document dropped from the previous sample and called through Word.Application COM.
VBA code:
olevba result:
- 2016-11-09 election-headlines-FTE2016.docm from APT29
The sample is a CDFV2 Encrypted with password 3209. password password source
APT29 is one of the groups with advanced TTPs
VBA code:
olevba result:
- 2020-03-03 비건미국무부부장관서신20200302.doc from Kimsuky APT
One of the more recent APT macro.
VBA code:
olevba result:
4.Observations
We have observed that:
- Some useful functions to obfuscate our macro are used to detect it. Ex: Evilclippy tricks like VBA stomping/pcode obfuscation are currently detected by olevba.
- There is no known way to obfuscate Sub Events, like AutoExec.
- There is no known way to obfuscate functions and methods.
- There are many examples of APT macros that prefer to leave the macro in plain text rather than obfuscate it through a well known technique or suspicious functions.
- Spotting Win32 Windows API through the Lib Declare Statement is trivial and in some contexts blue teams are beginning to block them through ASR.
- We also identified the following technical details related to the detection of the examined tools :
- Obfuscation performed through the Mid and the Array functions is not detected by the examined tools. There are plenty of ways to obfuscate the code and it does not seem worth detecting them all through a static approach.
- ExecQuery and other dangerous WMI methods to perform code execution like Create are not detected by the examined tools.
- ExpandEnvironmentStrings method of WScript.Shell Object is an Environ alternative to retrieve environment variables which not trigger by the examined tools.
Example:
- Other undetected alternatives could be:
- Using Environment Wscript.Shell method
- Calling an ExecQuery on Win32_Environment
- Querying directly inside HKCU\Environment
- In the common implementations of these tools some hits are systematically discarded to avoid false positives. Hex string detection in olevba is an example.
However, in well-managed infrastructures, it is possible to research some mandatory subroutines, methods and functions to systematically detect macros through a static approach, and to engaging an analyst.
5.Tests carried out
Starting from these osservations we have tried to build at least one PoC that performs 0 hits on olevba and a "macro ok" result in mraptor.
These are three minimum requirements for getting a foothold through a VBA macro:
- An Autoexec Sub Event to trigger our macro
- A code execution method/function
- Except for some trivial cases, you will normally need at least a COM/OLE/ActiveX Object, so you need to call at least one function like CreateObject.
Bypassing CreateObject detection
Even if performing alerting only on CreateObject is pretty impossible due to FP, it is possible to avoid this hit in some scenarios through GetObject.
At the time of writing, GetObject is not reported as a suspicious function by olevba.
Some automation servers have a running instance by default. Using one of these objects, it is possible to use GetObject, avoiding the call to CreateObject at all. See: differences between GetObject and CreateObject.
For these tests we decided to start with one of the most common objects with a default instance: Shell.Application.
Crafting our easy custom undetected obfuscation
After a first look we decided to use the CLSID avoid Shell.Application pattern match detection.
Here is how to easily get Shell.Application CLSID through powershell:
So instead of:
We used:
However the string was detected in a heuristic way by exchanging the CLSID with a hex string.
As mentioned above, it is a olevba hit that makes tons of FPs like this.
Despite this fact, we still tried to obfuscate this string creating our own obfuscation technique. It would also come in handy later to obfuscate our exploit.
After some research we discovered that adding chunks of strings to a variable containing an empty string prevents olevba from detecting and deobfuscating the string:
Note: without using a variable olevba detects the technique.
Killing ViperMonkey
After a quick test, we realized that ViperMonkey is able to decode our obfuscation method.
Despite being a big project, ViperMonkey is a experimental emulator, developers who integrate it into their platform are used to handling considerable number of crashes with legitimate macros, ergo seeing a ViperMonkey crash is not suspicious at all.
To crash vmonkey you need to find some VBA code accepted by our office program that crashes the vmonkey parser.
After some fuzzing we have identified the following simple code:
A declaration of a function with at least one argument inside an if statement will do the job. Microsoft Word will successfully run the macro:
The vmonkey parser will fail:
Undetected code execution methods/functions
We started digging into the documentation looking for unknown execution methods of Shell.Application Object
This is the section in the olevba code where the suspicious methods/functions list is defined by regex or keyword.
We have identified three interesting examples:
InvokeVerb and GitHub Pages: dropping an executable and calling the verb "Open" it is possible to run it. As soon as I spotted these methods I tweeted them. DoIt: slightly more tricky but same concept of invokeverb. ControlPanelItem: by dropping a malicious .cpl into the current directory, it is possible to execute malicious code While we were looking for undetected methods to perform code excecution, we have observed that, surprisingly, the Create method of Win32_Process, retrievable only through GetObject, is not yet detected by olevba.
However, we decided to avoid it because it has been used in many opportunistic campaigns and some custom implementations, such as ours, easily detect it.
All other interesting methods of WMI code execution, which are not detected by the analyzed tools, require a high integrity level, which is out of scope.
Undetected methods/functions to get our malicious file
Since we have identified three methods to launch an executable without arguments, we need a function to get our malicious executables.
In order not to leave our executable embedded in the document we decided to download our sample remotely through an UNC path
To achieve this task we decided to exploit SMB redirector or WebDAV redirector, this is because it is not possible to map a UNC resource on a unit or link it with a medium integrity level.
Allowing outbound SMB traffic is a bad practice, so we decided to take advantage of WebDav SSL (HTTPS).
So we decided to exploit the CopyHere.Application method to download our executables via WebDav SSL.
During the tests we also found that:
- Win32_Directory, a class with some filesystem methods not detected by olevba, does not support SMB/WebDav redirector.
- FilesystemObject supports the following undetected methods to achieve this task:
We decided to avoid FileSystemObject because it didn't work through GetObject, even if it is not enough to detect our samples it was interestingly keeping us on the 0 hits..
Undetected autoexec sub events
This is the section in the olevba code where the autoexec list is defined by regex or keyword
Hunting for undetected autoexec sub event:
https://docs.microsoft.com/en-us/office/vba/api/ov...
Docs/Office/VBA/Reference/
We have identified three interesting examples:
- Document_ContentControlOnEnter: it triggers when a user "enters" a content control, there are several content controls that can easily attract a user click.
- Worksheet_FollowHyperlink: it triggers when a user clicks on a link inside a worksheet, the link can also point to a worksheet component.
- Worksheet_Calculate: "Occurs after the worksheet is recalculated for the Worksheet object", this autoexec sub event is the most interesting we have envied: by using a cell with a formula that points to itself, it is possible to perform an autoexec without user interaction. As shown in the gif of the next section a prompt is shown to the user but, whatever the answer is, the macro is still executed.
Results
The outputs shown below are those of the following command:
olevba --decode --reveal <doc> # 0.56dev6 on Python 2.7.17
Document_ContentControlOnEnter.docm:
Worksheet_FollowHyperlink.xlsm:
In this case the VB_Base attribute triggers olevba, olevba exchanges the GUID for a Hex String. This is a known and usually filtered false positive. For this reason we can safely consider this sample as one with 0 hits.
Worksheet_Calculate.xlsm:
Also here as in the previous sample the hit is due to the VB_Base attribute.
Virustotal
For these tests only static analysis is significant, however these are the samples:
Document_ContentControlOnEnter.docm
Worksheet_FollowHyperlink.xlsm:
6.Suggestions for improvement
In order to detect the techniques proposed through olevba / mraptor, it would be important to note the following:
-
There are plenty of ways to obfuscate the code and it doesn't seem worth detecting them all through a static approach.
-
Although it is always important to take advantage of the attacker's mistakes, we think that the strength of a static analysis is to be able to detect all the keywords that cannot be obfuscated.
-
None of the tools analyzed detects the CLSID, even if dealing with strings it is particularly easy to obfuscate them.
-
There is a lot of room for improving the detection of Win32 Windows API.
-
This is the list of all the non-obfuscable keywords identified during our tests that olevba is unable to detect:
-
Filesystem interaction and UNC downloading functions:
-
Expand environment variables:
- ExpandEnvironmentStrings Wscript.Shell method
- Using Environment Wscript.Shell method
- Calling an ExecQuery on Win32_Environment
-
Functions and methods to perform code execution:
- Create
- InvokeVerb and InvokeVerbEx
- DoIt
- ControlPanelItem
- AppleScriptTask # OSX
-
Autoexec sub events:
-
The previous list was proposed on the public project through the following pull request: https://github.com/decalage2/oletools/pull/591
7.Conclusions
-
We have shown that digging in the documentation and with a little fuzzing it is possible to craft a 0 hit macro.
-
Chaining these samples with an unknown sandbox detection check and esoteric COM objects could have really unpleasant effects.
-
Implementing an open-source out-of-the-box solution may not be enough to defend your infrastructure.
-
It is very important to carry out periodic internal tests on all the detection components of your platform.
-
Performing periodic internal tests on all the detection components of the platform could significantly increase the detection capacity of your platform.