Snippets

Alexander Hanel brute_xor.py

Created by Alexander Hanel last modified
'''
    Author: 
        Alexander Hanel
    Name: 
        pe_brute_xor.py
    Purpose:
        - POC that searches for n-grams and uses them as the XOR key.  
        - Portable executables can contain a lot of padding because of 
        section alignment. 
        - When the padding is XORed it will imprint the XOR key.
        - For example the following hex shows some padding (not caused by section 
        alignment).

00000000  4d 5a 90 00 03 00 00 00  04 00 00 00 ff ff 00 00  |MZ..............|
00000010  b8 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 f8 00 00 00  |................|
00000040  0e 1f ba 0e 00 b4 09 cd  21 b8 01 4c cd 21 54 68  |........!..L.!Th|
        
        - The data was XORed with a key of J7QkcaYItXEsuDzUfhfmpUHx3k3Y0aHL. 
        - Remnants of the XOR key can be seen in the padding starting at 0x30.
        
00000000  07 6d c1 6b 60 61 59 49  70 58 45 73 8a bb 7a 55  |.m.k`aYIpXEs..zU|
00000010  de 68 66 6d 70 55 48 78  73 6b 33 59 30 61 48 4c  |.hfmpUHxsk3Y0aHL|
00000020  4a 37 51 6b 63 61 59 49  74 58 45 73 75 44 7a 55  |J7QkcaYItXEsuDzU|
00000030  66 68 66 6d 70 55 48 78  33 6b 33 59 c8 61 48 4c  |fhfmpUHx3k3Y.aHL|
00000040  44 28 eb 65 63 d5 50 84  55 e0 44 3f b8 65 2e 3d  |D(.ec.P.U.D?.e.=|
        
        - By searching the most common occuring n-grams the XOR key can be predicited. 
        - 9u8jreve is an encoded Locky payload. 
        
word@up:~/Downloads$ python pe_brute_xor.py 9u8jreve 
Length: 32, Count: 253 Key: J7QkcaYItXEsuDzUfhfmpUHx3k3Y0aHL File Name: 0.bin

'''
import sys
import pefile
import re
from collections import Counter
from itertools import cycle


def xor_mb(message, key):
    '''Multi-byte XOR of a string message and string key'''
    return''.join(chr(ord(m_byte)^ord(k_byte)) for m_byte,k_byte in zip(message, cycle(key)))

def pe_carv(data):
    '''carve out executable using pefile's trim'''
    c = 1
    for offset in [temp.start() for temp in re.finditer('\x4d\x5a',data)]:
        # slice out executable 
        temp_buff = data[offset:]
        try:
            pe = pefile.PE(data=temp_buff)
        except:
            continue
        return pe.trim()
    return None

def run():
    data = open(sys.argv[1],'rb').read()
    c = 0
    used_key = ""
    # source http://stackoverflow.com/a/25071991
    # most common XOR key sizes
    for n in [2,4,8,16,32,64,128,256,512]:
        substr_counter = Counter(data[i: i+n] for i in range(len(data) - n))
        # get top 32 most common
        sub_count = substr_counter.most_common(32)
        for temp in sub_count:
            key, count = temp
            if count == 1:
                    break
            temp = xor_mb(data, key)
            pe_c = pe_carv(temp)
            if pe_c:
                print "Length: %s, Count: %s Key: %s File Name: %s.bin" % (len(key), count, key, str(c))
                f = open(str(c) + ".bin", "wb")
                fk = open( "key-" + str(c) + ".bin", "wb")
                f.write(temp)
                fk.write(key)
                f.close()
                fk.close()
                c +=1
                # comment out if you want to see all the keys. 
                # It will match the same key but of different sizes. 
                return
                

run()

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.