OverTheWire Natas Levels 0-11 Thinking Out Loud
Tags: overthewire, webdev, security, webserver
Published: 2022-04-29 08:52:16

# Table of Contents
[level 0 - Inspect](#level-0)
[Level 1 - Shortcut](#level-1)
[Level 2 - Sources](#level-2)
[Level 3 - robots.txt](#level-3)
[Level 4 - HTTP request](#level-4)
[Level 5 - Cookies](#level-5)
[Level 6 - PHP](#level-6)
[Level 7 - local file](#level-7)
[Level 8 - PHP](#level-8)
[Level 9 - Command Injection](#level-9)
[Level 10 - grep](#level-10)
[Level 11 - Cookie](#level-11)

## Level 0
Open the website in Chrome browser. Once logged in to level 0, **right click > View Page Source**, or **right click > Inspect** Element. The password for next level is simply commented out below the visible content of the html document.

## Level 1
Right click is disabled on the website, so we need to find an alternatively to inspect the webpage. On windows, the shortcut to bring up inspector tools is F12, or **ctrl+shift+i**. On Mac, the corresponding shortcut is **cmd+opt+i**. The password again is hidden in the comment.

## Level 2
This time, when we go to the **Elements** section in the developer window, instead of a comment we get an image tag. When we hover over the src of the image, it displays a link. A click on the link directs us to the path:

> http://natas2.natas.labs.overthewire.org/files/pixel.png

There isn't anything interesting to see. Let's go back a step and just add /files to the end of the natas url:

> http://natas2.natas.labs.overthewire.org/files

There is a text file called users.txt which looks promising, opening the file indeed shows the password for next level!

## Level 3
The clue for this level is within the comment in html:

> No more information leaks!! Not even Google will find it this time...

Google uses web crawlers/robots to index web pages, web site owners use the /robots.txt file to give instructions about their site to web robots; this is called The Robots Exclusion Protocol. Robots.txt is a publicly available file, let's checkout the robots.txt file on this website

> http://natas3.natas.labs.overthewire.org/robots.txt
User-agent: *
Disallow: /s3cr3t/

- "User-agent: *" means this section applies to all robots. 
- "Disallow: /s3cr3t/" tells the robot that it should not visit directory /s3cr3t of this site. Need a separate "Disallow" line for every URL prefix you want to exclude.

This is telling us passwords must be in the excluded directory!
> http://natas3.natas.labs.overthewire.org/s3cr3t

We see a text file containing the password just like before.

[About robots.txt](http://www.robotstxt.org/robotstxt.html)

___
## Level 4
_Access disallowed. You are visiting from "" while authorized users should come only from "http://natas5.natas.labs.overthewire.org/"_

This message is telling us that we opened the link directly in browser and was not redirected to the current page from http://natas5.natas.labs.overthewire.org/. How does the website know this information? 

> In HTTP, "Referer" (a misspelling of Referrer) is the name of an optional HTTP header field that identifies the address of the web page (i.e., the URI or IRI), from which the resource has been requested. By checking the referrer, the server providing the new web page can see where the request originated.

Get the [ModHeader](https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj?hl=en) plugin for Chrome, while you're on the http://natas4.natas.labs.overthewire.org/ page, click on the plugin and put the required referer. Give the page a quick refresh to apply the HTTP proxy interception.

Remember to clear the **referer** after you get the password as this setting will be applied on all webpages you visit...

![Screenshot of plugin](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5jafrnwocf45wtt92tvc.png)

___
## Level 5
_Access disallowed. You are not logged in_

Normally there will a form for us to type our login details if login is required. Some websites use cookies to remember the user login information and grants us access without having to login next time.

A cookie is an HTTP request header and contains the cookies previously sent by the server using set-cookies.

Let's check what cookies the server has in place:
**Right click > Inspect** to open the developer console. Go to the **Applications** tab on the console. Expand the **Cookies** dropdown under the **Storage** section. 

Cookies are set of key-value pairs, under the domain natas5.natas.labs.overthewire.org has a key loggedin which is set to 0. This looks like a boolean key where 0=false, and tells the server the user has not been logged in. Let's modify our request header using the plugin, set cookie **loggedin=1**.

___
## Level 6

Password: aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1

Go to "View sourcecode" link where PHP code is exposed (PHP code runs on the web server, cannot be seen on "Source" in Dev Tools). PHP code is always wrapped between <?php and ?>:

```PHP
 <?  
 include "includes/secret.inc";  
   if(array_key_exists("submit", $_POST)) {  
     if($secret == $_POST['secret']) {  
     print "Access granted. The password for natas7 is <censored>";  
   } else {  
     print "Wrong secret";  
   }  
   }  
 ?>
```

Reading the code line to line, it first imports an external file in relative path "includes/secret.inc", then it checks if user clicked the submit button with some input data. If so, it compares user input to a variable $secret, which must have came from the external file. If the $secret and user input matches, the password is displayed.

Navigate to the relative path of the external file:

> http://natas6.natas.labs.overthewire.org/includes/secret.inc

Copy value of $secret, go back to the homepage and paste it into input field to reveal the password for next level.

_HELP: Comment below if you know what < censored > tag is!_

___
## Level 7

Passsword: 7z3hEENjQtflzgnT29q7wAvMNfZdh0i9

From the comments in source code, we know that the password is in etc/nats_webpass/natas8.

Notice in the URL, there is a path parameter "page" that changes depending on what page is on display. We can try tamper with the path parameter:

> http://natas7.natas.labs.overthewire.org/index.php?page=etc/natas_webpass/natas8

But this does not work! We need to access files that is outside current directory. Read about File Inclusion vulnerability for hints.

> The File Inclusion vulnerability allows an attacker to include a file, usually exploiting a "dynamic file inclusion" mechanisms implemented in the target application. The vulnerability occurs due to the use of user-supplied input without proper validation. 

> Local File Inclusion (also known as LFI) is the process of including files, that are already locally present on the server, through the exploiting of vulnerable inclusion procedures implemented in the application. This vulnerability occurs, for example, when a page receives, as input, the path to the file that has to be included and this input is not properly sanitized, allowing directory traversal characters (such as dot-dot-slash) to be injected. 

Since the page parameter takes a relative path, we can use "../" to go back up a level on the filesystem tree structure. How many steps it takes to go back to the root directory is unknown, but we can just keep adding the path traversal as there is no consequence of having too many "../". Password appears in the following URL:

> http://natas7.natas.labs.overthewire.org/index.php?page=../../../../etc/natas_webpass/natas8

[Testing for Local File Inclusion](https://wiki.owasp.org/index.php/Testing_for_Local_File_Inclusion)

___
## Level 8

Password: DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe

Click on "View Sourcecode" and read PHP code line by line:
```PHP
<?

$encodedSecret = "3d3d516343746d4d6d6c315669563362";

function encodeSecret($secret) {
    return bin2hex(strrev(base64_encode($secret)));
}

if(array_key_exists("submit", $_POST)) {
    if(encodeSecret($_POST['secret']) == $encodedSecret) {
    print "Access granted. The password for natas9 is <censored>";
    } else {
    print "Wrong secret";
    }
}
?>
```

The first line assigns a string to variable $encodedSecret. The function encodeSecret takes the input submitted by user and encode in base64, reverse the string, then converts hexadecimal to binary. This encoded user input is then compared with $encodedSecret and only shows the password if they match.

The $encodedSecret needs to be "decoded" before pasted into the input box. In order to reverse the encoding, you have to write some PHP code, either on a web server with PHP installed, or run on your local machine. The easiest way is to run the PHP code on the command line.

Install PHP on MacOs:
```
brew install php
```
Open PHP interactive shell:
```
php -a
```
Reverse the encoding:
```
$encodedSecret = "3d3d516343746d4d6d6c315669563362";
echo base64_decode(strrev((hex2bin($encodedSecret))));
```
Exit the shell:
ctrl + c
___
## Level 9

Password: W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl

Click on "View Sourcecode" and read the PHP code for this level:
```PHP
<pre>
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    passthru("grep -i $key dictionary.txt");
}
?>
</pre>
```

First line declares a $key variable and assigns an empty string to it. The first if statement looks for a key "needle" in the request, and assigns the key value to variable $key. The second if statement checks if the variable $key is not an empty string, and passes through the command "grep -i $key dictionary.txt". The "grep" command searches in the dictionary.txt for text that contains $key, "-i" makes it an case insensitive search.

Notice in the URL path parameter there is a key "needle", which matches what is expected in the PHP source code. Whatever you input in the search bar ends up being assigned to the "needle" key param, which is then copied to variable $key.

Level 7 reveals the directory of all passwords: "etc/natas_webpass/", so you need to navigate to that directory somehow. The only place you can interact with the server is when $key has a value.

Read about [Shell Injection](https://en.wikipedia.org/wiki/Code_injection#Shell_injection)

To complete this level, you should also know "ls" and "cat" terminal commands.

Now, you can use shell sequential execution character ";" to execute additional commands. Try the following in the search bar:

```
; ls
```
The semicolon skips over "grep" search output and executes "ls" command, which returns the file in current directory. Learning from level 7 using path traversal, try the following one by one:
```
; ls ..
; ls ../..
; ls ../../../../etc/natas_webpass
```
Notice a file called natas10 in /etc/natas_webpass, to view the content in the file, use "cat" command:
```
; cat ../../../../etc/natas_webpass/natas10
```

### Reference
[Command Injection](https://owasp.org/www-community/attacks/Command_Injection)
___
## Level 10

Password: nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu

Click on "View Sourcecode" to view the PHP code:

```PHP
<pre>
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    if(preg_match('/[;|&]/',$key)) {
        print "Input contains an illegal character!";
    } else {
        passthru("grep -i $key dictionary.txt");
    }
}
?>
</pre>
```
Compared to last level, there is some extra code to sanitise user input, so you cannot use any kind of sequential command this time. The only place of exploitation is the "grep" command.

> Grep is an acronym that stands for Global Regular Expression Print. When it finds a match, it prints the line with the result. To search multiple files with the grep command, insert the filenames you want to search, separated with a space character.

With the single "grep" command execution, we can search multiple files on the server! Try the following in search bar:
```
1  /etc/natas_webpass/natas11
```

This searches the number "1" in both dictionary.txt and natas11, because dictionary.txt contains word only, the output displayed is just the password from natas11.

___
## Level 11

Password: U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK

Click "View Sourcode", there are 2 sets of PHP code:
```PHP
<?
if($data["showpassword"] == "yes") {
    print "The password for natas12 is <censored><br>";
}

?>
```
This hints that the $data object has the key "showpassword", and must be set to "yes" to reveal the password.

```PHP
<?

$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);

?>
```

In summary, the $data object has keys "showpassword" and "bgcolor", and the $data "showpassword" value is normally set in cookies is XOR encrypted. It is not as easy as changing "showpassword":"no" to "showpassword:yes" in the cookies now.

The first line in PHP sets a value for the default parameter $defaultdata, it is an associative array with key "showpassword" and "bgcolor". 

Function loadData() is called first, it takes $defaultdata, copies the array to a local variable $mydata. It checks if there is a cookie data, if so, the cookie data is base64 decoded, xor_encrypted, json decoded to the associative array and saved in $tempdata. If $tempdata is an array that has keys "showpassword" and "bgcolor" just like $mydata, the values replace the ones in $mydata. loadData() returns $mydata and saved in $data.

$data is json encoded, xor_encrypted, base64_encoded and set as the new cookie data through saveData().

Check the cookie by **Right Click > Inspect > Application > Cookies**, the current value of data is:

> ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxEIaAw%3D 

This is indeed a string in base64.

Take a closer look at xor_encrypt(), It has a local variable $key which is censored. $text takes a copy of input parameter $in, in this case it is the cookie data. The for loop goes through each character of cookie data and XOR with characters of $key.

"^" is the bitwise exclusive or operator, which acts on binary numbers. The XOR operator on two binary numbers gives a 1 where only one of the numbers has a 1, hence exclusive (0^0 = 0, 0^1 = 1, 1^0 = 1, 1^1 = 0).

When you use XOR on characters, you're using their ASCII values. These ASCII values are integers (order on the ASCII table), so they're implicitly converted from decimal to binary before XOR, then converted back to decimal.

To find the password:
1. Find $key in xor_encrypt() so you can run the function locally
2. change "showpassword"=>"no" to "showpassword"=>"yes" in $defaultdata and pass it through the series of encoding
3. Paste the new encoded data in cookies

### Find key
A XOR B = C means that C XOR A = B, so you can XOR again to reverse and get the key. You will need to run some PHP code:

```PHP
$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");

function xor_encrypt($in) {
    $key = base64_decode("ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxEIaAw%3D");
    $text = $in;
    $outText = '';

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

    return $outText;
}

$key = xor_encrypt(json_encode($defaultdata));

echo $key;
```

The output is a repetition of qw8J:
> qw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8JqwnJq

This is expected because the json encoding string is probably longer than the key, and the XOR for loop cycles through the characters of key.

Let's double check the guess that $key=qw8J :
```PHP
$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");

$data = base64_encode(xor_encrypt(json_encode($defaultdata)));

echo $data;
```
This indeed returns the cookie data that is seen in Dev Tools.

### Show password
Now set the associative array "showpassword" to "yes":
```PHP
$defaultdata = array( "showpassword"=>"yes", "bgcolor"=>"#ffffff");

$data = base64_encode(xor_encrypt(json_encode($defaultdata)));

echo $data;
```

This returns the new encoded cookie data, with "showpassword" set as "yes":
> ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK

### New cookie data
Paste this new base64 string into the value for cookies, and give the page a refresh, you shall see the password shown on the page.

![Cookie](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7uvxoem64ull3x1ggf8b.png)