zloader: VBA, R1C1 References, and Other Tomfoolery

The other day, @reecDeep tweeted about new behavior from zloader documents. Another document from the same campaign crossed my path and I decided to take a crack at it.

SHA256: B29C145D4B78DAED34DEA28A0A11BAB857D5583DC6A00578A877511D0D01D3D2


Row and Column References

One of the first things you may notice is that this document doesn’t have the typical letters designating the columns. Instead, this document is using the R1C1 reference style. This was not done by accident. The Excel 4.0 macros used throughout this document depend upon this format.

Getting around “Enable content”

The first sticking point was getting the ability to control execution of the macro. This proved to be a bit difficult. If the ‘Enable Content’ button is showing up, this means that some macros must exist, right? However, the VBAProject contents showed both Sheet1 and ThisWorkbook as blank.

So there were no macros… yet we were still being prompted to enable them. If you did enable them, the macro would execute with no opportunity to interrupt anything before the document would close. I decided to add a simple macro to the project to see if that would help me control execution.

I noticed that when I saved my changes, the size of the document changed.

Opening the copy while holding down the shift key brought up this security notification. Choosing “Enable Macros” allowed me to control execution and continue the analysis.

Finding the Entry Point – R27455C174

As this is an XLM 4.0 macro document, the macro commands in the cells will execute sequentially until the commands send execution path elsewhere. Possible commands for which to search would be =FORMULA or =GOTO. I started by searching for =FORMULA. Once I found one, I started to step through the macro code to see what would happen. It took a few tries, but the entry point for this document is R27455C174. From here, you can right-click that cell and select Run.

We can also see how this document makes use of the R1C1 notation. From what I understand so far, a positive number means you add that number of rows/cells to the current row/cell, and a negative number means you subtract that number of rows/cells to the current row/cell. In this case, it seems that the row being referenced is 51762 rows down and 81 columns to the left. However, I tried going to that cell but found it to be empty. I might be missing something obvious, but in the grand scheme of things, knowing exactly how this particular cell works is more of an academic exercise.

Either way, you could just right-click on the cell, choose Run, Step Into, and then Evaluate a few times get the code execution rolling. You’ll see that =GOTO(”) ends up moving you to cell R46304C95.

BLOCK 1 – R46304C95

This cell is where characters from other cells are assembled into a string. We can see that the first one is “=CLOSE(FALSE)”. We can continue evaluating all of these until we get to the =GOTO() at the bottom.

BLOCK 2 – R48037C63

That =GOTO() takes us to R48037C63. This cell fills in the cells below with the same string. The commands in the following cells take the strings from Block 1 and write them to a new location. For example, let’s look at R48038C63. It says to take the information in a cell that is 1734 rows up and 32 columns to the right and move it 14892 rows up and 20 columns to the left. This continues on until the =GOTO() at the bottom of this block.

BLOCK 3 – R33147C43 – Evasion checks

The next block starts at R33147C43. It contains everything that was written above. Let’s analyze it in pieces. The first portion contains the familiar sandbox checks. Notice how if any of those checks fail, you GOTO(R33146C43). That cell contains =CLOSE(FALSE) which immediately stops execution.

A .vbs file is then created in the C:\Users\Public folder. The lines in cells 33160-65 are written to that file which is then closed.

The next section executes the .vbs file. This file reads information from the system registry containing VBAWarnings. The output is returned to the .txt file. The .vbs file is then deleted, the .txt file is opened, read, and deleted. If the .txt file contains a 1, go back to =CLOSE(FALSE). If not, check environment. If it has a 32 in the results (which it does), GOTO(R13419C196).

BLOCK 4 – R13419C196

This brings us to yet another series of cells getting assembled into strings. We can step through them as before to the =GOTO(”).

BLOCK 5 – R28840C118

Once again, this block takes the strings from above and copies them elsewhere.

BLOCK 6 – R38562C99

And finally, we can see the final execution commands in this document. There are four URLs from which to download a file to C:\Users\Public\lxlGZ4A.html and execute it using rundll32.exe. Notice that if it fails the size check, it doesn’t go through the rest of the URLs. Instead, it immediately jumps down to the ALERT message. I’m guessing this helps to hide the rest of the URLs from showing up in Wireshark or something.


Many of the sandbox evasion techniques used are the same as before. The added difficulty was the use of (nonexistent) VBA macros, more ways to disguise the commands being run, and ways to hide the other URLs.

As always, thanks for reading.

5 thoughts on “zloader: VBA, R1C1 References, and Other Tomfoolery

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s