1、相关概念
序列化(Serialization):将对象的状态信息转换为可以存储或传输的形式的过程,一般将对象转换为字节流。序列化时,对象的当前状态被写入到临时或持久性存储区(文件、内存、数据库等)。
反序列化(Deserialization):从序列化的表示形式中提取数据,即把有序字节流恢复为对象的过程
反序列化攻击:攻击者控制了序列化后的数据,将有害数据传递到应用程序代码中,发动针对应用程序的攻击。
反序列化漏洞(Deserialization Vulnerabilities)是一种常见的安全漏洞,通常出现在应用程序处理序列化数据时。序列化是将数据结构或对象转换为可传输或可存储的格式的过程,而反序列化是将这些数据重新还原为原始的数据结构或对象。这些漏洞的核心问题在于应用程序未能充分验证和过滤反序列化的输入数据,从而导致潜在的安全风险。
攻击者可以通过精心构造的恶意序列化数据来触发反序列化漏洞,导致以下问题:
- 执行远程代码:攻击者可以注入恶意代码并执行它,从而获得对目标系统的控制。
- 拒绝服务:攻击者可以通过发送恶意数据导致应用程序崩溃或变得不可用。
- 绕过身份验证和授权:攻击者可以伪装成授权用户,绕过应用程序的身份验证和授权机制。
反序列化漏洞在许多编程语言和框架中都存在,如Java、.NET、Python等,因此它们是一个广泛存在的威胁。为了防范反序列化漏洞,应采取以下措施:
- 输入验证:验证反序列化的输入数据,确保只接受预期格式的数据。
- 沙盒执行:在处理反序列化数据时,尽量将其隔离到一个安全的执行环境中,以防止执行恶意代码。
- 白名单:只接受已知合法的数据类型和对象,拒绝一切未知的数据。
- 更新库和组件:保持使用的序列化库和框架更新,因为新的漏洞可能在旧版本中修复。
- 监控和日志:监控应用程序的反序列化操作,以及日志记录潜在攻击尝试。
反序列化漏洞是复杂的安全问题,需要开发人员和安全专家密切合作来识别和解决潜在的漏洞。
2、漏洞原理
序列化: 游戏的存档 —— 把当前的状态保存下来
反序列化: 游戏的读档 —— 通过保存下来的信息恢复到当初的状态
在Python和PHP中,一般通过构造一个包含魔术方法(在发生特定事件或场景时被自动调用的函数,通常是构造函数或析构函数)的类,然后在魔术方法中调用命令执行或代码执行函数,接着实例化这个类的一个对象并将该对象序列化后传递给程序,当程序反序列化该对象时触发魔术方法从而执行命令或代码。在Java中没有魔术方法,但是有反射(reflection)机制:在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法,这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。一般利用反射机制来构造一个执行命令的对象或直接调用一个具有命令执行或代码执行功能的方法实现任意代码执行。
漏洞成因
在身份验证,文件读写,数据传输等功能处,在未对反序列化接口做访问控制,未对序列化数据做加密和签名,加密密钥使用硬编码(如Shiro 1.2.4),使用不安全的反序列化框架库(如Fastjson 1.2.24)或函数的情况下,由于序列化数据可被用户控制,攻击者可以精心构造恶意的序列化数据(执行特定代码或命令的数据)传递给应用程序,在应用程序反序列化对象时执行攻击者构造的恶意代码,达到攻击者的目的。
3、漏洞可能出现的位置
1.解析认证token、session的位置
2.将序列化的对象存储到磁盘文件或存入数据库后反序列化时的位置,如读取json文件,xml文件等
3.将对象序列化后在网络中传输,如传输json数据,xml数据等
4.参数传递给程序
5.使用RMI协议,被广泛使用的RMI协议完全基于序列化
6.使用了不安全的框架或基础类库,如JMX 、Fastjson和Jackson等
7.定义协议用来接收与发送原始的java对象
●远程和进程间通信(RPC/IPC)
●连线协议、Web服务、消息代理
●缓存/持久性存储区
●数据库、缓存服务器、文件系统
●HTTP cookie、HTML表单参数、API身份验证令牌
4、危害
●远程代码执行,如:system(‘whoami’)、system(‘cat/etc/passwd’)等
●重放攻击
●注入
●特权提升
5、魔术方法
__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 级别的致命错误
6 序列化与反序列化
例:
<?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:对象有三个属性
O:strlen(类名):类名:类的变量个数:{类型:长度:值;类型:长度:值…}
2句将会把序列化后的数据还原为对象。serialize()和unserialize()函数本身没有问题。反序列化漏洞之所以发生是在传给unserialize()的参数可控时,用户就可以注入精心构造的数据。当进行反序列化时就可能会触发对象的一些魔术方法,造成代码执行等危害。
反序列化特点:
php在反序列化时,底层代码是以 ;作为字段的分隔,以 } 作为结尾,这会造成随便在序列化数据后添加一些无用字符,反序列化的时候也会被忽略,因为遇到了;}会忽略后面的字符;
unserialize根据长度判断内容,长度不对应的时候会报错;
可以反序列化类中不存在的元素。
7、防御
对反序列数据加密或签名,且加密密钥和签名密钥不要使用硬编码
对反序列化接口添加认证授权
设置反序列化服务仅在本地监听或者设置相应防火墙策略
禁止使用存在漏洞的第三方框架库
过滤、禁用危险函数
过滤T3协议或限定可连接的IP
设置Nginx反向代理,实现t3协议和http协议隔离
最有效的方法是不接受来自不受信任源的序列化对象或者只使用原始数据类型的序列化,但这不容易实现。
●完整性检查,如:对序列化对象进行数字签名,以防止创建恶意对象或序列化数据被篡改。
●在创建对象前强制执行类型约束,因为用户的代码通常被期望使用一组可定义的类。
●记录反序列化的失败信息。比如传输的类型不满足预期要求或者反序列化异常情况,因为这有可能是攻击者的攻击尝试。
Views: 13