漏洞描述
反序列化漏洞是基于序列化和反序列化的操作,在反序列化——unserialize()时存在用户可控参数,而反序列化会自动调用一些魔术方法,如果魔术方法内存在一些敏感操作例如eval()函数,而且参数是通过反序列化产生的,那么用户就可以通过改变参数来执行敏感操作,这就是反序列化漏洞。
反序列化漏洞首次出现在2015。虽然漏洞较新,但利用十分热门,主要原因还是太过信任客户端提交的数据,容易被开发者忽略,该漏洞一般都可执行任意命令或代码,造成的影响较大。
什么是序列化和反序列化
序列化是将对象的状态信息转换为可以存储或传输的形式的过程,在序列化期间,对象将当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象状态,重新创建该对象。
简单的来讲:
- 序列化:把对象转换为字节序列的过程称为对象的序列化。
- 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
漏洞原理
在身份验证,文件读写,数据传输等功能处,在未对反序列化接口做访问控制,未对序列化数据做加密和签名,加密密钥使用硬编码(如Shiro 1.2.4),使用不安全的反序列化框架库(如Fastjson 1.2.24)或函数的情况下,由于序列化数据可被用户控制,攻击者可以精心构造恶意的序列化数据(执行特定代码或命令的数据)传递给应用程序,在应用程序反序列化对象时执行攻击者构造的恶意代码,达到攻击者的目的。
- 在Python和PHP中,一般通过构造一个包含魔术方法(在发生特定事件或场景时被自动调用的函数,通常是构造函数或析构函数)的类,然后在魔术方法中调用命令执行或代码执行函数,接着实例化这个类的一个对象并将该对象序列化后传递给程序,当程序反序列化该对象时触发魔术方法从而执行命令或代码。在Java中没有魔术方法,但是有反射(reflection)机制:在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法,这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。一般利用反射机制来构造一个执行命令的对象或直接调用一个具有命令执行或代码执行功能的方法实现任意代码执行。
- Java的序列化就是将对象转化为一个二进制的字节流的过程,该字节序列包含该对象的属性和方法;反序列化顾名思义就是将对象字节流转化为对象,而序列化时需要使用writeObject将对象转化为字节流,反序列化需要使用readObject将字节流转化为对象。
- Java反序列化漏洞其原理也是因为对用户的输入没有做严格的过滤,导致用户可传入精心构造的字节流。简单来说就是开发人员在重写readobject方法时没有进行严格的过滤,导致攻击者可以通过某些特定的条件执行命令,触发链式调用,最终造成危害。
漏洞场景
漏洞可能出现的位置
- 解析认证token、session的位置
- 将序列化的对象存储到磁盘文件或存入数据库后反序列化时的位置,如读取json文件,xml文件等
- 将对象序列化后在网络中传输,如传输json数据,xml数据等
- 参数传递给程序
- 使用RMI协议,被广泛使用的RMI协议完全基于序列化
- 使用了不安全的框架或基础类库,如JMX 、Fastjson和Jackson等
- 定义协议用来接收与发送原始的java对象
一些实战中的一些魔术方法。
- __construct(),类的构造函数
- __destruct(),类的析构函数
- __call(),在对象中调用一个不可访问方法时调用
- __get(),访问一个不存在的成员变量或访问一个private和protected成员变量时调用
- __set(),设置一个类的成员变量时调用
- __isset(),当对不可访问属性调用isset()或empty()时调用
- __unset(),当对不可访问属性调用unset()时被调用。
- __sleep(),执行serialize()时,先会调用这个函数
- __wakeup(),执行unserialize()时,先会调用这个函数
风险等级
反序列化漏洞是一种严重的安全漏洞,其风险等级通常被认为是高风险或者严重风险。这是因为反序列化漏洞可以导致恶意攻击者执行任意代码,从而对受影响的系统造成严重的安全威胁。
漏洞危害
Java反序列化漏洞的危害主要在于它可能被攻击者利用来执行恶意代码,从而实现对目标系统的攻击。 具体来说,当应用程序从不可信的数据源进行反序列化操作时,攻击者可以通过构造恶意的序列化数据,使得应用程序在反序列化过程中执行任意代码。 这样,攻击者就能够控制目标系统,窃取敏感信息,或者进行其他恶意行为。
- 不安全的反序列化,主要造成的危害是远程代码执行
- 如果无法远程代码执行,也可能导致权限提升、任意文件读取、拒绝服务攻击等
漏洞验证
我一般使用工具或者一些插件来跑看有无反序列化漏洞。
漏洞利用
- 构造恶意输入: 利用应用程序的反序列化功能,发送经过精心构造的恶意数据。
- 利用已知漏洞: 针对特定框架或库的已知反序列化漏洞进行攻击。
反序列化漏洞的发现一般需审计源码,寻找可利用的pop链,如Java常见的CC链
漏洞防御
- 应该尽量避免用户输入反序列化的参数
- 如果确实需要对不受信任的数据源进行反序列化,需要确保数据未被篡改,比如使用数字签名来检查数据的完整性
- 严格控制反序列化相关函数的参数,坚持用户所输入的信息都是不可靠的原则
- 对于反序列化后的变量内容进行检查,以确定内容没有被污染
- 做好代码审计相关工作,提高开发人员的安全意识
典型案例
- fastjson 1.2.24 反序列化导致任意命令执行漏洞(CVE-2017-18349)
- Drupal Core 8 PECL YAML 反序列化任意代码执行漏洞(CVE-2017-6920)
- Apache Shiro 1.2.4反序列化漏洞(CVE-2016-4437)
- Apache Log4j Server 反序列化命令执行漏洞(CVE-2017-5645)
- Weblogic < 10.3.6 'wls-wsat' XMLDecoder 反序列化漏洞(CVE-2017-10271)
555