0x00 前言
本来是昨天要写的文章,结果因为太懒了,就鸽了,今天补回来。真是一条大懒狗。
0x01 PHP-Session
创建流程
关于什么是session,这里就不说了。php中创建session的流程直接看下面这张图,或许会比我自己写的更加直观。
存储机制
php中的存储机制的设定,依靠的是php.ini
中的session.sava_handler
进行设置的,默认采用的是文件形式存储。php会将session存储在session.save_path
所设定的路径目录之中,并以sess_sessionid
(eg: sess_CheNLvTaNGisFool)的形式命名。
存储形式
当php采用不同的处理器设定时,所存储的session形式也略有不同,处理器的设定由php.ini
中的session.serialize_handler
选项进行设定。php中有三种不同的处理器可供使用,下面是利用三种不同处理器进行创建session的详细结果:
php
采用这种处理器的时候,存储格式为: 键名+竖线+经过serialize()函数处理的值
php_binary
这种处理器的存储格式为:键名的长度对应的ascii字符+键名+serialize()函数序列化的值
因为”chenlvtang”长度为10,十六就进制就是0a
,对应的Ascii是换行,所以看到有一个换行。
php_serialize
这个处理器有版本要求,PHP 版本大于5.5.4时,可以使用,它的存储方式为:经过serialize()函数序列化数组
0x02 使用不同的处理器引发的反序列化漏洞
上文说到了几种不同处理器的不同格式,那如果同时对一个session进行不同处理的时候,会不会有可能产生漏洞呢?
Session变量可控时
下面是一个网站的两个文件:
demo1.php:
<?php
ini_set("session.serialize_handler","php_serialize");
session_start();
$_SESSION['test'] = $_GET['a'];
var_dump($_SESSION);
?>
demo2.php:
<?php
session_start();
class student{
var $name;
var $age;
function __wakeup(){
echo $this->name;
}
}
?>
demo1.php中由于某种需求采用了另一种处理器,但在demo2.php中未进行设定,这样会默认采用php处理器,这时,两个文件采用的处理器就不同了。这里还需要补充一个关于session_start()
的小知识:
当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会调用会话管理器的 open 和 read 回调函数。 会话管理器可能是 PHP 默认的, 也可能是扩展提供的(SQLite 或者 Memcached 扩展), 也可能是通过 session_set_save_handler() 设定的用户自定义会话管理器。 通过 read 回调函数返回的现有会话数据(使用特殊的序列化格式存储), PHP 会自动反序列化数据并且填充 $_SESSION 超级全局变量。
可以看到,session_start函数会将已有的会话数据进行反序列,这里就是我们可以利用的反序列化点了。demo2中的处理器php会用|
进行区分键和序列化的值,所以如果我们现在demo1中生成一个session,并在其中插入|
,那么,再访问demo2,他是不是反序列化已有会话中序列化的值呢?
首先我们先访问demo1,并构造payload传值:
此时的session为:a:1:{s:4:"test";s:66:"|O:7:"student":2:{s:4:"name";s:10:"chenlvtang";s:3:"age";s:1:"3";}";}
然后我们访问demo2:
可以看到成功调用了__wakeup
,完成了反序列化。
Session变量不可控时
上文中,当session可控时,一切都是这么的简单。但如果我们控制不了session变量的时候,又该怎么做呢?
神说“要有光”,于是便有了光。既然我们现在不可控,那么我们是否能通过一种方法,从而实现对session变量的控制呢?
确实是有的,但是我不准备在这写,我只写一下他的名字: session.upload_progress