Understanding Template Injection Vulnerabilities

Executive Summary

Template injection is a class of vulnerabilities that are commonly found in web applications. These vulnerabilities consist of any vulnerability that results from parsing unvalidated input that is mistakenly evaluated as code by a templating engine. Attackers abuse these vulnerabilities to execute code on either the web browser (client-side) or directly on the web server itself (server-side).

Prisma Cloud Web Application and API Security (WAAS) module is a Web Application and API protection (WAAP) solution designed to protect web applications on any architecture. One of the many features of WAAS is the ability to secure applications via virtual patches, better known as “Custom Rules”. WAAS Custom Rules allow setting up custom signatures to detect and prevent exploitations of vulnerabilities that are recently published, environment-specific or discovered by security teams. In the following blog, we will cover Template Injection vulnerabilities and include an example of how Custom Rules can be configured and used to prevent such web application attacks.

What is a Template Engine?

Template engines are commonly used in web applications to parse dynamic content into a preconfigured template, for example, usernames, items chosen by the user, item prices, and so on.

For example, we receive letters with the same template message while only some parts relevant to our identity are changed (e.g., name, address, fees, dates, subscription ID).

Even some formal letters are written in the same way:

“Dear ______,

We regret to inform you that your application is denied.”

The benefits of using a template engine are clear, they allow a more efficient way to build web applications and reduce the complexity of user personalization’s.

Template engines can be instructed with placeholders within the preset templates and once the application renders the template to be presented to the user, the placeholders will be replaced with the relevant information, for example, John will authenticate to a portal that uses the following template:

“Hello {{username}}!”

Seeing as John’s username is John Doe the template engine will render the template and replace the placeholder with John’s username that way he will be greeted by the following message:

“Hello John Doe!”

We shall divide template engines into two groups to understand the impact of exploitation on each group.

Client-Side Template Engine

The first group will be the Client-Side template engines group, for example, AngularJS and Vue.js.

Client-Side Template Engines, as their name suggests, will render the data on the client-side. These engines can only access data that is accessible and presented to the client and will process the data locally. Their impact will be limited mostly to the user, for example, XSS attacks, which may lead to account hijacking, presenting false information, or committing actions on behalf of the user.

Additionally, Client-Side Template Injection (CSTI), if found, can be a great way for attackers to bypass weak defenses against XSS attacks such as input sanitization techniques where HTML entities encode the presented data.

The client-side template injection has a similar attack surface as XSS attacks. In the more powerful “persistent” template injection, it is possible to inject a payload to a message that will be saved persistently within the server and presented to all the users. While by itself will cause the payload to be executed on every client that exposes to it (item name for example). Alternatively, in a less powerful variant, the payload is exposed to the user as a malicious link that the user will have to interact with for the exploit to execute. For example,  the payload is hyperlinked to the username or personal setting and might require some social engineering, so the user interacts with the hyperlink.

Server-Side Template Engine

Server-Side Template Engines (SSTI) will render the data remotely at the server and present the already processed data to the user, these engines can access any information available to the server allowing the malicious actor to access and expose information that is typically not in their privileges.

Some known server-side template engines include Twig (PHP), Jinja2 (Python), Freemarker (Java) and EJS (JavaScript).

Server-Side Template Injection can become a deadly exploitation vector for an organization. Successful exploitation leads to impact limited only by the creativity of the attacker, it is possible to disclose sensitive information, upload malicious files, corrupt server files or functions while ultimately leading to complete remote code execution over the vulnerable server.

Vulnerable Demo Applications

No description beats a live demonstration. For this blog, we have created a vulnerable application that will be vulnerable to both vulnerabilities discussed. Our demo app will have two vulnerable routes. Our code will be a Python-based, Flask web application that will be using the Jinja2 template engine for the SSTI example and a second Vue.js 3 route for the CSTI example.

CSTI Route

CSTI Vulnerable route code snippet:

Figure 1. CSTI vulnerable application snippet
Figure 1. CSTI vulnerable application snippet

 

This route (/csti) is vulnerable to Client-Side Template Injection, in line 50 you can see that the “username” parameter is being concatenated to the “app” container. It should also be noted that the html.escape() function is used to introduce output encoding sanitations, in this case, HTMLEntityEncode the input value before presenting to the user, this way protecting the application from XSS attacks.

SSTI Route

SSTI Vulnerable route code snippet:

Figure 2. SSTI vulnerable application snippet
Figure 2. SSTI vulnerable application snippet

 

This route (/ssti) is vulnerable to Server-Side Template Injection, on line 24, the parameter “username” is concatenated to the string that is about to be rendered and presented to the user.

The Vulnerable Application

Once we fire the Docker container up and navigate to the application’s page, we will be greeted with the following home page:

Figure 3. Vulnerable application home page.

Client-Side Template Injection

Finding the vulnerability

Starting with the CSTI route, once we open it, we will be redirected to “/csti?username=TemplateInjection” with the following greeting:

Figure 4. CSTI vulnerable route.

We can see that the username is being reflected on the page. Let’s try to understand if the username parameter is vulnerable to CSTI. We can start by sending the following payload:

“{{8*8}}”

Normally, if the page is not vulnerable to CSTI, the page will greet us with “Hello {{8*8}}”. If the page is vulnerable to template injection and parses the input we send as a GET parameter, we will be greeted with “Hello 64”.

Upon sending the payload we are greeted with “Hello 64” thus verifying that the parameter “username” is vulnerable to CSTI.

Figure 5. Demonstration of a CSTI with simple arithmetic expression.

Now, if we refer to line 50 with the html.escape() method, we remember that this parameter shouldn’t be vulnerable to XSS. Thus, trying to input some simple payloads we might see that this is the case, but by exploiting the CSTI we can pop an alert on this page.

Exploiting the vulnerability

When attempting to pop an alert box using the template injection with “{{alert(1)}}” an error was introduced stating that “Uncaught TypeError: alert is not a function”, it appears that the error was introduced from Vue.js on line 15479.

Figure 6. Console message TypeError alert is not a function
Figure 6. Console message TypeError alert is not a function

 

Looking at line 15479 a new function was constructed using the parameter “code” as its content. To better understand the flow a breakpoint was set on this line.

Figure 7. Snippet from line 15479
Figure 7. Snippet from line 15479

 

Using the browser’s debugger, it appeared that the “code” parameter contains the following code:

Figure 8. Content of “code” parameter
Figure 8. Content of “code” parameter

 

It seems that the input from the injected template is concatenated into the “_toDisplayString” function which is referred to in the previous line along with other interesting functions such as “_openBlock” and “_createElementBlock”. The following payload “{{_createElementBlock}}” disclosed the “_createElementBlock” function’s code.

Figure 9. Content of “_createElementBlock” function
Figure 9. Content of “_createElementBlock” function

 

Seeing as JavaScript functions are objects we can use the “constructor” property which will allow us to refer to the constructor function that created this instance object using this payload: “{{_createElementBlock.constructor}}”

Figure 10. XSS vulnerability introduced through CSTI.

Now we can use the function’s constructor to create our own function which for the given example will be used to pop an alert box using this payload: {{_createElementBlock.constructor("alert(1)")}}

Figure 11. Using the function’s constructor to create our own function.

The only thing that is left to execute this function and exploit the XSS vulnerability is to call it using parentheses with a payload that should look like this: {{_createElementBlock.constructor("alert(1)")()}}

Figure 12. XSS vulnerability introduced through CSTI
Figure 12. XSS vulnerability introduced through CSTI

 

Server-Side Template Injection

Finding the vulnerability

Navigating to the SSTI link we are redirected to “/ssti?username=TemplateInjection” and the following message is presented to us:

Figure 13. SSTI vulnerable route
Figure 13. SSTI vulnerable route

 

Again, we can see that the username is being reflected within the page, we should check if the username parameter is vulnerable to SSTI in the same manner as previously with the CSTI by injecting “{{8*8}}”.

Figure 14. Demonstration of an SSTI with simple arithmetic expression
Figure 14. Demonstration of an SSTI with simple arithmetic expression

 

It appears that the username parameter is indeed vulnerable to some kind of template injection. We can verify that this is an SSTI and indeed Jinja2 behind the scenes by sending the following payload: “{{8*’8’}}”, if it is a CSTI we will receive “Hello 64!” again but if it is a Jinja2 SSTI we will receive “Hello 88888888!”, this behavior can be explained seeing as JavaScript’s multiplication operator “*” will only multiply numbers, if a string is introduced Javascript will attempt to convert the string to an integer to multiply it otherwise the result will be “NaN”. While Python’s multiplication operator allows multiplying strings with integers and will return the given string times the integer.

Figure 15. Demonstration of string multiplication using SSTI
Figure 15. Demonstration of string multiplication using SSTI

 

Great! We have verified this is indeed the case of an SSTI vulnerability. We can try some other tricks as well such as Python’s .upper() or .split() functions or any core function to confirm that Python is being used behind the scenes:

Figure 16. String manipulation using SSTI and Python .upper() function
Figure 16. String manipulation using SSTI and Python .upper() function

 

Figure 17. String manipulation using SSTI and Python .split() function
Figure 17. String manipulation using SSTI and Python .split() function

 

Now that we have verified the vulnerability, let’s understand how we can further escalate this exploit into a full-blown remote code execution attack.

Exploiting the vulnerability

Our code execution is at the Python’s context judging by previous behavior.

We are not able to import dependencies within our SSTI code because Jinja’s “import” statement is used to access macros from other templates. Achieving code execution might be tricky.

Judging by the behavior of previous requests it appeared that it is possible to call Python built-in methods like in the previous examples “.split() or .upper()”, combining this with the fact that Python is an object-oriented language we can inspect objects and their inheritances while looking for references to any module that might allow us to achieve OS-level code execution.

Our goal is to find a way to read a flag.txt file within the container using the SSTI vulnerability.

Seeing as everything is an object in Python, a payload such as “{{‘’.__class__}}” will present the class of ‘’ which should be “str”.

Figure 18. Response of __class__ Python builtin function
Figure 18. Response of __class__ Python builtin function

 

Now we can use the Method Resolution Order attribute (__mro__), this attribute should return a tuple of inheritances and types that the current class is derived from.

Figure 19. Using Python’s __mro__ attribute to disclose the inheritances of the class
Figure 19. Using Python’s __mro__ attribute to disclose the inheritances of the class

 

We can see that the response mentions “str” and “object”. The latter seems interesting because if we can try and get to classes that inherit the “object” class through it, we can achieve such a list using the “__subclasses__” method.

Figure 20. List of classes disclosed by __subclasses__ function
Figure 20. List of classes disclosed by __subclasses__ function

 

The “__subclasses__” method has disclosed a big list of classes that can be used to find the module we are looking for. We can use the “__init__” method to refer to the class constructor and further use the “__globals__” method to get the function’s global variables while looking for a module that will allow us to execute commands on the OS, we can look for the “sys” module and use it to refer to other modules imported by the Python interpreter for example the module OS.

Seeing as there are so many subclasses disclosed in the list that going over it manually might take quite some time.

We are going to use the following Python script to find the first subclass that can lead us to the “sys” module using the fact that if the module does not exist an exception will be raised and when the module exists the page will present the “Hello” greeting page.

Figure 21. Python script to discover subclasses that import the “sys” module
Figure 21. Python script to discover subclasses that import the “sys” module

 

After executing the script, we found out that the subclass on index 80 is the first class that has the “sys” module within its global variables.

Now the only thing that is left is to use the sys module’s method called “modules” which can point us to many Python built-in modules such as “os” for example which by itself will allow executing OS commands.

This payload can be used: “{{"".__class__.__mro__[1].__subclasses__()[80].__init__.__globals__['sys'].modules['os'].popen("ls").read()}}”

Figure 22. Successful RCE that executes “ls” command
Figure 22. Successful RCE that executes “ls” command

 

Finally, we have achieved remote code execution.

How can Prisma Cloud WAAS protect against Template Injections?

What is a Custom Rule?

WAAS Custom Rule is the name of a feature that allows creating custom rules to your liking. These rules consist of various modes (alert, prevent, and ban) that will check a request or response header names or values, parameter names or values, methods and more for particular strings or regular expressions. Once a rule detects the pattern that is instructed WAAS will behave accordingly to the mode that was set, for example, “prevent” will block the request, “ban” will blacklist the request issuer’s IP address and so on.

Once you have Prisma Cloud’s WAAS solution deployed we then start creating custom rules. We will create a custom rule based on a simple regular expression to block the aforementioned template injections.

Setting up an app policy

First, we must set up an app policy for WAAS to protect the web application.

In this case, our web application is container-based so we will navigate to Defend/WAAS tab and create a new container WAAS policy.

Figure 23. Prisma Cloud WAAS’s tab
Figure 23. Prisma Cloud WAAS’s tab

 

Giving it a general name such as TemplateInjectionApp.

Figure 24. Creation of a new WAAS policy
Figure 24. Creation of a new WAAS policy

 

Next, we need to select a scope for the WAAS policy.

Figure 25. Collections selection tab
Figure 25. Collections selection tab

 

Seeing as the scope of our container is not defined, we will use the “Add collection” to define it.

Figure 26. Creating a new collection scoped to our docker image
Figure 26. Creating a new collection scoped to our docker image

 

We will select our container image as the scope of the collection and name it accordingly.

Figure 27. Selecting our newly created collection
Figure 27. Selecting our newly created collection

 

Now that we have set the scope and name, we can add the app and continue setting up its policy.

Figure 28. Finalizing app policy creation
Figure 28. Finalizing app policy creation

 

We will use the “Add endpoint” button to specify the application’s endpoint details.

Figure 29. App policy configurations tab
Figure 29. App policy configurations tab

 

Once everything is set up, we can continue and create the endpoint and follow up on the custom rule creation.

Figure 30. Configuring policy endpoints
Figure 30. Configuring policy endpoints

 

Creating and deploying a Custom Rule

We will navigate to the Custom Rules tab within the app policy.

Figure 31. WAAS Custom rules tab
Figure 31. WAAS Custom rules tab

 

Now we can finally create the custom rule, we will use an example regex to catch the pattern being used both with the CSTI and SSTI examples “{{}}”, we will check the query parameters, body parameters and header values for the following regex:

(?i){{(?:\n|\r\n?|.)*?[^\w\d\s-,:_]+(?:\n|\r\n?|.)*?}}

Figure 32. Creating a custom rule using the given regex
Figure 32. Creating a custom rule using the given regex

 

We will specify the rule’s name, description and a forensic message that will be shown if the rule is triggered by any request.

Once the rule is created, we will set the rule to “prevent” mode which will block any requests that match the rule.

Figure 33. Applying the newly created custom rule on Prevent effect
Figure 33. Applying the newly created custom rule on Prevent effect

 

Preventing Exploitations with WAAS

Once the custom rule is created our vulnerable application is protected from exploitations and we can test this out by trying the same payloads used earlier and see what will happen.

Client-Side Template Injection

The CSTI payload was successfully blocked.

Figure 34. CSTI Successfully blocked
Figure 34. CSTI Successfully blocked

 

 

The “{{8*8}}” payload we used to discover the vulnerability was blocked.

Figure 35. CSTI simple arithmetic’s expression successfully blocked
Figure 35. CSTI simple arithmetic’s expression successfully blocked

 

Server-Side Template Injection

The SSTI payload was successfully blocked as well along with the vulnerability discovery payloads.

Figure 36. SSTI Successfully blocked
Figure 36. SSTI Successfully blocked

 

Conclusion

Both types of Template Injection attacks can result in severe consequences. The possible impact from a single CSTI or SSTI vulnerability ranges from user hijacking to complete remote takeover, yet many common web application protection platforms do not mitigate this attack vector.

Like most web application vulnerabilities template injection can be prevented by properly validating inputs before processing them, while this sounds simple enough it might require an extensive amount of effort and development resources which is why it is often overlooked.

Prisma Cloud WAAS will mitigate such threats via functionality such as the App Firewall or Bot Protection. In addition, WAAS is able to enforce Custom Rules which allow detecting and preventing unique attacks based on custom signatures or regular expressions as demonstrated earlier.

To test out all the great functionality and more with Prisma Cloud, request a hands on demo.