Who let the blacklists out?

Category: web

Solver: davex, shm0sby

Flag: HTB{wh0_l3t_th3_w4fs_0ut?!..w00f..w00f.w00f!}

Writeup

When you entered the site of the challenge the site directly gives you the source of the challenge.

<?php
require('database.php');

$user = $_GET['user'];
$pass = $_GET['pass'];

if (!isset($user) || !isset($pass) || preg_match_all('/(select|union|where|\(|\.|\')/i', $user.$pass))
{
    highlight_file(__FILE__);
    exit;
}

$mysql = get_db();
$mysql->multi_query("SELECT * FROM `users` WHERE `username` = '${user}' AND `password` = '${pass}'");

do
{
    if ($result = $mysql->store_result())
    {
        if ($row = $result->fetch_assoc())
        {
            echo json_encode($row) . '<br/>';
        }
        $result->free();
    }
}
while ($mysql->next_result());

$mysql->close();

As you can see we can enter two variables via getting parameters and they are checked for some keywords. Therefore we are not permitted to have a select, union or where statement (case insensitive) or neither a ' nor ( in our variables.

The most obvious attack would be to break out of the strings in the query string with a ' inside of our statement. We tried many things to bypass the check for a ' symbol, without success. Therefore it was clear that we could not simply break out at the end of the statement. Because we played mutch with encoding and things like that we realized, that a \' would escape the symbol. Therefore if we submit a username like ?user=\ we would achieve the following statement

SELECT * FROM `users` WHERE `username` = '\' AND `password` = ' ${pass}'

As you can see the checked username is now ' AND `password` =. This means if we now set the password to ; -- we would achieve a valid SQL statement. Now we could inject any valid SQL-query in between our password query. Therefore the first thing we tested was ?user=\&pass=OR true;-- because this would show us all entries in the users table.

When we executed it we received nothing and were confused. What went wrong with this execution? We decided to check if there are other tables and the user table is simply empty. For this we simply can execute ?user=\&pass=;show tables;-- and received showTables

Now it was obvious where the flag is, but our main question now: Where is the user table of the query? We looked again into the code and saw that we will receive only one result at a time. Because of this, we wanted to see if we can find the users table, and indeed we could find it with ?user=\&pass=;show tables LIKE "%users%";--.

We search up all commands that could show us any kind of content of the flag table, but select is the only query keyword that could give us any values. Because the query could not contain any type of the word select it was clear that we had to do something different.

We thought about using the existing select query. For this, we need to

  1. Remove the existing users table
  2. Rename the definitely_not_a_flag table to users
  3. Add a username field to the new users table
  4. Select all from the new users table

The first three steps could we achieve with the following SQL statement for the pass variable

; DROP TABLE users; ALTER TABLE definitely_not_a_flag RENAME TO users; ALTER TABLE users ADD username TEXT; --

Now should we be able to query the users table again and get the flag

flag