# Intro
In this post I want to delve into a, well understood, simple, deserialization chain which can lead to code execution. This is mostly to educate myself on the mechanics of `.NET deserialization`. Such arcane topics can be hard to understand and it is helpful for my own edification to write down my research notes.
### Relics from the Reflected Realm
Ok, if we take a high level view, what we want to do here is understand a simple deserialization chain.
```
Payload String -> XmlReader -> XamlReader (Formatter) -> ObjectDataProvider (Gadget)
```
The idea is pretty straight-forward:
- We create a simple XAML `ObjectDataProvider` payload string.
- We process the string using `XmlReader`.
- We `deserialize` the reader into objects with `XamlReader`. It's important to understand here that `XamlReader` knows how to interpret the XML elements and attributes as `object types`, `properties` and `methods`.
- Finally payload execution, what we end up deserializing is an `ObjectDataProvider` which has the intrinsic ability to execute methods (with parameters) on specific `object types` (such as `System.Diagnostics.Process`).
I know I know it's all a bit confusing but we will get there in the end!
### Gadgets & Formatters
It's fruitful to have some understanding about `Gadgets` and `Formatters` here, it will help clear up some of the mechanics of deserialization and give a clearer picture of what we are doing.
**Gadgets** are classes or objects which can be abused during or after deserialization to execute arbitrary code or to perform other unintended actions (e.g. arbitrary file deletion). They are pieces of benign code that can be co-opted essentially to perform "malicious" actions.
In our example `ObjectDataProvider` is the gadget. `ObjectDataProvider` is a legitimate class used by the Windows Presentation Foundation (WPF) for data binding and object instantiation but we can abuse it to call arbitrary methods when it is deserialized from controlled data. You can get more details [here](https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.objectdataprovider?view=windowsdesktop-7.0).
**Formatters** are components that are responsible for converting serialized data (byte streams, XML, JSON, etc.) to objects. They interpret serialized data and reconstruct objects from that data, conversely they can also take objects and turn them into serialized data (sometimes both, either/or).
In our example `XamlReader` is able to interpret XAML input and reconstruct an object from it, our `Gadget` in this case (`ObjectDataProvider`). You can get more details [here](https://learn.microsoft.com/en-us/dotnet/api/system.windows.markup.xamlreader?view=windowsdesktop-7.0).
# Chainspotting
Because it makes more sense we will work backwards, we will first analyse how the gadget works and then review the XAML reader to finally create a payload that can be processed and executed through deserialization.
![[deserialize_1.gif]]
### **Gadget** -> ObjectDataProvider
When we look at this class (or any class) it can be a bit difficult to understand how the control-flow works. There isn't really a set way to find that out but you can certainly review the class manually. We can see here that the class is not very large to begin with so this is quite feasible.
![[deserialize_2.png]]
Some background knowledge may, however, be helpful (as always). Looking at some other provider classes you will notice `BeginQuery` is a common theme. Additionally, if you analyse some of the methods, you may notice chained calls up to `BeginQuery`, like this:
![[deserialize_3.png]]
Anyway some light code-review shows that `ObjectDataProvider` will invoke `BeginQuery` (not very interesting of itself) which chains into `QueryWorker`. Looking then at `QueryWorker` shows some interesting elements.
![[deserialize_4.png]]
Note that:
- If `needNewInstance` is `true` and `mode` is `FromType` then it creates a new object using reflection.
- Additionally, if a `MethodName` was specified then it will attempt to execute the method on the created object.
Lets also briefly inspect the invoked `CreateObjectInstance`.
![[deserialize_5.png]]
That's a *bingo*, it looks like an `Object` array is created from a `constructorParameters` count and then `Activator.CreateInstance` is used to instantiate an object of `objectType` using the constructor parameters.
This is great, if we can control `objectType` and `constructorParameters` then we can likely create any object we like, also lets remember that this is a prelude to invoking a method on the object as we saw in `QueryWorker`.
We can test this out in a stand-alone C# project. Since we are manually implementing this we can't trigger execution in an automated fashion because `ObjectDataProvider` is designed to work within a WPF data-binding setting. We can however emulate execution using `reflection` to tigger the method.
Consider the following code.
```cs
public class ObjectDataProvider
{
public Type ObjectType { get; set; }
public String MethodName { get; set; }
public List<Object> MethodParameters { get; } = new List<Object>();
}
public static void Main(String[] args)
{
ObjectDataProvider odp = new ObjectDataProvider();
odp.ObjectType = typeof(MessageBox);
odp.MethodName = "Show";
odp.MethodParameters.Add("Hello, World!");
MethodInfo method = odp.ObjectType.GetMethod(odp.MethodName, new Type[] { typeof(String) });
method.Invoke(null, odp.MethodParameters.ToArray());
}
```
And the result, *a sublime artifact of eldritch design, wondrous as it is perilous*.
![[deserialize_6.png]]
Good, hopefully as an example that highlights what a `Gadget` could look like and what types of properties we would want to pay attention to when searching for new gadgets.
### **Formatter** -> XamlReader
Looking then at `XamlReader` we can quickly identify a number of methods to read input, either from an `XmlReader` or a `Stream` (something you would see on [MSDN](https://learn.microsoft.com/en-us/dotnet/api/system.windows.markup.xamlreader.load?view=windowsdesktop-7.0) also) which is then internally converted to `XmlReader`.
![[deserialize_7.png]]
Obviously these overloads chain into each other all the way down. You can see this `Stream` type conversion at work below.
![[deserialize_8.png]]
Note also that there appears to be an optional `restrictive XAML reader`. Further overload analysis shows that it is possible to provide an allow-list of safe types.
![[deserialize_9.png]]
Interestingly this [isn't documented](https://learn.microsoft.com/en-us/dotnet/api/system.windows.markup.parsercontext?view=windowsdesktop-7.0), one assumes that this is for some internal use (I didn't really delve into this). It would probably be smart to let people restrict type usage (*why you no allow, allow-list?*)..
If we continue tracing this `Load` method we eventually find that it calls into another class method `WpfXamlLoader.Load`, and the show goes on..
![[deserialize_10.png]]
Skipping forward a bit, aided by dynamic analysis, we can see that in `WpfXamlLoader.TransormNodes` we loop our `XamlReader object` using `System.Xaml.XamlXmlReader.Read` to rehydrate the XML back to managed objects.
![[deserialize_11.png]]
Stepping further through this process would also reveal that we identify and access properties of the object like the [ObjectType](https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.objectdataprovider.objecttype?view=windowsdesktop-7.0) which would be `typeof(MessageBox)` in our previous example.
![[deserialize_12.png]]
Similarly we would see a `MethodName`, if specified (like `Show`), and `MethodParameters` (like `Hello World!`). You get the idea, we reconstruct the object.
All that really remains if to create a `String` XML representation of the object we want to invoke, its method and parameters. We will do that in the last section.
##### Lore Diversion
Actually, I was curious about deserialization vulnerabilities, using `XamlReader` as the formatter. I found that the Exchange bugs (like `ProxyNotShell`) follow this pattern. You can do some background reading below:
- Star Labs - [Microsoft Exchange Powershell Remoting Deserialization leading to RCE (CVE-2023-21707)](https://starlabs.sg/blog/2023/04-microsoft-exchange-powershell-remoting-deserialization-leading-to-rce-cve-2023-21707/)
- ZDI - [Control Your Types or Get Pwned: Remote Code Execution in Exchange PowerShell Backend](https://www.zerodayinitiative.com/blog/2022/11/14/control-your-types-or-get-pwned-remote-code-execution-in-exchange-powershell-backend)
Notice also how Exchange uses this kind of restricted XAML reader with allow-listed types, interesting. The takeaway though is, when you have 1200 allowed types, you may be in for a bad time 🪦!
### **Payload** -> XAML & Pwn
Again, lets try to frame this a little bit so we understand how to build our payload. We can take process creation, through `System.Diagnostics.Process`, as an example. When the XAML parser encounters namespace declarations (e.g. `System.Diagnostics`), it resolves and understands that classes from that namespace can be used further in the XAML. Later when it finds an `ObjectDataProvider` element, it instantiates an `ObjectDataProvider` object. The properties of that object (`ObjectType`, `MethodName`, `MethodParameters`) are then set based on the attributes and child items of `ObjectDataProvider` in the XAML. At this point `ObjectDataProvider` has enough information to perform that method invocation we saw earlier (like `System.Diagnostics.Process.Start`). It will use reflection to call the method with an array of identified parameters (like `cmd` & `/c calc`).
For our purposes, we will stick to our `MessageBox` example, you can see the payload below.
```xml
<ObjectDataProvider xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:win="clr-namespace:System.Windows;assembly=PresentationFramework"
ObjectType="{x:Type win:MessageBox}"
MethodName="Show">
<ObjectDataProvider.MethodParameters>
<sys:String>Hello, World!</sys:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
```
If we break this down a little bit, we have some default declarations for `WPF` and `XAML`:
```
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
```
Then we need `namespaces` for classes we intend to use; `System` and `System.Windows`:
```
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:win="clr-namespace:System.Windows;assembly=PresentationFramework"
```
We define the `ObjectType` we want to create and the `MethodName` we want to call:
```
ObjectType="{x:Type win:MessageBox}"
MethodName="Show"
```
Finally, we define the parameters that the method expects:
```xml
<ObjectDataProvider.MethodParameters>
<sys:String>Hello, World!</sys:String>
</ObjectDataProvider.MethodParameters>
```
We can put the everything together and trigger deserialization through our formatter proxy as shown below. We are again putting everything in a C# test-harness.
```cs
String xamlPayload = @"
<ObjectDataProvider xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:sys=""clr-namespace:System;assembly=mscorlib""
xmlns:win=""clr-namespace:System.Windows;assembly=PresentationFramework""
ObjectType=""{x:Type win:MessageBox}""
MethodName=""Show"">
<ObjectDataProvider.MethodParameters>
<sys:String>Hello, World!</sys:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>";
StringReader sr = new StringReader(xamlPayload);
XmlReader xr = XmlReader.Create(sr);
System.Windows.Markup.XamlReader.Load(xr);
```
![[deserialize_13.png]]
Note however that I cheat here, *just a little bit*! The `.NET harness` imports `System.Windows` as a dependency. If that was not the case, we would have to adjust our payload to use some reflection so it would first dynamically resolve the `Type` representing `System.Windows.MessageBox` and then call it. This is irrelevant however as `MessageBox` is just used for demonstration purposes. Actual payloads would probably do some OS file manipulation or create a process where that wouldn't be required.
Finally, lets have a quick look at a payload generated by [ysoserial](https://github.com/pwntester/ysoserial.net).
![[deserialize_14.png]]
This is slightly different from what we did but hopefully when you look at this now it all makes much more sense. Notice also the ability to nest elements.
# Thoughts
Very interesting stuff, there are ideas here of using something like `Mono.Cecil` or `dnlib` to create `.NET scanners`, maybe sprinkle some graph theory in there (like [Tabby](https://github.com/wh1t3p1g/tabby) does for Java).
Everyone knows [Alvaro Muñoz](https://twitter.com/pwntester) is one of the OG's of deserialization so I want to just leave you with a great talk he did at `Insomni'hack` on attacking `.NET Deserialization`.
