欢迎来到 无奈人生 安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

PHP反序列化漏洞

来源: 作者: 时间:2019-02-24 19:29 点击: 我要投稿
广告位API接口通信错误,查看德得广告获取帮助

(反)序列化给我们传递对象提供了一种简单的方法。
serialize()将一个对象转换成一个字符串
unserialize()将字符串还原为一个对象
反序列化的数据本质上来说是没有危害的
用户可控数据进行反序列化是存在危害的
可以看到,反序列化的危害,关键还是在于可控或不可控。
0x01 PHP序列化格式
1. 基础格式
boolean
b:;
b:1; // True
b:0; // False
integer
i:;
i:1; // 1
i:-3; // -3
double
d:;
d:1.2345600000000001; // 1.23456(php弱类型所造成的四舍五入现象)
NULL

N; //NULL
string
s::"";
s"INSOMNIA"; // "INSOMNIA"
array
a::{key, value pairs};
a{s"key1";s"value1";s"value2";} // array("key1" => "value1", "key2" => "value2")
2. 序列化举例
test.php
class test
{
    private $flag = 'Inactive';
    public function set_flag($flag)
    {
        $this->flag = $flag;
    }
    public function get_flag($flag)
    {
        return $this->flag;
    }
}
我们来生成一下它的序列化字符串:
serialize.php
require "./test.php";
$object = new test();
$object->set_flag('Active');
$data = serialize($object);
file_put_contents('serialize.txt', $data);
代码不难懂,我们通过生成的序列化字符串,来细致的分析一下序列化的格式:

O:4:"test":1:{s:10:"testflag";s:6:"Active";}
O::""::{
}
3. 注意
这里有一个需要注意的地方,testflag明明是长度为8的字符串,为什么在序列化中显示其长度为10?
翻阅php官方文档我们可以找到答案:

对象的私有成员具有加入成员名称的类名称;受保护的成员在成员名前面加上'*'。这些前缀值在任一侧都有空字节。

所以说,在我们需要传入该序列化字符串时,需要补齐两个空字节:

O:4:"test":1:{s:10:"%00test%00flag";s:6:"Active";}
4. 反序列化示例
unserialize.php
$filename = file_get_contents($filename);
$object = unserialize($filename);
var_dump($object->get_flag());
var_dump($object);

0x02 PHP(反)序列化有关的魔法函数
construct(), destruct()
构造函数与析构函数
call(), callStatic()
方法重载的两个函数
__call()是在对象上下文中调用不可访问的方法时触发
__callStatic()是在静态上下文中调用不可访问的方法时触发。
get(), set()
__get()用于从不可访问的属性读取数据。
__set()用于将数据写入不可访问的属性。
isset(), unset()
__isset()在不可访问的属性上调用isset()或empty()触发。
__unset()在不可访问的属性上使用unset()时触发。
sleep(), wakeup()
serialize()检查您的类是否具有魔术名sleep()的函数。如果是这样,该函数在任何序列化之前执行。它可以清理对象,并且应该返回一个数组,其中应该被序列化的对象的所有变量的名称。如果该方法不返回任何内容,则将NULL序列化并发出E_NOTICE。sleep()的预期用途是提交挂起的数据或执行类似的清理任务。此外,如果您有非常大的对象,不需要完全保存,该功能将非常有用。
unserialize()使用魔术名wakeup()检查函数的存在。如果存在,该功能可以重构对象可能具有的任何资源。wakeup()的预期用途是重新建立在序列化期间可能已丢失的任何数据库连接,并执行其他重新初始化任务。
__toString()
__toString()方法允许一个类决定如何处理像一个字符串时它将如何反应。
__invoke()
当脚本尝试将对象调用为函数时,调用__invoke()方法。
__set_state()
__clone()
__debugInfo()
0x03 PHP反序列化与POP链
就如前文所说,当反序列化参数可控时,可能会产生严重的安全威胁。
面向对象编程从一定程度上来说,就是完成类与类之间的调用。就像ROP一样,POP链起于一些小的“组件”,这些小“组件”可以调用其他的“组件”。在PHP中,“组件”就是这些魔术方法(__wakeup()或__destruct)。
一些对我们来说有用的POP链方法:
命令执行:
exec()
passthru()
popen()
system()
文件操作:
file_put_contents()
file_get_contents()
unlink()
2. POP链demo
popdemo.php
class popdemo
{
    private $data = "demo\n";
    private $filename = './demo';
    public function __wakeup()

[1] [2] [3] [4] [5] [6] [7] [8]  下一页

(反)序列化给我们传递对象提供了一种简单的方法。
serialize()将一个对象转换成一个字符串
unserialize()将字符串还原为一个对象
反序列化的数据本质上来说是没有危害的
用户可控数据进行反序列化是存在危害的
可以看到,反序列化的危害,关键还是在于可控或不可控。
0x01 PHP序列化格式
1. 基础格式
boolean
b:;
b:1; // True
b:0; // False
integer
i:;
i:1; // 1
i:-3; // -3
double
d:;
d:1.2345600000000001; // 1.23456(php弱类型所造成的四舍五入现象)
NULL

N; //NULL
string
s::"";
s"INSOMNIA"; // "INSOMNIA"
array
a::{key, value pairs};
a{s"key1";s"value1";s"value2";} // array("key1" => "value1", "key2" => "value2")
2. 序列化举例
test.php
class test
{
    private $flag = 'Inactive';
    public function set_flag($flag) 本文来自无奈人生安全网
    {
        $this->flag = $flag;
    }
    public function get_flag($flag)
    {
        return $this->flag;
    }
}
我们来生成一下它的序列化字符串:
serialize.php
require "./test.php";
$object = new test();
$object->set_flag('Active');
$data = serialize($object);
file_put_contents('serialize.txt', $data);
代码不难懂,我们通过生成的序列化字符串,来细致的分析一下序列化的格式:

O:4:"test":1:{s:10:"testflag";s:6:"Active";}
O::""::{
}
3. 注意
这里有一个需要注意的地方,testflag明明是长度为8的字符串,为什么在序列化中显示其长度为10?
翻阅php官方文档我们可以找到答案:

copyright 无奈人生



对象的私有成员具有加入成员名称的类名称;受保护的成员在成员名前面加上'*'。这些前缀值在任一侧都有空字节。

所以说,在我们需要传入该序列化字符串时,需要补齐两个空字节:

O:4:"test":1:{s:10:"%00test%00flag";s:6:"Active";}
4. 反序列化示例
unserialize.php
$filename = file_get_contents($filename);
$object = unserialize($filename);
var_dump($object->get_flag());
var_dump($object);

0x02 PHP(反)序列化有关的魔法函数

copyright 无奈人生


construct(), destruct()
构造函数与析构函数
call(), callStatic()
方法重载的两个函数
__call()是在对象上下文中调用不可访问的方法时触发
__callStatic()是在静态上下文中调用不可访问的方法时触发。
get(), set()
__get()用于从不可访问的属性读取数据。
__set()用于将数据写入不可访问的属性。
isset(), unset()
__isset()在不可访问的属性上调用isset()或empty()触发。
__unset()在不可访问的属性上使用unset()时触发。
sleep(), wakeup()
serialize()检查您的类是否具有魔术名sleep()的函数。如果是这样,该函数在任何序列化之前执行。它可以清理对象,并且应该返回一个数组,其中应该被序列化的对象的所有变量的名称。如果该方法不返回任何内容,则将NULL序列化并发出E_NOTICE。sleep()的预期用途是提交挂起的数据或执行类似的清理任务。此外,如果您有非常大的对象,不需要完全保存,该功能将非常有用。
unserialize()使用魔术名wakeup()检查函数的存在。如果存在,该功能可以重构对象可能具有的任何资源。wakeup()的预期用途是重新建立在序列化期间可能已丢失的任何数据库连接,并执行其他重新初始化任务。
__toString()

copyright 无奈人生


__toString()方法允许一个类决定如何处理像一个字符串时它将如何反应。
__invoke()
当脚本尝试将对象调用为函数时,调用__invoke()方法。
__set_state()
__clone()
__debugInfo()
0x03 PHP反序列化与POP链
就如前文所说,当反序列化参数可控时,可能会产生严重的安全威胁。
面向对象编程从一定程度上来说,就是完成类与类之间的调用。就像ROP一样,POP链起于一些小的“组件”,这些小“组件”可以调用其他的“组件”。在PHP中,“组件”就是这些魔术方法(__wakeup()或__destruct)。
一些对我们来说有用的POP链方法:
命令执行:
exec()
passthru()
popen()
system()
文件操作:
file_put_contents()
file_get_contents()
unlink()
2. POP链demo
popdemo.php
class popdemo
{
    private $data = "demo\n";
    private $filename = './demo';
    public function __wakeup()
www.wnhack.com

[1] [2] [3] [4] [5] [6] [7] [8]  下一页

内容来自无奈安全网

。 (责任编辑:admin)
【声明】:无奈人生安全网(http://www.wnhack.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱472701013@qq.com,我们会在最短的时间内进行处理。