Deobfuscating the Latest Maldocs

The constant barrage of malicious emails seeping into your users' inboxes appear to be coming from Emotet, Hancitor, and Trickbot. Here are charts from Recorded Future (www.recordedfuture.com) showing cyber attacks from each of them. Emotet shows a huge bump in mid February, Hancitor takes over in early March, then Trickbot makes its presence known in late March.

I wanted to see how cybercriminals were obfuscating their maldocs so I pulled down infected documents from Brad's website, Malware Traffic Analysis. What I'm after are the URLs hidden inside the maldocs (or it could be malware callbacks) so they can be blocked at the gateway or used to detect computer infections.

Let's start off with Trickbot then move on to Emotet and Hancitor.

Trickbot Maldoc

After opening the document, I notice the macros button is dimmed...so no macro?

They're using a known trick to disable the button but the macros are definitely there.

You can step through the VBA code and watch as it builds up the final script by decoding its pieces. For a lot of these scripts, you can search for "run" (another good search term is "shell") and you might be able to find the point where the final script gets executed. We can see the "CallByName" method so it's executing the code stored in that long variable.

Let me add a breakpoint there and dump the value of the variable which turns out to be a PowerShell script.

The PowerShell script isn't obfuscated so I can get the URLs which is what I was after. Done!

Emotet Maldoc

Now let's analyze Emotet's maldoc.

In the "AutoOpen" function, I can already see the "run" method so I don't need to search for that. I'm interested in the variable at the end of the run line. Let me dump the variable out and put a breakpoint there.

What I get is an encrypted PowerShell script.

Let me fire up PowerShell ISE and paste in only the PowerShell code then execute it. The URLs appear at the bottom of the middle section. Done!

You can create a PowerShell wrapper or use AES to analyze the script statically. Check out this link for more info.

Hancitor Maldocs

Let's have a look at Hancitor. This is the document I'll be analyzing.

No macros?

Yes, there's macros but they're using another trick to hide it.

This VBA code is rather interesting. The top part of the macro selects an object on the actual document, selects and copies it (which presumably causes it to drop malware into the temp folder), then executes the malware. The bottom part deletes the object, puts the cursor back up to the top then re-saves the document.

Here's the object on the document. Wonder what it is?

Let me make the object bigger.

It's the actual malware file. I wonder how long they'll be using this method since the PE is bare and my VM didn't even get infected.

Let me pull down another maldoc from an earlier campaign.

I start by working my way through the VBA code as usual. And then I come across this. The "digester" value holds a large byte array.

I need to convert these bytes into hex so I can copy it out. I paste this short snippet into the macro in order to convert the values. Now I can copy this out.

This looks like shellcode but there's no visible URLs there. From here I can convert this into a PE file and examine it in that form but in this case it's easier to use a debugger. Keep this hex dump around because you can use this to search for the shellcode in the debugger.

After attaching a debugger, I follow the VBA code to see where it's loading up the shellcode and executing it. By the way, at the top of this module, you can see the API declarations making this VBA code harder to analyze than the previous ones.

Stepping through in the debugger I can see that it builds the imports and then fills some memory location (EAX) with gibberish. Then there's a loop that decrypts this blob into Base64 using XOR 0x78533835c9a14324 (see the bottom left).

Then it performs a base64 decode and you can clearly see the MZ header.

After this it runs svchost.exe and injects code into it.

From here I tried dumping the memory location from the debugger then running strings on it to find the URLs. Nothing. So I proceeded to dump the svchost.exe process.

And I was able to get the URLs in there.

A quicker way is to use URL Revealer and just open-all-the-things.

But then there's no fun in that...

Happy reversing!

Update - 04/25/18

Chris Pierce was kind enough to share his Python script for decoding SecureString:

# Standard Imports
from base64 import b64decode
from binascii import hexlify, unhexlify

#Non Standard Imports
from Crypto.Cipher import AES

# AES Decrypt using IV and CBC mode
def decrypt_aes(enc_key, data, iv):
  cipher = AES.new(enc_key, AES.MODE_CBC, iv) # set the cipher
  return cipher.decrypt(data) # decrypt the data

# Prints data out in hex_dump format.
def format_hex_dump(hexbytes, length=16):
  FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])
  lines = []
  for c in xrange(0, len(hexbytes), length):
    chars = hexbytes[c:c+length]
    hexbyte = ' '.join(["%02x" % ord(x) for x in chars])
    printable = ''.join(["%s" % ((ord(x) <= 127 and FILTER[ord(x)]) or '.') for x in chars])
    lines.append("%04x %-*s %s\n" % (c, length*3, hexbyte, printable))
    return ''.join(lines)

# Main Function
def main():
  # This data contains the B64 encoded, AES encrypted PowerShell command. The data is in UTF-16 format so converted the B64 decode from UTF-16 to UTF-8.
  # NOTE: removed the 32-byte header "76492d1116743f0423413b16050a5345" which resides in front of the B64 data.
  data = b64decode('').decode('utf-16').encode('utf-8')
  print '\nB64 Decoded Data:\n{0}'.format(data)
   
  # This is the AES encryption key: 24-bytes found within the sample hash.
  # Ex: [224..201]
  key_list = [224, 223, 222, 221, 220, 219, 218, 217, 216, 215, 214, 213, 212, 211, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201]
  key = ''.join([chr(x) for x in key_list])
  print '\nAES Key: {0}'.format(key.encode('hex'))

  # The B64 decode above contains a data blob delimited by "|". There are 3 elements after performing the split on "|".
  # The first element is the version number.
  version = data.split('|')[0]
  print 'Version: {0}'.format(version)

  # The second element is the B64 encoded AES IV.
  iv = b64decode(data.split('|')[1])
  print 'IV: {0}'.format(hexlify(iv))

  # The third element is the encrypted PowerShell code.
  enc_data = data.split('|')[2].decode('hex')

  # Passes the key, encrypted data, and iv to the AES decryption function.
  decrypt = decrypt_aes(key, enc_data, iv)

  # Converts the data from UTF-16 to UTF-8.
  clean_decrypt = decrypt.decode('utf-16').encode('utf-8')

  # Makes the PowerShell code more readable.
  clean_decrypt = clean_decrypt.replace(';', ';\r\n')
  print '\nDecrypted Data (prettified):\n\n{0}'.format(clean_decrypt)

if __name__ == "__main__":
  main()


Posted on: 04/14/2018