Insomnihack 2013 – Facebookalypse

This challenge was definitely one of the harder web missions and based on a redefined session handler mechanism that was initially discovered in a relatively well-know Firewall brand. It is also very similar to the example you can find on PHP’s own documentation here : http://php.net/manual/en/function.session-set-save-handler.php

When connecting to the challenge, you are asked to first create a user account. Then once logged in, you can upload a profile picture and modify some of your information.

falypse

In the mission description, you are given a hint, which is that the developer used Kate to write the application. A quick search on Google will reveal that by default Kate saves a backup of the files you are working on by adding a ~ to the file name (http://www.linuxquestions.org/questions/linux-newbie-8/how-to-prevent-backup-file-creation-kate-and-kwrite-940857/). So you could access the source code by viewing index.php~ for example. The interesting code in this case is found in session.php~.

class FileSessionHandler
{
 function
    open ($save_path, $session_name)
    {
	global $sess_save_path;

	$sess_save_path = $save_path;

	return true;
    }

    function
    close()
    {
	return true;
    }

    function
    read ($id)
    {
	global $sess_save_path;

	$sess_file = "$sess_save_path/sess_$id";
	return (string)@file_get_contents($sess_file);
    }

    function
    write ($id, $sess_data)
    {
	global $sess_save_path;

	$sess_file = "$sess_save_path/sess_$id";
	if ($fp = @fopen($sess_file, 'w')) {
	    $return = fwrite($fp, $sess_data);
	    fclose($fp);
	    return $return;
	} else {
	    return false;
	}

    }

    function
    destroy ($id)
    {
	global $sess_save_path;

	$sess_file = "$sess_save_path/sess_$id";
	return @unlink($sess_file);
    }

    function
    gc ($maxlifetime)
    {
	global $sess_save_path;

	foreach (glob("$sess_save_path/sess_*") as $filename) {
	    if (filemtime($filename) + $maxlifetime < time()) {
		@unlink($filename);
	    }
	}
	return true;
    }
}

$handler = new FileSessionHandler();
session_set_save_handler(
    array($handler, 'open'),
    array($handler, 'close'),
    array($handler, 'read'),
    array($handler, 'write'),
    array($handler, 'destroy'),
    array($handler, 'gc')
    );

We see (in bold) that when reading a session value (for example when accessing the $_SESSION variable), the application will try to read a file based on the name of the current session ID (which is found in the cookie, SENT BY THE USER). You will probably think that a file named /$sess_save_path/sess_XXX/../../../../../../../etc/passwd would be invalid and you would be right (PHP’s file_exists function for example will return false). However, file_get_contents will actually be able to resolve that path and read the file!!

shocked

So as a user, you are able to load any file on the disk as a session (more or less, during the challenge, open_basedir restricted you to the current working directory). Now all we need to get the flag is to make the application believe we are an admin. So essentially, we need to load a session file that specifies that the variable user is equal to ‘1’ (When listing the users on the system, you notice that the user with ID 1 is the administrator). This will allow us to view the admin’s profile.

Now in a session file, the data is actually serialized, so we cannot just use a file with user=’1′, but we need something like this : ;user|s:1:’1′;. Chances of finding such a file already on the system are slim, but remember we are allowed to upload files to the server!

As mentioned at the start, we can upload a picture to the server. We can therefore add our string in the raw image data somewhere. But the file must actually be a valid image, as PHP’s getimagesize function must return a value for the file to be uploaded correctly in the application.

$newname = "images/" . sha1($_SESSION['user'] . $_FILES["pic"]["name"]);
	    if(!is_null($_FILES["pic"]["tmp_name"]) && getimagesize($_FILES["pic"]["tmp_name"])) {
	       move_uploaded_file($_FILES["pic"]["tmp_name"],$newname);

All we need to do is add our string in the comments field of the file for example (or any other text header, …). We can then upload it, recover it’s new name (it will be displayed in your profile, or you can calculate the sha1 value as shown above) and use it as a session id!

Modify your session cookie to this : “toto/../../../../../../../../var/www/FOLDER/images/YOURIMAGEHASHNAME” (Yes, you had to guess the web app was in /var/www)

Reload the page and voilà! You are admin and the flag is in your description!