张 晨
(厦门软件职业技术学院 软件工程系,福建 厦门 361024)
关键字:网络安全 漏洞分析 Web安全 Java反序列化
Java Web是用Java技术来解决相关web互联网领域的技术栈[1],其在Web技术的基础上融合了Java语言的优势特点,是目前大型企业或者政府部门进行Web系统开发的主流方式。在互联网发展浪潮的背景下,Java Web以简单易用的特点成为互联网不可或缺的重要功能载体,但随之而来的相关网络安全问题成为了不得不面对的难题,根据Akamai的通报,每年针对Web应用程序的攻击数量居高不下,网络攻击的手段也呈现出了多样化的情况,其中不安全的反序列化、开源组件风险、SQL注入漏洞、XSS漏洞是产生网络安全威胁的主要因素。在OWASP(开放式Web应用程序安全项目)组织发布的2021年全球十大Web应用安全中[2],不安全设计(Insecure Design)和自带缺陷和过时的组件(Vulnerable and Outdated Components)榜上有名。
不存在完美的编程语言,并且任何系统和应用的安全性都不是绝对的,Java Web程序的安全问题也是不可避免的,一些看似合理的功能逻辑却常常有可能存在着可被非法利用的漏洞,如Java的反序列化机制所引发的反序列化漏洞问题。
Java序列化和反序列化的初衷是为了有效实现对象在不同系统和进程之间的数据快速传输和方便存储[3]。为了满足java的序列化与反序列化的需求,在开发过程中通常需要使用到一些公共库,Apache Commons Collections 便是其中之一,该库不仅能够提供数据结构类型还包含了许多jar工具和接口。作为Apache开源项目的重要组件,Commons Collections已被广泛应用于各种Java Web应用的开发,据国家网络安全漏洞共享平台(CNVD)收录的Apache Commons Components Invoker Transformer反序列化任意代码执行高危漏洞(编号为CNVD-2015-07556)显示[4],代码设计或自带缺陷以及陈旧的组件将导致反序列化漏洞的存在,相关漏洞影响到了WebLogic、JBoss、Jenkins、WebSphere、OpenNMS等诸多Web应用和中间件。虽然Apache Commons Collections不断推陈出新,各大应用厂商也陆续发布升级补丁,但网络上还有很多Web站点和应用受到此漏洞的影响。
反序列化漏洞的本质是一种对象化注入漏洞,常常会被非法利用于代码复用攻击。分析Java Web序列化与反序列化的机制:首先序列化的过程是将对象转换成包含该对象的数据、类型等状态信息的字节序列用来进行数据的传输和存储,即通过ObjectOutputStream类的writeObject()方法对参数指定的对象序列化的数据写入到一个目标输出流之中。而后再利用反序列化这一逆过程,通过ObjectInputStream类的readObject()方法从一个源输入流中读取字节序列,并还原转换成为对象。通常,序列化后的对象可以通过反序列化方法在其他平台上利用对象的类型信息、数据、数据类型在内存中完成对象的新建。反序列化适用于Java Web下的大量应用场景,例如在Web服务器的物理硬盘中暂存用户访问的Session对象,保证用户信息不丢失,并有效腾挪服务器的内存资源来提高相关业务的可用性。
Java的序列化和反序列化设计的初衷并不存在问题,但如果存在代码设计缺陷,例如ObjectInputStream类的readObject()方法内的代码逻辑缺陷,则可能会存在反序列化漏洞。当被非法利用时,恶意构造的数据被植入到了反序列化进程,而后又由于类库未对序列化对象作相关的检查或限制,就将导致没有经过检测而产生的对象执行任意非法的RCE(Remote command/Code execute,远程命令/代码执行)代码,从而出现各种危害系统的操作被执行的情况。Apache Commons Collections组件漏洞的问题主要出现在org.apache.commons.collections.Transformer接口上,产生的根本原因在于对数据缺乏检测与过滤的机制,这就造成在进行反序列化操作时不会调用构造函数对生成对象的类型进行检测与校验的结果。并且组件中的特殊接口类InvokerTransformer可以调用Java的反射机制来执行一些函数函数或特定的命令以及SQL语句等。Java反序列化漏洞广泛存在于Java Web中,只要包含了Commons Collections的jar包,无论源码是否使用到了组件中的类,都存在远程代码被执行的风险。
以Commons Collections6为例(以下简称CC6)对Java Web反序列化利用链进行分析:CC6的命令执行载体为org.apache.commons. collections. functors.ChainedTransformer函数,反序列化对象为HashMap,反序列化集合载体为HashSet。如图1所示为漏洞的利用链,CC6利用链是利用HashSet来触发LazyMap的get方法,即HashSet.readObject()完成元素的反序列化后,会调用HashMap.put()将结果放进去,当通过 TiedMapEntry.hashCode()计算hash时,会调用getValue()触发LazyMap.get(),从而导致相关目标命令的执行。
图1 CC6的利用链
由于反序列化漏洞都需要用到程序中存在的已知类,因此查找存在安全漏洞的类很重要。反序列化代码审计需要从Java Web应用的本身以及应用与Web服务器之间的组件所涉及到的序列化对象入手检查,要定位出应用的源码中哪些是调用了反序列化函数的代码。同时,序列化内容都需要遵从序列化协议,分析反序列数据特征,Java序列化后的数据包开头部分通常是一个双字节的魔法数字0xACED。定位的过程可以使用Wireshark等工具对数据包进行分析,查看数据包是否包含以“AC ED 00 05”标记开头的序列化数据,再进一步审计目标应用Class Path中的Apache Commons Collections库相关代码[5]。
通常一个完整的漏洞包括已知原生反序列化源方法(source)和触发漏洞的目标方法(sink)的链式调用。source作为引入点可能会获取外部不可信的输入数据并返回,再通过递归检查寻找一条调用链(gadget)进行传播,最后在目标sink触发执行。代码审计分析的过程需要判断在sources和sinks之间是否存在一条可达的利用路径。Java Web反序列化漏洞利用的常常是在目标Web应用中找到一个可接收外部输入的序列化对象的反序列化接收点,再利用反序列化的payload针对目标应用进行注入,来触发ObjectInputStream 的反序列化操作,并通过反射调用RunTime.exec从而实现对漏洞的利用。因此Java Web的代码审计要先检查找到HashSet.readObject、Fastjson等反序列化入口,再探寻调用链,从而找到LazyMap.get()、Runtime.exec等触发漏洞的目标方法。
如表1和表2所示为序列化和反序列化函数的使用实例:从“user.bin”二进制文件中读取数据,并对其进行反序列化。在该实例中如果未设置检查和过滤机制,并且“user.bin”的文件内容是用户可控的,即用户可以修改内容的输入,那么可能会存在反序列化漏洞。
表1 序列化serialization函数使用实例
表2 反序列化unserialization函数使用实例
反序列化的一系列的变换操作,是通过定义一个ChainedTransformer来实现的。对commons collections中利用的类进行分析,在该组件中有一个Transformer接口。通过传入一个Transformer数组即可定义一个ChainedTransformer来实现一系列的变换操作。表3为漏洞利用代码,commons collections组件中InvokerTransformer是实现Transformer接口的类。RunTime类常用于调用外部程序,将可能被用于执行RCE。要找到调用了Transform方法的节点,可以通过ReadObject入手,再顺着利用链一步步分析定位。
表3 CC6的利用示例
网络安全漏洞难以避免,采用安全技术手段也无法将其完全清除,但是使用合理有效的防范方法能够降低网络安全风险,保证Web互联网程序更加正常和稳定的运行[6]。针对Java Web的网络安全问题,应当在站点和程序的规划设计阶段就将各种可能产生漏洞的因素考虑进去,在开发阶段和后期维护阶段还要不断进行代码审计、测试验证。做到以下几点来保证客户端和服务端的安全:
在站点程序设计、开发建设阶段和后期的维护过程都需要分析Java Web反序列化漏洞具体形成原因,有针对性地关注具体应用中的可序列化类,开展相关应用的安全性检查,通过代码审计和行为分析等手段发现站点漏洞所在的靶点[7]。需要关注相关厂家官方的补丁发布情况及时进行升级,定期对站点的基础库进行安全性检查。定期采用白盒与黑盒相结合的方式进行站点漏洞测试,对程序执行动态检测,通过动态检测对运行的程序进行网络安全测试,再对比静态分析结果可以进一步确认漏洞点,并且还可以发现其他潜在的威胁漏洞。加强对Runtime.exec等相关代码的检测,并对存在风险的应用及时进行代码安全加固。
做好文件、接口、协议等细节问题的优化,在保证功能和业务正常运行的前提下,删除存在风险的文件、停用非必要的接口,如禁止JVM执行外部命令和程序。关注敏感信息的加密,确保对象的成员变量符合正确的约束条件。对于HTTP请求中的cookies、Parameters和RMI远程调用接口、JMX扩展接口等基于序列化操作的接口及协议开展监测。合理并有效使用加密功能进行加密存储。设计严格的访问和认证的机制,利用数字签名、数字证书等方式完成身份认证,防止程序代码的泄露及不合法的程序复制。反序列化漏洞被利用时,通常攻击者会通过如文件上传、SQL注入、XSS利用等手段上传一个webshell来进一步入侵系统,从而达到远程执行命令、操作敏感信息的目的,因此要加强系统入侵检测。另外,还可以利用蜜罐系统,来侦测异常行为和隐藏真实的程序系统。
全球的网络安全技术一直在不断的变化和发展,网络攻击的发起者与系统开发者以及网络安全防御者也始终处于彼此博弈的状态之中。作为系统开发和维护人员不能仅仅将目光局限在系统的开发和功能需求的实现上,在网络安全已上升到国家高度的背景下,更需要加强网络安全知识的学习,及时关注全球网络安全态势、了解最新的网络安全事件和主要的网络攻击手段,不断精进开发技术。围绕Java Web站点的开发工作,从系统功能、网络安全等多角度来分析、完善和提升相关程序的安全防御性能,保障网络安全。
信息化时代,互联网数据呈现爆炸式的增长,随着《个人信息保护法》的施行,对于互联网个人信息的保护也上升到了一个新的高度。在信息全球化发展的背景下,网络黑客无处不在,因应用系统存在漏洞,而造成个人信息泄露或经济损失等事件层出不穷。反序列化的漏洞存在于许多编程语言之中,是近些年出现较为频繁的高危漏洞之一,也成为应用安全研究的热点之一。在Java Web中由于 Apache Commons Collections库组件的广泛使用加上该组件未能对用户数据做出有效过滤,导致攻击者可以在对象属性中构造恶意代码进而控制服务器[8]。基于此,本文对序列化和反序列化的过程展开分析,通过对Java Web反序列化漏洞形成原因及利用原理的研究,提出反序列代码审计和安全检查的的基本思路以及漏洞的防范方法,以帮助开发和运维人员提升相关程序的安全性,提高业务系统的防护水平。