Malicious Document Analysis – Macro to Shellcode
I came across an interesting Word document which at first glance definitely looked malicious. It had everything from random variable names to lyrics from Garbage - Run Baby Run in the comments. It also used an uncommon but very cool method for moving from macro to machine code execution.
Macro Breakdown
The macro starts by creating pointers to DLL exports already loaded in memory by Word:
'Run from the noise of the street and the loaded gun
Public Declare Function whitworth Lib "kernel32.dll" Alias "VirtualAllocEx" (castroism As Long, accidence As Long, ByVal graze As Long, ByVal footstool As Long, ByVal vicariate As Long) As Long
'Life can be so cruel
Public Declare Sub intermission Lib "ntdll.dll" Alias "RtlMoveMemory" (septentrional As Any, ByVal ploy As Any, ByVal cinders As Long)
'Every time you give yourself away
Public Declare Function bruckenthalia Lib "kernel32.dll" Alias "EnumTimeFormatsW" (ByVal khartoum As Any, ByVal grinding As Any, ByVal fie As Any) As Long
'So run my baby run my baby run
Public Declare Function briefcase Lib "user32" Alias "SetParent" (ByVal anomalousness As Long, ByVal chewink As Long, advert As Long) As Long
'Run my baby run my baby run
Public Declare Function ex Lib "user32" Alias "GetUpdateRect" (drakes As Long, lubricitate As Long, pelham As Long) As Boolean
'You can keep it pure on the inside
Public Declare Function charioteer Lib "user32" Alias "EndPaint" (guttling As Long, cervical As Long) As Long
'You can keep it pure on the inside
Public Declare Function gigartinaceae Lib "user32" Alias "OpenClipboard" (enliven As Long) As Boolean
'You can keep it pure on the inside
Public Declare Function malnourished Lib "kernel32.dll" Alias "Sleep" (subsequent As Long)
Of note are the function pointers whitworth
and bruckenthalia
which allows the macro to allocate memory (VirtualAllocEx
) with the right permissions, jump to it and execute arbitrary code.
VirtualAllocEx
is fairly straightforward in that the macro uses it to return a pointer to a memory block set as RWX
(read-write-execute permissions).
EnumTimeFormatsW
is more interesting:
BOOL EnumTimeFormats(
_In_ TIMEFMT_ENUMPROC lpTimeFmtEnumProc,
_In_ LCID Locale,
_In_ DWORD dwFlags
);
The macro can use the first parameter to pass a pointer to an arbitrary function in memory and have it executed on return. According to MSDN:
lpTimeFmtEnumProc [in]
Pointer to an application-defined callback function. For more information, see EnumTimeFormatsProc.
Walking through the macro execution I can see that the value returned by whitworth
is a long integer which translates to a location in memory. Specifically, the address returned by VirtualAllocEx
:
The address will vary but in this case whitworth
returned 115212288
, which translates to 0x06DE0000
.
Right before calling EnumTimeFormatsW
and ultimately transferring control to shellcode, the macro adds an offset of 0xE5D
to the address:
The final value passed as the callback function in my case was 115215965
, aka 0x6DE0E5D
.
The allocated memory segment with RWX
permissions stands out:
This memory block contains some seemingly random data:
Since the callback function passes in the 0xE5D
offset, I can assume at this location there is code and I should be able to disassemble it:
A breakpoint here will allow me to see what this thing is doing once it transfers control from the macro.
Shellcode Harness
To make debugging easier, I can save this section of memory to disk and add it to a shellcode harness. Any executable with a section large enough to hold the size of the shellcode (around 12kb) will suffice:
This particular executable (sc.exe
) has a .data
section large enough to hold the shellcode. I can safely overwrite the whole section with the shellcode and dump the changes using the OllyDumpEx
plugin.
The resulting harness executable’s .data
section should look something like this:
.data
can be disassembled and I can see what will become the new entry point for the shellcode harness executable (the location the macro jumps to when transferring control):
The last step is to modify the PE header for the harness executable and make sure the Entry Point offset is set to 0x0000CE5D
. This should land in the .data
section right on the shellcode entry point:
IDA should now be able to disassemble the shellcode and I can take a closer look.
Shellcode Analysis
The code goes through a great deal of trouble to stealthily load the DLLs it needs and download the next stage.
It’s a good idea to pass Access Violation
exceptions from the debugger to the application as they will interfere with the reversing process.
The shellcode first checks for the occurrence of BULLSHIT
somewhere in the process memory space. The original document had this embedded and this check would normally pass.
The first check is at ESI=0x00001000
for BULL
and the second is at ESI+0x4
for SHIT
.
I can either add the word in the memory space or set the zero flag after each CMP
instruction (EFL=0x00000040
):
The shellcode continues to laboriously load DLLs, among them urlmon.dll
. The URLDownloadToCacheFileA()
function from this library is used to download the next stage:
There is also an operating system architecture check:
On x64 systems, svchost.exe
is executed from the %windir%\SysWow64
folder (32-bit). On x86 systems %windir%\explorer.exe
is launched instead.
It looks like the process is started with the CREATE_SUSPENDED (0x00000004)
flag, followed by a call to NtUnmapViewOfSection()
:
This appears to be a technique called Process Hollowing, in which a common process is launched in suspended mode and its code replaced by attacker code:
In this case more shellcode is injected into the hallowed svchost.exe
process. The thread context is adjusted and the thread is resumed, executing malicious code:
I can attach to the running but suspended svchost.exe
process and look for any segments with RWX
permission.
If I set a break-on-access
breakpoint on this segment in the hallowed process, I can start the analysis for the next stage of the shellcode and grab more IOCs. Once the WINWORD.exe
shellcode completes, the on-access breakpoint in svchost.exe
should trigger at 0x004020D0
:
…and the process starts all over again :)
There’s lots of other interesting stuff in svchost.exe
, including some anti-debug and more C2 hosts.
#IOCs
Family (behaves like):
Hancitor / Chanitor
C2:
// from WINWORD.exe
hxxp://hoentoftfa.com/blt/path1.php?v=[1-9]{2}
// from svchost.exe
hxxp://hoentoftfa.com/ls5/gate.php
hxxp://gonynamo.ru/ls5/gate.php
hxxp://forpartinsa.ru/ls5/gate.php
Callout to get external IP:
hxxp://api.ipify.org
Dynamically resolved and loaded DLLs:
Strings of note:
usps85902802.doc
hashes:
MD5: 03FD8CFB582F4AE09C2BC4E9D2172AC0
SHA-1: 91C36066241D1C0D4574FDB9C6AA035EA486929B
SHA-256: 45289367EA1DDC0F33E77E2499FDE0A3577A5137037F9208ED1CDDED92EE2DC2
SSDeep: 3072:q2RxSO8YmDd3RJticBrsOmqHQvZ2YftJ+:JxoYmIO9JYF