0xBOverchunked: SQL Injection

Platform: Hack The Box

Category/Tags: Web, SQL Injection

Difficulty: Easy

Introduction

Are you able to retrieve the 6th character from the database?

Information Gathering

A simple web application that search a certain id that returns some information of a character.

landing page

I download the zip file containing the source code of this web application.

I inspect the SearchHandler.php, this is the entrypoint of the request. If the request is being sent by chunks it will respond to “No post id found.” or Internal Server Error (500). Otherwise, if the id is found, it will return the character’s information. If the server detected a malicious characters that relates to SQL, it may respond it that there’s an attempt of SQL Injection.

<?php
require_once '../Database/Cursor.php';
require_once '../WAF/waf.php';

if (isset($_SERVER["HTTP_TRANSFER_ENCODING"]) && $_SERVER["HTTP_TRANSFER_ENCODING"] == "chunked")
{
    $search = $_POST['search'];

    $result = unsafequery($pdo, $search);

    if ($result)
    {
        echo "<div class='results'>No post id found.</div>";
    }
    else
    {
        http_response_code(500);
        echo "Internal Server Error";
        exit();
    }

}
else
{
    if ((isset($_POST["search"])))
    {
        $search = $_POST["search"];
        if (waf_sql_injection($search))
        {
            $result = safequery($pdo, $search);
            if ($result)
            {
                echo '
                    <div class="grid-container">
                    <div class="grid-item">
                        <img class="post-logo" src="../../assets/images/posts/' . $result["image"] . '" width="100">
                    </div>
                    <div class="grid-item">
                        <p><font color="#F44336">Name</font>: ' . $result["gamename"] . '</p>
                        <p><font color="#F44336">Description</font>: ' . $result["gamedesc"] . '</p>
                    </div>
                    </div>';
            }
            else
            {
                echo "<div class='results'>No post id found.</div>";
            }
        }
        else
        {
            echo "<div class='results'>SQL Injection attempt identified and prevented by WAF!</div>";
        }
    }
    else
    {
        echo "<div class='results'>Unsupported method!</div>";
        http_response_code(400);
    }

}

?>

This is the waf.php, a.k.a. Web Application Firewall wherein there is a list of blacklisted characters and syntax that is related when we run an SQL query.

<?php
function waf_sql_injection($input)
{
    $sql_keywords = array(
        'SELECT',
        'INSERT',
        'UPDATE',
        'DELETE',
        'UNION',
        'DROP',
        'TRUNCATE',
        'ALTER',
        'CREATE',
        'FROM',
        'WHERE',
        'GROUP BY',
        'HAVING',
        'ORDER BY',
        'LIMIT',
        'OFFSET',
        'JOIN',
        'ON',
        'SET',
        'VALUES',
        'INDEX',
        'KEY',
        'PRIMARY',
        'FOREIGN',
        'REFERENCES',
        'TABLE',
        'VIEW',
        'AND',
        'OR',
        "'",
        '"',
        "')",
        '-- -',
        '#',
        '--',
        '-'
    );

    foreach ($sql_keywords as $keyword)
    {
        if (stripos($input, $keyword) !== false)
        {
            return false;
        }
    }
    return true;
}

?>

If the value of search parameter is not an attempt of an SQL Injection, it will now query the database base on the id. If id is equal to 6, then it will return that we cannot view this information. So, we assume that the flag is in the id of 6.

<?php
require_once 'Connect.php';

function safequery($pdo, $id)
{
    if ($id == 6)
    {
        die("You are not allowed to view this post!");
    }

    $stmt = $pdo->prepare("SELECT id, gamename, gamedesc, image FROM posts  WHERE id = ?");
    $stmt->execute([$id]);

    $result = $stmt->fetch(PDO::FETCH_ASSOC);

    return $result;
}

function unsafequery($pdo, $id)
{
    try
    {
        $stmt = $pdo->query("SELECT id, gamename, gamedesc, image FROM posts WHERE id = '$id'");
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        return $result;
    }
    catch(Exception $e)
    {
        http_response_code(500);
        echo "Internal Server Error";
        exit();
    }
}

?>

So, we saw that the flag is in the id of 6. We will need to see the information of that id to gain the flag that is in the gamedesc column,

CREATE TABLE posts (
  id INTEGER PRIMARY KEY,
  gamename TEXT NOT NULL,
  gamedesc TEXT NOT NULL,
  image BLOB NOT NULL
);

INSERT INTO posts (gamename, gamedesc, image)
VALUES
  ('Pikachu', 'A small, yellow, mouse-like creature with a lightning bolt-shaped tail. Pikachu is one of the most popular and recognizable characters from the Pokemon franchise.', '1.png'),
  ('Pac-Man', 'Pac-Man is a classic arcade game where you control a yellow character and navigate through a maze, eating dots and avoiding ghosts.', '2.png'),
  ('Sonic', 'He is a blue anthropomorphic hedgehog who is known for his incredible speed and his ability to run faster than the speed of sound.', '3.png'),
  ('Super Mario', 'Its me, Mario, an Italian plumber who must save Princess Toadstool from the evil Bowser.', '4.png'),
  ('Donkey Kong', 'Donkey Kong is known for his incredible strength, agility, and his ability to swing from vines and barrels.', '5.png'),
  ('Flag', 'HTB{f4k3_fl4_f0r_t35t1ng}', '6.png');

Vulnerability

This query if they do not use some libraries, frameworks or ORMs it may be vulnerable to SQL Injection.

$stmt = $pdo->query("SELECT id, gamename, gamedesc, image FROM posts WHERE id = '$id'");
$result = $stmt->fetch(PDO::FETCH_ASSOC);

Exploitation

So after trying some bypass in the search parameter, I already decide to use sqlmap; to dump the posts table to get the value of gamedesc column. I intercept the request using burpsuite, and paste it in as text file.

http request

I tried all the flags in sqlmap, and changing options can change the response (sometimes I got “unable to connect to the target URL”). After trying some options, I get the gamedesc value using this command,

sqlmap -r req.txt --level 5 --risk 3 --dbms sqlite --tables --dump posts --chunked --random-agent --flush-session --technique=BEUS

I use the most aggressive level to perform extent techniques, highest risk to reduce the false positives, I specify that the database is sqlite, I also need to dump the posts table, I want the data stream divided into chunks, I also use the random-agent flag to always change the user-agent, I also want to create a new session and I want to use the BEUS (Boolean-based blind SQL injection, Error-based SQL injection, Union-based SQL injection, Stacked queries SQL injection) technique. This will take some time because it guesses the letter 1 by 1 and gets the whole value of column by each rows, So I wait until the posts table dumped perfectly.

Gotcha! we got the dumped table and the flag is in the gamedesc column of id 6. You can also visit the dumped logs and csv through ~/.local/share/sqlmap/output/<MACHINE_IP>.

dumped table

Last updated