All the programming languages provide various ways to take input from the user, such as plaint user text, JSON string, XML, pure HTML or a custom or generic templates. Server-Side Template Injection (SSTI) is one such attack vector in which a user can inject a custom code in the form of templates which are supported by the backend server.
There are various popular templating libraries such as Jinja2, XML, FreeMaker etc. for generating dynamic responses including but not limited to HTML responses in case of a web application. Based on the templating engine used, an attacker can inject malicious code to execute arbitrary commands on the web server.
Depending on the templating engine detected by the attacker, the attacker can refer this Github Repository to find appropriate payloads for injection.
How it works?
To demonstrate the SSTI vulnerability, we will be using a self-created vulnerable application.
For the sample application to work, your system should have docker. The guide to install the same can be found on Docker Installation
Run vulnerable SSTI application using the command below.
docker run --rm -it -p5000:5000 sonofasura/ssti:v0.0.2
Open the browser and visit the URL http://127.0.0.1:5000
to open the vulnerable application
The sample application returns the mythological information about our great Asuras, let’s try to input Bakasura
to view that Asuras details.
Wonderful! We are able to get some information about Bakasura
, now let’s try to write some random string to view if we encounter some error. Let’s try to type hello
as input.
Question
Did you notice something???
The input which was provided by us, is displayed as it is. Therefore, there is a possibility that the input is a plaintext or it is using a templating engine underneath.
Let’s try to get deeper into fetching the information about the web server.
Now lets try to make curl request with header verbose flag on, such that we can get the web server information.
┌──(kali㉿kali)-[~/docker/ssti]
└─$ curl -v http://127.0.0.1:5000
* Trying 127.0.0.1:5000...
* Connected to 127.0.0.1 (127.0.0.1) port 5000
> GET / HTTP/1.1
> Host: 127.0.0.1:5000
> User-Agent: curl/8.8.0
> Accept: */*
>
* Request completely sent off
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 950
< Server: Werkzeug/2.0.3 Python/3.12.4
< Date: Tue, 06 Aug 2024 20:12:23 GMT
<
......
If you notice that Server: Werkzeug/2.0.3 Python/3.12.4
field which suggests that it is using Python3 with Werkzeug
. If we google search the keyword Werkzeug
we can see various information related to the web server under use.
Now that we know our server could be made of flask there is a high possibility, it might be using Jinja2 template for dynamic rendering of html responses.
If you navigate back to the Github Repository mentioned previously, you can find a Jinja2 - Basic Injection script.
Therefore now lets try a basic Jinja2 injection, for example: {{7*6}}
. If the same injection renders in the output 42
i.e. the Jinja2 template is executed at the server, then it is a confirmation that the web server is using Jinja2 templating engine for processing this input field.
Voila! Indeed we got the output 42
, confirming that the web server is using Jinja2 templates.
We can now use a file reading based payload to read the /etc/passwd
file from the webserver. The payload used is:
{{ config.__class__.from_envvar.__globals__.import_string("os").popen("cat /etc/passwd").read() }}
And the following is the output of the payload.
Todo
Can you try to create a reverse shell on the web server? Hint: Navigate to the Github Repository to find appropriate payload
In the next section, we will look at the programmatic view of the vulnerability such that same can be mitigated.