0x00 前言
从2017年3月15日,fastjson官方主动爆出框架存在远程代码执行高危安全漏洞以后,好像fastjson库漏洞频出。正好最近又爆出了个CVE-2019-14540,影响jackson,同时也影响fastjson,漏洞原理都是利用反序列化造成RCE,所以借此分析CVE-2019-14540的同时好好学习一下相关知识。
0x01 影响范围
fastjson影响版本: version < 1.2.60
,修复commit: https://github.com/alibaba/fastjson/commit/5d09b913a533cf2d2eeea1124337681494804336,建议升级到1.2.60或以上版本。
jackson-databind影响版本: version < 2.9.10
,官方发布详情: https://github.com/FasterXML/jackson-databind/blob/master/release-notes/VERSION-2.x,修复commit: https://github.com/FasterXML/jackson-databind/commit/d4983c740fec7d5576b207a8c30a63d3ea7443de,建议升级到2.9.10或以上版本。
0x02 漏洞分析
分析CVE之前我们先一起学习下前置知识,以便于对漏洞有个更深刻的认识。
前置知识
序列化与反序列化
先来看一下序列化与反序列化的概念:
序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
什么意思呢?以Java为例,在描述一个事物的时候会定义一个类,当需要真正使用这个类的时候,通常需要把它实例化成一个对象,这个对象它是实实在在占用内存空间的,而内存里面的数据表现形式都是二进制,如果我们想把内存中的对象持久化保存起来,这段二进制存储成本地文本文件的过程就可以把它理解成对象的序列化。反之亦然,把文本文件恢复到内存中的过程就可以理解成对象的反序列化。当然,序列化除了本地持久化以外,还可以把内存中的二进制转换成数据流用于网络传输。
RMI远程方法调用
RMI(Remote Method Invocation)是JDK 1.2版本中实现的一种远程方法调用机制,是分布式编程中的基本思想。利用这种机制可以让某台服务器上的对象在调用另外一台服务器上的方法时,和在本地机上对象间的方法调用的语法规则一样。实现原理图如下:
下面我们来看demo代码帮助理解RMI:
服务端工程目录结构如下:
RMIServerDemo│ .classpath│ .project│├─.settings│ org.eclipse.jdt.core.prefs│├─bin│ └─com│ └─rmitest│ RmiSample.class│ RmiSampleImpl.class│ RmiSampleServer.class│└─src └─com └─rmitest RmiSample.java RmiSampleImpl.java RmiSampleServer.java
定义远程接口RmiSample.java
package com.rmitest;import java.rmi.Remote;import java.rmi.RemoteException;//远程接口必须继承Remotepublic interface RmiSample extends Remote{
//所有远程实现方法必须抛出RemoteException public int sum(int a,int b) throws RemoteException;}
实现远程接口RmiSampleImpl.java
package com.rmitest;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;//必须继承UnicastRemoteObjectpublic class RmiSampleImpl extends UnicastRemoteObject implements RmiSample{
//覆盖默认构造函数并抛出RemoteException public RmiSampleImpl() throws RemoteException{
super(); } //方法实现,所有远程实现方法必须抛出RemoteException public int sum(int a,int b) throws RemoteException{
return a+b; }}
服务器程序RmiSampleServer.java
package com.rmitest;import java.rmi.Naming;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;