PropertyBomb – An Old-New Technique for Arbitrary Code Execution in VBA Macro
A few days ago, BitDam’s Deep Immunization (DI) Engine detected a malicious file with an interesting behaviour, which we suspect was designed to bypass common dynamic analysis solutions.
The file uses a VBA macro in order to run a binary-level shellcode, used to download and execute a second-stage payload.
The file was found in the wild back in February, under the name ‘ADV_13022018.xls’.
However, it’s modus operandi was quite different from “standard” VBA macro attacks, and no information about this file nor it’s operation techniques were found out there in the web.
This technique is unique, as far as I could find online. It’s “Old-New” because the file was found in the wild back in February, but as it seems, no one has paid attention to its operation, which I found quite interesting. The “PropertyBomb” is using window’s property lists as a trigger for running arbitrary code that was made in VBA macro.
When the file is opened, it asks the victim to “Enable Content”, using social engineering in order to make the malicious macro run automatically.
When the content is enabled, the “regular” scenario is that Excel exits.
It was time to dig a little deeper in order to understand what the file does:
When opening the VB editor, we can see very long, complex, obfuscated code. The code deals with both VBA 7 and under – for compatibility reasons. We can see that clearly with If-Else statements in the next figure:
We can see that the attackers used “Sub WORkBooK_oPen()” instead of “Sub Workbook_Open()” in order to try to bypass static analysis. We can also notice the declarations of some WinAPI functions (in yellow).
The code that uses these imports:
In order to simplify, I’ve converted it’s logic to a simple “C” code:
“C” code summary:
The attackers created an execute-enabled heap object to allocate an Execute-Read-Write memory region in the heap. After creating an array of numbers from 0-255 in VBA (represented in “Shellcode”), the numbers (Chars) are copied into the allocated heap. The calculated array is actually a shellcode that will run later on. Using a handle to the desktop window, an entry in it’s property list is added, and by calling EnumProps – the shellcode starts to run (now in “Callback” after being copied from “Shellcode”).
We can see the Callback buffer before and after the initialization using Windbg:
Filling an array in VB that will contain an arbitrary code is pretty easy. The file used a lot of de-obfuscation techniques in order to build the code that will run later on.
However, the interesting piece was just how the callback was triggered later on.
The call to SetPropA gets a handle to window, string and data as parameters.
So in our case it adds to the Global Atom Table a new entry (“sj1m6”), and gets an ATOM. This ATOM is then passed to NtUserSetProp routine in win32k.sys, where a new property list entry is added.
So now we have a new entry in the window’s property list (in our case – the Desktop Window), and a new entry in the Global Atom Table.
Moving forward to EnumProps, the function gets two parameters – the Handle to the window and a PROPENUMPROC callback.
Briefly, it allocates memory in heap, calls NtUserBuildPropList routine in win32k.sys which fills the buffer with PROPERTY_LIST_ITEMs (tries to do so every time until gets STATUS_SUCCESS, each time with a bigger buffer by shifting left by 4).
After it got successfully all the property list items, it searches for the atom name in the Global Atom Table (added previously in SetProp).
When it was found, it proceeds to calling our Callback:
So this way, we can easily pass an array of bytes, made in VBA macro, copied into executable pages and run it. Nice!
The shellcode itself is mostly XOR-encrypted, and only few portions if it are used to decrypt it (or more precisely – 1145 bytes of it:
After decryption it is hard to miss the parts of strings that appear in memory.
By extracting the decrypted code and viewing it in a disassembler, we can see the hardcoded strings much better:
(Note: address 0x0000007B is actually “mov eax, dword ptr fs:[30h]”. Because I extracted a binary blob, IDA fails to understand it’s the PEB we’re looking for 🙂 )
We can also see the memory of the stack, and notice that all of these constants are actually strings that are passed to the stack memory:
Following up with the shellcode’s flow, the malware does the following (not necessarily in that order):
- Finds kernel32.dll base address from the process’ PEB
- Finds kernel32!LoadLibraryAStub and kernel32!GetProcAddressStub using pre-calculated hashes
- It will later on find urlmon.dll and shell32.dll bases using kernel32!LoadLibraryAStub
- It finds the pointers to functions:
- kernel32! ExitProcessImplementation
- kernel32! ExpandEnvironmentStringsW
- urlmon! URLDownloadToFileW
- shell32! ShellExecuteW
- It expands %TEMP% using ExpandEnvironmentStringsW, it tries to download a second stage payload from hxxp://msudryuyer[.]org/sql/omoku.exe into the Temp directory, and save it as a file named “elam.exe”.
- If it downloads the file successfully – it will then call ShellExecuteW and execute elam.exe.
- Either way, it will call ExitProcess right after it is done.
The second-stage payload itself is a subject for future investigation, in this post I wanted to describe this original method for executing arbitrary binary code from inside a VBA script.
About the author