(360 A-TEAM 长期招收高级安全研究人员,APT 攻防人员,请联系 wufangdong@360.net)
n1nty@360 A-TEAM
有不少文章说过 Mimikatz 能导出明文密码的原因是因为 Wdigest SSP 在内存中以加密的形式保存了用户的明文。但也仅此而已,从没看到有文章说为什么 Wdigest SSP 会这么做。
缓存在 LSASS 中的明文凭据(或可逆的密文)
根据微软官方博客的说法,在 KB2871997 出现之前,除了 Wdigest SSP 外,还有其他几个 SSP 也会在用户进行了交互式登陆后缓存用户的明文凭据。在 KB2871997 后,就只有 Wdigest SSP 依然有可能缓存用户明文凭据(取决于注册表中 UseLogonCredential 的值)。
Digest
与 NTLM 类似也一种挑战认证的协议,MSDN 的文档说这个协议目前主要只用于 IIS 与 LDAP。
挑战认证的基本流程就是:
客户端访问服务端发起认证
服务端返回一个随机值
客户端利用自己知道的密码或一些其他信息来对这个随机值做一些计算,得运算结果 response,将 response 发送至服务端
服务端利用同样的方法也计算出一个 response,并将自己计算出的这个与客户端发送过来的 response 进行对比,如果一致,则证明客户端确实是知道密码,认证成功。
关于 Digest 认证,有两份 RFC 文档:
RFC 2069
RFC 2617
微软的 Digest SSP (wdigest.dll) 是对这两份 RFC 的实现。Digest 本身并不是一个非常安全的认证协议,它出现的主要目的是为了替代 HTTP BASIC 认证。BASIC 认证会直接将认证的账号密码以近乎明文(BASE64)的方式发送,Digest 是为了解决这个问题。并且 Digest SSP 只能用于进行域账号认证,不能像 NTLM 一样进行本地账号认证。
RFC 2069 中 Response 的计算方式
Hash1=MD5(username:realm:password)
Hash2=MD5(method:digestURI)
response=MD5(Hash1:nonce:Hash2)
各个字段解释
Username
客户端用户名
Realm
用户所属的域的名字,有可能是 NETBIOS NAME 或 FQDN。
Password
客户端密码
Method
如果是 HTTP 协议的话,就是HTTP METHOD。
如果是LDAP的话,没去深究
digestURI
如果是HTTP 协议的话就是HTTP URI。
如果是LDAP 协议,那这个 URI 是类似
LDAP/dc01.east.com/east.com 的值,没去深究这个到底是什么
nonce
服务端返回给客户端的那个随机值
RFC 2617 中 Response 的计算方式
Hash1=MD5(username:realm:password)
Hash2=MD5(method:digestURI)
response=MD5(Hash1:nonce:nonceCount:cnonce:qop:Hash2)
相比 2069 中的计算方式,2617 的计算方式包含了几个新的字段。我没去深究这些字段到底是做什么的,因为跟主题无关。
Digest 验证与明文凭据
可以看到前面无论是 RFC 2069 还是 RFC 2617,在计算 Hash 1 的时候,都需要用到明文密码。
所以,在用户进行了交互式登陆后,为了实现 Digest 认证机制的 SSO,Digest SSP 只能将密码加密后保存在内存里面,在有任何客户端软件需要与远程服务器进行 Digest 认证的时候解密还原成明文用于计算 Digest response。
Digest 客户端 与 Digest 服务端
使用 Digest SSP 与远程服务器进行身份认证的一端叫做 Digest 客户端,而接收并验证 Digest 客户端发起的验证请求的是 Digest 服务端。
Digest 服务端(MSDN 文档说 Digest 认证一般情况下只用于 IIS 和 LDAP,那么可以将 Digest 服务端狭义地理解为 IIS 或 LDAP,当然肯定其他的程序也可以用)为了验证 Digest 客户端发送过来的 Response 是否正确,所以他自身也要计算 Response,来与客户端 response 进行对比。所以,Digest 服务端也会需要用到账号的明文密码(为了不超出本文主题,这里省略了一点东西),所以要求被验证的账号的密码需要被明文保存在域里,所以这里就涉及到 Windows 域的 Reversible Encryption 功能(MSDN 说 Digest SSP 只能用于在域环境下进行域账号的身份验证)。
Reversible Encryption
默认情况下,域里的所有账号都是关闭这个功能的(在没有 Advanced digest 的时候,关闭了这个功能的域账号将不能使用 digest 进行认证)。所以,域数据库里面默认只保存了密码的 Hash 而没有保存密码本身。
开启 Reversible Encryption 的三种方法:
在域控的组策略里面对所有域成员开启,此时除了 HASH 外,所有域成员的明文密码都会以一种可逆加密的形式保存在域数据库中。以可逆加密形式存储的明文密码,跟直接存储明文没有什么区别
在域用户管理里面,单独为某些用户启用 Reversible Encryption 功能。启动了此功能的用户会在它们的 UserAccessControl 属性里面留下标志
利用 fine grained password policy 启用
Exploit Reversible Encryption
harmj0y 与 adsecurity.org 上提到过通过开启 Reversible Encryption 来留后门的文章:
Dump Clear-Text Passwords for All Admins in the Domain Using Mimikatz DCSync
Targeted Plaintext Downgrades with PowerView
http://www.harmj0y.net/blog/redteaming/targeted-plaintext-downgrades-with-powerview/
简而言之
Digest 客户端如果使用名为 user01 的域账号与 Digest 服务端进行 Digest 身份认证,那么就需要 user01 的密码是以可逆加密的形式保存在域数据库中的,否则 Digest 服务端无法计算 response,也就无法判断 Digest 客户端提交的认证信息是否正确。在 Windows 2003 以前,确实是这样的,不过从 Windows 2003 开始,引入了 Advanced Digest(就是 Digest 的升级版)。
Digest vs Advanced Digest
从 Windows 2003 开始, wdigest.dll 实现的就是 Advanced digest,而不再是老的 digest 认证协议了。其中一项改变就是,它允许不再存储账号的明文密码。
重新来看一下 Digest Response 的计算方法:
Hash1=MD5(username:realm:password)
Hash2=MD5(method:digestURI)
response=MD5(Hash1:nonce:nonceCount:cnonce:qop:Hash2)
明文密码是需要的因为我们要利用它来计算出 Hash 1(也被称为 HA1)。而 Advanced digest 解决这个问题的方法就是,他会预先计算好 Hash 1。
比如域的 FQDN 为 EAST.COM,域账号为 user01,密码为 123456。那么域控并不会保存 user01 账号的明文密码 123456,而是将 MD5(user01:EAST.COM:123456) 后的 HASH保存在域数据库中,这样就绕过了需要明文密码的问题。
实际上并没有这么简单,根据 [MS-SAMR] 文档,域控一共保存了 29 种 Hash 1,计算方法分别如下:
MD5(sAMAccountName, NETBIOSDomainName, password)
MD5(LOWER(sAMAccountName), LOWER(NETBIOSDomainName), password)
MD5(UPPER(sAMAccountName), UPPER(NETBIOSDomainName), password)
MD5(sAMAccountName, UPPER(NETBIOSDomainName), password)
MD5(sAMAccountName, LOWER(NETBIOSDomainName), password)
MD5(UPPER(sAMAccountName), LOWER(NETBIOSDomainName), password)
MD5(LOWER(sAMAccountName), UPPER(NETBIOSDomainName), password)
MD5(sAMAccountName, DNSDomainName, password)
MD5(LOWER(sAMAccountName), LOWER(DNSDomainName), password)
MD5(UPPER(sAMAccountName), UPPER(DNSDomainName), password)
MD5(sAMAccountName, UPPER(DNSDomainName), password)
MD5(sAMAccountName, LOWER(DNSDomainName), password)
MD5(UPPER(sAMAccountName), LOWER(DNSDomainName), password)
MD5(LOWER(sAMAccountName), UPPER(DNSDomainName), password)
MD5(userPrincipalName, password)
MD5(LOWER(userPrincipalName), password)
MD5(UPPER(userPrincipalName), password)
MD5(NETBIOSDomainName\sAMAccountName, password)
MD5(LOWER(NETBIOSDomainName\sAMAccountName), password)
MD5(UPPER(NETBIOSDomainName\sAMAccountName), password)
MD5(sAMAccountName, "Digest", password)
MD5(LOWER(sAMAccountName), "Digest", password)
MD5(UPPER(sAMAccountName), "Digest", password)
MD5(userPrincipalName, "Digest", password)
MD5(LOWER(userPrincipalName), "Digest", password)
MD5(UPPER(userPrincipalName), "Digest", password)
MD5(NETBIOSDomainName\sAMAccountName, "Digest", password)
MD5(LOWER(NETBIOSDomainName\sAMAccountName), "Digest", password)
MD5(UPPER(NETBIOSDomainName\sAMAccountName), "Digest", password)
你使用 mimikatz 的 dcsync 或其他功能看到的一大堆 Wdigest 的 Hash,就是这 29 种 Hash。而你如果给账号开了 Reversible Encryption ,则 dcscync 会将明文密码读出来,如下图:
Advanced Digest 只能杜绝在服务端存储明文的问题,无法解决在客户端存储明文的问题。因为对于域来说,它的 realm (domain name 是固定的),所以可以预先计算 Hash 1 并保存。但是对于客户端来说,客户端有可能利用 Digest SSP 与任何域进行认证,所以无法预先计算 Hash 1。
参考资料
https://technet.microsoft.com/pt-pt/library/cc778868(v=ws.10).aspx
https://www.wikiwand.com/en/Digest\_access\_authentication
https://blogs.technet.microsoft.com/srd/2014/06/05/an-overview-of-kb2871997/
360 A-TEAM 是隶属于 360 企业安全集团旗下的纯技术研究团队。团队主要致力于 Web 渗透,APT 攻防、对抗,前瞻性攻防工具预研。从底层原理、协议层面进行严肃、有深度的技术研究,深入还原攻与防的技术本质。
欢迎有意者加入!