Introduction 介绍
In modern web development, while cookies are the go-to method for transmitting session IDs, the .NET Framework also provides an alternative: encoding the session ID directly in the URL. This method is useful to clients that do not support cookies. An example of this URL encoding can be seen here:
在现代 Web 开发中,虽然 cookie 是传输会话 ID 的首选方法,但 .NET Framework 还提供了另一种方法:直接在 URL 中对会话 ID 进行编码。此方法对于不支持 Cookie 的客户端很有用。可以在此处查看此 URL 编码的示例:
https://[targetdomain]/(S(aaaaaaaaaaaaaaaaaaaaaaaa))/default.aspx
This technique is known as the “cookieless” feature in the .NET Framework:
这种技术在.NET Framework中被称为“无cookie”功能:
https://learn.microsoft.com/en-us/previous-versions/dotnet/articles/aa479314(v=msdn.10)
Many developers and security testers overlook this option, primarily because of its rarity in real-world applications. Historically, this has turned it into a goldmine for discovering client-side vulnerabilities, such as session fixation, session hijacking, HTML injection, and cross-site scripting. Additionally, this feature can be leveraged to circumvent path-based firewall rules that aren’t configured to recognize the cookieless approach.
许多开发人员和安全测试人员忽略了此选项,主要是因为它在实际应用程序中很少见。从历史上看,这已成为发现客户端漏洞的金矿,例如会话固定、会话劫持、HTML 注入和跨站点脚本。此外,可以利用此功能来规避未配置为识别无 Cookie 方法的基于路径的防火墙规则。
For a deeper dive into security issues stemming from the use of cookieless sessions, consider these references:
要更深入地了解因使用无 Cookie 会话而产生的安全问题,请考虑以下参考:
- https://blog.isec.pl/all-is-xss-that-comes-to-the-net/ (A highly recommended read)
- https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/march/security-briefs-protect-your-site-with-url-rewriting
- https://www.sans.org/blog/session-attacks-and-asp-net-part-2/
Importantly, due to inherent security concerns, the cookieless feature was omitted from .NET Core and subsequent .NET versions. You can learn more about this decision in the following discussions:
重要的是,由于固有的安全问题,.NET Core 和后续 .NET 版本中省略了无 cookie 功能。您可以在以下讨论中了解有关此决定的更多信息:
- https://github.com/dotnet/aspnetcore/issues/37978
- https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/fundamentals/app-state.md
However, let’s not forget the vast number of web applications still humming along on the classic .NET Framework (with the capital ‘F’)!
但是,我们不要忘记大量的Web应用程序仍然在经典的.NET Framework上嗡嗡作响(大写字母“F”)!
Finding the vulnerability
查找漏洞
I was initially trying to find a new method to improve my IIS Short File Name Disclosure technique. As part of this, I realised that the cookieless part can be used twice within the path, and I quickly wrote a Twitter (X) post about how WAFs can be potentially bypassed using this:
我最初试图找到一种新方法来改进我的 IIS 短文件名披露技术。作为其中的一部分,我意识到无 cookie 部分可以在路径中使用两次,我很快写了一篇关于如何使用这个潜在绕过 WAF 的 Twitter (X) 帖子:
https://twitter.com/irsdl/status/1640390106312835072
However, later on I identified a strange anomaly when the cookieless pattern was repeated twice. This resulted in two vulnerabilities reported to Microsoft as their impact and the exploitation were different:
然而,后来我发现了一个奇怪的异常现象,当无 cookie 模式重复两次时。这导致向Microsoft报告了两个漏洞,因为它们的影响和利用是不同的:
- IIS restricted path bypass leading to potential authentication and path-filtration bypass
IIS 受限路径绕过导致潜在的身份验证和路径筛选绕过 - Application Pool confusion leading to potential privilege escalations
应用程序池混乱导致潜在的权限提升
Microsoft addressed both of these issues as part of one patch under CVE-2023-36899.
Microsoft CVE-2023-36899 下的一个补丁中解决了这两个问题。
I got the following comment from Microsoft when I was trying to see why one of them was assessed as a duplicate reducing the bounty:
当我试图了解为什么其中一个被评估为减少赏金的副本时,我从Microsoft那里得到了以下评论:
While it’s not technically a dupe from your side, it is from ours because the same patch fixes both thing, although it’s by accident rather than deliberation.
虽然从技术上讲,这不是您这边的骗局,但它来自我们的,因为相同的补丁修复了这两件事,尽管这是偶然的而不是深思熟虑的。
IIS Restricted Path Bypass
IIS 受限路径绕过
The cookieless feature of .NET Framework could be abused to access protected directories or those blocked by URL filters in IIS. For instance, on the victim.com website, consider:
.NET Framework 的无 cookie 功能可能被滥用来访问受保护的目录或被 IIS 中的 URL 筛选器阻止的目录。例如,在 victim.com 网站上,请考虑:
- The page:
/webform/protected/target1.aspx
within the/protected/
directory that enforces Basic authentication.
页面:/webform/protected/target1.aspx
在强制实施基本身份验证的/protected/
目录中。 - The page:
/webform/bin/target2.aspx
that was temporarily moved to the/bin/
folder, making it inaccessible.
页面:/webform/bin/target2.aspx
已临时移动到/bin/
该文件夹,使其无法访问。
Normally, accessing the pages through these URLs would be blocked in IIS:
通常,通过这些 URL 访问页面将在 IIS 中被阻止:
https://victim.com/webform/protected/target1.aspx
https://victim.com/webform/bin/target2.aspx
However, the cookieless feature can be exploited to access these pages with the following patterns:
但是,可以利用无 Cookie 功能以以下模式访问这些页面:
https://victim.com/webform/(S(X))/prot/(S(X))ected/target1.aspx
https://victim.com/webform/(S(X))/b/(S(X))in/target2.aspx
Here is how IIS was configured as an example to set authentication for the /protected/
path:
以下是将 IIS 配置为为 /protected/
路径设置身份验证的示例的方式:
When trying the standard approach, IIS authentication for the /protected/
path behaves as expected, redirecting unauthorized users to the login page:
尝试标准方法时, /protected/
路径的 IIS 身份验证将按预期方式运行,将未经授权的用户重定向到登录页面:
Still, the bypass technique allows access without authentication, using the Anonymous user. This can sometimes lead to errors if the system expects a specific profile:
尽管如此,绕过技术仍允许使用匿名用户进行无需身份验证的访问。如果系统需要特定的配置文件,这有时会导致错误:
The target1.aspx
code was:
target1.aspx
代码是:
Here I am, I am a protected page, how can you be here?!
You username is: <%=HttpContext.Current.User.Identity.Name%>
The root cause: 根本原因:
The vulnerability stems from the way cookieless paths are rewritten in the .NET Framework. The following code was responsible for the final rewrite:
该漏洞源于在 .NET Framework 中重写无 cookie 路径的方式。以下代码负责最终重写:
https://referencesource.microsoft.com/#System.Web/HttpResponse.cs,50b59e7205970b81
internal String RemoveAppPathModifier(string virtualPath) {
if (String.IsNullOrEmpty(_appPathModifier))
return virtualPath;
int pos = virtualPath.IndexOf(_appPathModifier, StringComparison.Ordinal);
if (pos <= 0 || virtualPath[pos-1] != '/')
return virtualPath;
return virtualPath.Substring(0, pos-1) + virtualPath.Substring(pos + _appPathModifier.Length);
}
The RemoveAppPathModifier
method used by the RemoveCookielessValuesFromPath
method of the CookielessHelperClass
class as can be seen here:
CookielessHelperClass
类 RemoveCookielessValuesFromPath
的方法使用 RemoveAppPathModifier
的方法如下所示:
https://referencesource.microsoft.com/#System.Web/Security/CookielessHelper.cs,113
By the time the function is invoked, the initial cookieless value is already removed. Due to this behavior, the path doesn’t adhere to restriction rules, bypassing authentication or filter checks. Therefore, it changes the /prot/(S(X))ected/
path to /protected/
facilitating the observed bypasses. A screenshot, provided below, captures the RemoveAppPathModifier
method in action during the debugging of the .NET Framework:
在调用函数时,初始无 cookie 值已被删除。由于此行为,路径不遵守限制规则,绕过身份验证或筛选器检查。因此,它改变了 /prot/(S(X))ected/
路径以促进 /protected/
观察到的旁路。下面提供的屏幕截图捕获了在调试 .NET Framework 期间运行 RemoveAppPathModifier
的方法:
Application Pool Confusion
应用程序池混淆
Another notable issue involves how IIS manages Application Pools, potentially leading to privilege escalations or security bypasses. It’s possible to manipulate the cookieless feature in .NET Framework to compel an IIS application to run using its parent’s Application Pool instead of its own.
另一个值得注意的问题涉及 IIS 如何管理应用程序池,这可能会导致权限提升或安全绕过。可以操纵 .NET Framework 中的无 cookie 功能,强制 IIS 应用程序使用其父级的应用程序池而不是自己的应用程序池运行。
To illustrate: 举例说明:
- The root (
/
) of the website is running with theDefaultAppPool
Application Pool
网站的根目录 (/
) 正在使用DefaultAppPool
应用程序池运行 - The
/classic/
application uses the.NET v4.5 Classic
Application Pool
应用程序使用/classic/
.NET v4.5 Classic
应用程序池 - The
/classic/nodotnet/
application uses theNoManagedCodeClassic
Application Pool, which doesn’t support Managed Code.
/classic/nodotnet/
应用程序使用NoManagedCodeClassic
不支持托管代码的应用程序池。
A C# file named AppPoolPrint.aspx
, accessible across all the above applications, reveals the current Application Pool name:
名为 (可在上述所有应用程序中访问)的 AppPoolPrint.aspx
C# 文件显示当前应用程序池名称:
<%@ Page Language="C#" %>
<%
string appPoolName = System.Environment.GetEnvironmentVariable("APP_POOL_ID");
Response.Write("App Pool Name: " + appPoolName);
%>
Based on the regular structure, accessing this page would result in:
根据常规结构,访问此页面将导致:
/AppPoolPrint.aspx -> DefaultAppPool
/classic/AppPoolPrint.aspx -> .NET v4.5 Classic
/classic/nodotnet/AppPoolPrint.aspx -> Error: 404 Not Found (as Managed Code isn't supported)
However, by using the cookieless pattern twice, we can run this page using its parent Application Pool:
但是,通过使用两次无 cookie 模式,我们可以使用其父应用程序池运行此页面:
/(S(X))/(S(X))/classic/AppPoolPrint.aspx -> DefaultAppPool
/(S(X))/(S(X))/classic/nodotnet/AppPoolPrint.aspx -> DefaultAppPool
/classic/(S(X))/(S(X))/nodotnet/AppPoolPrint.aspx -> .NET v4.5 Classic
This allows even the pages within /classic/nodotnet/
(which shouldn’t execute Managed Code) to run the ASPX page using their parent’s Application Pools. This behavior can lead to privilege escalation on IIS.
这甚至允许其中的 /classic/nodotnet/
页面(不应执行托管代码)使用其父级的应用程序池运行 ASPX 页。此行为可能会导致 IIS 上的权限提升。
Update 10/08/2022 更新 10/08/2022
A new bypass of the CVE-2023-36899 patch has been reported to Microsoft. This bypass operates only on specific files, and I cannot discuss it in further detail at the moment.
已向Microsoft报告了 CVE-2023-36899 修补程序的新绕过。此旁路仅适用于特定文件,我目前无法进一步详细讨论它。
Furthermore, the patch only disabled the aggressive path replacement by default configuration. Thus, it’s possible to reintroduce the problematic behavior using the following settings in the web.config
:
此外,该修补程序仅禁用默认配置的主动路径替换。因此,可以使用以下设置重新引入有问题的行为 web.config
:
<appSettings>
<add key="aspnet:RestoreAggressiveCookielessPathRemoval" value="true" />
</appSettings>