XLSB: Analyzing a Microsoft Excel Binary Spreadsheet

The @InQuest crew has been putting some unusual documents out on the Twitters and I thought I’d take a closer look at one of them. And as ALWAYS, new documents are never that straight forward to analyze. Attackers always put a twist on what has already been done.

In this case, we’ve got an .xlsb file, XLM code, hidden sheets, protected sheets, and the ever-so-sneaky-hiding-in-plain-sight white font.

Here’s the doc: https://app.any.run/tasks/47e1c347-664c-4ada-9655-1724387e859e/#

Microsoft Excel Binary Spreadsheets (.xlsb)

I can’t say that I’ve ever seen these until now. They open and function like any other spreadsheet. The difference is under the hood. They store the spreadsheet using a binary format (BIFF12) rather than the typical .xlsx or .xls. This means that .xlsb files are usually bigger as they’re not compressed. Extremely complicated spreadsheets (ones with lots of formulas, charts, and shapes) can benefit from this file format as they may save and load much faster.

But this format means that some of our normal analysis tools do not work. For example, oledump can’t find any OLE objects in the file.

OfficeMalScanner will expand the document if you use the inflate option. However, the output doesn’t show the normal vbaproject.bin. There’s a ton of other files that are NOT vbaproject.bin. (Of course, there’s no vbaproject.bin. Oledump.py didn’t find any macros, right?)

In hindsight, all of these .bin files make sense as this is an .xlsb file. It certainly didn’t make sense the first time I cracked it open, though.

Analysis from within the document

First, there are a bunch of hidden sheets.

Quite a few of these sheets contain a lot of nonsense.

However, Auto_Open is pointing to Sheet11!A1. If we go there, we can see a empty columns containing XLM in white font.

And of COURSE we can’t edit the font because a bunch of these sheets are protected AND we don’t have the password!

It is possible to enable content and step through the XLM like we’ve done before. However, the other sheets contain macro code and characters that are spread out all over the place. That’s annoying enough to analyze even if it wasn’t in white font.

Unprotecting the sheets

All hope is not lost. We can bypass the protected sheets if we save the document in a different macro-enabled format like .xlsm. This will also change the worksheets from .bin files to .xml files. This will be important in a moment.

Change your newly created .xlsm file to a .zip and navigate your way into xl\worksheets and xl\macrosheets to find .xml sheets. We will un-protect these sheets by taking out a section called sheetProtection. Delete everything from <sheetProtection to its trailing />, including the two < > characters, and then save the .xml. Delete sheet protection wherever you find it.

You’ll have to drag the various .xml files out of the .zip file, edit them, and then copy them back into their appropriate locations. Finally, change the file from .zip to .xlsm.

Analyzing the XLM

Now that the sheets are unprotected, we can finally get rid of that white font and see the XLM. Auto_open is pointing to Sheet11!A1. I color-coded the extraneous lines and analyzed the three important =CALL() commands.

Ultimately, it downloads a .dll to C:\ProgramData\fps\ and registers that .dll via rundll32.exe.

As a bonus, Excel does some of the XLM concatenating for us making analysis easier. Here’s an example from Sheet1. You can see that cell L3 contains =CONCATENATE(Sheet…), but the cell itself shows the result.

Good times!

Thanks for reading.

Trickbot/Excel 4.0 macros: There’s got to be a better way

Zloader malicious documents that make use of Excel 4.0 macros are neat and tidy. They’ve got the entire macro all lined up in nice and even rows. They all get executed in order starting with column A, then B and so on to column P.

As the macros run, column Q gets filled up with more commands. Cell P21 says to jump to Q4 and keep executing whatever is there.

This is not the case with this document here: https://app.any.run/tasks/0e65341d-54c0-4886-ba2b-22b91665d922. If we unhide all of the extra sheets, we see what seems to be an empty macro. However, if we zoom all the way out, we can see data scattered all over the place.

What is similar

Like the zloader Excel 4.0 macros, the commands start in column A and work their way down. Then they go to column B and so on until the final column (in this case, it is column IO). These macros also write new data to new cells. This new data is used to put together a string to download data from a URL.

What is different

The big difference is that the new data is scattered all over the place. As =RUN() commands are executed, new data is written elsewhere. Who knows where it is going to land? The sheer size of these worksheets makes analysis difficult.

How to analyze?

Behavioral analysis can get you the URL that the document is trying to reach. Fire up Wireshark, enable content, and you ought to get what you need.

What about working within the document itself? If we have the document open and ‘enable content’, the macro will execute and new data will be written to cells. It is likely those cells will contain strings that get executed by the macros. However, those strings will be scattered all over the place. How can we get rid of all that white space?

It is possible to do this in Excel. We can select all of the blank cells and then shift everything up. First, go to “Find & Select” and choose “Go To Special…”, select “Blanks”, and then click OK.

Once all of the blanks are selected, press CTRL – (minus sign). Select “Shift cells up” and click OK.

All of the empty cells will be removed and the cells with data will get moved to the top of their columns. It is important to note that many of the =RUN() cells will be broken thanks to shifting all of the cells up.

We can then start scrolling to the right to look for strings that weren’t there before.

Here’s the URL that it’s call out to. URLhaus has it tagged as Trickbot.

There’s got to be a better way…

So what’s faster? Wireshark? A bunch of clicking and screwing around with Excel? I don’t know. But there’s got to be a better way. At least, I hope there’s a better way. Maybe this will spark an idea for someone.

Until then, thanks for reading!

COVID-19, Excel 4.0 Macros, and Sandbox Detection – #zloader

This email came across my desk this morning. It has a COVID-19 theme along with this .xls attachment.


First things first, we can see in the first picture below that it does say we’ve got an Excel 4.0 macro sheet, very hidden. This is good information for how to tackle this document. While olevba.py does extract the OLE stream, the output isn’t all that helpful. I don’t blame that on the tool. I believe it is due to the way this macro has been obfuscated. Either way, we can tell that something is going on here.


This blog post from SANS shows how to examine the OLE object using oledump.py. Again, the output isn’t very helpful.

Getting to the macro

How then can we examine the actual macro inside the document? Notice how the output of both olevba.py and oledump.py showed that the Excel 4.0 macrosheet was very hidden.

Google tells you that there are multiple ways to get these sheets visible. I tried a bunch of them and the method described here worked for me. You will need the following code:

Sub ShowAllSheets()
    Dim sh As Worksheet
    For Each sh In ActiveWorkbook.Sheets
    sh.Visible = True
End Sub

Copy that into the exiting macro sheet like so and click play/Run/(press F5).


After running the macro, you will see the unhidden sheet:


This is where things get interesting. We can see columns A – O full of =CHAR(xx) characters. They all get sewn together in column P and the output is tossed in column Q. I’ve hidden a few of the columns to show what’s going on a bit easier. Please pardon my anemic drawing skills.

In the section below, the formula in P1 takes all of the characters from column A, sews them together, and places them in column Q4. The rest of the commands are put together in a similar manner by continuing down column P and adding more commands to column Q. The last line in column P then says =GOTO(Q4).

Column Q: Macro Analysis and Sandbox Evasion Techniques

Let’s look at the commands in column Q section by section. I found the .pdf here to be very helpful in understanding what is going on.

This first section of the macro shows the first attempt to see if it is being executed in a sandbox. GET.WORKSPACE 19 and 42 check to see if a mouse is present and if the computer is capable of playing sounds, respectively. GET.WORKSPACE(1) checks the environment in which Microsoft Excel is running followed by the environment’s version number.


If those three are true, then copy the Excel security registry keys to C:\Users\public\1.reg.

=CALL("Shell32","ShellExecuteA","JJCCCJJ",0,"open","C:\Windows\system32\reg.exe","EXPORT HKCU\Software\Microsoft\Office\"&GET.WORKSPACE(2)&"\Excel\Security c:\users\public\1.reg /y",0,5)

Next, it waits for three seconds. It then opens 1.reg, starts at byte position 215, and reads the next 255 bytes.

=FPOS(Q10, 215)
=FREAD(Q10, 255)

What is in that section of 1.reg? Below is the highlighted section in a hex editor and Notepad. It contains the registry values for AccessVBOM and especially VBAWarnings.

1.reg gets closed and deleted. It then searches what was read (stored in cell Q12) for the string “0001”. This is the second test to see if it is in a sandbox. If it finds that string, it will close the spreadsheet. If it does not find the string “0001”, it will then attempt to download a file and save it as an .html file in C:\Users\Public\.


What does the string “0001” have to do with anything? Changing the settings for what Excel does with macros changes the registry settings for VBAWarnings. If our macro sees that VBAWarnings are set to 1, this means that “Enable all macros” have been set. And only sandboxes would have macros set to always run… right?

So as a reward for having your company’s group policy set to disable all macros, our malicious document then shows an alert, followed by a call to run the saved .html file (which by the way is actually an .exe).

=ALERT("The workbook cannot be opened or repaired by Microsoft Excel because it's corrupt.",2)

I’m not exactly sure what initial loader gets downloaded, but I will update this when I get more info.

UPDATE: Word on the street is zloader gets downloaded.

Until then, thanks for reading!