序列化与反序列
序列化:把对象转换为字节序列的过程称为对象的序列化
反序列化:把字节序列恢复为对象的过程称为对象的反序列化
漏洞成因及本质
成因:反序列化对象中存在魔术方法,而且魔术方法中的代码可以被控制,漏洞根据不同的代码可以导致各种攻击,如代码注入、SQL注入、目录遍历等等。
本质:unserialize函数的变量可控、php文件中存在可利用的类,类中有魔术方法。
魔术方法
魔术方法 | 触发条件 |
---|---|
__wakeup() | 使用unserialize时触发 |
__sleep() | 使用serialize时触发 |
__destruct() | 对象被销毁时触发 |
__call() | 在对象上下文中调用不可访问的方法时触发 |
__callStatic() | 在静态上下文中调用不可访问的方法时触发 |
__get() | 用于从不可访问的属性读取数据 |
__set() | 用于将数据写入不可访问的属性 |
__isset() | 在不可访问的属性上调用isset()或empty()触发 |
__unset() | 在不可访问的属性上使用unset()时触发 |
__toString() | 把类当作字符串使用时触发 |
__invoke() | 当脚本尝试将对象调用为函数时触发 |
代码展示
首先先展示序列化和反序列的原理,因为不同的变量属性序列化的结果也不尽相同。
<?php
class test{
private $test1 = "SQYY 2018";
/* public $test2 = "<?php phpinfo();?>";*/
public $test2 = "SQYY 2018";
protected $test3 = "SQYY 2018";
}
$test = new test();
echo serialize($test);
这里解释一下,O表示有4个对象,一个test类,s代表字节数,有关序列化参数的可以参考以下链接
https://www.cnblogs.com/wayne173/p/3747465.html
接下来我们展示因为反序列化时参数可控引发的反序列化漏洞导致代码执行
<?php
class A{
var $a = 'test';
public function __destruct()
{
// TODO: Implement __destruct() method.
$fp = fopen("C:\Program Files\PHPStudy\WWW\PHP\hello.php","w");
fputs($fp,$this->a);
fclose($fp);
}
}
//$b = new A();
//echo serialize($b);
$object = unserialize($_POST['obj']);
require "C:\Program Files\PHPStudy\WWW\PHP\hello.php";
这是是将反序列化的结果存进一个hello.php的文件中,接下来我们展示攻击过程,可以看到上面的$object
变量可控,因此我们改变序列化中对应参数的值,从而导致恶意代码的执行
首先我们先把序列化的结果打印输出,这里提示我们缺少了一个obj参数,这个是可控变量,等会儿通过POST方式传入
然后我们更改序列化参数的值,执行输出<?php phpinfo();?>
这段恶意代码,因为这段代码总共包含18个字节,因此将s的值更改为18,代码执行成功
这里分享一个Freebuf大佬在CTF中遇见的PHP反序列化漏洞
https://www.freebuf.com/news/172507.html