Java反序列化漏洞浅析与应对
一、摘要
Java语言由于具有可移植性高及完全面向对象的特点,在各种领域都得到了广泛的应用,例如WebLogic、Jenkins、OpenNMS等组件或中间件都基于Java进行开发。所以,这也导致基于Java的组件或中间件成为黑客重点攻击的对象,其中利用Java的反序列化机制发起的远程命令执行攻击利用难度低、影响范围广,频繁爆发的反序列化漏洞给信息系统安全带来极大的挑战。本文首先简要介绍Java的反序列化机制,然后按照恶意代码被引入目标靶机的不同方式,将Java反序列化漏洞的漏洞利用方法分为内嵌型漏洞利用和引用型漏洞利用两种方法,针对这两种利用方法介绍了相应的修复措施,并在最后结合中小型金融机构实际情况给出了应对Java反序列化漏洞攻击的建议。
二、Java反序列化漏洞的危害
Java语言自1995年诞生至今已经超过28年,对于知识快速更新的IT行业来说算是一门十分古老的编程语言。然而Java凭借其高可扩展性、平台独立性、支持嵌入式和移动设备,以及广泛的开发者生态系统等特性,依然具有强大的生命力,Java在我国金融行业也同样是一门不可或缺的编程语言。因此,基于Java的应用也成为黑客重点攻击的对象,其中具有代表性的是Java反序列化漏洞,即利用Java的反序列化机制,对各类Java组件或中间件攻击导致的远程恶意命令执行漏洞。
这类漏洞影响范围广、利用难度低,可以远程执行恶意命令,对信息系统安全构成极大危害。例如在2015年,借助Java反序列化机制和Apache Commons Collections这一Java基础类库,实现远程恶意命令执行攻击的反序列化漏洞(CVE-2018-2628)被曝光。由于Apache Commons Collections是对Java标准库的扩展,提供了很多强有力的数据结构类型,并且是各种Java基础工具类的集合,是被最广泛使用的第三方基础库之一。该漏洞被曝光后各大Java Web Server纷纷中招,波及面涉及WebLogic、WebSphere、JBoss、Jenkins、OpenNMS等主流Java基础软件。
三、Java反序列化漏洞诞生背景
序列化机制包括序列化和反序列化。序列化(Serialization)是指将数据结构或对象状态转换为一个可以存储或传输的字节流格式的过程。反序列化(Deserialization)是序列化的逆过程,是将字节流格式的序列化数据恢复为其原始形式的过程。
序列化机制是分布式计算发展的产物。在计算机发展的早期阶段,计算机系统架构主要是集中式计算。集中式计算是指由一台计算机组成的中心节点,数据集中存储于这个中心节点中,并且整个系统的所有业务单元都集中部署在这个中心节点上,系统的所有功能均由其集中处理。集中式计算最大的特点是部署结构简单,由于集中式计算往往基于底层性能卓越的大型主机,因此,无需考虑如何对服务进行多个节点的部署,也就不用考虑负载均衡问题。但缺点也明显,比如集中式计算架构在设计上是一个单点,且单服务器的造价昂贵,所以系统横向扩展性差;再如,发生单点故障(单机不可用即全部不可用)会导致系统停机,且维护时要暂停业务,对业务运营影响严重。
由于传统的集中式计算需要使用大型机、成本高昂,也存在着较大的单点故障风险。同时,计算机系统也在朝着微型化、网络化发展,为了降低成本、规避风险,分布式计算技术应运而生。
分布式计算是使多台计算机协同工作以解决共同问题的方法。它使计算机网络看起来就像一台功能强大的计算机,可提供大规模资源来应对复杂的挑战。分布式计算相比于集中式计算最大的优势是可扩展性,且成本低廉。分布式计算引入了中间件的概念,中间件运行在应用层和操作系统之间,它对应用层屏蔽掉了异构的底层操作系统和硬件层,从而将一个计算机网络变成一个功能强大的计算机。
序列化机制进一步地使程序的数据结构或对象在分布式计算环境中能自由“迁徙”。以Java类及其实例化后的Java类对象为例,在传统的不支持序列化机制的计算机中,Java类对象仅能在实例化该对象的本地计算机环境运行,脱离本地计算机环境将报错“对象未定义”。在引入Java中间件后,分布式的计算机成为一个整体,再结合序列化机制,使得Java类对象可以在其中一台计算机中被“打包”(即序列化)后通过网络传输到另一台计算机上,在被还原出来后(即反序列化)仍然能正常运行。程序的灵活性因此大为增加。
然而灵活与安全是一对矛盾关系。序列化机制在增加Java程序灵活性的同时也埋下了因为反序列化漏洞带来的远程代码执行安全隐患。如前所述,反序列化本质上是允许外部输入的代码在本地计算机运行,攻击者正可以利用这一特性将恶意代码与Java类一起“打包”,在目标靶机反序列化后即成功实施远程代码执行攻击。
四、两种漏洞利用方法及修复措施
由上一章节分析可知,反序列化漏洞是为了获取程序设计的灵活性而带来的“副作用”,因此反序列化漏洞将会在信息系统中长期存在,难以彻底根除。
Java反序列化漏洞的根本原因是在目标靶机的Java组件或中间件(例如WebLogic、FastJson)中存在某些Java类,攻击者通过对这些Java类采取某种巧妙的利用方法,能借助这些Java类对目标靶机执行由攻击者定义的恶意代码,从而实施远程代码执行攻击。
笔者经过对WebLogic、XStream、Shiro、FastJson等Java组件或中间件的反序列化漏洞进行对比分析,发现按照恶意代码被引入目标靶机的不同方式,攻击者对这些Java反序列化漏洞的利用方式可以分为两种:一种是内嵌型漏洞利用,一种是引用型漏洞利用。这两种利用方式的差异总结为以下表格。
1.内嵌型漏洞利用
内嵌型漏洞利用是指恶意代码被直接嵌入到Java类对象中。当Java类对象在目标靶机中被反序列化后,恶意代码就会立即运行。WebLogic反序列化漏洞、XStream反序列化漏洞即、Shiro反序列化漏洞属于内嵌型漏洞利用。
存在内嵌型漏洞利用的原因是某些Java类具有特定的调用链。通过层层地类实例或类方法之间的调用关系,程序最终执行到一个可以执行任意指令的方法。攻击者会巧妙地利用这些Java类的调用链,在最终的方法中插入恶意代码,从而让程序执行由攻击者定义的恶意代码。
这些Java类的调用链往往十分复杂,涉及到多个不同的Java类和它们的方法,有的调用链多达数十层。最终执行任意指令的方法通常属于另一个类,和调用链起点的类并非同一个类。例如,WebLogic反序列化漏洞CVE-2015-4852的调用链起点是ObjectInputStream类的readObject方法,而最终执行任意指令的是Runtime类的exec方法。
在一个Java反序列化漏洞被曝光后,其漏洞利用调用链即被公开。而一些功能完善的工具的出现大大降低了攻击者完成漏洞利用脚本的复杂度。攻击者只需简单的几步操作就能完成攻击准备,攻击成本大大降低。例如,Ysoserial就是一个Java反序列化漏洞利用和POC验证工具。
以漏洞编号为CVE-2018-2628的WebLogic反序列化漏洞为例。Apache Commons Collections是一个Java基础类库,被WebLogic、WebSphere、JBoss、Jenkins、OpenNMS等Java应用所引用,应用范围十分广泛。因为Commons Collections 存在可以构造出执行任意类的任意方法的调用链。攻击者利用该调用链构造出包含恶意代码的序列化数据,发送给靶机的WebLogic进行反序列化,在靶机反序列化还原出Java类对象并最终运行恶意代码,造成远程代码执行攻击。
该漏洞的调用链层层嵌套,涉及不同的类和方法,调用链有16层,如下图所示:
2.引用型漏洞利用
引用型漏洞利用是指Java类对象中嵌入的并非恶意代码,而是指向恶意代码的引用地址。真正的恶意代码作为文件保存在由攻击者控制的服务器中。当该Java类对象在目标靶机被反序列化后,程序会根据恶意代码的引用地址通过远程访问协议(指JNDI远程调用),从攻击者指定的服务器中读取恶意代码文件,取回包含恶意代码的文件并在目标靶机运行。FastJson反序列化漏洞就属于引用型漏洞利用。
存在引用型漏洞利用的原因是某些Java类属性支持通过JNDI远程调用对类属性赋值,给攻击者利用这一特性发起攻击创造了条件。
以漏洞编号为CVE-2017-18349的FastJson反序列化漏洞为例。在对访问数据库的Java类com.sun.rowset.JdbcRowSetImpl进行反序列化时,通过设置dataSourceName属性和autoCommit属性为特定的值,将在目标服务器执行恶意代码。
攻击者首先准备好保存有恶意代码的文件attack.class,存放在由攻击者控制的服务器中。然后攻击者给目标靶机发送以下格式的http请求。请求体body是对Java类com.sun.rowset.JdbcRowSetImpl序列化后的数据,在目标靶机反序列化后dataSourceName属性将触发执行基于rmi协议的JNDI远程调用,下载attack.class文件到目标靶机并执行其中的恶意代码。http请求如下图所示:
3.修复措施
根据上述分析,利用反序列化漏洞发起远程代码执行攻击有3个关键动作:动作一是目标靶机接收序列化数据,动作二是成功完成反序列化,在目标靶机还原出原Java类对象,动作三是反序列化的同时发起JNDI远程调用,将恶意代码下载到本地并执行。对于内嵌型利用的反序列化漏洞,仅涉及前两个动作。对于引用型漏洞,则三个动作都涉及。任意一个动作无法执行成功,攻击者都不能成功发起攻击。
相应的,当遇到新的反序列化漏洞时应采取3类修复措施:第1类是关闭接收序列化数据的网络端口。例如,WebLogic反序列化漏洞利用的是WebLogic的T3协议或IIOP(Internet Inter-ORB Protocol)协议,关闭相应的端口后WebLogic将不再接收外部的序列化数据,从而避免不安全的反序列化。第2类是阻止恶意的反序列化操作。这里主要是依靠升级软件版本、打官方补丁等方式修复漏洞。官方通常采用黑名单方式修复反序列化漏洞,将可能造成反序列化漏洞的Java类加入反序列化黑名单,在程序反序列化的时候碰到黑名单Java类不再反序列化,从而阻止反序列化恶意类对象。第3类针对引用型漏洞利用的反序列化漏洞,如果能阻止JNDI远程调用,则同样能阻断恶意代码执行。在JDK高版本中引入了限制JNDI远程调用的系统参数,例如JDK 6u132, JDK 7u122, JDK 8u113等版本的JDK的系统参数com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase的默认值变为false,即默认不允许基于rmi协议的JNDI远程调用。
尽管采取3类修复措施的任意一类都能阻止反序列化漏洞攻击,但是第1类和第3类修复措施是属于缓释措施,虽然能阻止漏洞攻击,但是不能彻底修复漏洞,而且可能导致应用无法正常运行。只有在面临突发的漏洞攻击风险的情况下,两害相权取其轻,在充分评估相关风险后再采取第1类和第3类修复措施进行临时应急。在漏洞攻击风险得到有效管控后,应尽快采取第2类修复措施,即升级软件版本或打补丁。第2类修复措施则既能阻止漏洞攻击,又能彻底修复漏洞。
五、Java反序列化漏洞应对建议
中小型金融机构的信息安全队伍规模无法和大型金融机构相提并论,但是面临的信息安全漏洞威胁和大型金融机构是一致的,面临突发的信息安全漏洞攻击威胁时需采取的应急措施也是类似的,因此中小型金融机构需要持续探索、不断总结提炼适合自身实际情况的信息安全漏洞管控方法论。为了全面降低包括Java反序列化漏洞在内的漏洞风险,有效应对突发的反序列化漏洞信息安全事件,针对该类漏洞的特点及相应的修复措施,结合中小型金融机构的实际情况,这里提出事前积极防御,事中有效处置,事后复盘提升的反序列化漏洞应对建议。
1.事前:积极防御
知己知彼百战百胜。首先,对于信息安全团队,信息安全意识的提升要放在平时的工作中,由安全团队牵头,加强对全行人员的安全培训,让研发、运维人员都知道漏洞原理,知其然更要知其所以然。对于研发团队,在平时编码中有了安全意识,就不容易犯可能导致漏洞风险的低级错误。运维人员理解漏洞原理后,在突发的漏洞应急处置过程中,也知道该采取什么应急措施。其次,运维团队和安全团队要持续完善网络安全资产管理。资产指企业所拥有的一切可能被潜在攻击者利用的设备、信息、应用等数字资产,具体包括但不限于硬件设备、操作系统、IP地址、端口、域名、Web应用、中间件、开发框架、开放的互联网资产、源代码等。运维团队开展资产管理是从运维视角出发,侧重于业务连续性、稳定性、可扩展性、配置管理等,例如CMDB系统。安全团队要建立安全攻防视角,加强互联网暴露面资产管理,例如开放互联网访问的IP、端口、应用系统、URL、VPN、入口、后台入口等。
2.事中:有效处置
在遭遇突发的Java反序列化漏洞事件时,安全团队要牵头和研发团队、运维团队一起明确分工、密切协同,第一时间开展漏洞应急处置工作。应急处置分为3个阶段。
第1阶段是排查漏洞影响范围,即排查涉及漏洞的系统、服务器、存在漏洞的Java组件或中间件版本等详细信息。运维团队和安全团队要第一时间排查梳理漏洞影响范围,例如涉及的应用系统清单,服务器IP地址,是否开放互联网访问,以及存在漏洞的Java组件或中间件的版本号等信息。
第2阶段是采取紧急措施阻断漏洞利用。首先,安全团队在采取应急措施的时候区分轻重缓急,例如先处置互联网系统后处置内网系统,先处置重要业务系统后处置非业务系统。其次,要协调研发和运维团队,按照上一节所述的第1类和第3类修复措施,在充分评估漏洞威胁和紧急修复措施对应用可用性的影响后,采取相应的应急措施,包括关闭相关网络服务端口,调整安全参数,禁止命中漏洞的服务器发起外连等。第三,安全团队还要及时升级waf、hids等安全设备的防护策略。
第3阶段是彻底修复漏洞。在Java组件或中间件官方发布新版本或新补丁后,研发和运维团队要第一时间进行升级或打补丁,彻底修复漏洞。
3.事后:复盘提升
突发的信息安全事件对企业来说既是挑战,同时也是机遇。每一次漏洞应急都是一次实战化的应急演练。以此为契机,安全团队至少可以在三方面推动信息安全管理水平和技术水平持续提升:一是以此为契机推动收敛互联网暴露面。二是以此为契机推动应下线未下线的系统尽快下线。三是以此为契机推动基础软件更新升级。进一步地,安全团队通过持续地总结和复盘,不断总结提炼成功经验,形成常态化的信息安全漏洞管控机制,缩短漏洞存活期,缩短应收敛的互联网暴露面存活期,持续推动系统应下尽下以及基础软件更新升级。