Tags: waf parser json 


[Link to original writeup](https://wrecktheline.com/writeups/m0lecon-2021/#waffle)

# Waffle (28 solves, 165 points)
by 0xkasper

We needed to upgrade this app but no one here can code in Go. Easy fix: just put a custom waf in front of it.

Author: Xato

In the attachments we have 2 files: main.go and waf.py. The main.go file is the main webserver that runs the Waffle application. The waf.py is running as a WAF in front of it and forwards requests if they're not malicious.

When going to the main web application at `http://waffle.challs.m0lecon.it/` we see that no data loads, only an error message that says we need a valid token.
At the token page at `http://waffle.challs.m0lecon.it/gettoken.html` we can fill in a credit card and promo code. Filling in random stuff gives you an error message.
From the code in main.go, we see that if the promo code is equal to 'FREEWAF', we can receive a token. However, the WAF also checks for this value at line and makes it return 'Coupon expired'.

The WAF is made in Python Flask and first checks whether 'gettoken' is in the path and then checks the creditcard and promo query arguments. If promo is not equal to 'FREEWAF', it will forward the request to the Go webserver. By making the parameters part of the path using encoding, we can trick it in thinking there are no arguments, but when it decodes the path and forwards, the arguments suddenly appear:
Now we have a token: LQuKU5ViVGk4fsytWt9C, and we can make requests at the front page.

After a quick look at the Go application, it's obviously vulnerable to SQL injection. Parameters are directly inserted into queries.
However, the WAF checks the parameters for type and alphanumeric characters.
It does this by parsing the data to JSON and checking each of the three arguments.
The JSON parser differs from the one in the Go application: if we add a parameter with the same name, the WAF will take last one, while the Go application will take the first one. If we make the last one valid and the first one a SQL injection payload, we can bypass the WAF:
Sending `{"name":"'","min_radius":1,"max_radius":1000,"name":"Big"}` gives a database error.

We already know it returns 4 columns and we know it's SQLite from the Go code, so:
`{"name":"Small' UNION SELECT name,1,1,1 FROM sqlite_master WHERE type='table'--","min_radius":1,"max_radius":1000,"name":"Big"}`
returns all the tables: `flag`, `sqlite_sequence` and `waffle`.
By guessing the column name `flag` in table `flag`:
`{"name":"Small' UNION SELECT flag,1,1,1 FROM flag--","min_radius":1,"max_radius":1000,"name":"Big"}`
We get the flag: `ptm{n3ver_ev3r_tru5t_4_pars3r!}`

Original writeup (https://wrecktheline.com/writeups/m0lecon-2021/#waffle).