CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE

渗透技巧 2年前 (2022) admin
955 0 0

★且听安全-点关注,不迷路!



CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE

★漏洞空间站-优质漏洞资源和小伙伴聚集地!



漏洞信息


使用 C# 做过界面开发的小伙伴应该对 DevExpress 框架库非常熟悉,它是一套基于 .Net 的 UI 控件库,也是目前 .Net 下最为强大的完整的一套 UI 控件库,主要集成了 WinForm 和 WebForm 下的一些常用控件和 UI 元素。


在分析绕过 `SerializationBinder` 不安全的类型绑定过程中,关注到了 DevExpress CVE-2022-28684 反序列化漏洞。ZDI 通报如下:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


影响版本如下:


  • DevExpress: before 18.1.18, 18.2.17, 19.1.15, 19.2.14, 20.1.15, 20.2.11, 21.1.9, 21.2.7, 22.1.1


在阅读文章开始前,建议小伙伴们先移步公众号的前一篇文章:


.NET反序列化漏洞之绕过 SerializationBinder 不安全的类型绑定

QCyber,公众号:且听安全.NET反序列化漏洞之绕过 SerializationBinder 不安全的类型绑定


大家更加关注有实际影响力的漏洞,但是我觉得对想学习漏洞挖掘和代码审计的小伙伴而言,分析基础框架的通用性漏洞更有价值。


SafeBinaryFormatter


在 DevExpress 提供了一个 `SafeBinaryFormatter` 的安全反序列化静态类:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


对外提供了三个反序列化函数接口:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


我们可以尝试调用 `Deserialize` 来加载 `YSoSerial.Net` 生成的反序列化载荷:


using (var fileStream = new FileStream(file, FileMode.Open)){    SafeBinaryFormatter.Deserialize(fileStream);}


抛出异常,无法像直接使用 .NET 官方提供的 `BinaryFormatter` 那样实现 RCE :


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


失败原因分析


`SafeBinaryFormatter#Deserialize` 反序列化处理过程:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


跟进 `DeserializeWithSecurityExceptionUnwrap` 函数:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


通过 `Instance` 只读属性获取 `BinaryFormatter` 对象,然后调用 `BinaryFormatter#Deserialize` 执行反序列化操作。我们重点看一下 `BinaryFormatter` 的定义过程:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


到这里大家就明白了失败的原因。`SafeBinaryFormatter` 自定义的 `BinaryFormatter` 对象在初始化时会绑定一个继承于 `SerializationBinder` 的 `DXSerializationBinder` 对象,限制反序列化操作的代码位于 `DXSerializationBinder#BindToType` 函数中:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


跟进分析 `DXSerializationBinder#BindToType` 函数:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


进入 `SafeSerializationBinder#Ensure` :


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


首先利用 `UnsafeType#Match` 进行匹配,匹配成功将调用 `XtraSerializationSecurityTrace.UnsafeType` 断言判断抛出异常。`UnsafeType#Match` 定义如下:


public static bool Match(string assembly, string type){  return SafeSerializationBinder.TypeRanges.Match(SafeSerializationBinder.UnsafeTypes.typeRanges, assembly, type) ||  SafeSerializationBinder.TypeRanges.MatchDX(SafeSerializationBinder.UnsafeTypes.dxTypeRanges, assembly, type) ||  SafeSerializationBinder.UnsafeTypes.types.Contains(type) ||  (FrameworkVersions.IsMonoRuntime &&SafeSerializationBinder.UnsafeTypes.wasmTypes.Contains(type));}


`SafeSerializationBinder.UnsafeTypes.typeRanges` 定义如下:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


`SafeSerializationBinder.UnsafeTypes.dxTypeRanges` 定义如下:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


`SafeSerializationBinder.UnsafeTypes.types` 定义如下:


private static readonly HashSet<string> types = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase){  "System.DelegateSerializationHolder",  "System.DelegateSerializationHolder+DelegateEntry",  "System.Reflection.MemberInfoSerializationHolder",  "System.UnitySerializationHolder",  "System.Management.Automation.PSObject",  "System.Workflow.ComponentModel.Serialization.ActivitySurrogateSelector",  "System.AppDomainSetup",  "System.Exception",  "System.BadImageFormatException",  "System.MissingFieldException",  "System.MissingMemberException",  "System.MissingMethodException",  "System.IO.FileLoadException",  "System.IO.FileNotFoundException",  "System.Security.SecurityException",  "System.Security.Permissions.PublisherIdentityPermissionAttribute",  "System.Security.Permissions.PermissionSetAttribute",  "System.Collections.Generic.ComparisonComparer`1",  "System.Reflection.RuntimeAssembly",  "System.Reflection.DefaultMemberAttribute",  "System.Reflection.Emit.ModuleBuilderData",  "System.Net.FileWebRequest",  "System.Net.HttpWebRequest",  "System.Media.SoundPlayer",  "System.Configuration.ConfigurationException",  "System.Runtime.Versioning.FrameworkName",  "System.Drawing.Design.ToolboxItem",  "System.RuntimeType",  "System.RuntimeTypeHandle",  "System.Resources.ResourceManager",  "System.Reflection.RuntimeConstructorInfo",  "System.Reflection.RuntimeEventInfo",  "System.Reflection.RuntimeFieldInfo",  "System.Reflection.RuntimeMethodInfo",  "System.Reflection.RuntimePropertyInfo",  "System.Reflection.RuntimeParameterInfo",  "System.Reflection.RuntimeModule",  "System.Reflection.RtFieldInfo",  "System.Reflection.MdFieldInfo",  "System.Reflection.ParameterInfo",  "System.Reflection.Pointer",  "System.Reflection.TypeDelegator",  "System.Reflection.CustomAttributeTypedArgument",  "System.Runtime.CompilerServices.RequiredAttributeAttribute",  "System.Runtime.CompilerServices.StateMachineAttribute",  "System.Security.Permissions.ResourcePermissionBase",  "System.ComponentModel.LicenseException",  "System.CodeDom.Compiler.TempFileCollection",  "System.Data.DataSet"};


包括我们测试使用的 `System.Data.DataSet` 利用链在内的几乎所有 `YSoSerial.Net` 中已知的 Gadget ,调用过程在 `BindToType` 函数中抛出异常,最终导致反序列化操作被阻断。


阻断绕过(一)


回顾前面公众号文章《.NET反序列化漏洞之绕过 SerializationBinder 不安全的类型绑定》,我们可以通过修改 `YSoSerial.Net` 代码段 `/Generators/DataSetGenerator.cs` 来通过 `SafeSerializationBinder#Ensure` 检查:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


继续往下走,发现 `EnsureAssemblyQualifiedTypeName` 函数会修改 `assemblyName` 和 `typeName` 的值,从而再次调用 `Ensure` 函数时还是会检查失败抛出异常:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


跟进 `EnsureAssemblyQualifiedTypeName` 函数:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


我们的目标是想让 `EnsureAssemblyQualifiedTypeName` 函数返回 `false` ,这样将可以避开 `Ensure` 函数对 `typeName` 的二次检查。一共有三处确定返回 `false` 的地方,我们可以重点关注第 88 行处的处理过程:


  •  `num2` 变量的取值为 `typeName` 最后一个 `,` 的索引;

  •   代码中会根据尝试截取生成新的 `typeName` ,截取的位置刚好就是 `num2` ;

  •   如果 `typeName` 中包含 `version=` ,将提取 `typeName` 最后一个 `]` 的位置并赋值给 `num4` ,当 `num4 > num2` 时函数将返回 `false`。


因此可以修改 `DataSetGenerator.cs` 代码中的 `GetObjectData` ,直接在后面添加 `,]` 就可以满足上面的条件,同时为了符合 .NET `Type` 全名命名规则,最终修改如下:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


重新生成反序列化载荷再次测试:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


返回 `false` ,绕过了 `Ensure` 函数对 `typeName` 的二次检查。


阻断绕过二


回到 `BindToType` 函数继续往下走:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


第 466 行 `SafeSerializationBinder.DXSerializationBinder#GetAssembly` 尝试根据 `assemblyName` 获取 `assembly` ,跟进:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


第 488 行 `Assembly.Load` 加载不存在的组件名会导致 `BindToType` 函数返回 `null`:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


在《.NET反序列化漏洞之绕过 SerializationBinder 不安全的类型绑定》中已经分析,当 `BindToType` 返回 `null` 时,还会继续调用 `FastBindToType` 对象:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


因为 `assemblyName` 不存在会抛出异常,获取 `Type` 类型为空,从而导致反序列化操作再次被阻断。这个地方绕过很容易,我们再次修改  `DataSetGenerator.cs` 代码,在 `GetObjectData` 中赋值一个合理的组件名,比如 `System`:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


再次测试成功通过 `FastBindToType` 组件检查并返回合法 `Type` :


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


最终触发 RCE。


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


修复方式


漏洞修复主要位于 `EnsureAssemblyQualifiedTypeName` 函数,新增 `AssemblyQualifiedTypeNameParser` 密封类:


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


对参数  `assemblyName` 和 `typeName` 的处理和检查逻辑更加复杂,利用前面的方法无法绕过检查。



由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用本人负责,且听安全及文章作者不为此承担任何责任。



★且听安全-点关注,不迷路!

CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE



★漏洞空间站-优质漏洞资源和小伙伴聚集地!

CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE


原文始发于微信公众号(且听安全):CVE-2022-28684 .NET 最强大 UI 框架 DevExpress 如何绕过反序列化绑定类型限制实现 RCE

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...