Cofense Logo - Email Security Solutions

Down the Rabbit Hole

Share Now


I regularly check each of our sandbox environments for reports with suspicous yara rule hits and yet no IoCs or network traffic. If any of these reports are maldocs, then I attempt to deobfuscate them and discover whether a sandbox check resulted in the failure to detonate. During one of these check-ups, I discovered that one environment was having no problem detonating an XLM maldoc where another environment was failing. And to make matters worse, a tool that I developed to speed up deobfuscation of Excel documents was also failing to dump any useful cells. We know from our signature hits that this is an XLM maldoc, but let’s run it through a couple triage tools and our Excel dumper tool.


Figure 1 – Triage

We can see from Philippe Lagadec’s olevba that the maldoc has an XLM macro with Auto_Open set on a cell. And we learn from Didier Steven’s oledump that there are no other types of macros and embedded objects in this maldoc. And finally, we see that my original dump_cells script only saw one worksheet. While developing dump_cells, I chose to use the xlrd python module as it worked for my use case and was the first module that I discovered. Since that time, I became familiar with Amirreza Niakanlahiji’s XLMMacroDeobfuscator and learned about his updated module, xlrd2. Seeing as this module is an extension of the original xlrd it only took an hour to update dump_cells to use xlrd2; and another hour to add shortened command line options, provide a configurable delineator, and discover that each cell has a formula and value attribute. Let’s give it a test drive!

Dump cells

Figure 2 – Dump cells

Excellent. We can now dump both worksheets, and we were even able to dump the XLM “formula” values from the XLM worksheet. The XLM is a rather simple function to deobfuscate and run the next stage of the maldoc. It uses two while loops to loop over each cell, starting at $C$50, and concatenating all cells in a single column into an XLM function and executing it. The outer loop stops after 32 columns and the inner loop increments by 2 for each iteration. And a static string is used to determine when to stop concatenating cells from a single column, “gnfhKMlzrdu” for this sample. This shouldn’t be hard to convert into python code.

Stage 1 decoder

Figure 3 – Stage 1 decoder

I wish I was surprised. Just like past analyses, the deobfuscated cells are another function to deobfuscate those float values on the first worksheet. Again, we have two loops where cells in a single column are deobfuscated and concatenated together. The outer loop has 17 iterations defined by the variable slightly. And the inner loop continues until the value of the cell is over 1000. A list of keys is defined at R50C3:R59C3 or C50:C59. And each cell is deobfuscated by subtracting the key from value of the cell and converting that float into its respective ASCII character. During analysis, it was also determined that an empty cell represents 0. Let’s convert this to python and continue down the rabbit hole.

Stage 2 decoder

Figure 4 – Stage 2 decoder

Now we’re getting somewhere. During our sandbox analysis we saw an error prompt. And as we can see, it was produced by the XLM. We also see why the sample failed to detonate; it attempts to delete the MOTW for this file and closes if the file does not exist. The other checks have become standard and are well known – check the environment for “Win”, check the resolution, check for a mouse, and check the sound card. Unfortunately, this is not the end and the next chunk of lines is obfuscated with a new list of keys. These keys are constructed from the results of the environment checks and will ensure that the maldoc fails to continue if the checks fail. Let’s convert the python code into a function and call the next chunk of lines with our new list of keys.

Sandbox checks

Figure 5 – Sandbox checks

We can see two more checks – an attempt towrite to disk and a check to determine if macros have been enabled without warnings. This check has also become a standard for these maldocs. Aren’t we glad that we created that function? The next chunk of lines is obfuscated with two more keys appended to the previous list.


Figure 6 – IoCs

We’ve finally come to the bottom of the rabbit hole. A DLL is downloaded from two possible URLs and the DllRegisterServer exported function is ran. And the email that started it all.

Original email

Figure 7 – Original email

Appendix – IoCs

IoC TypeIoC Value

All third-party trademarks referenced by Cofense whether in logo form, name form or product form, or otherwise, remain the property of their respective holders, and use of these trademarks in no way indicates any relationship between Cofense and the holders of the trademarks.  


We use our own and third-party cookies to enhance your experience by showing you relevant content, personalizing our communications with you, and remembering your preferences when you visit our website. We also use them to improve the overall performance of our site. You can learn more about the cookies and similar technology we use by viewing our privacy policy. By clicking ‘Accept,’ you acknowledge and consent to our use of all cookies on our website.

This site is registered on as a development site.