在Oracle官方发布的2020年10月关键补丁更新公告CPU中,包含一个存在于Weblogic Console中的高危远程代码执行漏洞CVE-2020-14882。该漏洞与CVE-2020-14883权限绕过漏洞配合,可以使得攻击者在未经身份验证的情况下执行任意代码并接管WebLogic Server Console。
在这篇文章中,我们首先来看看CVE-2020-14882代码执行漏洞。而后续下一篇文章,我将深入的分析下CVE-2020-14883权限绕过漏洞,并说明二者是如何配合使用的。
首先我们来研究下Weblogic Console HTTP协议远程代码执行漏洞。这个漏洞影响范围广:影响范围包含了Oracle Weblogic Server10.3.6.0.0、12.1.3.0.0、12.2.1.3.0、12.2.1.4.0、14.1.1.0.0这几个版本。
网上关于这个漏洞的分析报告,多数是以Weblogic12版本展开的,10版本与12版本下的漏洞触发点相同点,但利用链不同。所以我在这里就拿Weblogic 10.3.6.0.0版本,对这个漏洞进行分析。
按照惯例,我们从CVE-2020-14882相关漏洞细节入手,看看能不能还原出poc。
关于CVE-2020-14882的漏洞详情如下:
“结合 CVE-2020-14883 漏洞,远程攻击者可以构造特殊的 HTTP请求,在未经身份验证的情况下接管 WebLogic Server Console ,并在 WebLogic Server
Console 执行任意代码。”
从描述上来看,CVE-2020-14883是权限绕过漏洞,而CVE-2020-14882是后台代码执行漏洞。我们要是想利用CVE-2020-14882。因此我们可以得到如下两个信息:
攻击是通过向后台发送HTTP请求实现的
CVE-2020-14882须要有后台管理员权限
在此之外,我们还通过一些披露可以知道:
本次漏洞实际执行点位于com/bea/console/handles/HandleFactory.class中的getHandle方法下图红框处
下面我们就试着还原一下这个漏洞:
首先我们先以后台管理员的身份,访问一下后台地址
http://localhost:7001/console/console.portal?_nfpb=true&_pageLabel=HomePage1
在我们的请求发送到Weblogic服务器后,程序会执行到com/bea/console/utils/BreadcrumbBacking.class,并调用其中init方法,见下图
BreadcrumbBacking翻译过来大概是面包屑导航支持的意思。从笔者的理解来看,上图这里应该是Weblogic用来解析传入的url的作用。
我们继续看init方法
public void init(HttpServletRequest req, HttpServletResponse res) { if (req.getParameter(NO_BC) == null) { ... String handleStr = this.findFirstHandle(req); if (this.handle == null && handleStr != null && !handleStr.equals("")) { try { this.handle = HandleFactory.getHandle(handleStr); String name = this.handle.getDisplayName(); req.getSession().setAttribute(BREADCRUMB_CONTEXT_VALUE, name); } catch (Exception var6) { } } this.dispatchedValue = (BCValue)req.getSession().getAttribute(DISPATCHED_BREADCRUMB); } }
上面节选了init方法中的一块代码片段,为什么要节选这个片段呢?原因很简单:这块代码段里多次出现了”Handle”字眼,这意味着代码涉及到从请求(req)中获取与Handle相关的操作。而代码中也存在着”HandleFactory.getHandle(handleStr);”
回顾上文,本次漏洞实际执行点不正是位于com/bea/console/handles/HandleFactory.class中的getHandle方法中吗?
看来漏洞入口被我们找到了,下面来看看怎么建立一条从url到漏洞入口的道路,重点分析如下代码
从上图可见,如果想执行69行的this.handle = HandleFactory.getHandle(handleStr);进入漏洞触发点,首先要满足67行的this.handle == null && handleStr != null && !handleStr.equals("")条件
而String类型的handleStr变量是从66行处的this.findFirstHandle(req)获取到的看看weblogic如何从请求中获取handle
public String findFirstHandle(HttpServletRequest request) { String handle = null; Enumeration parms = request.getParameterNames(); while(parms.hasMoreElements()) { String parmName = (String)parms.nextElement(); String parm = request.getParameter(parmName); if (LOG.isDebugEnabled()) { LOG.debug("Looking at parameters = " + parmName); if (parmName.toLowerCase().indexOf("password") == -1 && parm.toLowerCase().indexOf("password") == -1) { LOG.debug("Looking at parm value = " + parm); } else { LOG.debug("Looking at parm value = ************"); } } if (this.currentUrl.getParameter(parmName) == null) { this.currentUrl.addParameter(parmName, parm); } if (parmName.indexOf(REQUEST_CONTEXT_VALUE) != -1) { handle = parm; } } return handle; }
findFirstHandle方法将会遍历请求中所有参数名以及参数值,随后通过如下代码判断参数名是否为”
handle”,代码如下:
if (parmName.indexOf(REQUEST_CONTEXT_VALUE) != -1) { handle = parm; }
这里的REQUEST_CONTEXT_VALUE值为”handle”,见下图:
当请求参数中有名为handle的参数时,findFirstHandle会将该参数的值进行返回。我们动态调试一下,看看分析的对不对,构造如下url:
http://localhost:7001/console/console.portal?_nfpb=true&_pageLabel=HomePage1&handle=熊本熊本熊
可见findFirstHandle将”熊本熊本熊”字符串返回
回到init方法中,findFirstHandle将返回的url中handle参数值传递给handleStr变量,见下图66行
handleStr变量进入67行if分支,并传递给漏洞执行点getHandle方法中
getHandle方法中代码如下:
public static Handle getHandle(String serializedObjectID) { if (StringUtils.isEmptyString(serializedObjectID)) { throw new InvalidParameterException("No serialized object string specified"); } else { serializedObjectID = serializedObjectID.replace('+', ' '); String serialized = HttpParsing.unescape(serializedObjectID, "UTF-8"); int open = serialized.indexOf(40); if (open < 1) { throw new InvalidParameterException("Syntax error parsing serializedObjectID string: " + serialized); } else { String className = serialized.substring(0, open); String objectIdentifier = serialized.substring(open + 2, serialized.length() - 2); try { Class handleClass = Class.forName(className); Object[] args = new Object[]{objectIdentifier}; Constructor handleConstructor = handleClass.getConstructor(String.class); return (Handle)handleConstructor.newInstance(args); } catch (ClassNotFoundException var8) { throw new InvalidParameterException("No handle class found for type: " + className); } catch (Exception var9) { throw new InvalidParameterException("Unable to instanciate handle type: " + className, var9); } } } }
其实这里的内容很简单,让我们换一个poc,大家就会很清晰的了解getHandle的功能以及为什么可以造成代码执行漏洞。我们这里更换的poc如下:
此时传入getHandle的变量为String类型的字符串:java.lang.String("kumamon.fun"),见下图
接着getHandle方法判断字符串中左括号“(”的位置。(左括号ascii码值为40),见下图
紧接着,通过左括号位置,获取className以及objectIdentifier
这里className为字符串:java.lang.String而objectIdentifier为字符串kumamon.fun
接着程序做了三件事情:
通过className获取对应的Class对象
将String类型的objectIdentifier存入 Object[]数组中
获取该类的带String类型参数的构造方法
最后,程序调用该带String类型参数的构造方法,实例化一个对象
漏洞触发点到此已经分析结束了。回顾上文,当我们构造一个如下url时:
http://localhost:7001/console/console.portal?_nfpb=true&_pageLabel=HomePage1&handle=a("b")
Weblogic将会调用a方法的带String类型参数的构造方法,并将b字符串传入执行
那么我们怎么样才能找到这样一个利用链呢?需要满足恶意类中存在带String类型参数的构造方法、且该构造方法中存在着可以利用的地方,使得我们构造的字符串传入后可以顺利的执行。
不得不说,Weblogic的漏洞往往有着关联,比如说CVE-2019-2725,这个漏洞当时遇到的payload的条件与这次的几乎一样,在CVE-2019-2725漏洞中,廖新喜给出了一个无视jdk版本限制的利用链:
com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext
http://xxlegend.com/2019/04/30/CVE-2019-2725%E5%88%86%E6%9E%90/
但廖大神并没有给出具体的利用方式。详细的Poc构造以及利用可以参考我的这篇文章:
https://kumamon.fun/CVE-2020-14882/
里面利用到的利用链即为:
com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext 这条
于此同时,com.bea.core.repackaged.springframework.context.support.ClassPathXmlApplicationContext
与FileSystemXmlApplicationContext相同,都是继承了AbstractXmlApplicationContext类,因此ClassPathXmlApplicationContext这条也是可用的
本来想将CVE-2020-14882、CVE-2020-14883放到一篇文章中来介绍,但奈何CVE-2020-14883这个漏洞中内容也挺多的,放在一块文章太长了。写的多不如写的细,因此把他们分开成为两个文章。
后续关于FileSystemXmlApplicationContext这条利用链是如何构造的,我也想写一篇文章详细的谈谈,希望大家喜欢。