一个get请求拿下weblogic-rce---CVE-2020–14882分析

TL;DR

  • 注: 本文原作者为一个越南人,图片需要挂梯子。

这是一年前的事了,去年的这个时候,和GadgetInspector在一起,发现了CVE-2020-2555,并向ZDI报告。

如果你想读,就把它拉下来。

那一天,每次Oracle CPU发布时,都在等着看自己的bug是否已经被修复,或者是否已经公开了。

当时有点天真,不认为找到一个新的反序列化链是一个新的bug,它被打了9.8/10。

在CVE-2020-2883和2884之后,厌倦了寻找gadget chain,并在weblogic上重复一个T3入口。

在这段时间里,这也是一个设计时期,所以把所有的工作都交给了同事

在CVE-2020-2555之后,许多人发现连贯性图书馆有很多有趣的东西可以利用。从那时起,每次Oracle CPU的发布都是weblogic T3的一堆新设备链的升级,有时甚至会有一个与另一个数字匹配的cve:

事实上,找到gadget chain很有趣,但只有在entrypoint上才有趣。

拦截很简单,只要禁用T3/IIOP,游戏就结束了 ¯_(ツ)_/¯.

.

.

.

直到10月21日,Oracle CPU Oct才带来了一些惊喜。
看了weblogic一眼,就像往常一样:T3, IIOP…
但这里有一个新观点:

CVE-2020–14882:

  • CVSS: 9.8/10
  • 协议: HTTP
  • 影响所有版本

这是一个中国年轻人报道的。

在weblogic之前,T3 Deser几乎没有bug,只是修补,然后绕过,然后绕过…
好吧,得把帕奇拉回来…

选择的版本是Weblogic 12.2.1.3,这个版本的10月号是3191038,7月号是31535411。

Diff 2版本的补丁,在组件控制台发现,1个文件类在新版本中被修改了:

1
HandleFactory.class và MBeanUtilsInitSingleFileServlet.class

The Sink

继续查看这些文件中的更改,首先是

  • com.bea.console.handles.HandleFactory.getHandle():

简单地看一下就可以看出区别:

这个HandleFactory的补丁重新测试了handleClass的类型,只允许类是com.bea.console.handle的实例。句柄通过:

根据HandleFactory.getHandle方法的流,接收的数据是一个字符串,经过多次处理后,它会到达Class.forName来加载这个类。然后在类中查找一个构造函数,它有一个arg作为字符串来创建一个新的实例。

解释流程如下:

可以通过一个后续的GET请求触发此入口点。

1
http://<target>/console/console.portal?_nfpb=true&_pageLabel=HomePage1&handle=java.lang.String("ahihi")

在HandleFactory.getHandle()中设置断点,handleConstructor的值,args都由输入控制。

这也是CVE-2020-14882中RCE的位置

为了利用这个entrypoint,需要找到一个包含公共构造函数的类,它有一个参数=字符串。

当调试到这里时,打算把它放在那里,但是sink知道,直到找到pre-auth的
entrypoint,才会运行工具来找到类触发bug!

The Source

对于HandleFactory.getHandle()来说,这是bug的接收器,所以另一个补丁的类—MBeanUtilsInitSingleFileServlet可能是bug的来源!

修复后的类将增加一个字段“IllegalUrl”如下:

1
private static final String[] IllegalUrl = new String[]{";", "%252E%252E", "%2E%2E", "..", "%3C", "%3E", "<", ">"};

这个servlet的处理代码只是检查url是否存在字符串。“如果有,就拒绝这个请求!”一定有什么问题。这就是它被过滤的原因。

此外,要到达MBeanUtilsInitSingleFileServlet.service(),还需要进一步的步骤 ¯\ (ツ)/¯。

“那么后认证系统,怎么可能是9.8/10呢?” 想了想,继续摸索。

.

.

也许就像java上的servlet一样,weblogic也有webapp文件夹,对于控制台来说是这样的:

1
“\Middleware\Oracle_Home\wlserver\server\lib\consoleapp\webapp”

当然,还有web配置文件。和往常一样:

根据网络配置。xml, MBeanUtilsInitSingleFileServlet是AppManagerServlet的一个init-param,

AppManagerServlet映射到以下url模式:

  • /appmanager/*

  • *.portlet

  • *.portion

  • *.portal

包括“/console/console.portal”,可以在登录控制台后立即看到:

在挖掘了一段时间后,找到了这个逻辑类,决定了这个请求应该是正确的还是错误的,在weblogic中是12.2.1.3。

  • weblogic.servlet.security.internal.WebAppSecurity.checkAccess():

在这里,WebAppSecurityWLS.getConstraint将从当前的请求中获取约束,每个约束包含以下信息:

在这种情况下,如果该约束具有标记unlimit=true,则该请求将被指定为unauth,否则将返回到登录页面。

继续F7进入WebAppSecurityWLS.getConstraint(),在这里可以得到所有的约束,在此基础上可以确定哪些url将被免检认证,列出免检的url模式如下:

  • /bea-helpsets/*

  • /framework/skins/wlsconsole/images/*

  • /framework/skins/wlsconsole/css/*

  • /framework/skeletons/wlsconsole/js/*

  • /framework/skeletons/wlsconsole/css/*

  • /css/*

  • /common/*

  • /images/*

根据这些模式的列表,并尝试添加以前的绕过url的技巧,例如:“..;/, /#/../”,但还是没有发现任何有趣的东西……

被困在这里,什么都想不出来!

在推特上,没有人发过PoC,所以抛开所有的一切,从兄弟那里得到提示!(允许匿名)

是啊,然后给了我一个PoC的照片,真是太好了,太慷慨了!

其中一个是HandleFactory,另一个是url: “/console/console%2E%2E.portal” A.K.A “/console/console…portal”

试了,但不是很好,url是“/console/console%2E%2E.portal”的约束返回仍然是“unrestricted=false”,这意味着仍然需要引用:

另一个年轻人,他是这么说的:

最初发送的PoC只是一个提示,为了绕过它,需要一些技巧,滥用一些资源,然后就可以变成unauth RCE了!

回到原来的位置,

在那之后,很沮丧,不得不让更多的人来做研究。这是玩CTF的那一天的倒计时,每次都要把想法传达给队友,分享他们的想法,而且效率总是比一个人玩要好得多……

这个群体只有三个人:我. PeterJson和@Quynh Le。专门研究java上的bug

然后德国年轻人发现了需要看到的东西,一个url可以触发MBeanUtilsInitSingleFileServlet,而不是:

1
/console/css/changemgmt.portal

当看到这个请求时,有点惊讶,但是测试确实可以触发它。

在调试器中再次检查,约束映射到“/css/”应该是不受限制的,

更令人惊讶的是,当再次检查servlet句柄时,这个入口点显示AsyncInitServlet正在处理,而不是FileServlet!

与普通的css请求相比,FileServlet将处理这个请求。

现在认为FileServlet有什么神奇的地方,所以一直在寻找它的错误。

但事实上,魔法并不存在……它存在于web.xml中!

查看web.xml的静态资源声明:

  • Url模式”/common/*”由JSPCServlet处理

  • url模式“/framework/*”由FileServlet处理

然而,对于url模式“/images/*”和“/css/*”,没有声明任何servlet将处理它们!

所以当请求url “/console/css/aaasdasdasd.portal”时。AppManagerServlet将处理这个请求。

总结如下:

  • “/css/*” 作为绕过认证
  • “*.portal” 作为触发器

在这里,可以访问MBeanUtilsInitSingleFileServlet.service(),而不需要其他任何东西。

然而,由于url模式与portlet不匹配,请求仍然无法访问HandleFactory.getHandle()!

就在那时,根据那个中国年轻人最初的暗示:

那就打个信号。”..”在这里,是提示使用的意思。”..”双编码触发bug,再试一次,这是真的:

到目前为止,可以写完整的PoC,但进一步分析一下,

在经过MBeanUtilsInitSingleFileServlet.service()并在uiservletintern.gettree()之后,url模式再次被提取并再次解码url:

解码:

这解释了为什么双编码的url可以绕过最初的标准化url处理,但仍然可以使servlet处理正确!

触发RCE

在PoC中,这个年轻人使用com.bea.core.repackage.springframework.context.support.filesystemxmlapplicationcontext()来触发RCE,但是这个链需要有一个outbound。

我是一个完美主义者,而且实际上有很多服务器拦截出站,所以决定找到一个新的,不需要出站,一击拿下RCE!

在liferay的日子里,经常编辑GadgetInspector工具,用于跟踪代码。

到目前为止,它一直在工作,把它拉出来,修改代码中的一些条件,以适应新的上下文,在5分钟的范围内,有很多结果。

其中有一个很有潜力的利用链:

这个链在从构造函数接收到arg后,将调用ShellSession.exec()来执行这个arg:

exec()还有很长的路要走,总结如下:

所以可以执行任意的MVEL表达式。

事实上,直到PoC在这个新连锁反应上取得成功,还是很惊讶,不相信有什么东西可以利用。不确定这是不是代码中留下的后门。

在把所有这些东西结合在一起,再加一点修饰之后,写了一个PoC执行命令,然后得到了一个响应。PoC最终由德国而不是自己完成,

  • poc视频

  • poc如下:

1
2
3
4
http://x.x.x.x:7001/console/images/%252E%252E%252Fconsole.portal

POST:
_nfpb=true&_pageLabel=&handle=http://com.tangosol.coherence.mvel2.sh.ShellSession(%22java.lang.Runtime.getRuntime().exec(%27calc.exe%27);%22)

MVP to @voidfyoo

感谢中国青年, PeterJson和@Quynh

谢谢大家的阅读!

Jang

翻译自,略有修改(尊重原作者劳动输出)

  • Weblogic RCE by only one GET request — CVE-2020–14882 Analysis