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:

Macro Code - 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:

Macro Code - Jump to Shellcode

The final value passed as the callback function in my case was 115215965, aka 0x6DE0E5D.

The allocated memory segment with RWX permissions stands out:

IDA Pro Memory Map - RWX Segment

This memory block contains some seemingly random data:

IDA Pro Memory Garbage 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:

IDA Pro Memory - Disassemble Shellcode Entry Point

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:

OllyDbg Harness - Find Large Section

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:

OllyDbg Harness - Data Section

.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):

OllyDbg - Confirm Shellcode Entry Point

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:

CFF Explorer - Modify 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):

IDA Pro - Scan for BULLSHIT

The shellcode continues to laboriously load DLLs, among them urlmon.dll. The URLDownloadToCacheFileA() function from this library is used to download the next stage:

IDA Pro - C2 URL Resolved

There is also an operating system architecture check:

IDA Pro - 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():

IDA Pro - Process Hollowing

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:

ProcExp - svchost suspended

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:

IDA Pro - Set Threat Context

I can attach to the running but suspended svchost.exe process and look for any segments with RWX permission.

IDA Pro - RWX Segments in svchost.exe

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.


Family (behaves like):

Hancitor / Chanitor  


// from WINWORD.exe

// from svchost.exe

Callout to get external IP:


Dynamically resolved and loaded DLLs:
IDA Pro - Stack View - Resolved DLLs

Strings of note:
IDA Pro - Stack View - Resolved Strings

usps85902802.doc hashes:

MD5: 03FD8CFB582F4AE09C2BC4E9D2172AC0  
SHA-1: 91C36066241D1C0D4574FDB9C6AA035EA486929B  
SHA-256: 45289367EA1DDC0F33E77E2499FDE0A3577A5137037F9208ED1CDDED92EE2DC2  
SSDeep: 3072:q2RxSO8YmDd3RJticBrsOmqHQvZ2YftJ+:JxoYmIO9JYF