def search(self, searchBase=None, scope=None, derefAliases=None, sizeLimit=0, timeLimit=0, typesOnly=False, searchFilter='(objectClass=*)', attributes=None, searchControls=None, perRecordCallback=None): if searchBase is None: searchBase = self._baseDN if scope is None: scope = Scope('wholeSubtree') if derefAliases is None: derefAliases = DerefAliases('neverDerefAliases') if attributes is None: attributes = [] searchRequest = SearchRequest() searchRequest['baseObject'] = searchBase searchRequest['scope'] = scope searchRequest['derefAliases'] = derefAliases searchRequest['sizeLimit'] = sizeLimit searchRequest['timeLimit'] = timeLimit searchRequest['typesOnly'] = typesOnly searchRequest['filter'] = self._parseFilter(searchFilter) searchRequest['attributes'].setComponents(*attributes) done = False answers = [] # We keep asking records until we get a SearchResultDone packet and all controls are handled while not done: response = self.sendReceive(searchRequest, searchControls) for message in response: searchResult = message['protocolOp'].getComponent() if searchResult.isSameTypeWith(SearchResultDone()): if searchResult['resultCode'] == ResultCode('success'): done = self._handleControls(searchControls, message['controls']) else: raise LDAPSearchError( error=int(searchResult['resultCode']), errorString='Error in searchRequest -> %s: %s' % (searchResult['resultCode'].prettyPrint(), searchResult['diagnosticMessage']), answers=answers) else: if perRecordCallback is None: answers.append(searchResult) else: perRecordCallback(searchResult) return answers
def search(self, searchBase=None, searchFilter=u'(objectClass=*)', scope=SCOPE_SUB, attributes=None, derefAliases=DEREF_NEVER, sizeLimit=0, searchControls=None): if searchBase is None: searchBase = self._baseDN searchRequest = SearchRequest() searchRequest['baseObject'] = LDAPDN(searchBase) searchRequest['scope'] = Scope(scope) searchRequest['derefAliases'] = DeRefAliases(derefAliases) searchRequest['sizeLimit'] = IntegerPositive(sizeLimit) searchRequest['timeLimit'] = IntegerPositive(0) searchRequest['typesOnly'] = Boolean(False) searchRequest['filter'] = self._parseFilter(searchFilter) searchRequest['attributes'] = AttributeSelection() if attributes is not None: searchRequest['attributes'].setComponents(*attributes) done = False answers = [] # We keep asking records until we get a searchResDone packet while not done: response = self.sendReceive('searchRequest', searchRequest, searchControls) for message in response: protocolOp = message['protocolOp'] if protocolOp.getName() == 'searchResDone': if protocolOp['searchResDone']['resultCode'] == ResultCode( 'success'): done = self._handleControls(searchControls, message['controls']) else: raise LDAPSearchError( error=int( protocolOp['searchResDone']['resultCode']), errorString='Error in searchRequest -> %s:%s' % (protocolOp['searchResDone'] ['resultCode'].prettyPrint(), protocolOp['searchResDone']['diagnosticMessage']), answers=answers) else: answers.append(message['protocolOp'][protocolOp.getName()]) return answers # ToDo: sorted(answers) ?
def login(self, user='', password='', domain='', lmhash='', nthash='', authenticationChoice='sicilyNegotiate'): """ logins into the target system :param string user: username :param string password: password for the user :param string domain: domain where the account is valid for :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param string authenticationChoice: type of authentication protocol to use (default NTLM) :return: True, raises a LDAPSessionError if error. """ bindRequest = BindRequest() bindRequest['version'] = 3 if authenticationChoice == 'simple': if '.' in domain: bindRequest['name'] = user + '@' + domain elif domain: bindRequest['name'] = domain + '\\' + user else: bindRequest['name'] = user bindRequest['authentication']['simple'] = password response = self.sendReceive(bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyPackageDiscovery': bindRequest['name'] = user bindRequest['authentication']['sicilyPackageDiscovery'] = '' response = self.sendReceive(bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyNegotiate': # Deal with NTLM Authentication if lmhash != '' or nthash != '': if len(lmhash) % 2: lmhash = '0' + lmhash if len(nthash) % 2: nthash = '0' + nthash try: # just in case they were converted already lmhash = unhexlify(lmhash) nthash = unhexlify(nthash) except TypeError: pass bindRequest['name'] = user # NTLM Negotiate negotiate = getNTLMSSPType1('', domain) bindRequest['authentication'][ 'sicilyNegotiate'] = negotiate.getData() response = self.sendReceive(bindRequest)[0]['protocolOp'] # NTLM Challenge type2 = response['bindResponse']['matchedDN'] # NTLM Auth type3, exportedSessionKey = getNTLMSSPType3( negotiate, bytes(type2), user, password, domain, lmhash, nthash) bindRequest['authentication']['sicilyResponse'] = type3.getData() response = self.sendReceive(bindRequest)[0]['protocolOp'] else: raise LDAPSessionError( errorString="Unknown authenticationChoice: '%s'" % authenticationChoice) if response['bindResponse']['resultCode'] != ResultCode('success'): raise LDAPSessionError( errorString='Error in bindRequest -> %s: %s' % (response['bindResponse']['resultCode'].prettyPrint(), response['bindResponse']['diagnosticMessage'])) return True
def kerberosLogin(self, user, password, domain='', lmhash='', nthash='', aesKey='', kdcHost=None, TGT=None, TGS=None, useCache=True): """ logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported. :param string user: username :param string password: password for the user :param string domain: domain where the account is valid for (required) :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho) :param struct TGT: If there's a TGT available, send the structure here and it will be used :param struct TGS: same for TGS. See smb3.py for the format :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False :return: True, raises a LDAPSessionError if error. """ if lmhash != '' or nthash != '': if len(lmhash) % 2: lmhash = '0' + lmhash if len(nthash) % 2: nthash = '0' + nthash try: # just in case they were converted already lmhash = unhexlify(lmhash) nthash = unhexlify(nthash) except TypeError: pass # Importing down here so pyasn1 is not required if kerberos is not used. from impacket.krb5.ccache import CCache from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS from impacket.krb5 import constants from impacket.krb5.types import Principal, KerberosTime, Ticket import datetime if TGT is not None or TGS is not None: useCache = False if useCache: try: ccache = CCache.loadFile(os.getenv('KRB5CCNAME')) except: # No cache present pass else: # retrieve domain information from CCache file if needed if domain == '': domain = ccache.principal.realm['data'].decode('utf-8') LOG.debug('Domain retrieved from CCache: %s' % domain) LOG.debug('Using Kerberos Cache: %s' % os.getenv('KRB5CCNAME')) principal = 'ldap/%s@%s' % (self._dstHost.upper(), domain.upper()) creds = ccache.getCredential(principal) if creds is None: # Let's try for the TGT and go from there principal = 'krbtgt/%s@%s' % (domain.upper(), domain.upper()) creds = ccache.getCredential(principal) if creds is not None: TGT = creds.toTGT() LOG.debug('Using TGT from cache') else: LOG.debug('No valid credentials found in cache') else: TGS = creds.toTGS(principal) LOG.debug('Using TGS from cache') # retrieve user information from CCache file if needed if user == '' and creds is not None: user = creds['client'].prettyPrint().split(b'@')[0] LOG.debug('Username retrieved from CCache: %s' % user) elif user == '' and len(ccache.principal.components) > 0: user = ccache.principal.components[0]['data'] LOG.debug('Username retrieved from CCache: %s' % user) # First of all, we need to get a TGT for the user userName = Principal( user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) if TGT is None: if TGS is None: tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT( userName, password, domain, lmhash, nthash, aesKey, kdcHost) else: tgt = TGT['KDC_REP'] cipher = TGT['cipher'] sessionKey = TGT['sessionKey'] if TGS is None: serverName = Principal( 'ldap/%s' % self._dstHost, type=constants.PrincipalNameType.NT_SRV_INST.value) tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS( serverName, domain, kdcHost, tgt, cipher, sessionKey) else: tgs = TGS['KDC_REP'] cipher = TGS['cipher'] sessionKey = TGS['sessionKey'] # Let's build a NegTokenInit with a Kerberos REQ_AP blob = SPNEGO_NegTokenInit() # Kerberos blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] # Let's extract the ticket from the TGS tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0] ticket = Ticket() ticket.from_asn1(tgs['ticket']) # Now let's build the AP_REQ apReq = AP_REQ() apReq['pvno'] = 5 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = [] apReq['ap-options'] = constants.encodeFlags(opts) seq_set(apReq, 'ticket', ticket.to_asn1) authenticator = Authenticator() authenticator['authenticator-vno'] = 5 authenticator['crealm'] = domain seq_set(authenticator, 'cname', userName.components_to_asn1) now = datetime.datetime.utcnow() authenticator['cusec'] = now.microsecond authenticator['ctime'] = KerberosTime.to_asn1(now) encodedAuthenticator = encoder.encode(authenticator) # Key Usage 11 # AP-REQ Authenticator (includes application authenticator # subkey), encrypted with the application session key # (Section 5.5.1) encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None) apReq['authenticator'] = noValue apReq['authenticator']['etype'] = cipher.enctype apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator blob['MechToken'] = encoder.encode(apReq) # Done with the Kerberos saga, now let's get into LDAP bindRequest = BindRequest() bindRequest['version'] = 3 bindRequest['name'] = user bindRequest['authentication']['sasl']['mechanism'] = 'GSS-SPNEGO' bindRequest['authentication']['sasl']['credentials'] = blob.getData() response = self.sendReceive(bindRequest)[0]['protocolOp'] if response['bindResponse']['resultCode'] != ResultCode('success'): raise LDAPSessionError( errorString='Error in bindRequest -> %s: %s' % (response['bindResponse']['resultCode'].prettyPrint(), response['bindResponse']['diagnosticMessage'])) return True