一个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 | http://x.x.x.x:7001/console/images/%252E%252E%252Fconsole.portal |
MVP to @voidfyoo
感谢中国青年, PeterJson和@Quynh
谢谢大家的阅读!
Jang
翻译自,略有修改(尊重原作者劳动输出)
- Weblogic RCE by only one GET request — CVE-2020–14882 Analysis