php-session反序列化

本文深入探讨了PHP中的session机制,包括其产生、存储、写入和读取过程,并详细解析了session反序列化的原理。通过实例展示了如何利用不同session存储处理器的混合使用来构造漏洞,执行任意代码。了解到session_start()在重用会话时会自动反序列化数据,从而揭示了session反序列化漏洞的利用方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

除前面文章写过的 传统的unserialize()函数来调用反序列化,phar触发反序列化、还有一种方法是 利用session 来进行反序列化、或文件包含。因此通过这篇文章,来看一下如何利用 session

目录

<1> session是什么?

<2> session的产生和存储

<3> session的写入和读取

<4> php中session相关配置

<5>session反序列化原理

(1) 原理介绍

(2) 实例分析


<1> session是什么?

在利用之前,我们先来了解一下 什么是session

session的本质和cookie是差不多的,保存着http状态信息。简单来说、Session是一次浏览器和服务器的交互的会话

那么我们说了,什么是会话? 比如我们问别人:How are you? 他们回答: Fine thank u。 这就是一次会话。而cookie和session是客户机与服务器之间的会话产生的。客户机和服务器每进行一次会话会记录一个值赋给$_SESSION['name'],这也是为什么,如果你在登录页面输入了账号密码,当它眺转到另一个页面也是以你之前的这个登录状态执行的。

但有cookie了,为什么还会出现Session会话呢?

        因为我们用浏览器访问网站用的是http协议,http协议是一种无状态的协议,就是说它不会储存任何东西,每一次的请求都是没有关联的,无状态的协议好处就是快速;

        但它也有不方便的地方,比如说我们在login.php登录了,我们肯定希望在index.php中也是登录的状态,否则我们点开其他服务的时候又需要登录一次,那在login.php处登录还有什么意义呢?但前面说到了http协议是无状态的协议,那访问两个页面就是发起两个http请求,他们俩之间是无关联的,所以无法单纯的在index.php中读取到它在login.php中已经登陆了的状态信息。

        为了解决这个问题,cookie就诞生了,cookie是把少量数据存在客户端,它在一个域名下是全局的,相当于php可以在这个域名下的任何页面读取cookie信息,那只要我们访问的两个页面在同一个域名下,那就可以通过cookie获取到登录信息了。

cookie虽然避免了前面提到的 重复登录的问题,但是cookie存在一定的不安全性。因为cookie是存在于客户端的,那用户就是可见的,并且可以修改的;那如何又要安全,又可以全局读取信息呢?那么,Session就出现了,其实它的本质和cookie是一样的,只不过它是存在于服务器端的 更安全

<2> session的产生和存储

前面提到他是会话过程中产生的,那具体是怎么产生的呢?

这里我们说一下php中的Session机制

说到这个,肯定离不开 session_start()函数,它的作用是打开Session,并且随机生成一个32位的session_id,session的全部机制也是基于这个session_id,服务器就是通过这个唯一的session_id来区分出这是哪个用户访问的。

<?php
highlight_file(__FILE__);
session_start();
echo "session_id 为: ".session_id()."<br>";
echo "COOKIE 为: ".$_COOKIE["PHPSESSID"];

 这里可以看出session_id()这个系统方法是输出了本次生成的session_id,并且存入了COOKIE中,参数名为PHPSESSID,如果我们不关闭浏览器,只刷新的话,它的值都是不变的,但当你关掉浏览器之后,重新打开 会生成一个新的session_id

相比于把账户密码存在cookie中,这种一个周期的存储更加安全

 session_id的值存储在服务器的临时目录tmp/temp下. 通过配置文件 php.ini 可以看见,这里我们看看本地phpstudy_pro session_id的值存储在哪个文件路径。

 可以看见 他的存储文件格式为:sess_ + session_id

session的一些默认存储位置

/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID

/var/lib/php5/sess_PHPSESSID
/var/lib/php5/sessions/sess_PHPSESSID

/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID

 

虽然说session存储在服务器里,那我们能不能通过修改COOKIEPHPSESSID的值来修改session_id呢?

 成功更改 同时,看一下本地保存的路径下 也出现了sess_123文件

 

 但是 文件是0Kb 里面什么东西都没有 空文件肯定是没用的 那我们怎么样往里写入一些东西呢

<3> session的写入和读取

在php中,session的使用是通过预定义数组$_session的调用和读取来完成。

在网站的页面中,在注册页面对$_session数组进行赋值,在其他的页面中对$_session数组进行读取。我们更改之前的代码,加上数组赋值语句

可以看见执行完之后,sess_123文件里写入了赋值的内容。格式为 键名|序列化数据

我们看见| 之后那类似php序列化格式串里的数据,这不就和反序列化联系起来了吗🤩

大概梳理一下前三小节的铺垫内容:

        当我们在浏览器中,HTTP请求一个页面时,其中后端php执行session_start()会打开一个会话,此时会寻找COOKIE中的PHPSESSID 是否为空,若不为空session_id()就是这个值,若为空则随机生成一个32位session_id存入PHPSESSID中,然后在对应临时文件路径下生成一个文件名为sess_+session_id 的文件以及将$_SESSION的键值对按照一定格式存储到文件中

终于引入了正题,让我们继续往下看

<4> php中session相关配置

看一下phpinfo()里 session有关选项

session.auto_start,这个开关是指定是否在请求开始时就自动启动一个会话,默认为Off;如果它为 On 的话,相当于就先执行了一个session_start(),会生成一个session_id 便不用再在php里写入session_start()

session.save_path 里面是sess_session_id 文件的保存路径

 session.save_handler 里面是用户自定义session存储的选项,默认是files 即文件形式存储

 session.serialize_handler 里面是序列化存储格式

 这个是至为重要的一环:php存储session有三种模式,php_serialize, php_binary

让我们依次来看一下这三种模式 的区别

首先看php 也是默认的

上文中我们其实已经见过了 ,它的规则是$_SESSION是个数组,数组中的键和值中间用 | 来分割如果是数组或对象按照序列化的格式存储

再看php_serialize模式

注意,php_serialize在5.5版本后新加的一种规则,5.4及之前版本,如果设置成php_serialize会报错

session.serialize_handler = php    一直都在 它是用 |分割

session.serialize_handler = php_serialize    5.5之后启用 它是用serialize反序列化格式分割

首先我们还是得先用ini_set进行设置,语句如下:

ini_set('session.serialize_handler','php_serialize');

根据报错,我们需要在保证session会话未激活时设置 放在session_start()前面

 查看sess_session_id文件,可以看见这个是完全按照 php序列化来存储的,键名也包含在其中,返回回来一个数组

再看 php_binary

这里我们还是得先用ini_set进行设置,语句如下:

ini_set('session.serialize_handler','php_binary');

 这个处理器的格式是键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数序列化处理后的值;注意这个键名的长度所所对应的ASCII字符,就比如说键名长度为4,那它对应的就是ASCII码为4的字符,是个不可见字符EOT

因此我们看见的 □ 就是这个键名的长度5所所对应的ASCII字符。

<5>session反序列化原理

(1) 原理介绍

上文提到了那么多,什么是session,session的产生、写入、读取,session存储文件内容格式为序列化数据等等,都没提到说反序列化,那为什么会产生session反序列化漏洞呢?

类似于phar:// 会对文件里序列化数据自动进行反序列化,Session反序列化也不需要unserialize()函数就可以实现。怎么实现的呢,让我们看一下session_start()函数介绍:

 我们可以看到官方解释:当session_start重用现有会话时,php内部调用会话管理器的read函数,通过read回调函数调用现有会话数据(文件里序列化内容),php会自动反序列化数据 填充到$_SESSION 数组中

那就好说了,会调用反序列化的话,那我们是不是 可以通过写入自己构造的序列化数据到sess_sessid 文件里,然后在有反序列化漏洞页面刷新页面,由于这个页面依然有session_start(),那它就去读取那个文件的内容,从而自动进行反序列化呢?

 当然可以。不过问题又来了,我们怎么去控制sess_sessid 文件里的内容呢?

 这里就要利用到两种模式的区别了,虽然上文提到的 php模式、php_serialize 两个模式的2个序列化格式本身没有问题,但是如果2个混合起用就会造成危害。

形成原理:是在用session.serialize_handler = php_serialize存储的字符可以引入 | , 再用session.serialize_handler = php格式取出$_SESSION的值时 "|"会被当成键值对的分隔符

 干说可能看起来比较晕,这里我们实际操作一下大家就明白了:

(2) 实例分析

写入一个test1.php  里面包含一个Test类,当他被反序列化时会调用__wakeup() 造成eval执行命令

#test1.php
<?php
highlight_file(__FILE__);
ini_set('session.serialize_handler', 'php');
session_start();
class Test{
    public $code;
    function __wakeup(){
    eval($this->code);
    }
}
?>

再看test2.php

<?php
highlight_file(__FILE__);
ini_set('session.serialize_handler', 'php_serialize');
session_start();
if(isset($_GET['test'])){
    $_SESSION['test']=$_GET['test'];
    echo session_id();
}
?>

在test2.php中接受用户传入的参数并储存至session中,序列化处理器为php_serialize

比如我们想利用 session反序列化 执行phpinfo()    那我们应该在sess_id 存储文件里写入的序列化内容为:O:4:"Test":1:{s:4:"code";s:10:"phpinfo();";}

 首先我们通过test2.php 将" | " + O:4:"Test":1:{s:4:"code";s:10:"phpinfo();";} 通过GET传参传入到$_SESSION数组,由于是php_serialize模式,sessid存储文件里内容为:

 可以看见文件里成功写入。如果此时我们再用test1.php的 php 模式去读取,我们知道php模式下 sess_id存储文件的内容格式为:键名 + |分隔符 + 序列化数据。那么此时会把我们构造的O:4:"Test":1:{s:4:"code";s:10:"phpinfo();";} 当初序列化数据来反序列化,那便会执行phpinfo命令

我们来试一下:

成功执行phpinfo()    这就是session反序列化利用的原理

 参考:PHP之序列化与反序列化(session反序列化篇)_errorr0

 反序列化篇之Session反序列化 | Arsene.Tang

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值