报告编号:B6-2020-101904
报告来源:360-CERT
报告作者:360-CERT
更新日期:2020-10-19
0x01 基础知识
1.1 Netlogon协议
Netlogon 远程协议(也称为MS-NRPC)是一种 RPC 接口,由加入域的设备独占使用。 MS-NRPC 包括一种身份验证方法和一种建立 Netlogon 安全频道的方法。 这些更新强制指定的 Netlogon 客户端行为将安全 RPC 与成员计算机和 Active Directory (AD)域控制器(DC)之间的 Netlogon 安全频道配合使用。
Netlogon“网络登录”系统服务维护计算机和域控制器之间的安全通道,对用户和服务进行身份验证。它将用户的凭据传递给域控制器,然后返回用户的域安全标识符和用户权限。这通常称为 pass-through 身份验证。
验证过程如下:
1.2 Challenge/Response 认证
(Challenge/Response)方式的身份认证系统就是每次认证时认证服务器端都给客户端发送一个不同的"Challenge"字串,客户端程序收到"Challenge"后,做出相应的"Response",以此机制而研制的系统。
认证过程如下:
- 客户端向认证服务器发出请求,要求进行身份认证;
- 认证服务器从用户数据库中查询用户是否是合法的用户,若不是,则不做进一步处理;
- 认证服务器内部产生一个随机数,作为"Challenge",发送给客户端;
- 客户端将用户名字和随机数合并,使用单向Hash函数生成一个字节串作为应答;
- 认证服务器将应答串与自己的计算结果比较,若二者相同,则通过一次认证;否则,认证失败;
- 认证服务器通知客户端认证成功或失败。
1.3 涉及相关的数据结构
NetrServerReqChallenge (Opnum 4)
用于客户端请求身份认证,并返回服务端的Challenge。
NTSTATUS NetrServerReqChallenge(
[in, unique, string] LOGONSRV_HANDLE PrimaryName,
[in, string] wchar_t* ComputerName,
[in] PNETLOGON_CREDENTIAL ClientChallenge,
[out] PNETLOGON_CREDENTIAL ServerChallenge
);
PrimaryName:句柄
ComputerName:客户端计算机的NetBIOS名称,即计算机名
ClientChallenge:填充客户端请求的用户密码明文
ServerChallenge:填充服务端返回的Challenge
返回值:成功则返回0x00000000
NetrServerAuthenticate3 (Opnum 26)
在调用NetrServerReqChallenge后调用该方法,即接收域控服务器的"ServerChallenge" 后, 客户端将密码和"Challenge"进行加密得到一个密文字符串,作为ClientCredential响应回域控服务器,域控服务器得到响应后,与自己计算的结果进行对比,如果二者相同,则通过认证。
NTSTATUS NetrServerAuthenticate3(
[in, unique, string] LOGONSRV_HANDLE PrimaryName,
[in, string] wchar_t* AccountName,
[in] NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
[in, string] wchar_t* ComputerName,
[in] PNETLOGON_CREDENTIAL ClientCredential,
[out] PNETLOGON_CREDENTIAL ServerCredential,
[in, out] ULONG * NegotiateFlags,
[out] ULONG * AccountRid
);
PrimaryName:句柄
AccountName:账户名,exp填充的是域控服务器的机器名+$
SecureChannelType:NETLOGON_SECURE_CHANNEL_TYPE 类型的一个枚举值
ComputerName:填充域控服器的机器名
ClientCredential:填充客户端利用之前服务器返回的Challenge进行加密后的密文
ServerCredential:填充服务器返回的凭证,如果认证失败则返回0x0
NegotiateFlags:标志位,包括 signing 和 sealing,可以将它们置0,之后的交互就不会通过session key加密
返回值:成功则返回0x00000000
NetrServerPasswordSet2 (Opnum 30)
如果客户端认证通过,并和域控服务器建立了安全链接,则可以通过该方法设置域控服务器的密码。客户端也可以利用该方法清空域控服务器的密码。
NTSTATUS NetrServerPasswordSet2(
[in, unique, string] LOGONSRV_HANDLE PrimaryName,
[in, string] wchar_t* AccountName,
[in] NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
[in, string] wchar_t* ComputerName,
[in] PNETLOGON_AUTHENTICATOR Authenticator,
[out] PNETLOGON_AUTHENTICATOR ReturnAuthenticator,
[in] PNL_TRUST_PASSWORD ClearNewPassword
);
PrimaryName:句柄
AccountName:账户名,exp填充的是域控服务器的机器名+$
SecureChannelType:NETLOGON_SECURE_CHANNEL_TYPE 类型的一个枚举值
ComputerName:填充域控服器的机器名
Authenticator:填充的是NETLOGON_AUTHENTICATOR结构
ClearNewPassword :填充的是NL_TRUST_PASSWORD结构
返回值:成功则返回0x00000000
NETLOGON_AUTHENTICATOR
typedef struct _NETLOGON_AUTHENTICATOR {
NETLOGON_CREDENTIAL Credential;
DWORD Timestamp;
} NETLOGON_AUTHENTICATOR,
*PNETLOGON_AUTHENTICATOR;
Credential:NetrServerAuthenticate3 操作请求的ClientCredential, 即加密后的密文凭证
Timestamp:时间戳
NL_TRUST_PASSWORD
typedef struct _NL_TRUST_PASSWORD {
WCHAR Buffer[256];
ULONG Length;
} NL_TRUST_PASSWORD,
*PNL_TRUST_PASSWORD;
Buffer中包含重置的密码,Length为重置密码的长度,但exp中将Length设为0,相当于将密码置空。
0x02 环境搭建
(1)受害机环境搭建(IP为192.168.148.134):
windows server 2012 R2
新建一个AD 域服务:
https://wenku.baidu.com/view/21c848a29989680203d8ce2f0066f5335a8167c7.html#
(2)攻击机环境搭建:
ubuntu 20.04 python 3.8.2
下载相关文件:
git clone https://github.com/De4dCr0w/Vulnerability-analyze.git
a、安装依赖:
pip3 install -r requirements.txt
pip3 install impacket
b、测试是否存在漏洞:
python3 zerologon_tester.py WIN-CQTUN83PA9O 192.168.148.134
WIN-CQTUN83PA9O 为windows server 2012 计算机用户名,保证能ping 通WIN-CQTUN83PA9O即可。
c、执行命令:
python3 CVE-2020-1472.py WIN-CQTUN83PA9O WIN-CQTUN83PA9O$ 192.168.148.134
d、获取hash:
python3 secretsdump.py d4y3.com/WIN-CQTUN83PA9O\$@192.168.148.134 -just-dc -hashes :
利用前:
利用后:
0x03 漏洞分析
漏洞点在于进行AES加密运算过程中,使用了AES-CFB8模式并且错误的将IV设置为全零。凭证计算在 ComputeNetlogonCredential函数中实现,如下:
Encrypt的过程采用的是AES-CFB8,过程如下(黄色部分为IV,蓝色部分为明文):
密码反馈模式(Cipher FeedBack (CFB))的加密过程如下:
这种流加密算法要求IV 每次都要随机不同,然而ComputeNetlogonCredential 函数中IV却是固定,且都为\x00(图中黄色部分)。如果密码明文都为\x00时(图中蓝色部分),因为每一次返回server challenge都不同, 而AES加密使用的key会随着每一轮server challenge的变化而变化,所以有1/256 的概率使得加密后的第一个字节为\x00,而加密第二个字节和加密第一个字节相同,IV,明文以及AES使用的key都未变(因为还是在这次NetrServerAuthenticate3 响应中,sever challenge未变)。这样一直加密后,最终导致密文也都为\x00:
这就导致我们可以有概率可以碰撞密文值,通过NetrServerAuthenticate3 的验证,完成了域身份认证,就可以调用 Netlogon call 完成利用。
0x04 漏洞利用
抓取netlogon 进行验证的几种包进行分析,wireshark 过滤包信息:
netlogon.opnum == 4 || netlogon.opnum == 26 || netlogon.opnum == 30
攻击者ip(域内的一台机器):192.168.148.138
受害者ip(域控服务器):192.168.148.134
(1)发送NetrServerReqChallenge (Opnum 4) 请求,攻击者请求和域控服务器建立连接,此时Client Challenge为 0000000000000000b,即明文密码:
(2)返回NetrServerReqChallenge (Opnum 4) 响应,域控服务器返回成功,同意建立连接:
(3)发送NetrServerAuthenticate3 (Opnum 26)请求,接下来攻击者向域控服务器请求认证,此时Client Credential(netlogon.clientcred)为 0000000000000000b,即ciphertext(密文),netlogon.neg_flags 为 0x212fffff,表示后续不用session key 进行加密交互:
(4)返回NetrServerAuthenticate3 (Opnum 26)响应,域控服务器返回STATUS_ACCESS_DENIED(0xc0000022),认证失败
因为认证次数没有限制,可以反复进行认证这个过程,直至服务器那端计算出来的密文hash也全是0,就可认证通过。
(5)最后认证成功,返回STATUS_SUCCESS
以上即是绕过验证的过程,网上的Poc测试脚本就到此,没有进行密码重置操作。
(6)接下来就是发送 NetrServerPasswordSet2 (30) 请求,进行密码重置,利用将NL_TRUST_PASSWORD结构中的Length 填充为0,清空密码。
(7)最终返回NetrServerPasswordSet2 (30) 响应。
整个交互过程如下:
交互网络包如下:
(8)提取域控服务器用户密码hash
重置的密码是存在AD中用于校验的密码,而不是本地账户密码,所以还需要通过Impacket 中secretsdump.py 脚本进行hash提取。
可以通过彩虹表或者在线hash破解网站进行破解,可以看到D4y3账户的密码为Test123:
0x05 补丁分析
补丁在netlogon.dll中,bindiff结果如下:
补丁中添加了NlIsChallengeCredentialPairVulnerable函数,如下:
和第一个字节进行比较,检查challenge的前5个字节是否相同,如果相同,则认为是有漏洞的。一旦发现challenge至少两个字节不同,认为是没有漏洞的。微软认为以 11111, 22222, 33333这种开头的challenge也可能造成漏洞,虽然概率更小。
0x06 时间线
2020-08-11 微软发布漏洞通告
2020-08-12 360CERT发布通告
2020-09-11 secura公开分析报告及PoC
2020-09-15 360CERT更新通告
2020-10-19 360CERT发布漏洞分析报告