diff --git a/malware/Xtreme RAT/xtrat_decrypt_config.py b/malware/Xtreme RAT/xtrat_decrypt_config.py index c539393..6db20d0 100644 --- a/malware/Xtreme RAT/xtrat_decrypt_config.py +++ b/malware/Xtreme RAT/xtrat_decrypt_config.py @@ -1,4 +1,4 @@ -# Copyright (c) 2014 FireEye, Inc. All rights reserved. +# Copyright (c) 2015 FireEye, Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -44,13 +44,8 @@ key = key[0:keylen] f = args.i - rp = open(f, "rb") - ciphertxt = rp.read() - rp.close() + with open(f, 'rb') as fd: + ciphertxt = fd.read() rc = ARC4.new(key) - print rc.decrypt(ciphertxt) - - - - + print(rc.decrypt(ciphertxt)) diff --git a/malware/Xtreme RAT/xtrat_decrypt_keylog.py b/malware/Xtreme RAT/xtrat_decrypt_keylog.py index a27350a..1bbad6c 100644 --- a/malware/Xtreme RAT/xtrat_decrypt_keylog.py +++ b/malware/Xtreme RAT/xtrat_decrypt_keylog.py @@ -1,4 +1,4 @@ -# Copyright (c) 2014 FireEye, Inc. All rights reserved. +# Copyright (c) 2015 FireEye, Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,44 +27,42 @@ import argparse def two_byte_xor(buf, key, preserve_unicode=True): - out = '' - for i in range(0,len(buf)/2): - c = struct.unpack(">H", buf[(i*2):(i*2)+2])[0] - if c == 0x0a00 or c == 0x0d00: - pass - else: - c ^= key - out += struct.pack(">H", c) - if preserve_unicode: - return out - else: - return out.replace("\x00","") + out = '' + for i in range(len(buf) / 2): + c = struct.unpack(">H", buf[(i * 2):(i * 2) + 2])[0] + if c not in (0x0a00, 0x0d00): + c ^= key + out += struct.pack(">H", c) + + if preserve_unicode: + return out + return out.replace("\x00", "") if __name__ == "__main__": - parser=argparse.ArgumentParser(description="decrypts and prints an Xtreme RAT keylog file") - parser.add_argument('i', metavar='Input', help='a path to an Xtreme RAT keylog file') - parser.add_argument('-u', action='store_true', help='Unicode output') - - args = parser.parse_args() - preserve_unicode = args.u - keylog = args.i - if os.path.isfile(keylog): - rp = open(keylog, "rb") - buf = rp.read() - rp.close() - dec = two_byte_xor(buf, 0x3fa5, preserve_unicode) - if "---" in dec.replace("\x00","") or "---" in dec or "
" in dec.replace("\x00","") or "
" in dec: - print dec - else: - dec = two_byte_xor(buf, 0x5500, preserve_unicode) - if "---" in dec.replace("\x00","") or "---" in dec or "
" in dec.replace("\x00","") or "
" in dec: - print dec - else: - dec = two_byte_xor(buf, 0x1300, preserve_unicode) - if "---" in dec.replace("\x00","") or "---" in dec or "
" in dec.replace("\x00","") or "
" in dec: - print dec - else: - print "could not decrypt keylog.." - else: - print "argument supplied is not a file!" - \ No newline at end of file + parser=argparse.ArgumentParser(description="decrypts and prints an Xtreme RAT keylog file") + parser.add_argument('i', metavar='Input', help='a path to an Xtreme RAT keylog file') + parser.add_argument('-u', action='store_true', help='Unicode output') + + args = parser.parse_args() + preserve_unicode = args.u + keylog = args.i + if not os.path.isfile(keylog): + print("argument supplied is not a file!") + sys.exit() + with open(keylog, 'rb') as fd: + buf = fd.read() + + dec = two_byte_xor(buf, 0x3fa5, preserve_unicode) + if "---" in dec.replace("\x00", "") or "---" in dec or "
" in dec.replace("\x00","") or "
" in dec: + print(dec) + else: + dec = two_byte_xor(buf, 0x5500, preserve_unicode) + if "---" in dec.replace("\x00","") or "---" in dec or "
" in dec.replace("\x00","") or "
" in dec: + print(dec) + else: + dec = two_byte_xor(buf, 0x1300, preserve_unicode) + if "---" in dec.replace("\x00","") or "---" in dec or "
" in dec.replace("\x00","") or "
" in dec: + print(dec) + else: + print("could not decrypt keylog..") + \ No newline at end of file diff --git a/malware/Xtreme RAT/xtrat_find_config.py b/malware/Xtreme RAT/xtrat_find_config.py index 43890cc..970d0e1 100644 --- a/malware/Xtreme RAT/xtrat_find_config.py +++ b/malware/Xtreme RAT/xtrat_find_config.py @@ -1,4 +1,4 @@ -# Copyright (c) 2014 FireEye, Inc. All rights reserved. +# Copyright (c) 2015 FireEye, Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,119 +29,107 @@ from pprint import pprint def getUnicodeString(buf,pos): - out = "" - for i in range(len(buf[pos:])): - if ord(buf[pos+i]) == 0 and ord(buf[pos+i+1]) == 0: - out += "\x00" - break - out += buf[pos+i] - - if out == "": - return None - else: - return out - + out = "" + for i in range(len(buf[pos:])): + if ord(buf[pos+i]) == 0 and ord(buf[pos+i+1]) == 0: + out += "\x00" + break + out += buf[pos+i] + + if out == "": + return None + return out + def getMutex(buf, exitpos, persistpos): - #do a backwards comparison from PERSIST and EXIT and continue until there is a mismatch or two null bytes, calculate beginning of the mutex this way - cnt = 1 - while 1: - exitc = buf[exitpos - cnt] - exitc2 = buf[exitpos - cnt - 1] - persistc = buf[persistpos - cnt] - persistc2 = buf[persistpos - cnt - 1] - if exitc != persistc: - if buf[persistpos - cnt + 1] == "\x00": - out = buf[persistpos - cnt + 2:persistpos] - else: - out = buf[persistpos - cnt + 1:persistpos] - return out - if (exitc == "\x00" and exitc2 == "\x00") or (persistc == "\x00" and persistc2 == "\x00"): - out = buf[persistpos - cnt + 1:persistpos] - return out - cnt+=1 - - + #do a backwards comparison from PERSIST and EXIT and continue until there is a mismatch or two null bytes, calculate beginning of the mutex this way + cnt = 1 + while 1: + exitc = buf[exitpos - cnt] + exitc2 = buf[exitpos - cnt - 1] + persistc = buf[persistpos - cnt] + persistc2 = buf[persistpos - cnt - 1] + if exitc != persistc: + if buf[persistpos - cnt + 1] == "\x00": + out = buf[persistpos - cnt + 2:persistpos] + else: + out = buf[persistpos - cnt + 1:persistpos] + return out + if (exitc == "\x00" and exitc2 == "\x00") or (persistc == "\x00" and persistc2 == "\x00"): + out = buf[persistpos - cnt + 1:persistpos] + return out + cnt+=1 def unicodifyre(s): - return "\\x00".join(s) + "\\x00" - + return "\\x00".join(s) + "\\x00" + def unicodify(s): - return "\x00".join(s) + "\x00" + return "\x00".join(s) + "\x00" def getConfig(f,buf): - persistre = unicodifyre("PERSIST") - persist = unicodify("PERSIST") - exit = unicodify("EXIT") - persistpattern = r"((?:[^\x00]\x00){4,})" + persistre - match = re.findall(persistpattern, buf) - if len(match) > 0: - configpos = None - lastfoundoffs = 0 - for m in match: - print "possible xtrat config located in %s" % f - persistpos = string.find(buf[lastfoundoffs:],m) - persistoffs = string.find(buf[persistpos + lastfoundoffs:],persist) - persistpos += persistoffs + lastfoundoffs - lastfoundoffs = persistpos + len(persist) - exitpos = string.rfind(buf[:persistpos],exit) - #match last 3 characters of mutex as a sanity check - if exitpos != -1 and buf[exitpos-6:exitpos] == m[-6:]: - if persistpos - exitpos == 0x60: - print "possible version 1.3.6.x config block found.." - mutex = getMutex(buf, exitpos, persistpos) - print "mutex: %s" % mutex - configpos = persistpos - len(mutex) - 0x3c8 - configlen = 0xe10 - break - - elif persistpos - exitpos == 0x38: - print "possible version 3.x config block found.." - mutex = getMutex(buf, exitpos, persistpos) - print "mutex: %s" % mutex - configpos = persistpos - len(mutex) - 0x33e - configlen = 0x7f0 - break - - elif persistpos - exitpos == 0x7a: - print "possible version 2.x config block found.." - mutex = getMutex(buf, exitpos, persistpos) - print "mutex: %s" % mutex - configpos = persistpos - len(mutex) - 0x109e - configlen = 0x1390 - break - - if configpos is not None: - print "config located at %08X" % configpos - wp = open(f + ".cfg", "wb") - wp.write(buf[configpos:configpos+configlen]) - wp.close() - - else: - print "could not locate xtrat config" + persistre = unicodifyre("PERSIST") + persist = unicodify("PERSIST") + exit = unicodify("EXIT") + persistpattern = r"((?:[^\x00]\x00){4,})" + persistre + match = re.findall(persistpattern, buf) + if len(match) > 0: + configpos = None + lastfoundoffs = 0 + for m in match: + print("possible xtrat config located in %s" % f) + persistpos = string.find(buf[lastfoundoffs:],m) + persistoffs = string.find(buf[persistpos + lastfoundoffs:],persist) + persistpos += persistoffs + lastfoundoffs + lastfoundoffs = persistpos + len(persist) + exitpos = string.rfind(buf[:persistpos],exit) + #match last 3 characters of mutex as a sanity check + if exitpos != -1 and buf[exitpos-6:exitpos] == m[-6:]: + if persistpos - exitpos == 0x60: + print("possible version 1.3.6.x config block found..") + mutex = getMutex(buf, exitpos, persistpos) + print("mutex: %s" % mutex) + configpos = persistpos - len(mutex) - 0x3c8 + configlen = 0xe10 + break + elif persistpos - exitpos == 0x38: + print("possible version 3.x config block found..") + mutex = getMutex(buf, exitpos, persistpos) + print("mutex: %s" % mutex) + configpos = persistpos - len(mutex) - 0x33e + configlen = 0x7f0 + break + elif persistpos - exitpos == 0x7a: + print("possible version 2.x config block found..") + mutex = getMutex(buf, exitpos, persistpos) + print("mutex: %s" % mutex) + configpos = persistpos - len(mutex) - 0x109e + configlen = 0x1390 + break + + if configpos is not None: + print("config located at %08X" % configpos) + with open(os.path.join(f, '.cfg'), 'wb') as fd: + fd.write(buf[configpos:configpos+configlen]) + else: + print("could not locate xtrat config") def getFile(f): - rp = open(f, "rb") - buf = rp.read() - rp.close() - return buf + with open(f, 'rb') as fd: + return fd.read() if __name__ == "__main__": - parser=argparse.ArgumentParser(description="scans for Xtreme RAT config in memory dumps and writes them to a file of the same name with '.cfg' appended") - parser.add_argument('i', metavar='Input', help='a path to a memory dump or directory of memory dumps') - - args = parser.parse_args() - - if os.path.isfile(args.i): - f = args.i - buf = getFile(f) - getConfig(f,buf) - elif os.path.isdir(args.i): - d = args.i - for f in os.listdir(d): - path = os.path.join(d, f) - if os.path.isfile(path): - buf = getFile(path) - getConfig(path,buf) - + parser = argparse.ArgumentParser(description="scans for Xtreme RAT config in memory dumps and writes them to a file of the same name with '.cfg' appended") + parser.add_argument('i', metavar='Input', help='a path to a memory dump or directory of memory dumps') + args = parser.parse_args() + if os.path.isfile(args.i): + f = args.i + buf = getFile(f) + getConfig(f,buf) + elif os.path.isdir(args.i): + d = args.i + for f in os.listdir(d): + path = os.path.join(d, f) + if os.path.isfile(path): + buf = getFile(path) + getConfig(path,buf) diff --git a/malware/Xtreme RAT/xtrat_parse_config.py b/malware/Xtreme RAT/xtrat_parse_config.py index 0b11a10..312f667 100644 --- a/malware/Xtreme RAT/xtrat_parse_config.py +++ b/malware/Xtreme RAT/xtrat_parse_config.py @@ -1,4 +1,4 @@ -# Copyright (c) 2014 FireEye, Inc. All rights reserved. +# Copyright (c) 2015 FireEye, Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -28,139 +28,128 @@ import argparse def getString(buf,pos): - out = "" - for c in buf[pos:]: - if ord(c) == 0: - break - out += c - - if out == "": - return None - else: - return out + out = "" + for c in buf[pos:]: + if ord(c) == 0: + break + out += c + + if out == "": + return None + return out def getUnicodeString(buf,pos): - out = "" - for i in range(len(buf[pos:])): - if not (ord(buf[pos+i]) >= 32 and ord(buf[pos+i]) <= 126) and not (ord(buf[pos+i+1]) >= 32 and ord(buf[pos+i+1]) <= 126): - out += "\x00" - break - out += buf[pos+i] - - if out == "": - return None - else: - return out.replace("\x00","") - + out = "" + for i in range(len(buf[pos:])): + if not (ord(buf[pos+i]) >= 32 and ord(buf[pos+i]) <= 126) and not (ord(buf[pos+i+1]) >= 32 and ord(buf[pos+i+1]) <= 126): + out += "\x00" + break + out += buf[pos+i] + + if out == "": + return None + return out.replace("\x00","") + def parseConfig(f,buf): - #2.9.x - if len(buf) == 0x1390: - v = 3 - #3.x - elif len(buf) == 0x7f0: - v = 2 - #1.3.x - elif len(buf) == 0xe10: - v = 1 - else: - print "unrecognized config.." - return - ftpserveroffs = None - ftpuseroffs = None - ftppassoffs = None - - if v == 1: - idoffs = 0x1b4 - groupoffs = 0x1de - versionoffs = 0x312 - mutexoffs = 0x33e - installdiroffs = 0x234 - ftpserveroffs = 0x430 - ftpuseroffs = 0x4d4 - ftppassoffs = 0x526 - - elif v == 2: - idoffs = 0x1b4 - groupoffs = 0x1ca - versionoffs = 0x2d8 - mutexoffs = 0x2f0 - installdiroffs = 0x1f8 - ftpserveroffs = 0x380 - ftpuseroffs = 0x424 - ftppassoffs = 0x476 - #delayoffs = 0x378 - - elif v == 3: - idoffs = 0x9e0 - groupoffs = 0xa5a - versionoffs = 0xf2e - mutexoffs = 0xfaa - installdiroffs = 0xb50 - - - print f - id = getUnicodeString(buf,idoffs) - group = getUnicodeString(buf,groupoffs) - version = getUnicodeString(buf,versionoffs) - mutex = getUnicodeString(buf,mutexoffs) - installdir = getUnicodeString(buf,installdiroffs) - ftpuser = None - ftppass = None - ftpserver = None - - print "ID: %s" % id - print "Group: %s" % group - print "Version: %s" % version - print "Mutex: %s" % mutex - print "Install Dir: %s" % installdir - if ftpserveroffs != None: - ftpserver = getUnicodeString(buf,ftpserveroffs) - if ftpserver != "ftp.ftpserver.com".join("\x00"): - print "FTP Server: %s" % ftpserver - ftpuser = getUnicodeString(buf,ftpuseroffs) - ftppass = getUnicodeString(buf,ftppassoffs) - print "FTP User: %s" % ftpuser - print "FTP Pass: %s" % ftppass - - print "\n" - return (id,group,version,mutex,installdir,ftpserver,ftpuser,ftppass) - + #2.9.x + if len(buf) == 0x1390: + v = 3 + #3.x + elif len(buf) == 0x7f0: + v = 2 + #1.3.x + elif len(buf) == 0xe10: + v = 1 + else: + print("unrecognized config..") + return + ftpserveroffs = None + ftpuseroffs = None + ftppassoffs = None + + if v == 1: + idoffs = 0x1b4 + groupoffs = 0x1de + versionoffs = 0x312 + mutexoffs = 0x33e + installdiroffs = 0x234 + ftpserveroffs = 0x430 + ftpuseroffs = 0x4d4 + ftppassoffs = 0x526 + elif v == 2: + idoffs = 0x1b4 + groupoffs = 0x1ca + versionoffs = 0x2d8 + mutexoffs = 0x2f0 + installdiroffs = 0x1f8 + ftpserveroffs = 0x380 + ftpuseroffs = 0x424 + ftppassoffs = 0x476 + #delayoffs = 0x378 + elif v == 3: + idoffs = 0x9e0 + groupoffs = 0xa5a + versionoffs = 0xf2e + mutexoffs = 0xfaa + installdiroffs = 0xb50 + + print(f) + _id = getUnicodeString(buf,idoffs) + group = getUnicodeString(buf,groupoffs) + version = getUnicodeString(buf,versionoffs) + mutex = getUnicodeString(buf,mutexoffs) + installdir = getUnicodeString(buf,installdiroffs) + ftpuser = None + ftppass = None + ftpserver = None + + print("ID: %s" % _id) + print("Group: %s" % group) + print("Version: %s" % version) + print("Mutex: %s" % mutex) + print("Install Dir: %s" % installdir) + if ftpserveroffs != None: + ftpserver = getUnicodeString(buf,ftpserveroffs) + if ftpserver != "ftp.ftpserver.com".join("\x00"): + print "FTP Server: %s" % ftpserver + ftpuser = getUnicodeString(buf,ftpuseroffs) + ftppass = getUnicodeString(buf,ftppassoffs) + print "FTP User: %s" % ftpuser + print "FTP Pass: %s" % ftppass + + print("\n") + return (_id,group,version,mutex,installdir,ftpserver,ftpuser,ftppass) + def getFile(f): - rp = open(f, "rb") - buf = rp.read() - rp.close() - return buf + with open(f, 'rb') as fd: + return fd.read() if __name__ == "__main__": - parser=argparse.ArgumentParser(description="parses Xtreme RAT config and prints config data") - parser.add_argument('i', metavar='Input', help='a path to a config dump or directory of config dumps') - parser.add_argument('-csv', metavar="CSV", help='write results to CSV in addition to printing to console') - - args = parser.parse_args() - - if os.path.isfile(args.i): - f = sys.argv[1] - buf = getFile(f) - parseConfig(f,buf) - elif os.path.isdir(args.i): - if args.csv: - csv = open(args.csv,"w") - headers = "file,id,group,version,mutex,installdir,ftpserver,ftpuser,ftppass\n" - csv.write(headers) - else: - csv = None - d = args.i - for f in os.listdir(d): - path = os.path.join(d, f) - if os.path.isfile(path): - buf = getFile(path) - res = parseConfig(path,buf) - if csv is not None: - if res is not None: - csv.write("%s,%s,%s,%s,%s,%s,%s,%s,%s\n" % (f, res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7])) - - if csv is not None: - csv.close() - + parser=argparse.ArgumentParser(description="parses Xtreme RAT config and prints config data") + parser.add_argument('i', metavar='Input', help='a path to a config dump or directory of config dumps') + parser.add_argument('-csv', metavar="CSV", help='write results to CSV in addition to printing to console') + args = parser.parse_args() + if os.path.isfile(args.i): + f = sys.argv[1] + buf = getFile(f) + parseConfig(f,buf) + elif os.path.isdir(args.i): + if args.csv: + csv = open(args.csv,"w") + headers = "file,id,group,version,mutex,installdir,ftpserver,ftpuser,ftppass\n" + csv.write(headers) + else: + csv = None + d = args.i + for f in os.listdir(d): + path = os.path.join(d, f) + if os.path.isfile(path): + buf = getFile(path) + res = parseConfig(path,buf) + if csv and res is not None: + csv.write("%s,%s,%s,%s,%s,%s,%s,%s,%s\n" % (f, res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7])) + + if csv is not None: + csv.close()