Overview / TL;DR

  • As said in the description, this is NOT a writeup, there are many writeups online that get straight to the point (how2solve challenge pls) which unfortunately this gist is not :books:

  • I didn’t solve the challenge mainly because I got ahead of myself and missed a crucial detail in one of the many approcahes I took, in a sense I dived too deep down a rabbit hole which closed me off from the real answer, while I was digging deeper for one that simply wasn’t there

  • natas15 is a Blind SQL Injection challenge, which I did not have knowledge of before (but I do now), in fact the only SQLi I knew of before this was the simple auto bypass method (basically ' OR 1=1; -- ), so I’m actually glad I did this challenge til the very end because I ended up learning a lot !

Wtf is a “Blind” SQL Injection ?

It’s basically an SQL Injection but under the circumstances where the results aren’t returned directly to you

  • More often than not you will only get responses along the lines of:

    The query didn’t return anything (which means False :negative_squared_cross_mark:)
    The query returned a row/ or multiple rows (which means True :ballot_box_with_check:)
    An error in your query :interrobang: (which also might return nothing)


  • As you can imagine it makes things a lot harder for an attacker (but not imppossible), as they can only ask the database True/False questions





An example implementation in the natas15 sourcecode:

$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
if(array_key_exists("debug", $_GET)) {         // ^^^^^^^^^^^^^^^^^^    
  
        echo "Executing query: $query<br>";
}

$res = mysql_query($query, $link);
if($res) {
        if(mysql_num_rows($res) > 0) {
                echo "This user exists.<br>";
        } else {
                echo "This user doesn't exist.<br>";
        }
} else {
        echo "Error in query.<br>";
} 

:warning: In this case the username variable is exploitable as it isn’t being sanitized properly

What I did at the beginning

Although this approach was incorrect it taught me a lot about the UNION and INTO OUTFILE clauses and how they can be abused

One of my first ideas was that since the results weren’t returned directly to the user, I could use UNION to link a new select statement which would ignore the old results and dump any new results into a temporary file to be read later. The many possible issues with this approach is :

  • The user currently executing the query may not have write access
  • The file has to be written to somewhere accessible by the user and within the webroot directory
  • The user has to have the privileges (in this case FILE privileges) to even use commands like LOAD FILE or INTO OUTFILE

Example URL:

http://natas15.natas.labs.overthewire.org/?username=" AND 1=2 UNION SELECT * INTO OUTFILE '/tmp/out' FROM users ; --

Server-side query:

SELECT * from users where username="" AND 1=2 UNION SELECT * INTO OUTFILE '/tmp/out' FROM users ; -- "

Side Note: A more dangerous version of this injection would be similar to:

SELECT * from users where username="" AND 1=2 UNION SELECT '1','<?php system($_GET['cmd']) ?>' INTO OUTFILE '/whateverpath/directory/script.php'; -- 

which basically stores a PHP script containing a shell that you can then use to execute arbitrary commands on

I executed variations of this query mutliple times to no success, so I began to suspect that this user account had very limited privileges, unfortunately I was right

Using the built-in functions

mySQL has very handy functions like user() that returns who the current user is, version() that returns the running version , etc …
I also found out that SQL supports the LIKE clause, which is perfect for avoiding true equality “=”


Starting off with a simple query like this :
SELECT * from users where username="" OR user() LIKE "%"

  • % when used alone matches everything so the expression user() LIKE "%" is basically saying user() matches ANYTHING, which is true as long as the thing that user() returns is not NULL

  • % when used with other characters will also match up to a certain pattern such as "%a" matching everything that ends with "a" and "a%" matching everything starting with "a"

  • The _ character functions similarly to "." in regex. It matches any character, so as you can see in the gif below, it can be used to guess the length of a certain field, while combined with the % character can also be used to match patterns

Underscore

(After guessing the value of a certain field the LIKE can be replaced with = to ensure the correct value, since the query would evaluate to False otherwise)

Guess

  • Using trial-and-error method or even a brute-force method its a slow but surefire method of leaking useful data from the database such as

    • The current user (as shown above)

    • What privileges this user had
      (USAGE = no privileges besides logging in and SELECT’ing) Usage

    • The version of mySQL running

      URL:

       http://natas15.natas.labs.overthewire.org/?username=" OR @@version LIKE "5.5.55-0%2Bdeb8u1& debug (%2B = +)
      

      Query:

       SELECT * from users where username="" OR @@version LIKE "5.5.55-0+deb8u1"
      
    • The dummy user accounts being used pw

I was stupid

This is where I screwed up, I had already acquired a method to leak usernames and passwords from the users table, but somehow it never occured to me to look for a user called natas16, instead my smart-ass brain kept pestering me to find a way to execute arbitrary commands so I could cat the password file:/etc/natas_webpass/natas16 :confused:


Although I did most of this brute-forcing stuff by hand (since it was fairly short) a proper script is recommended for brute-forcing the password since it is 32 characters long and there are 26 lowercase letters + 26 uppercase + 10 digits = 62 possible characters for each of the 32 character slots, which is 62 ^ 32 possible combinations