Overthewire Natas Level 22, 23, 24 and 25

Table of Contents

Overthewire - Natas

This series on the overthewire webpage challenges you to think outside the box and more about communication between client and server in order to find the hidden flag on the website for the next level. The structure of the challenge is that for each level you require a user name and password to authenticate to the next level challenge website. The username is natas[X] with X being the current level (e.g. natas5 for level 5), the corresponding URL is “http: //natas[X].natas.labs.overthewire.org/", X again being the level number e.g. “http://natas5.natas.labs.overthewire.org/" for the fifth level, and the password consists of 32 alphanumeric characters.

And now without further ado, let’s get to it:

natas22

As ever so often we start with a page. Duh. But this time, we only see the button to reveal the source code to us:

Only. Source. Code.

session_start();
if(array_key_exists("revelio", $_GET)) {
    // only admins can reveal the password
    if(!($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1)) {
    header("Location: /");
    }
}

<html>
[...]
<body>
<h1>natas22</h1>
<div id="content">
<?
    if(array_key_exists("revelio", $_GET)) {
    print "You are an admin. The credentials for the next level are:<br>";
    print "<pre>Username: natas23\n";
    print "Password: <censored></pre>";
    }
?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html> 

Actually this seems like an easy one. If there is the word “revelio” set in the $_GET parameter we should get the password. However, if we simply send it by extending the URL by “?revelio=1”, we will get two replies from the server. One with the credentials and one with the blank page with “View source code” on it. Thus we need burp repeater:

When we send data to the URL extended by “?revelio=1”, we get the reply with natas23 credentials.
Spoiler natas23: Username: natas23

Password: D0vlad33nQF0Hz2EP255TP5wSW9ZsRSE

URL: http://natas23.natas.labs.overthewire.org

natas23

Natas 23 begins with a password field and this possibility to see source code.

This time we can enter a password and source code.

We get the following source code:

Password:
<form name="input" method="get">
    <input type="text" name="passwd" size=20>
    <input type="submit" value="Login">
</form>
  if(array_key_exists("passwd",$_REQUEST)){
      if(strstr($_REQUEST["passwd"],"iloveyou") && ($_REQUEST["passwd"] > 10 )){
          echo "<br>The credentials for the next level are:<br>";
          echo "<pre>Username: natas24 Password: <censored></pre>";
      }
      else{
          echo "<br>Wrong!<br>";
      }
  }
  // morla / 10111

Ok if we have an array key with “passwd” as key in our $_REQUEST and pass the next comparison, we get the credentials. The second comparison includes strstr, which “Find the first occurrence of a string”. Thus this operation finds the first appearance of “iloveyou” in the value stored with the key “passwd” in the $_REQUEST variable. From the official PHP manual: strstr ( string $haystack , mixed $needle [, bool $before_needle = FALSE ] ) : string Returns part of haystack string starting from and including the first occurrence of needle to the end of haystack. What does $_REQUEST["passwd"] > 10 do?

There is a pretty cool webpage from w3schools, that allows you to write quick HTML/PHP examples and evaluate what they do.

First, let’s read more about PHP superglobals. In general, PHP variables have three different scopes where they are available: local, global and static. A variable of global scope is defined outside a function and can only be accessed outside a function. A variable with local scope is defined within a function and can only be accessed within that function. If we want a variable to maintain is asserted value after a function has been executed, we can do that by assigning it the static keyword. Furthermore some rules apply for PHP variables: most importantly a variable starts with a “$” and continues with the name of the variable, e.g. $txt = "Test";. So what are superglobals now? Superglobal variables, are always accessible, regardless of scope - and you can access them from any function, class or file without having to do anything special. [Source] There are: $GLOBALS, $_SERVER, $_REQUEST, $_POST, $_GET, $_FILES, $_ENV, $_COOKIE, $_SESSION So far we have dealt with $_REQUEST, $_POST, $_GET, $_COOKIE and $_SESSION and for this level we are going to analyze the $_REQUEST variable: “PHP $_REQUEST is a PHP super global variable which is used to collect data after submitting an HTML form.” And yes this is exactly what we are doing here in this level. The “name” field specifies the key by which the value can be extracted from the variable like this: $_REQUEST[key] = [value] So in our case the key is “passwd”. With all that being said, we can fool around with the PHP try out webpage from w3schools:

We copy parts of the natas23 php code into the editor, create the variable $_REQUEST[“passwd”] and play around with some values.

If we comment out the $_REQUEST["passwd"] > 10 part, the level is easy to solve:

As strstr() finds the first occurence of a string and returns true if it found one, simply entering “iloveyou” would suffice to break the level. But there is more…

Let’s play around a little:

$_REQUEST["passwd"] = "123iloveyou456";
echo strstr($_REQUEST["passwd"],"iloveyou");
echo ($_REQUEST["passwd"] > 10);

returns iloveyou4561. Thus strstr() does exactly what we expected, it finds the first occurence of a string and returns it together with every subsequent string attached to it, thus “iloveyou456”. Why the 1 at the end? Because ($_REQUEST["passwd"] > 10) evaluates to true. With that the level should be broken.

Let’s try that out. Let’s enter “123iloveyou456” and see what happens. We get this URL back… http://natas23.natas.labs.overthewire.org/?passwd=123iloveyou456

We are done! :)

Spoiler natas24: Username: natas24

Password: OsRmXFguozKpTZZ5X14zNO43379LZveg

URL: http://natas24.natas.labs.overthewire.org

natas24

We have yet another page, that dares us to enter a password. The source code reveals, that this time we are dealing with strcmp and we do not know the password, that we compare against. Tricky… :)

<?php
    if(array_key_exists("passwd",$_REQUEST)){
        if(!strcmp($_REQUEST["passwd"],"<censored>")){
            echo "<br>The credentials for the next level are:<br>";
            echo "<pre>Username: natas25 Password: <censored></pre>";
        }
        else{
            echo "<br>Wrong!<br>";
        }
    }
    // morla / 10111
?>  

We know that the password field has a maximum length of 20. We could try to bruteforce every option. Let’s assume an alphabet of lower and upper case English alphabets and the numbers 0-9. That is for a 1 digit password 26+26+10 = 62 possible options. For a 2 digit password that is 62 * 62 = 3844 possible options. For a 20 digit password that is $62^{20} = 7,044 * 10^{35}$. Hm, that is way too big. So what else can we do?

Let’s read more about strcmp: If two strings are equal, the function returns 0, thus we need to negate that (thus the ! in the if-clause), to get access if we actually entered the correct password.

Googling for a stcmp vulnerability led me to this page. Here the vulnerability is presented very clearly: strcmp returns 0 if a string is compared with an empty array!

So let’s try this: We intercept the request with a simple password set with burp and put it to the repeater:

Using burp, we change passwd to passwd[] and thus from string to an array. With this we get the password for natas25.
Spoiler natas25: Username: natas25

Password: GHF6X7YwACaYYssHVY05cFq83hRktl4c

URL: http://natas25.natas.labs.overthewire.org

natas25

Well, this is a new design.

This time we get quotes.

What does the source code say?


// cheers and <3 to malvina
// - morla

function setLanguage(){
    /* language setup */
    if(array_key_exists("lang",$_REQUEST))
        if(safeinclude("language/" . $_REQUEST["lang"] ))
            return 1;
    safeinclude("language/en"); 
}

function safeinclude($filename){
    // check for directory traversal
    if(strstr($filename,"../")){
        logRequest("Directory traversal attempt! fixing request.");
        $filename=str_replace("../","",$filename);
    }
    // dont let ppl steal our passwords
    if(strstr($filename,"natas_webpass")){
        logRequest("Illegal file access detected! Aborting!");
        exit(-1);
    }
    // add more checks...

    if (file_exists($filename)) { 
        include($filename);
        return 1;
    }
    return 0;
}

function listFiles($path){
    $listoffiles=array();
    if ($handle = opendir($path))
        while (false !== ($file = readdir($handle)))
            if ($file != "." && $file != "..")
                $listoffiles[]=$file;
    
    closedir($handle);
    return $listoffiles;
} 

function logRequest($message){
    $log="[". date("d.m.Y H::i:s",time()) ."]";
    $log=$log . " " . $_SERVER['HTTP_USER_AGENT'];
    $log=$log . " \"" . $message ."\"\n"; 
    $fd=fopen("/var/www/natas/natas25/logs/natas25_" . session_id() .".log","a");
    fwrite($fd,$log);
    fclose($fd);
}
<h1>natas25</h1>
<div id="content">
<div align="right">
<form>
<select name='lang' onchange='this.form.submit()'>
<option>language</option>
foreach(listFiles("language/") as $f) echo "<option>$f</option>";
</select>
</form>
</div>
session_start();
setLanguage();

echo "<h2>$__GREETING</h2>";
echo "<p align=\"justify\">$__MSG";
echo "<div align=\"right\"><h6>$__FOOTER</h6><div>";
<p>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

Ok there is a lot going on. Essentially there is the possibility to choose between different languages for that page. Default is “en” but there is also the option “de”. Next thing is that we can include a new language via the $_REQUEST variable. Thus if we send something like $_REQUEST[lang] = "fr", we should get the language option “fr”. However, whatever we insert, it is first checked against path traversal (e.g. ../) and whether we try to read out the password directly via “natas_webpass…". Only if those tests are passed and the file exists, it appears on the list.
Ok where does the program get the quotes from? With the include statement, the php script of the page DOES have a problem: “include will only produce a warning (E_WARNING) and the script will continue”. Thus we can see whether a file exists or not.
Furthermore there is a log. If we include path traversal tries or the natas password file, this will get logged into "/var/www/natas/natas25/logs/natas25_" . session_id() .".log" file. But there is more: we cannot manipulate the messages that are being written into the log file, however we can manipulate the “$_SERVER[‘HTTP_USER_AGENT’]". Let’s try this:

  1. Get out session cookie. We intercept a message via burp and extract the session cookie from “Cookie: PHPSESSID=HERE IS YOUR SESSION COOKIE”
  2. With the session cookie, we know into which file we are writing: /var/www/natas/natas25/logs/natas25_SESSIONCOOKIE.log
  3. Let’s try to read out that log file.
    A word on Server: Apache/2.4.10 (Debian) folder structure: The classic index.html is typically located in /var/www/html/index.html. As every natas level has its own webpage, let’s assume that the index.php of the natas25 page lies in: /var/www/natas/natas25/index.php.
    How do we fool now the directory traversal guard? Well, if we include “…/./” that gets substituted to “../". We can test this again on the w3school website:
    Testing the “…/./” substitution. As we see, it works!

    Equipped with that knowledge, let’s try to include the “/var/www/natas/natas25/index.php.” file:
    We include the index.php file. It works. Nice.

Ok now let’s move onto the “/var/www/natas/natas25/logs/natas25_SESSIONCOOKIE.log” file:

We include the natas25_SESSIONCOOKIE.log file. It works. Nice. We see that some directory path traversal attempts were done. Well by us… ;)

So far so good, now we need to include something useful into the logfile! What about <?php readfile("/etc/natas_webpass/natas26") ?>? With that, we should get the content of the next level printed out to screen!
Let’s try this:

We include the natas25_SESSIONCOOKIE.log file. And as User-Agent, we include the php command to read us the content of the next password file. It works. Nice.

With that we have cracked natas25!!!

Spoiler natas26: Username: natas26

Password: oGgWAJ7zcGT28vYazGo4rkhOPDhBu34T

URL: http://natas26.natas.labs.overthewire.org

Summary

Thi level we have worked a lot with PHP and even used an online PHP editor from w3schools to test out our ideas on how to break into a level. We exploited vulnerabilities in strcmp(), strstr(), learnt about PHP’s different important superglobal variables and at the end exploited several the fact, that we could write into a file which’s content got executed in the end.

That’s it for now! See you soon. :)