Overview

The week of May 9th, 2022 Emotet released an update to their x64 malware that used "stack strings" and an obfuscator to protect the strings, keys, and c2s. This was a change from the enrypted strings and c2 tables that were usually stored at the beginning of the .text and .data sections. Our new approach for config extraction will be to identify the functions used to supply the strings and c2s and emulate them.

Samples

  • packed: 92033dc85730f7dc5dbd85369ea1db8806ce7581c1e9b4764a82abfc54e3146e
  • unpacked: c688e079a16b3345c83a285ac2ae8dd48680298085421c225680f26ceae73eb7

Tools

  • Dumpulator minidump emulation github

Using Dumpulator Emulation

from dumpulator import Dumpulator

dp = Dumpulator("/tmp/emo2.dmp", quiet=True)
fn_addr_list = [0x07FFA3BA235D0, 0x7FFA3BA213C4,0x7FFA3BA21AAC,0x7FFA3BA2400C,0x7FFA3BA282D8,0x7FFA3BA2A36C,0x7FFA3BA2D370,0x7FFA3BA2DD3C,0x7FFA3BA2E468,0x7FFA3BA30C28,0x7FFA3BA31960,0x7FFA3BA33F28,0x7FFA3BA35980,0x7FFA3BA35B04,0x7FFA3BA3AFB0,0x7FFA3BA3F9A8,0x7FFA3BA3FEE8,0x7FFA3BA4012C,0x7FFA3BA41124,0x7FFA3BA412A4,0x7FFA3BA415A0,0x7FFA3BA42224,0x7FFA3BA43224,0x7FFA3BA44AEC,0x7FFA3BA465F0,0x7FFA3BA46744,0x7FFA3BA47140,0x7FFA3BA472A8,0x7FFA3BA490F8,0x7FFA3BA49850,0x7FFA3BA49A58,0x7FFA3BA49D04,0x7FFA3BA49FB4,0x7FFA3BA4BCB4,0x7FFA3BA4C168] 

for fn_addr in fn_addr_list:
    out = dp.call(fn_addr, [])
    ptxt_str = dp.read_str(out, encoding='utf-16')
    print(f"{hex(fn_addr)}: {ptxt_str}")
    
    
    
0x7ffa3ba235d0: %s%s.dll
0x7ffa3ba213c4: ObjectLength
0x7ffa3ba21aac: SOFTWARE\Microsoft\Windows\CurrentVersion\Run
0x7ffa3ba2400c: WinSta0\Default
0x7ffa3ba282d8: RNG
0x7ffa3ba2a36c: AES
0x7ffa3ba2d370: %s\%s
0x7ffa3ba2dd3c: %s\*
0x7ffa3ba2e468: SHA256
0x7ffa3ba30c28: %s\%s
0x7ffa3ba31960: urlmon.dll
0x7ffa3ba33f28: advapi32.dll
0x7ffa3ba35980: %s:Zone.Identifier
0x7ffa3ba35b04: %s%s.exe
0x7ffa3ba3afb0: %s\regsvr32.exe "%s\%s" %s
0x7ffa3ba3f9a8: HASH
0x7ffa3ba3fee8: Microsoft Primitive Provider
0x7ffa3ba4012c: bcrypt.dll
0x7ffa3ba41124: ECDH_P256
0x7ffa3ba412a4: Cookie: %s=%s

0x7ffa3ba415a0: shlwapi.dll
0x7ffa3ba42224: shell32.dll
0x7ffa3ba43224: ECCPUBLICBLOB
0x7ffa3ba44aec: Content-Type: multipart/form-data; boundary=%s

0x7ffa3ba465f0: wininet.dll
0x7ffa3ba46744: ECDSA_P256
0x7ffa3ba47140: %s\%s%x
0x7ffa3ba472a8: wtsapi32.dll
0x7ffa3ba490f8: POST
0x7ffa3ba49850: %s\regsvr32.exe "%s" %s
0x7ffa3ba49a58: %s\regsvr32.exe "%s\%s"
0x7ffa3ba49d04: userenv.dll
0x7ffa3ba49fb4: %u.%u.%u.%u
0x7ffa3ba4bcb4: crypt32.dll
0x7ffa3ba4c168: KeyDataBlob
dp.read_str(out, encoding='utf-16')
'%s%s.dll'
import struct 
key_decrypt_functions = [0x7FFA3BA33B90, 0x7FFA3BA22048]

for key_decrypt_function in key_decrypt_functions:
    tmp_arg = dp.allocate(8)
    out = dp.call(key_decrypt_function, [tmp_arg,tmp_arg, tmp_arg, tmp_arg])
    key_header = bytes(dp.read(out, 8))
    key_len = struct.unpack('<I',key_header[4:8])[0]
    full_key_len = 8 + 2 * key_len
    key = bytes(dp.read(out, full_key_len))
    print(key)
b'ECK1 \x00\x00\x00\xf3\xa35\xb5\x0e.+\xf45V\xcd\nL)>|\xf1\x10\xdd\xcb\xb0O \xb3\xfa\x02 \xceL\xb6\x0c\x1eD\x96\xbe\xb4\x0e\xe6\xc9[\x9a\xbdN\xbd\x9d\x8f\xcf\xe0\x10[4L\x82\x04&\x02\xd3\xba\xac\xf1\xfb\x9f,v'
b'ECS1 \x00\x00\x00@_t\xb6\xc4\xd8\xdc\x0c=\x1f\x06z7\xdc\xb9\xf9\xb7\xbd^\x8a/\xa6\xa1\xf2\x0f\xa1y\r\x14\xe5\xf51\xe8\xb0\n\x1e<\x8b?{\x90\x1d&&1\x86e|\x1a\xad\xd9\xc3\\\xacH\xf0`\x87\x18\xd9t<X\xf9'
c2_fns = [0x07FFA3BA2E70C, 0x7FFA3BA30D88,0x7FFA3BA4B054,0x7FFA3BA21528,0x7FFA3BA4A4CC,0x7FFA3BA4C2B8,0x7FFA3BA4BF80,0x7FFA3BA4AA74,0x7FFA3BA2DAB0,0x7FFA3BA43584,0x7FFA3BA34644,0x7FFA3BA2FD58,0x7FFA3BA35690,0x7FFA3BA3975C,0x7FFA3BA23BD0,0x7FFA3BA3519C,0x7FFA3BA2B610,0x7FFA3BA4C8B0,0x7FFA3BA3C9F8,0x7FFA3BA36A10,0x7FFA3BA4339C,0x7FFA3BA21F58,0x7FFA3BA4557C,0x7FFA3BA28BC8,0x7FFA3BA3C5B4,0x7FFA3BA45498,0x7FFA3BA21000,0x7FFA3BA24E50,0x7FFA3BA2FBC4,0x7FFA3BA33278,0x7FFA3BA468C0,0x7FFA3BA464FC,0x7FFA3BA28EE0,0x7FFA3BA274A0,0x7FFA3BA3092C,0x7FFA3BA24D58,0x7FFA3BA3E274,0x7FFA3BA2BCF8,0x7FFA3BA4CAF8,0x7FFA3BA4A340,0x7FFA3BA29820,0x7FFA3BA4A0F8,0x7FFA3BA494D8,0x7FFA3BA35C7C,0x7FFA3BA3D5C8,0x7FFA3BA21D48,0x7FFA3BA4103C,0x7FFA3BA28DCC,0x7FFA3BA22F64,0x7FFA3BA301BC,0x7FFA3BA2F454,0x7FFA3BA2B9E4,0x7FFA3BA24C38,0x7FFA3BA3CF80,0x7FFA3BA3E360,0x7FFA3BA45264,0x7FFA3BA49C14,0x7FFA3BA469D0,0x7FFA3BA281E4,0x7FFA3BA2DC28,0x7FFA3BA26F38,0x7FFA3BA45678,0x7FFA3BA24868,0x7FFA3BA35598]

def get_c2_from_fn(c2_fn):
    c2_ip = dp.allocate(4)
    c2_port = dp.allocate(4)
    ret = dp.call(c2_fn, [c2_ip, c2_port])
    c2_port_bytes = dp.read(c2_port, 4)
    c2_port = struct.unpack('<H',c2_port_bytes[2:4])[0]
    c2_ip_bytes = dp.read(c2_ip, 4)
    c2_ip = f"{c2_ip_bytes[0]}.{c2_ip_bytes[1]}.{c2_ip_bytes[2]}.{c2_ip_bytes[3]}"
    return f"{c2_ip}:{c2_port}"

for c2_fn in c2_fns:
    c2 = get_c2_from_fn(c2_fn)
    print(f"{c2}")
172.104.251.154:8080
209.250.246.206:443
110.232.117.186:8080
164.68.99.3:8080
119.193.124.41:7080
212.237.17.99:8080
107.182.225.142:8080
185.8.212.130:7080
153.126.146.25:7080
77.81.247.144:8080
209.126.98.206:8080
201.94.166.162:443
131.100.24.231:80
45.235.8.30:8080
213.241.20.155:443
103.43.46.182:443
0.0.0.0:0
129.232.188.93:443
103.132.242.26:8080
151.106.112.196:8080
45.118.115.99:8080
185.4.135.165:8080
103.70.28.102:8080
51.91.7.5:8080
27.54.89.58:8080
196.218.30.83:443
206.189.28.199:8080
91.207.28.33:8080
79.137.35.198:8080
51.254.140.238:7080
173.212.193.249:8080
203.114.109.124:443
94.23.45.86:4143
63.142.250.212:443
189.126.111.200:7080
160.16.142.56:8080
102.222.215.74:443
5.9.116.246:8080
158.69.222.101:443
167.172.253.162:8080
82.165.152.127:8080
212.24.98.99:8080
197.242.150.244:8080
72.15.201.15:8080
101.50.0.91:8080
51.91.76.89:8080
183.111.227.137:8080
188.44.20.25:443
58.227.42.236:80
45.176.232.124:443
185.157.82.211:8080
163.44.196.120:8080
159.65.88.10:8080
146.59.226.45:443
1.234.2.232:8080
149.56.131.28:8080
209.97.163.214:443
46.55.222.11:443
150.95.66.124:8080
103.75.201.2:443
216.158.226.206:443
134.122.66.193:8080
1.234.21.73:7080
167.99.115.35:8080

Automated Function Identification

Now that we have a way to extract the data from the functions using emulation all we need to do is create some regexes to indetify the functions and complete our automation.

import re
import struct
import pefile

dump_image_base = 0x7FFA3BA20000

FILE_PATH = '/tmp/emo_unpacked_1020000.bin'

file_data = open(FILE_PATH, 'rb').read()
pe = pefile.PE(data=file_data)

egg = rb'\x48\x8D\x05(....)\x48\x89\x81..\x00\x00'

for m in re.finditer(egg, file_data):
    fn_rel_offset = struct.unpack('<i', m.group(1))[0]
    inst_offset = m.start() 
    fn_rva = pe.get_rva_from_offset(inst_offset) + 7 + fn_rel_offset
    fn_addr = dump_image_base + fn_rva
    c2 = get_c2_from_fn(fn_addr)
    print(f"{hex(fn_addr)}: {c2}")
0x7ffa3ba30d88: 209.250.246.206:443
0x7ffa3ba4b054: 110.232.117.186:8080
0x7ffa3ba21528: 164.68.99.3:8080
0x7ffa3ba4a4cc: 119.193.124.41:7080
0x7ffa3ba4c2b8: 212.237.17.99:8080
0x7ffa3ba4aa74: 185.8.212.130:7080
0x7ffa3ba2dab0: 153.126.146.25:7080
0x7ffa3ba43584: 77.81.247.144:8080
0x7ffa3ba34644: 209.126.98.206:8080
0x7ffa3ba2fd58: 201.94.166.162:443
0x7ffa3ba35690: 131.100.24.231:80
0x7ffa3ba3975c: 45.235.8.30:8080
0x7ffa3ba23bd0: 213.241.20.155:443
0x7ffa3ba3519c: 103.43.46.182:443
0x7ffa3ba2b610: 0.0.0.0:0
0x7ffa3ba4c8b0: 129.232.188.93:443
0x7ffa3ba3c9f8: 103.132.242.26:8080
0x7ffa3ba36a10: 151.106.112.196:8080
0x7ffa3ba4339c: 45.118.115.99:8080
0x7ffa3ba21f58: 185.4.135.165:8080
0x7ffa3ba4557c: 103.70.28.102:8080
0x7ffa3ba28bc8: 51.91.7.5:8080
0x7ffa3ba3c5b4: 27.54.89.58:8080
0x7ffa3ba21000: 206.189.28.199:8080
0x7ffa3ba2fbc4: 79.137.35.198:8080
0x7ffa3ba33278: 51.254.140.238:7080
0x7ffa3ba468c0: 173.212.193.249:8080
0x7ffa3ba464fc: 203.114.109.124:443
0x7ffa3ba28ee0: 94.23.45.86:4143
0x7ffa3ba3092c: 189.126.111.200:7080
0x7ffa3ba24d58: 160.16.142.56:8080
0x7ffa3ba3e274: 102.222.215.74:443
0x7ffa3ba2bcf8: 5.9.116.246:8080
0x7ffa3ba4a340: 167.172.253.162:8080
0x7ffa3ba29820: 82.165.152.127:8080
0x7ffa3ba494d8: 197.242.150.244:8080
0x7ffa3ba35c7c: 72.15.201.15:8080
0x7ffa3ba3d5c8: 101.50.0.91:8080
0x7ffa3ba21d48: 51.91.76.89:8080
0x7ffa3ba4103c: 183.111.227.137:8080
0x7ffa3ba28dcc: 188.44.20.25:443
0x7ffa3ba22f64: 58.227.42.236:80
0x7ffa3ba301bc: 45.176.232.124:443
0x7ffa3ba2b9e4: 163.44.196.120:8080
0x7ffa3ba24c38: 159.65.88.10:8080
0x7ffa3ba3cf80: 146.59.226.45:443
0x7ffa3ba3e360: 1.234.2.232:8080
0x7ffa3ba49c14: 209.97.163.214:443
0x7ffa3ba469d0: 46.55.222.11:443
0x7ffa3ba2dc28: 103.75.201.2:443
0x7ffa3ba26f38: 216.158.226.206:443
0x7ffa3ba45678: 134.122.66.193:8080
0x7ffa3ba24868: 1.234.21.73:7080
0x7ffa3ba35598: 167.99.115.35:8080