目录
一、反序列化漏洞原理
1、相关概念
序列化(Serialization):将对象的状态信息转换为可以存储或传输的形式的过程,一般将对象转换为字节流。序列化时,对象的当前状态被写入到临时或持久性存储区(文件、内存、数据库等)。
反序列化(Deserialization):从序列化的表示形式中提取数据,即把有序字节流恢复为对象的过程
反序列化攻击:攻击者控制了序列化后的数据,将有害数据传递到应用程序代码中,发动针对应用程序的攻击。
2、序列化出现场景
●远程和进程间通信(RPC/IPC)
●连线协议、Web服务、消息代理
●缓存/持久性存储区
●数据库、缓存服务器、文件系统
●HTTP cookie、HTML表单参数、API身份验证令牌
3、反序列化攻击分类
●对象和数据结构攻击:应用中存在反序列化过程中或者之后被改变行为的类
●数据篡改攻击:使用了当前序列化的数据结构,但是内容被改编
4、危害
●远程代码执行,如:system(‘whoami’)、system(‘cat/etc/passwd’)等
●重放攻击
●注入
●特权提升
5、序列化与反序列化过程
例:
<?php
class demo {
private $pv= 'private';
protected $pt = 'protected';
public $pb= 'public';
public function set_pv ($pv) {
$this->pv=$pv;
}
public function get_pv(){
return $this->pv;
}
}
$object = new demo() ;
$object->set_pv ('newprivate') ;
$data = serialize ($object) ; //1句
echo $data;
$object_de=unserialize ($data); //2句
?>
//serialize()和unserialize()是实现序列化和反序列化的两个函数
这段代码定义了一个demo类,并通过serialize()将其序列化。执行该段代码,1句执行后将得到如下序列化后的数据:
O:4:"demo":3:{s:8:"demopv";s:10:"newprivate";s:5:"*pt";s:9:"protected";s:2:"pb";s:6:"public";}
//O:表示这是一个对象
//4:对象名占4个字符
//"demo":对象名
//3:对象有三个属性
2句将会把序列化后的数据还原为对象。serialize()和unserialize()函数本身没有问题。反序列化漏洞之所以发生是在传给unserialize()的参数可控时,用户就可以注入精心构造的数据。当进行反序列化时就可能会触发对象的一些魔术方法,造成代码执行等危害。
二、常用的魔术方法
魔术方法(Magic Method)就是指所有以__(两个下划线)开头的类的方法。在某些条件下自动执行会导致反序列化漏洞。在PHP中常见的魔术方法有如下这些:
__sleep() //使用serialize()时触发
__destruct() //对象被销毁时触发,在脚本终止或对象引用计数为0时调用,通常会执行数据清除就或连接断开操作
__call() //在对象上下文中调用不可访问的方法时触发 ,通常用于错误处理,防止脚本因为调用错误而终止执行
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据,通常用于设置和获取对象私有属性
__set() //用于将数据写入不可访问的属性,通常用于设置和获取对象私有属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发
__clone() //当把一个对象赋给另一个对象时自动调用
__wakeup() //unserialize函数会检查是否存在wakeup方法,如果存在则先调用wakeup方法,做一些必要的初始化连数据库等操作
__construct() //PHP5允许在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法
__destruct() //PHP5引入析构函数的概念,析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行
__toString() //用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误
三、反序列化漏洞的防御
●最有效的方法是不接受来自不受信任源的序列化对象或者只使用原始数据类型的序列化,但这不容易实现。
●完整性检查,如:对序列化对象进行数字签名,以防止创建恶意对象或序列化数据被篡改。
●在创建对象前强制执行类型约束,因为用户的代码通常被期望使用一组可定义的类。
●记录反序列化的失败信息。比如传输的类型不满足预期要求或者反序列化异常情况,因为这有可能是攻击者的攻击尝试。