作者:n1nty@360 A-Team
此次 CVE 所涉及到的背景知识与我之前看过的一些协议资料重合度很高,所以在这里尝试对此 CVE 进行一下初步的解读,有错误的话还请各位指出。看的时候最好配合原文一起看,原文中有一些图片会助于理解。
https://blog.preempt.com/how-we-exploited-the-authentication-in-ms-rdp
纠错
首先,标题的描述并不是太准确。不准确在于:
- 漏洞存在于 CredSSP 中,并不是直接存在于 MS-RDP。MS-RDP 只不过利用到了 CredSSP 进行了身份验证(只有在开启 NLA 的时候)。所以理论上只要是用到了 CredSSP 的协议都会有这个问题。
- 这并不是一个直接的 RCE 漏洞。
本次 CVE 的本质
从目前的作者原文披露的信息来看,这是一次突破了 SSPI 签名与加密的 Kerberos Relay。利用中间人技术,将 MS-RDP 中用到的 CredSSP 验证数据包 Relay 至了远程的 RPC 接口用于达到类似横向移动的效果(基本所有的横向移动全是依赖的 RPC),并且突破了数据包签名与加密。作者目前只实现了 Kerberos Relay,并没有实现 NTLM Relay,且目前只实现了 Relay 至 RPC 服务。
需要知道的背景知识简述
- CredSSP
- Kerberos
- Credential Relay 攻击的原理
CredSSP
这是 Windows 内置的一种身份验证实现,它实现了 SSPI。它并不是一个全新的身份验证协议,它只是 Negotiate SSP 的一个包装器。CredSSP 的出现主要是为了解决 A-TEAM 之前在另一篇公众号文章 老牌工具 PsExec 一个琐碎的细节 中提到的 “authentication double hop” 的问题。不明白 authentication double hop 是什么的朋友可以看一下我那篇文章 。
CredSSP 只是 Negotiate SSP 的一个包装器,工作流程大概如下:
- 建立 TLS 会话
- 利用 Negotiate SSP 与远端进行身份验证。Negotiate SSP 则将选择性地利用 Kerberos SSP 或 NTLM SSP 与远端进行身份验证。Negotiate SSP 生成的 auth token 将会被 CredSSP client 封装在 TSRequest 消息中发送至 CredSSP server,同样 CredSSP server 端所依赖的 Negotiate SSP 所生成的回复 auth token 也将会封装在 TSRequest 被返回至客户端。这个过程也许会往返多次
- 身份验证通过后,通过 TSCredential 将客户端的原始凭据发送至远端服务器。以解决远端服务器 double hop 的问题。
问题出现在上面提到的第 2 步,所以这里再细化一下第 2 步。下面是一张更详细的 CredSSP 流程图:
第 1 步至第 4 步是建立 TLS 会话链接的过程。第 5 第 6 步是 CredSSP 使用下层的 Negotiate SSP 进行身份验证的过程。这两步有可能会反复多次被执行。
在第 5 步和第 6 步执行完后,下层的 Negotiate SSP 已经完成了身份验证,且已经得了用于进行 SSPI 签名与加密的 session key。
第 7 步,CredSSP client 利用 session key 对服务端的证书(CredSSP 是基于 TLS 会话的) public key structure 进行了 SSPI 加密,将结果发送至 CredSSP server 进行验证。
第 8 步,CredSSP server 利用 session key 对自身证书的 public key structure 进行 SSPI 加密,将结果发送至 CredSSP client 进行验证。
这两步的本意是为了防止 Credential Relay 一类的攻击(我的理解是这种方式依然只能防止攻击者伪装成一个恶意的 CredSSP Server 来将 CredSSP client 的验证信息 relay 至另一个 CredSSP server。而无法防止将 CredSSP client 的验证信息 relay 至使用其他 SSP 且没有启用 SSPI 加密签名功能的其他协议 比如 SMB。),像 A-TEAM 之前在另一篇公众号文章 360 A-TEAM 带你走进 NTLM-Relay 中提到的那样,在验证阶段就引入了 session key。
问题出现在第 7 步。
第 7 步
第 7 步的时候,CredSSP client 将 CredSSP server 端的 public key structure 进行了 SSPI 加密与签名,并发回了 CredSSP server。
原作者很机智地想到,如果 CredSSP server 的 public key 是我们自己可控的呢?有没有可能有这么一段数据,它既是一个有效的 RSA public key,又是其他可利用协议的一段有效数据?原作者最终解决了这个问题,而且他找到了一个可利用的协议:RPC,并利用 RPC 的远程计划任务接口实现了 RCE。
因为 POC 还没有公布,所以以下步骤只是我的猜测:
- 攻击者站在中间人的位置,伪装成一个 CredSSP server
- 受害人利用 RDP 连接远端机器利用 CredSSP 进行验证,实际上连接到的是攻击者伪装的 CredSSP server
- 因为 CredSSP 基于 TLS,所以攻击者给客户端返回一个自己的证书(要注意的是,CredSSP client 本身是不会对 CredSSP server 的证书进行 CA 检查的),用于解密 CredSSP client 发往自身的加密验证流量。
- 根据上面那张图,CredSSP 的第 5 与第 6 步是双方进行认证数据包的交换(auth token)。一旦攻击者发现 CredSSP client 开始利用 TSRequest 与自己来进行身份认证,攻击者则开始将这些认证数据包 Relay 至目标的 RPC 服务。当 CredSSP Client 认证完成后,攻击者与远程的 RPC 服务也已经完成了认证。
- 感觉一直到上一步,看起来都只是一个正常的 Credential relay 的过程,并没有什么新的东西。原作者的机智才刚刚展现。
- 攻击者与远程 RPC 服务进行了身份验证后,是无法进行 RPC 调用的。因为 RPC 对后面的数据包有签名与加密的检查。攻击者无法得知用于签名与加密的 session key。
- 回到上面那张图的第 7 步,CredSSP client 会利用 session key 将服务端发来的 public key structure 进行加密然后发送至 CredSSP Server。这个时候,如果我们之前给客户端返回的 TLS 证书中的 public key 正好就是用于进行 RPC 调用的那个数据包呢?核心就在这里。client 会帮我们对这个数据包进行加密与签名,所以攻击者在无需知道 session key 的情况下,绕过了加密与签名进行了远程 RPC 调用。因为 CredSSP client 只会对 public key structure 进行一次加密与签名,所以要求被我们利用的那个协议只需要发送一个加密的数据包就可以完成敏感操作,而 RPC 符合这个要求。
为了更清晰地理解上面的步骤,这里借用原文的一张图:
原作者解决了一些其他的问题:
- 为了解决 public key 是一个有效的 RSA public key 的同时,又是一段有效的 RPC 数据,进行了那段我看不懂的数学。:(
- CredSSP client 并不是单纯对 public key 进行加密,而是对 public key structure 进行加密。public key structure 是对 public key 进行 DER 编码后的结构。因为 DER 是 TLV 格式的编码,编码后会在原始数据前加上表示 type 与 length 的字节,所以客户端最终加密的那段 public key structure 的前几个字节是不可控的代表 type 与 length 的字节,后面才是我们的 public key。作者通过调用那些第一个参数是字符串或指针的 RPC 方法来解决了这些不可控的字节带来的问题。
3.为什么 NTLM 无法使用,只能使用 Kerberos
原文中作者提到,如果想 Relay 至 RPC 协议,则用于认证的协议只能是 Kerberos,不能是 NTLM。(当然这是跟 RPC 协议自身有关的,如果你能找到其他可利用的协议的话,也许就没有这个问题。)原文中给出了一张图,用于解释这个问题:
RPC 如果使用 NTLM 进行了身份验证,则要求后续的方法调用数据包的 RPC Application Data 部分是加密的,再将 DCE Header 与 加密后的 RPC Application Data 进行签名。CredSSP 第 7 步是直接对整个数据包进行加密与签名,所以不符合这个要求。所以在 RPC 的场景下,不能进行 NTLM Relay,只能进行 Kerberos Relay。
Kerberos 与 SPN
原文中作者目前只实现劫持 RDP 中的 CredSSP 并 Kerberos Relay 至 RPC。这一点说明了,只能 Relay 至与原始服务(RDP)是同一个启动账号的其他服务。因为:
- 受害者通过 RDP 连接尝试连接 Server1, CredSSP 将使用 Kerberos 与 Server1 进行认证,
- 攻击者劫持了这个连接,尝试将 Kerberos 认证 Relay 至 Server2
- 因为受害者传递过来的 ticket 是由 Server1 的机器账号密码加密(因为 RDP 是由 Server1 的 SYSTEM 启动)的,所以攻击者只能将这个 ticket relay 至由 Server1 的 SYSTEM 权限启动的其他服务。如果是 relay 至非 Server1 SYSTEM 启动的服务,则这个 ticket 将无法被目标服务解密,认证将会失败 。
那么另一个问题就是,受害者原本是想访问 Server1 的 RDP 服务,所以受害者从 KDC 处申请到的 service ticket 中的 sname 应该是 RDP/SERVER1 之类的。为什么这张票可以被用于访问 RPC 服务呢(很明显 RPC 服务的 SPN 不可能与 RDP 服务的 SPN 相同)。关于这个问题,可以参考一下下面这篇由 impakcet 作者写的文章:
https://www.coresecurity.com/blog/kerberos-delegation-spns-and-more
一句话就是:ticket 中的 SPN 是什么并不重要,你可以将 RDP/SERVER1 改成 XXX/SERVER1,只要目标服务器上的 RDP 与 XXX 服务的启动账号是同一个。
文章转载自360 A-TEAM
(360 A-TEAM 长期招收高级安全研究人员,APT 攻防人员,请联系 wufangdong@360.net)