diff --git a/trickbot/trick_config_decoder.py b/trickbot/trick_config_decoder.py index 74c280f..c94739b 100755 --- a/trickbot/trick_config_decoder.py +++ b/trickbot/trick_config_decoder.py @@ -1,49 +1,59 @@ -#!/usr/bin/python2.7 +#!/usr/bin/env python2.7 "Decodes AES encrypted modules of TrickBot" +# Crypto implementation taken from: https://github.com/kevthehermit/RATDecoders/blob/master/decoders/TrickBot.py +# resource module by dummys __AUTHOR__ = 'hasherezade' import argparse -import hashlib +from hashlib import sha256 from Crypto.Cipher import AES +from pefile import PE +from struct import unpack_from -BS = 16 -pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) -unpad = lambda s : s[:-ord(s[len(s)-1:])] -def hash_rounds(data_buf): - while len(data_buf) <= 0x1000: - buf_hash = hashlib.sha256(data_buf).digest() - data_buf += buf_hash - return buf_hash +def derive_key(n_rounds, input_bf): + intermediate = input_bf + for i in range(0, n_rounds): + sha = sha256() + sha.update(intermediate) + current = sha.digest() + intermediate += current + return current + def aes_decrypt(data): - key = hash_rounds(data[:0x20])[:0x20] - iv = hash_rounds(data[0x10:0x30])[:0x10] + key = derive_key(128, data[:32]) + iv = derive_key(128, data[16:48])[:16] aes = AES.new(key, AES.MODE_CBC, iv) - data = pad(data[0x30:]) - return aes.decrypt(data) - -def find_pe(data): - while len(data): - mz_start = data.find('MZ') - if mz_start == -1: - return None - pe_start = data[mz_start:] - data = data[mz_start + len('MZ'):] - pe = data.find('PE') - if pe != -1: - return pe_start - return None + mod = len(data[48:]) % 16 + if mod != 0: + data += '0' * (16 - mod) + return aes.decrypt(data[48:])[:-(16 - mod)] + + +def find_rsrc(pe): + """Assumption is that the RSRC is called 'RES' + """ + + for rsrc in pe.DIRECTORY_ENTRY_RESOURCE.entries: + for entry in rsrc.directory.entries: + if entry.name.string == "RES": + offset = entry.directory.entries[0].data.struct.OffsetToData + size = entry.directory.entries[0].data.struct.Size + return pe.get_memory_mapped_image()[offset:offset + size] + return 0 + def dump_to_file(filename, data): with open(filename, 'wb') as f: f.write(data) + def dexor(data, key): maxlen = len(data) keylen = len(key) - j = 0 #key index + j = 0 # key index decoded = "" for i in range(0, maxlen): kval = key[j % keylen] @@ -51,51 +61,65 @@ def dexor(data, key): j += 1 return decoded + def main(): parser = argparse.ArgumentParser(description="TrickBot AES decoder") - parser.add_argument('--datafile',dest="datafile",default=None,help="File with data", required=True) - parser.add_argument('--botkey',dest="botkey",default=None,help="BotKey (SHA256)", required=False) - parser.add_argument('--outfile',dest="outfile",default=None, help="Where to dump the output", required=False) - parser.add_argument('--pe_name',dest="pe_name",default=None, help="Where to dump the PE", required=False) + group = parser.add_mutually_exclusive_group(required=True) + + group.add_argument('--executable', dest="executable", default=None, help="Malware executable") + group.add_argument('--datafile', dest="datafile", default=None, help="encrypted module or config") + parser.add_argument('--botkey', dest="botkey", default=None, help="BotKey (SHA256)", required=False) + parser.add_argument('--outfile', dest="outfile", default=None, help="Where to dump the output", required=False) + args = parser.parse_args() + rsrc_mode = False + if args.executable: + pe = PE(args.executable) + data = find_rsrc(pe) + if not data: + # we did not found an encrypted config + print "Didn't found encrypted config" + return -1 + rsrc_mode = True + data_len = unpack_from("