Tofsee
Taking a look at Tofsee
Overview
Samples
96baba74a907890b995f23c7db21568f7bfb5dbf417ed90ca311482b99702b72
malshare
References
Analysis
According to CERTPL (2016) ref
The C2 IP address list is hardcoded in the binary in an encrypted form. The algorithm used for obfuscation is very simple – it XORs the message with the hardcoded key. Data decrypts to three IP+port pairs – at least in the analyzed sample, the port was equal to 443 for all of them. The probable reason is to conceal communication by using port dedicated for SSL traffic.
Decrypt Strings
data = bytes.fromhex('97db17556bbee0734195d17d')
out = []
key_xor = 0xE4
key_add = 0xc8
for i in range(len(data)):
out.append(key_xor ^ data[i])
if bool(i%2):
key_xor = (key_xor + key_add - 1) & 0xff
else:
key_xor = (key_xor + key_add + 1) & 0xff
print(bytes(out))
out = []
key_xor = 0xE4
key_add = 0xc8
flip = True
for c in data:
out.append(c ^ key_xor)
if flip:
key_xor = (key_xor + key_add + 1) & 0xff
else:
key_xor = (key_xor + key_add - 1) & 0xff
flip = not flip
print(bytes(out))
def decrypt(data,key_xor,key_add):
out = []
flip = True
for c in data:
out.append(c ^ key_xor)
if flip:
key_xor = (key_xor + key_add + 1) & 0xff
else:
key_xor = (key_xor + key_add - 1) & 0xff
flip = not flip
return bytes(out)
decrypt(bytes.fromhex('97db17556bbee0734195d17d'), 0xE4, 0xc8)
Locate Encrypted Strings
68 C8 00 00 00 push 0C8h ; 'È'
68 E4 00 00 00 push 0E4h ; 'ä'
6A 2E push 2Eh ; '.' - size
68 AC 06 41 00 push offset byte_4106AC
BF F8 22 41 00 mov edi, offset byte_4122F8
57 push edi
E8 EE A3 FF FF call mw_decrypt
68 C8 00 00 00 push 0C8h ; 'È'
68 E4 00 00 00 push 0E4h ; 'ä'
6A 09 push 9 - size
68 88 07 41 00 push offset byte_410788
BF F8 22 41 00 mov edi, offset byte_4122F8
57 push edi
E8 66 95 FF FF call mw_decrypt
57 push edi
56 push esi
6A 17 push 17h - size
68 80 10 41 00 push offset byte_411080
55 push ebp
E8 0C 49 FF FF call mw_decrypt
import pefile
import re
import struct
def is_ascii(s):
return all(c < 128 or c == 0 for c in s)
file_data = open('/tmp/toffsee.bin','rb').read()
pe = pefile.PE(data=file_data)
rdata_vstart = None
rdata_vend = None
pe_base = pe.OPTIONAL_HEADER.ImageBase
for s in pe.sections:
if s.Name.startswith(b'.rdata'):
rdata_vstart = s.VirtualAddress + pe_base
rdata_vend = rdata_vstart + s.Misc_VirtualSize
assert rdata_vstart is not None
print(f"rdata_vstart: {hex(rdata_vstart)}")
print(f"rdata_vend: {hex(rdata_vend)}")
for s in pe.sections:
if s.Name.startswith(b'.rdata'):
rdata_vstart = s.VirtualAddress + pe_base
rdata_vend = rdata_vstart + s.Misc_VirtualSize
egg_string_push_loose = rb'\x6A(.)\x68(....)(.){1,6}\xe8'
egg_string_push_tight = rb'\x68(....)\x68(....)\x6A(.)\x68(....)(.){1,6}\xe8'
key_adds = []
key_xors = []
for m in re.finditer(egg_string_push_tight, file_data):
key_adds.append(struct.unpack('<I',m.group(1))[0])
key_xors.append(struct.unpack('<I',m.group(2))[0])
key_xor = max(set(key_xors), key = key_xors.count)
key_add = max(set(key_adds), key = key_adds.count)
print(f"xor: {hex(key_xor)} add:{hex(key_add)}")
enc_strings = []
for m in re.finditer(egg_string_push_loose, file_data):
str_len = struct.unpack('B', m.group(1))[0]
str_vaddr = struct.unpack('<I', m.group(2))[0]
if rdata_vstart <= str_vaddr <= rdata_vend:
enc_strings.append(pe.get_data(str_vaddr - pe_base, str_len))
strings = []
for e in enc_strings:
tmp_str = decrypt(e, key_xor, key_add)
if is_ascii(tmp_str):
print(tmp_str)
strings.append(tmp_str)
decrypt(bytes.fromhex('97db154f70acf83b4c88dd106a79bbed642df4bd844d14dda46d34fdc48d541de4ad743d04cd945d24edb47d440dd49d642df4bd844d14dda46d34fdc48d541dbb010000018ec200486aa5f13449c3da1c2968d49d642df4bd844d14dda46d34fdc48d541de4ad743d04cd945d24edb47d440dd49d642df4bd844d14dda46d34fdc48d541dbb'),0xe4,0xc8 )
TODO: The C2 is stored in the .data section not the .rdata so we need to create a second regex to find this specific encrypted "string". The following is an example.
56 push esi
57 push edi
6A 02 push 2
BE 59 20 41 00 mov esi, offset unk_412059
5F pop edi
68 C8 00 00 00 push 0C8h ; 'È'
68 E4 00 00 00 push 0E4h ; 'ä'
6A 40 push 40h ; '@'
56 push esi
56 push esi
E8 39 60 FF FF call mw_decrypt