PHP反序列化漏洞
(反)序列化给我们传递对象提供了一种简单的方法。
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()
(反)序列化给我们传递对象提供了一种简单的方法。
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] 下一页