通过对网络访问帐户进行解密来探索 SCCM

Microsoft系统中心配置管理器(SCCM)。SCCM是一款微软产品体系架构下的桌面端,服务器,移动端管理产品。主要是负责桌面标准化,网络批量安装部署软件和操作系统,杀毒策略,资产收集,移动端管理等等。是一款作为IT管理员,企业基础架构管理的必备工具。在这篇文章中,我们将介绍 SCCM 如何使用其 HTTP API 来初始化客户端。我们还将了解如何从 SCCM 检索网络访问帐户,以及我们如何解密这些凭据而无需使用 DPAPI 或管理员帐户。

实验室设置

对于我们的实验室设置,我们将使用默认的 SCCM 部署。我发现最简单的方法是通过自动化实验室,它支持通过 ConfigurationManager 角色进行安装:

1.png

我们将在这篇文章中使用的版本是 Configuration Manager 2103,我们将把我们的主站点命名为 P01。本实验的服务器将称为 SCCM01,我们将配置为使用 HTTP 进行通信。

一旦SCCM服务器的设置完成,我们将把一切都保留为标准,并添加一个Network Access Account供以后使用:

2.png

完成后,我们就可以继续探索了!

客户端注册

让我们首先看看客户机尝试向SCCM注册时生成的请求。为了做到这一点,我们使用@_Mayyhem awesome SharpSCCM工具:

3.png

当 SharpSCCM 调用实际的 .NET 客户端库时,我们会收到一个清晰的请求,我们可以使用 WireShark 来识别它。客户端向 SCCM 服务器注册的初始步骤如下:

4.png

这个 HTTP 请求被发送到 SCCM 服务器,由两部分组成,一个是 XML 编码的标头,另一个是在多部分/混合 HTTP 请求中发送的 XML 编码的正文。有趣的是,该协议还使用了 CCM_POST 的 HTTP 方法。

标头采用 UTF-16 编码,如下所示:

5.png

请求正文是zlib压缩和Unicode编码:

6.png

为了简单起见,我删除了一些较长的十六进制字符串,但我们在这里看到的是三个十六进制blob被发送到服务器,以及一些关于我们的客户端的初始信息。

让我们转储 Signing blob:

7.png

如果我们看这个,我们实际上会看到这是一个 DER 编码的证书:

8.png

生成此证书时,添加了两个扩展密钥使用 OID:

9.png

这些将证书标记为短信签名证书(自签名)。

因此,我们看到客户端证书被传递给SCCM服务器以供稍后使用,但是SignatureValue字段呢?让我们启动dnSpy并深入研究System.ConfigurationManagement.Messaging.dll程序集,该程序集位于安装了客户端的主机上的 C:\Windows\CCM 中。

经过一番搜寻,我们在Interop.Crypt32.HashAndSignData中找到了答案:

10.png

这表明,使用带有PKCSv15填充的RSA- sha256正在使用与证书关联的 RSA 私钥对 XML 请求正文进行签名。

这里需要注意的一件奇怪的事情是,一旦生成签名,字节在ASCII十六进制编码并添加到请求¯\_(ツ)_/¯之前会被反转。

当服务器响应这个客户端注册请求时,我们再次看到有一个 XML 标头和正文,其中正文数据被 zlib 压缩。可以看到我们被分配给了 ClientID,它是在我们的客户端与服务器通信期间使用的 UUID:

11.png

在这个阶段值得注意的是,这个请求可以发送到未经身份验证的SCCM URL http://SCCM01/ccm_system/request。这足以向SCCM添加一个客户端条目,但是我们将处于“未批准”状态。这种状态在以后会变得很重要:

12.png

政策要求

客户端注册后,客户端执行的下一个阶段是检索策略列表。此调用还使用

13.png

不幸的是,这是事情变得有点复杂的地方。我们首先需要关注的是 PublicKey blob。这实际上只是客户端之前为证书生成的 RSA 公钥,但是这次它被编码为 PUBLICKEYBLOB。

接下来是 ClientIDSignature。这是我们之前看到的用于签署 ClientID 的 RSA-SHA256 签名,前面带有 GUID:,然后转换为 Unicode。例如:

14.png

最后是PayloadSignature,它是随后压缩的主体的签名。

这个请求的主体是 zlib 压缩和 Unicode 编码的,带有我们客户端的信息:

15.png

对该请求的响应是XML主体中可用的策略列表:

16.1.png

16.2.png

虽然这里有很多有趣的东西,但我们现在要关注的领域将是网络访问帐户的共享方式。

秘密政策

如果你遍历我们检索到的策略列表,你一定会遇到标记为“秘密”的策略。其中一个策略是NAAConfig,它包含了网络访问帐户:

17.png

你可能已经看到 @gentilkiwi 在 2021 年发布的 Mimikatz 更新中引用了这些帐户,这表明通常这些凭据是在 SCCM 客户端上使用 DPAPI 加密的:

18.png

但是,如果我们尝试使用 RequestAssignments 请求返回的 URL 直接下载此策略,我们会看到出现一个错误。

19.png

这样做的原因是需要对秘密策略的请求进行身份验证。但是由于这是SCCM,所以还需要进行另一种类型的身份验证。

经过一番搜寻之后,我发现了对 SCCM 服务器上名为 ccmgencert.dll 的 DLL 中使用的身份验证类型的引用:

20.png

既然我们知道了一些使用的签名方法,这些标头实际上可以很容易被创建。看看被添加到SCCM平台的客户端,我们看到它们是这样的:

21.png

ClientToken只是我们的cliententid和当前DateTime的一个连接。ClientTokenSignature是使用上面相同的RSA-SHA256算法的签名。

让将这些标头添加到我们的请求中,看看会得到什么:

22.png

这一次,我们得到了不同的回应。我的意思是我们出现错误,但我们也没有得到任何不好的数据。

事实证明,对于我们的客户端实际请求秘密策略,他们需要在服务器上处于 Approved 状态。

默认情况下,SCCM 安装有以下内容:

23.png

那么,计算机是如何自动自我认可的呢?还有另一个URL被/ccm_system_windowsauth/request的客户端使用。如果之前的XML ClientRegistrationRequest被发送到这个路径,并完成NTLMSSP验证计算机帐户,则客户端设置为 Approved 状态:

24.png

现在,当对此 URL 进行身份验证时,我们似乎可以使用任何随机域用户帐户。然而,问题是它似乎不足以迫使客户进入批准状态。相反,我们需要使用计算机帐户(尽管它不需要与我们尝试注册的客户端相对应),所以 addcomputer.py 在这里非常有用。

25.png

通过将此身份验证步骤添加到我们的注册请求并强制我们的客户端进入 Approved 状态,下次我们请求 NAAConfig 策略时,我们会得到如下所示的内容:

26.png

好吧,回到 dnSpy,我们去尝试弄清楚。答案在 Interop.DecryptMessageFromCertificateFile 方法中找到,该方法显示了 CryptDecryptMessage API 调用的使用。

27.png

参数显示此加密策略是使用 3DES CBC 的 PKCS7 编码 blob,其密钥源自我们之前在证书中提供的 RSA 公钥。

解密后,我们终于看到了一些实际的凭证,如下所示:

28.png

网络访问帐户混淆

起初,获取这些账户似乎需要更多的加密货币。但是 MimiKatz 已经向我们展示了这些凭据最终可以在客户端上访问,因此我们知道我们的客户端必须能够在使用 DPAPI 保护它们之前以某种方式解密这些凭据……那么密钥是什么?在寻找这个加密是如何完成的之后,我在客户端上找到了一个名为 PolicyAgent.dll 的 DLL。

最重要的是调试字符串:

29.png

UnobfuscateString 听起来很有希望,当然听起来比 DecryptString 更好。我没有深入研究这个反汇编的所有部分,而是创建了一个快速调试工具来调用这个函数。

30.png

当运行在与SCCM实验室网络无关的主机上时,并且连接到调试器以逐步解决通过以这种方式调用方法而发生的不可避免的访问冲突时,就会解密用户名和密码:

31.png

32.png

这意味着所使用的加密与描述的完全一样,它是被混淆的!我们拥有在密文中解密这些凭证所需的所有信息,而且我们可以完全离线完成!

通过重新创建unobfuscation方法的步骤,我们可以创建如下所示的解密代码。

33.1.png

有了计算机帐户,我们就可以在SCCM 中添加虚假客户端,下载加密的网络访问帐户凭据,并对其进行解密,而无需处理提升权限或任何 DPAPI 解密。