Skip to content

Instantly share code, notes, and snippets.

@hasherezade
Created May 31, 2016 23:57
Show Gist options
  • Save hasherezade/c78c15eb8acb54e1f13dfc5d9bec6af8 to your computer and use it in GitHub Desktop.
Save hasherezade/c78c15eb8acb54e1f13dfc5d9bec6af8 to your computer and use it in GitHub Desktop.
Decoder for 7even-HONE$T ransomware - variant: 08a53eb5d54c6829cf6ea29bd61ea161
#!/usr/bin/python
# CC-BY: hasherezade
"""Finds R5A key for 7even-HONE$T ransomware - variant: 08a53eb5d54c6829cf6ea29bd61ea161"""
import argparse
import os
PREFIX = 'M'
SUFFIX = '*'
#hardcoded keys for particular variant of 7ev3n HONE$T:
FNAME_KEY_R5A = "777EJfngmfiob5mvX5i4omgfogkthmgA7Jergth3ng5m896h65f34f55gtiyht4jfkdfvb5iy76h544uekdfi5otjg5k0i6h595f"
def decode(data, key, offset=0):
maxlen = len(data)
keylen = len(key)
j = 0 #key index
decoded = bytearray()
for i in range(offset, maxlen):
dec = data[i] ^ key[j % keylen]
j += 1
decoded.append(dec)
return decoded
def search_suffix(filep):
filep.seek(0, os.SEEK_END)
size = filep.tell()
pos = size - 1
filep.seek(pos, os.SEEK_SET)
data = filep.read(1)
if data != '\x0a':
return None
pos -= 1
buffer = ""
prev_data = None
while pos > 0:
filep.seek(pos, os.SEEK_SET)
data = filep.read(1)
pos -= 1
if data == SUFFIX and prev_data == SUFFIX:
break
if data == SUFFIX:
prev_data = SUFFIX
continue
else:
if prev_data == SUFFIX:
buffer = prev_data + buffer
prev_data = None
buffer = data + buffer
return buffer
def merge_win_path(path, filename):
if path.endswith('\\') or path.endswith('/'):
path = path[:len(path)-1]
return path + '\\' + filename
def extend_key(key, out_len):
while len(key) < out_len:
key = key + key
return key[:out_len]
class R5A_decoder():
"""Decoder for R5A algorithm"""
def __init__(self, f_path, data):
self.f_path = f_path
self.data = bytearray(data)
self.size = len(data)
self.half_size = len(data) >> 1
self.quarter_size = self.half_size >> 1
def decode(self):
for i in (0,1):
self.loop2(i)
self.loop1(i)
return self.data
def loop2(self, index2):
#process quarter of the content
my_quarter = self.half_size * index2 + self.quarter_size
end_quarter = my_quarter + self.quarter_size
for i in range(my_quarter, my_quarter + self.quarter_size):
dx = i % 255
self.data[i] = self.data[i] ^ dx
return self.data
def loop1(self, index2):
#process quarter of the content
my_quarter = self.half_size * index2
other_quarter = my_quarter + self.quarter_size
for i in range(0, self.quarter_size):
other_val = self.data[other_quarter + i]
my_val = self.data[my_quarter + i]
self.data[my_quarter + i] = my_val ^ other_val
return self.data
def decode_content(fp, orig_file_name):
suffix_len = len(orig_file_name) + len('**') + len('\x0a') + 1
data = read_encrypted(fp, suffix_len)
r5a_decoder = R5A_decoder(orig_file_name, data)
return r5a_decoder.decode()
def read_encrypted(filep, suffix_len):
filep.seek(0, os.SEEK_END)
size = filep.tell()
if size < suffix_len:
return None
filep.seek(0, os.SEEK_SET)
data = filep.read(1)
if data != PREFIX:
print "encrypted not found"
return None
return filep.read(size - suffix_len)
def check_key_len(keydata, suggested_len):
if len(keydata) < suggested_len * 2:
print "[-] Those files are too short to recover keys. Choose pair of bigger files"
return False
key1 = keydata[:suggested_len]
start2 = suggested_len
key2 = keydata[start2: start2 + suggested_len]
if key1 == key2:
return True
return False
def find_key_len(keydata, min_keylen=10):
if keydata == None or len(keydata) < min_keylen:
print "[-] Invalid key data"
return None
start_len = 4
while (start_len < len(keydata)):
key_start = keydata[:start_len]
next_occur = keydata.find(key_start, start_len)
if check_key_len(keydata, next_occur) == True:
return next_occur
start_len = next_occur
if next_occur == -1:
return None
return None
def main():
parser = argparse.ArgumentParser(description="Recover R5A key")
parser.add_argument('--encfile', dest="encfile", default=None, help="Encrypted file", required=True)
parser.add_argument('--origfile', dest="origfile", default=None, help="Original file", required=True)
args = parser.parse_args()
if args.encfile.endswith("R5A"):
fname_key = FNAME_KEY_R5A
else:
print "[-] Invalid arg: %s is not a R5A file!" % args.encfile
return (-1)
fp = open(args.encfile, 'rb')
fname = search_suffix(fp)
if fname == None or len(fname) == 0:
print "Failed to recover file name"
exit (-1)
orig_file_name = decode(bytearray(fname), bytearray(fname_key))
print "[+] Original name: " + orig_file_name
if not args.origfile.endswith(orig_file_name):
print "[-] ERROR: Names mismatch. Supply the original file corresponding to the encrypted file: " + orig_file_name
exit (-1)
outdata = decode_content(fp, orig_file_name)
if outdata is None:
print "[-] Decoding failed"
return (-2)
fp.close()
fp2 = open(args.origfile, 'rb')
if fp2 is None:
print "[-] Failed to read original file"
exit (-1)
orig_data = fp2.read()
fp2.close()
r5a_key = decode(bytearray(outdata), bytearray(orig_data))
keylen = find_key_len(r5a_key)
if keylen is None:
print "[-] Filed to recover the key! Maybe invalid path or pair of files?"
return (-2)
if check_key_len(r5a_key, keylen):
print "[+] Detected key length: %d" % keylen
r5a_key = r5a_key[:keylen]
else:
print "[-] Key is invalid! Maybe you gave an incorrect pair of files?"
return (-2)
key_fname = "r5a_key.bin"
keyfile = open(key_fname, 'wb')
if keyfile is None:
print "Error creating file: " + key_fname
return (-2)
keyfile.write(r5a_key)
keyfile.close()
print "[+] R5A key saved to: " + key_fname
return 0
if __name__ == "__main__":
main()
#!/usr/bin/python
# CC-BY: hasherezade
"""Decoder for 7even-HONE$T ransomware - variant: 08a53eb5d54c6829cf6ea29bd61ea161"""
import argparse
import os
PREFIX = 'M'
SUFFIX = '*'
#hardcoded keys for particular variant of 7ev3n HONE$T:
FNAME_KEY_R4A = 'ifgj5906jHg948f8g46hgkf054k6h0igjf45n9g5fj90g64g5'
FNAME_KEY_R5A = '777EJfngmfiob5mvX5i4omgfogkthmgA7Jergth3ng5m896h65f34f55gtiyht4jfkdfvb5iy76h544uekdfi5otjg5k0i6h595f'
def decode(data, key, offset=0):
maxlen = len(data)
keylen = len(key)
j = 0 #key index
decoded = bytearray()
for i in range(offset, maxlen):
dec = data[i] ^ key[j % keylen]
j += 1
decoded.append(dec)
return decoded
def search_suffix(filep):
filep.seek(0, os.SEEK_END)
size = filep.tell()
pos = size - 1
filep.seek(pos, os.SEEK_SET)
data = filep.read(1)
if data != '\x0a':
return None
pos -= 1
buffer = ""
prev_data = None
while pos > 0:
filep.seek(pos, os.SEEK_SET)
data = filep.read(1)
pos -= 1
if data == SUFFIX and prev_data == SUFFIX:
break
if data == SUFFIX:
prev_data = SUFFIX
continue
else:
if prev_data == SUFFIX:
buffer = prev_data + buffer
prev_data = None
buffer = data + buffer
return buffer
def merge_win_path(path, filename):
if path.endswith('\\') or path.endswith('/'):
path = path[:len(path)-1]
return path + '\\' + filename
def extend_key(key, out_len):
while len(key) < out_len:
key = key + key
return key[:out_len]
class R5A_decoder():
"""Decoder for R5A algorithm"""
def __init__(self, f_path, data):
self.f_path = f_path
self.data = bytearray(data)
self.size = len(data)
self.quarter_size = self.size >> 2
self.half_size = self.quarter_size * 2
def decode(self, r5a_key, key_len):
for i in (0,1):
self.loop2(i)
self.loop1(i)
path_key = extend_key(self.f_path, key_len)
hard_key = extend_key(r5a_key, key_len)
self.data = decode(self.data, bytearray(hard_key))
return self.data
def loop2(self, index2):
#process quarter of the content
my_quarter = self.half_size * index2 + self.quarter_size
end_quarter = my_quarter + self.quarter_size
for i in range(my_quarter, my_quarter + self.quarter_size):
dx = i % 255
self.data[i] = self.data[i] ^ dx
return self.data
def loop1(self, index2):
#process quarter of the content
my_quarter = self.half_size * index2
other_quarter = my_quarter + self.quarter_size
for i in range(0, self.quarter_size):
other_val = self.data[other_quarter + i]
my_val = self.data[my_quarter + i]
self.data[my_quarter + i] = my_val ^ other_val
return self.data
def decode_content(fp, is_r4a, fname_key, orig_file_name, r5a_key=None, r5a_keylen=None):
suffix_len = len(orig_file_name) + len('**') + len('\x0a') + 1
data = read_encrypted(fp, suffix_len)
if is_r4a:
return decode(bytearray(data), bytearray(fname_key))
f_path = orig_file_name
r5a_decoder = R5A_decoder(f_path, data)
return r5a_decoder.decode(r5a_key, r5a_keylen)
def read_encrypted(filep, suffix_len):
filep.seek(0, os.SEEK_END)
size = filep.tell()
if size < suffix_len:
return None
filep.seek(0, os.SEEK_SET)
data = filep.read(1)
if data != PREFIX:
print "encrypted not found"
return None
return filep.read(size - suffix_len)
def main():
parser = argparse.ArgumentParser(description="Data XOR")
parser.add_argument('--file', dest="file", default=None, help="Input file", required=True)
parser.add_argument('--r5a_key', dest="r5a_key", default="r5a_key.bin", help="File with the R5A key")
parser.add_argument('--r5a_keylen', dest="r5a_keylen", type=int, default=None, help="Length of R5A key")
args = parser.parse_args()
fp = open(args.file, 'rb')
fname = search_suffix(fp)
if fname == None or len(fname) == 0:
print "Failed to recover file name"
exit (-1)
is_r4a = False
if args.file.endswith("R5A"):
fname_key = FNAME_KEY_R5A
print "R5A"
else:
fname_key = FNAME_KEY_R4A
is_r4a = True
print "R4A"
orig_file_name = decode(bytearray(fname), bytearray(fname_key))
print "[+] Original name: " + orig_file_name
dirname = os.path.dirname(args.file)
orig_fname = os.path.join(dirname, orig_file_name)
r5a_key = None
if args.r5a_key is not None:
keyfile = None
try:
keyfile = open(args.r5a_key, 'rb')
except IOError:
print "[-] Cannot open file with R5A key. Please supply the valid file as a parameter"
return -1
if keyfile is None:
print "[-] Cannot open the file with R5A key: %s" % args.r5a_key
return -1
r5a_key = keyfile.read()
keyfile.close()
print "[+] R5A key fetched from file: %s" % args.r5a_key
if not is_r4a and r5a_key is None:
print "[-] You need to supply R5A key"
return -1
r5a_keylen = len(r5a_key)
if args.r5a_keylen is not None:
r5a_keylen = args.r5a_keylen
print "[+] Using R5A key length: %s" % r5a_keylen
outdata = decode_content(fp, is_r4a, fname_key, orig_file_name, r5a_key, r5a_keylen)
if outdata is None:
print "[-] Decoding failed"
return (-2)
outfile = open(str(orig_fname), 'wb')
if outfile is None:
print "Error creating file: " + orig_fname
return (-2)
outfile.write(outdata)
outfile.close()
print "[+] Decoded to: " + orig_fname
return 0
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment