Products

Solutions

Resources

Search for API Security Tests

Server-side Template Injection: Explanation, Discovery, Exploitation, and Prevention

Server-side template injection (SSTI) is a vulnerability that can allow attackers to execute arbitrary code on the server.

Server-side template injection (SSTI) is a vulnerability that can allow attackers to execute arbitrary code on the server.

SSTI vulnerability
SSTI vulnerability
SSTI vulnerability
Author

Medusa

6 mins

6 mins

Server-side template injection (SSTI) is a vulnerability that can allow attackers to execute arbitrary code on the server.

In 2017, an Australian software company called PageUp suffered a data breach that exposed the personal information of millions of job seekers. The company offered cloud-based recruitment services to major corporations and government agencies around the world.

PageUp’s HR software used a web application framework that contained a SSTI vulnerability. Cyber criminals exploited this to gain access to the underlying database.

It's important to be aware of SSTI vulnerabilities to prevent attackers from executing arbitrary code on your servers.

This blog covers:

  • What are Server-Side Templates?

  • Functions of Templates

  • What is Server-Side Template Injection?

  • How does SSTI occur?

  • Prevention

What are Server-Side Templates?

Templates are files that define how a web application will display its content to users. They contain placeholders that are filled in with data from the application which are usually written in languages like HTML and can include variables and control structures like loops and conditionals.

Here is an example of a simple HTML template:

<html> 
  <head> 
    <title>{{ title }}</title> 
  </head> 
  <body> 
    <h1>{{ heading }}</h1> 
    <ul> 
      {% for item in items %} 
      <li>{{ item }}</li> 
      {% endfor %} 
    </ul> 
  </body> 
</html>

This template defines a basic HTML page with a title and a heading, as well as a list of items that are generated dynamically using a for loop. Placeholders in the template are indicated by double curly braces ({{% %}}) and can contain variables, like title and heading, or expressions, like the loop over items.

Templates are a fundamental part of many web frameworks and can be used to render HTML pages, email messages, and other types of content.

Some common programming languages used for server-side templates include:

  • PHP: PHP is a server-side scripting language that is often used to create dynamic web pages. It includes a template engine called Smarty that allows developers to separate the presentation layer from the business logic layer.

  • Python: Python is a general-purpose programming language that can be used for web development, among other things. The Django web framework includes a templating engine that allows developers to create reusable HTML templates.

  • Ruby: Ruby is a dynamic, object-oriented programming language that is often used for web development. The Ruby on Rails web framework includes a templating engine called ERB (Embedded Ruby) that allows developers to embed Ruby code in HTML templates.

  • JavaScript: JavaScript is a client-side scripting language that can also be used to create server-side templates. The Node.js runtime environment includes a templating engine called EJS (Embedded JavaScript) that allows developers to embed JavaScript code in HTML templates.

If you would like to watch a practical video, check out the video on YouTube.

Why are Server-Side Templates important?

Server-side templates are important because they allow web applications to separate the presentation layer from the business logic layer. By using templates, developers can create reusable components that can be used across multiple pages and views. This makes it easier to maintain and update the application over time. Additionally, templates can help prevent common security issues like cross-site scripting (XSS) by automatically escaping user input.

How do SSTI Vulnerability arise?

Server-side template injection (SSTI) occurs when an attacker is able to inject code into a server-side template, which is then executed by the server. This can occur when an application uses user input to construct a template without properly validating or sanitizing the input.

For example, consider an application that uses a templating engine to render user profiles. The templating engine uses a template file that contains placeholders for the user's name and profile picture. The application prompts the user to enter their name, which is then inserted into the template using string concatenation. However, if the user enters a name that contains template code, such as {{ 2+2 }}, the code will be executed by the server and the result will be included in the rendered page.

SSTI can be used by attackers to execute arbitrary code on the server, including commands that allow them to run commands on the system or access sensitive data. It is a serious vulnerability that can be difficult to detect and prevent, especially in complex web applications.

Here is an example of vulnerable code that uses the Flask web framework and the Jinja2 templating engine:

from flask import Flask, abort, request, render_template_string
import jinja2, re, hashlib

app = Flask(__name__)

@app.route('/', methods=['GET'])
def no_filter():
        
        name = request.args.get('name')

        template = '''
        <!DOCTYPE html>
        <html>
          <head>
            <title>Greeting</title>
          </head>
          <body>
            <p>Hi there, ''' + name + '''!</p>
          </body>
        </html>'''

        return render_template_string(template)

if __name__ == '__main__':
        app.run(host='0.0.0.0', port=8888, debug=False)

This code defines a vulnerable Flask application with a single endpoint that accepts a name parameter. This code is vulnerable to Server-Side Template Injection (SSTI) because user input was directly concatenated to the template. There is never a good reason to concatenate user input to a template. Instead, the user input should become a variable that is passed to the template, like this:

from flask import Flask, abort, request, render_template_string
import jinja2, re, hashlib

app = Flask(__name__)

@app.route('/', methods=['GET'])
def no_filter():
        
        template = '''
        <!DOCTYPE html>
        <html>
          <head>
            <title>Greeting</title>
          </head>
          <body>
            <p>Hi there, {{ name }}!</p>
          </body>
        </html>'''

        return render_template_string(template, name=request.args.get('name'))

if __name__ == '__main__':
        app.run(host='0.0.0.0', port=8888, debug=False)

An attacker can inject malicious code into the name parameter, which will then be executed by the server when the template is rendered. This can allow the attacker to execute arbitrary code on the server or access sensitive information.

For example, an attacker could send a request to the application with the following URL: The server would then render the following template:

Payload: {{7*7}}

SSTI Payloads

  • {{ 7*'7' }}: This payload will cause the server to execute the expression 7*'7', which will result in the string '7777777'.

  • {{ config.items() }}: This payload will return a dictionary of configuration items for the server, including sensitive information like database credentials.

  • {{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}: This payload will execute the read method of the file class on the /etc/passwd file, returning the contents of the file to the attacker.

  • {{ ''.__class__.__mro__[1].__subclasses__()[133].__init__.func_globals.linecache.os.popen('id').read() }}: This payload will execute the id command on the server and return the result to the attacker.

Server-side template injection (SSTI) is a vulnerability that can allow attackers to execute arbitrary code on the server.

In 2017, an Australian software company called PageUp suffered a data breach that exposed the personal information of millions of job seekers. The company offered cloud-based recruitment services to major corporations and government agencies around the world.

PageUp’s HR software used a web application framework that contained a SSTI vulnerability. Cyber criminals exploited this to gain access to the underlying database.

It's important to be aware of SSTI vulnerabilities to prevent attackers from executing arbitrary code on your servers.

This blog covers:

  • What are Server-Side Templates?

  • Functions of Templates

  • What is Server-Side Template Injection?

  • How does SSTI occur?

  • Prevention

What are Server-Side Templates?

Templates are files that define how a web application will display its content to users. They contain placeholders that are filled in with data from the application which are usually written in languages like HTML and can include variables and control structures like loops and conditionals.

Here is an example of a simple HTML template:

<html> 
  <head> 
    <title>{{ title }}</title> 
  </head> 
  <body> 
    <h1>{{ heading }}</h1> 
    <ul> 
      {% for item in items %} 
      <li>{{ item }}</li> 
      {% endfor %} 
    </ul> 
  </body> 
</html>

This template defines a basic HTML page with a title and a heading, as well as a list of items that are generated dynamically using a for loop. Placeholders in the template are indicated by double curly braces ({{% %}}) and can contain variables, like title and heading, or expressions, like the loop over items.

Templates are a fundamental part of many web frameworks and can be used to render HTML pages, email messages, and other types of content.

Some common programming languages used for server-side templates include:

  • PHP: PHP is a server-side scripting language that is often used to create dynamic web pages. It includes a template engine called Smarty that allows developers to separate the presentation layer from the business logic layer.

  • Python: Python is a general-purpose programming language that can be used for web development, among other things. The Django web framework includes a templating engine that allows developers to create reusable HTML templates.

  • Ruby: Ruby is a dynamic, object-oriented programming language that is often used for web development. The Ruby on Rails web framework includes a templating engine called ERB (Embedded Ruby) that allows developers to embed Ruby code in HTML templates.

  • JavaScript: JavaScript is a client-side scripting language that can also be used to create server-side templates. The Node.js runtime environment includes a templating engine called EJS (Embedded JavaScript) that allows developers to embed JavaScript code in HTML templates.

If you would like to watch a practical video, check out the video on YouTube.

Why are Server-Side Templates important?

Server-side templates are important because they allow web applications to separate the presentation layer from the business logic layer. By using templates, developers can create reusable components that can be used across multiple pages and views. This makes it easier to maintain and update the application over time. Additionally, templates can help prevent common security issues like cross-site scripting (XSS) by automatically escaping user input.

How do SSTI Vulnerability arise?

Server-side template injection (SSTI) occurs when an attacker is able to inject code into a server-side template, which is then executed by the server. This can occur when an application uses user input to construct a template without properly validating or sanitizing the input.

For example, consider an application that uses a templating engine to render user profiles. The templating engine uses a template file that contains placeholders for the user's name and profile picture. The application prompts the user to enter their name, which is then inserted into the template using string concatenation. However, if the user enters a name that contains template code, such as {{ 2+2 }}, the code will be executed by the server and the result will be included in the rendered page.

SSTI can be used by attackers to execute arbitrary code on the server, including commands that allow them to run commands on the system or access sensitive data. It is a serious vulnerability that can be difficult to detect and prevent, especially in complex web applications.

Here is an example of vulnerable code that uses the Flask web framework and the Jinja2 templating engine:

from flask import Flask, abort, request, render_template_string
import jinja2, re, hashlib

app = Flask(__name__)

@app.route('/', methods=['GET'])
def no_filter():
        
        name = request.args.get('name')

        template = '''
        <!DOCTYPE html>
        <html>
          <head>
            <title>Greeting</title>
          </head>
          <body>
            <p>Hi there, ''' + name + '''!</p>
          </body>
        </html>'''

        return render_template_string(template)

if __name__ == '__main__':
        app.run(host='0.0.0.0', port=8888, debug=False)

This code defines a vulnerable Flask application with a single endpoint that accepts a name parameter. This code is vulnerable to Server-Side Template Injection (SSTI) because user input was directly concatenated to the template. There is never a good reason to concatenate user input to a template. Instead, the user input should become a variable that is passed to the template, like this:

from flask import Flask, abort, request, render_template_string
import jinja2, re, hashlib

app = Flask(__name__)

@app.route('/', methods=['GET'])
def no_filter():
        
        template = '''
        <!DOCTYPE html>
        <html>
          <head>
            <title>Greeting</title>
          </head>
          <body>
            <p>Hi there, {{ name }}!</p>
          </body>
        </html>'''

        return render_template_string(template, name=request.args.get('name'))

if __name__ == '__main__':
        app.run(host='0.0.0.0', port=8888, debug=False)

An attacker can inject malicious code into the name parameter, which will then be executed by the server when the template is rendered. This can allow the attacker to execute arbitrary code on the server or access sensitive information.

For example, an attacker could send a request to the application with the following URL: The server would then render the following template:

Payload: {{7*7}}

SSTI Payloads

  • {{ 7*'7' }}: This payload will cause the server to execute the expression 7*'7', which will result in the string '7777777'.

  • {{ config.items() }}: This payload will return a dictionary of configuration items for the server, including sensitive information like database credentials.

  • {{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}: This payload will execute the read method of the file class on the /etc/passwd file, returning the contents of the file to the attacker.

  • {{ ''.__class__.__mro__[1].__subclasses__()[133].__init__.func_globals.linecache.os.popen('id').read() }}: This payload will execute the id command on the server and return the result to the attacker.

Server-side template injection (SSTI) is a vulnerability that can allow attackers to execute arbitrary code on the server.

In 2017, an Australian software company called PageUp suffered a data breach that exposed the personal information of millions of job seekers. The company offered cloud-based recruitment services to major corporations and government agencies around the world.

PageUp’s HR software used a web application framework that contained a SSTI vulnerability. Cyber criminals exploited this to gain access to the underlying database.

It's important to be aware of SSTI vulnerabilities to prevent attackers from executing arbitrary code on your servers.

This blog covers:

  • What are Server-Side Templates?

  • Functions of Templates

  • What is Server-Side Template Injection?

  • How does SSTI occur?

  • Prevention

What are Server-Side Templates?

Templates are files that define how a web application will display its content to users. They contain placeholders that are filled in with data from the application which are usually written in languages like HTML and can include variables and control structures like loops and conditionals.

Here is an example of a simple HTML template:

<html> 
  <head> 
    <title>{{ title }}</title> 
  </head> 
  <body> 
    <h1>{{ heading }}</h1> 
    <ul> 
      {% for item in items %} 
      <li>{{ item }}</li> 
      {% endfor %} 
    </ul> 
  </body> 
</html>

This template defines a basic HTML page with a title and a heading, as well as a list of items that are generated dynamically using a for loop. Placeholders in the template are indicated by double curly braces ({{% %}}) and can contain variables, like title and heading, or expressions, like the loop over items.

Templates are a fundamental part of many web frameworks and can be used to render HTML pages, email messages, and other types of content.

Some common programming languages used for server-side templates include:

  • PHP: PHP is a server-side scripting language that is often used to create dynamic web pages. It includes a template engine called Smarty that allows developers to separate the presentation layer from the business logic layer.

  • Python: Python is a general-purpose programming language that can be used for web development, among other things. The Django web framework includes a templating engine that allows developers to create reusable HTML templates.

  • Ruby: Ruby is a dynamic, object-oriented programming language that is often used for web development. The Ruby on Rails web framework includes a templating engine called ERB (Embedded Ruby) that allows developers to embed Ruby code in HTML templates.

  • JavaScript: JavaScript is a client-side scripting language that can also be used to create server-side templates. The Node.js runtime environment includes a templating engine called EJS (Embedded JavaScript) that allows developers to embed JavaScript code in HTML templates.

If you would like to watch a practical video, check out the video on YouTube.

Why are Server-Side Templates important?

Server-side templates are important because they allow web applications to separate the presentation layer from the business logic layer. By using templates, developers can create reusable components that can be used across multiple pages and views. This makes it easier to maintain and update the application over time. Additionally, templates can help prevent common security issues like cross-site scripting (XSS) by automatically escaping user input.

How do SSTI Vulnerability arise?

Server-side template injection (SSTI) occurs when an attacker is able to inject code into a server-side template, which is then executed by the server. This can occur when an application uses user input to construct a template without properly validating or sanitizing the input.

For example, consider an application that uses a templating engine to render user profiles. The templating engine uses a template file that contains placeholders for the user's name and profile picture. The application prompts the user to enter their name, which is then inserted into the template using string concatenation. However, if the user enters a name that contains template code, such as {{ 2+2 }}, the code will be executed by the server and the result will be included in the rendered page.

SSTI can be used by attackers to execute arbitrary code on the server, including commands that allow them to run commands on the system or access sensitive data. It is a serious vulnerability that can be difficult to detect and prevent, especially in complex web applications.

Here is an example of vulnerable code that uses the Flask web framework and the Jinja2 templating engine:

from flask import Flask, abort, request, render_template_string
import jinja2, re, hashlib

app = Flask(__name__)

@app.route('/', methods=['GET'])
def no_filter():
        
        name = request.args.get('name')

        template = '''
        <!DOCTYPE html>
        <html>
          <head>
            <title>Greeting</title>
          </head>
          <body>
            <p>Hi there, ''' + name + '''!</p>
          </body>
        </html>'''

        return render_template_string(template)

if __name__ == '__main__':
        app.run(host='0.0.0.0', port=8888, debug=False)

This code defines a vulnerable Flask application with a single endpoint that accepts a name parameter. This code is vulnerable to Server-Side Template Injection (SSTI) because user input was directly concatenated to the template. There is never a good reason to concatenate user input to a template. Instead, the user input should become a variable that is passed to the template, like this:

from flask import Flask, abort, request, render_template_string
import jinja2, re, hashlib

app = Flask(__name__)

@app.route('/', methods=['GET'])
def no_filter():
        
        template = '''
        <!DOCTYPE html>
        <html>
          <head>
            <title>Greeting</title>
          </head>
          <body>
            <p>Hi there, {{ name }}!</p>
          </body>
        </html>'''

        return render_template_string(template, name=request.args.get('name'))

if __name__ == '__main__':
        app.run(host='0.0.0.0', port=8888, debug=False)

An attacker can inject malicious code into the name parameter, which will then be executed by the server when the template is rendered. This can allow the attacker to execute arbitrary code on the server or access sensitive information.

For example, an attacker could send a request to the application with the following URL: The server would then render the following template:

Payload: {{7*7}}

SSTI Payloads

  • {{ 7*'7' }}: This payload will cause the server to execute the expression 7*'7', which will result in the string '7777777'.

  • {{ config.items() }}: This payload will return a dictionary of configuration items for the server, including sensitive information like database credentials.

  • {{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}: This payload will execute the read method of the file class on the /etc/passwd file, returning the contents of the file to the attacker.

  • {{ ''.__class__.__mro__[1].__subclasses__()[133].__init__.func_globals.linecache.os.popen('id').read() }}: This payload will execute the id command on the server and return the result to the attacker.

Test for SSTI using the best proactive API Security product

Our customers love us for our proactive approach and world class API Security test templates. Try Akto's test library yourself in your testing playground. Play with the default test or add your own.

Test it out with Akto! Check out Akto's test library on SSTI.

Here are three tests you can perform on SSTI using Akto:

Practical Demonstration of SSTI

Target: Portswigger SSTI Lab

Goal: To solve this lab, we have to execute an arbitrary code and delete a morale.txt file.

The application is a typical e-commerce website that displays products for users to order. Clicking on "view details" shows more information about a particular product. However, if a product is out of stock and you click on "view details", a message will appear on the screen.

As my proxy is on, I captured the requests in BurpSuite. Please take a look at the third request, which is a GET request with a "message" parameter that retrieves a message stating, "Unfortunately, this product is out of stock.”

This process can also be accomplished by directly modifying the URL in the browser. In this case, I used BurpSuite for better view of requests and responses.

The same message is reflected in the response.

Send the request to the repeater and analyze the request and response.

Exploitation

To test for SSTI vulnerability, we will try to inject a payload in the message parameter

Payload: <%= 7*7 %>

In this case, the code will be evaluated as 7*7, resulting in the integer 49.

In the ERB documentation, discover that the syntax <%= someExpression %> is used to evaluate an expression and render the result on the page.

After sending the request, the response shows that the expression was evaluated by the server and the result is 49.

Now let’s try to execute an OS command on the server.

Payload: <%= system("rm /home/carlos/morale.txt") %>

The above payload will delete morale.txt file from the “carlos” account.

Don’t forget to URL encode the payload and send the request. Let’s go back to the application to check that the lab is solved!

Prevention

The best prevention is to never include user input in templates. Here are some other best practices to prevent SSTI:

  • The best strategy is to not allow users to submit or modify templates. Whenever possible, use string-like substitution instead of modifying the template itself. Do not concatenate user input with the template.

    • render( "Logged in as {name}", Map("name" => "Daniel") ) - Right!

    • render( "Logged in as " + queryParam["name"] ) - Wrong!

  • Use logic-less templates - Template substitution should only perform a string replace without executing any code. These are also known as "logic-less" templates. A number of authors recommend using an engine like Mustache, which separates "appearance" from "logic".

By following these best practices, developers can reduce the risk of SSTI vulnerabilities and improve the overall security of their APIs.

Test it out with Akto! Check out Akto's test library on SSTI.

Here are three tests you can perform on SSTI using Akto:

Practical Demonstration of SSTI

Target: Portswigger SSTI Lab

Goal: To solve this lab, we have to execute an arbitrary code and delete a morale.txt file.

The application is a typical e-commerce website that displays products for users to order. Clicking on "view details" shows more information about a particular product. However, if a product is out of stock and you click on "view details", a message will appear on the screen.

As my proxy is on, I captured the requests in BurpSuite. Please take a look at the third request, which is a GET request with a "message" parameter that retrieves a message stating, "Unfortunately, this product is out of stock.”

This process can also be accomplished by directly modifying the URL in the browser. In this case, I used BurpSuite for better view of requests and responses.

The same message is reflected in the response.

Send the request to the repeater and analyze the request and response.

Exploitation

To test for SSTI vulnerability, we will try to inject a payload in the message parameter

Payload: <%= 7*7 %>

In this case, the code will be evaluated as 7*7, resulting in the integer 49.

In the ERB documentation, discover that the syntax <%= someExpression %> is used to evaluate an expression and render the result on the page.

After sending the request, the response shows that the expression was evaluated by the server and the result is 49.

Now let’s try to execute an OS command on the server.

Payload: <%= system("rm /home/carlos/morale.txt") %>

The above payload will delete morale.txt file from the “carlos” account.

Don’t forget to URL encode the payload and send the request. Let’s go back to the application to check that the lab is solved!

Prevention

The best prevention is to never include user input in templates. Here are some other best practices to prevent SSTI:

  • The best strategy is to not allow users to submit or modify templates. Whenever possible, use string-like substitution instead of modifying the template itself. Do not concatenate user input with the template.

    • render( "Logged in as {name}", Map("name" => "Daniel") ) - Right!

    • render( "Logged in as " + queryParam["name"] ) - Wrong!

  • Use logic-less templates - Template substitution should only perform a string replace without executing any code. These are also known as "logic-less" templates. A number of authors recommend using an engine like Mustache, which separates "appearance" from "logic".

By following these best practices, developers can reduce the risk of SSTI vulnerabilities and improve the overall security of their APIs.

Test it out with Akto! Check out Akto's test library on SSTI.

Here are three tests you can perform on SSTI using Akto:

Practical Demonstration of SSTI

Target: Portswigger SSTI Lab

Goal: To solve this lab, we have to execute an arbitrary code and delete a morale.txt file.

The application is a typical e-commerce website that displays products for users to order. Clicking on "view details" shows more information about a particular product. However, if a product is out of stock and you click on "view details", a message will appear on the screen.

As my proxy is on, I captured the requests in BurpSuite. Please take a look at the third request, which is a GET request with a "message" parameter that retrieves a message stating, "Unfortunately, this product is out of stock.”

This process can also be accomplished by directly modifying the URL in the browser. In this case, I used BurpSuite for better view of requests and responses.

The same message is reflected in the response.

Send the request to the repeater and analyze the request and response.

Exploitation

To test for SSTI vulnerability, we will try to inject a payload in the message parameter

Payload: <%= 7*7 %>

In this case, the code will be evaluated as 7*7, resulting in the integer 49.

In the ERB documentation, discover that the syntax <%= someExpression %> is used to evaluate an expression and render the result on the page.

After sending the request, the response shows that the expression was evaluated by the server and the result is 49.

Now let’s try to execute an OS command on the server.

Payload: <%= system("rm /home/carlos/morale.txt") %>

The above payload will delete morale.txt file from the “carlos” account.

Don’t forget to URL encode the payload and send the request. Let’s go back to the application to check that the lab is solved!

Prevention

The best prevention is to never include user input in templates. Here are some other best practices to prevent SSTI:

  • The best strategy is to not allow users to submit or modify templates. Whenever possible, use string-like substitution instead of modifying the template itself. Do not concatenate user input with the template.

    • render( "Logged in as {name}", Map("name" => "Daniel") ) - Right!

    • render( "Logged in as " + queryParam["name"] ) - Wrong!

  • Use logic-less templates - Template substitution should only perform a string replace without executing any code. These are also known as "logic-less" templates. A number of authors recommend using an engine like Mustache, which separates "appearance" from "logic".

By following these best practices, developers can reduce the risk of SSTI vulnerabilities and improve the overall security of their APIs.

Share this post

Share this post

Share this post

Monthly product updates in your inbox. No spam.