CVE-2022-22954 VmwareONEAccess漏洞复现

admin 2022年4月22日22:10:50评论374 views字数 8347阅读27分49秒阅读模式

星期五实验室

阅读须知


星期五实验室的技术文章仅供参考,此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息造成的直接或间接后果和损失,均由使用者本人负责。


星期五实验室拥有对此文章的修改、删除和解释权限,如转载或传播此文章,需保证文章的完整性,未经授权,不得用于其他。




01

简介

CVE-2022-22954 VmwareONEAccess漏洞复现

VMware Workspace ONE Access是VMware公司开发的一款智能驱动型数字化工作空间平台,旨在通过多因素身份验证、条件访问和单点登录,让工作人员更快地访问 SaaS、Web 和本机移动应用程序。

VMware Workspace ONE Access及Identity Manager存在一个由服务器模板注入导致的远程命令执行漏洞,未经身份验证的攻击者可以利用此漏洞进行远程任意代码执行。


受影响的版本:

  • VMware WorkSpace ONE Access Appliance:Version 21.08.01、21.08.0.0、21.10.0.1、20.10.0.0

  • VMware Identity Manager Appliance:Version 3.3.6、3.3.5、3.3.4、3.3.3

  • VMware Realize Automation:Version 7.6



02

环境搭建

CVE-2022-22954 VmwareONEAccess漏洞复现

在西普数据中下载21.08.0.0的ova
http://file.xipudata.com/?dir=/2_software/Vmware/Horizon8

然后在Vmware中打开ova文件,注意这里默认打开使用的桥接方式,因为最开始需要使用网络下载东西,因此这里改成nat方式,在打开ova文件导入虚拟时,需设置Networking Properties的FQDN,如下图:

CVE-2022-22954 VmwareONEAccess漏洞复现


文件完全打开之后,会提示需要登录,这里的初始密码是不知道的,可以参考这篇文章修改。
http://www.360doc.com/content/20/1028/12/19798709_942786717.shtml

接下来修改/etc/ssh/sshd_config,配置允许root远程登录,方便调试(设置PertmitRootLogin为yes,然后重启ssh即可)
然后访问https://ip测试是否能够成功访问,这里如果报错可以不用管,只要能够正常测试即可。

小结一下环境搭建几个需要注意的点:
  • 网络设置为nat模式
  • 打开ova文件时,设置FQDN
  • 第一次打开虚拟机需要自行修改密码
  • 成功打开虚拟机后,可以设置允许ssh远程登录

  • 虚拟机默认开启防火墙,为了方便后面的远程调试,可以把防火墙关闭(systemctl stop iptables.service)




03

原理分析

CVE-2022-22954 VmwareONEAccess漏洞复现

漏洞确定
这里从官方给出的修复脚本来逆向分析,脚本下载地址
https://vmware-gs.my.salesforce.com/sfc/p/#f40000003u6t/a/5G0000002Nla/9COLfZG7uu8iDjLoZTis5Ltzmd1H87iToXs9uLZF8IU

官方给出的脚本首先修改web.xml文件,然后删除/opt/vmware/horizon/workspace/webapps/catalog-portal/WEB-INF/lib/endusercatalog-ui-1.0-SNAPSHOT-classes.jar中的templates/customError.ftl,不出意外,漏洞基本上就是来自这里,因此将虚拟机中的catalog-portal文件夹下载到本地分析。


CVE-2022-22954 VmwareONEAccess漏洞复现

发现了eval,因此这里只要errorObj可控,即可造成服务端模板注入(Server-Side Template Injection,简称SSTI),因此接下来主要关注可以跳转到customError.ftl的路由。为了方便,这里需要使用IDEA开启远程调试。



IDEA开启远程调试

1.打开要调试的源码文件并设置dependencies

获取catalog-portal文件源码后,使用IDEA打开,选择File->Project Structure(Windows下的快捷键ctrl + alt + shift + s),然后在如下图添加WEB-INF下lib和classes下的jar包,如下图:

CVE-2022-22954 VmwareONEAccess漏洞复现


2.IDEA添加一个远程调试

首先选择Edit Configurations,如下图:

CVE-2022-22954 VmwareONEAccess漏洞复现


然后新增一个remote jvm debug,其中的host需要设置为远程机器的ip,port使用默认的5005,其他均使用默认的配置即可,如下图:

CVE-2022-22954 VmwareONEAccess漏洞复现

3.远程服务器设置启动选项

在JAVA_OPTS中添加如下内容:
vim /opt/vmware/horizon/workspace/bin/setenv.sh-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

然后重启服务即可,可以通过lsof -i:5005查看是否成功开启调试端口,注意这里为了方便,关闭了目标服务器的防火墙。
systemctl restart horizon-workspace.service    #重启服务systemctl stop iptables.service                #关闭防火墙


4.IDEA启动调试
在IDEA中启动调试,出现如下字样,表示调试开启成功。

CVE-2022-22954 VmwareONEAccess漏洞复现



漏洞测试

前面知道漏洞来自于customError,因此这里需要找到customError的路由即可,在
com.vmware.endusercatalog.ui.web.UiErrorController#handleGenericError中会返回到customError的路由,并且errorObj的取值来自于errorMessage
  private String handleGenericError(HttpServletRequest request, HttpServletResponse response, Map<String, Object> model, boolean isAWJade, boolean garnetAndAbove, String excpClass, int errorCode, String errorMessage) {        String localizedMessageHeader = this.messages.getLocalizedErrorMessage("errorPage.errorHeading", request.getLocale(), (Object[])null);        String localizedMessage = this.messages.getLocalizedErrorMessage(this.getLocalizedMessageKey(excpClass), request.getLocale(), (Object[])null);        if (errorCode != -1) {            response.setStatus(errorCode);        }
       model.put("errorObj", errorMessage);        model.put("messageHeader", localizedMessageHeader);        model.put("genericErrorMsg", localizedMessage);        model.put("contextPath", request.getContextPath());        ....        return "customError";    }


继续往上走,直到找到调用该方法的路由为止,往上走发现有两个方法调用了上述方法,分别是:
com.vmware.endusercatalog.ui.web.UiErrorController#getErrorPagecom.vmware.endusercatalog.ui.web.UiErrorController#handleUnauthorizedError


因此这里可能有两条路径,这里首先分析getErrorPage,可以发现在UiErrorController中的两条路由均会走到getErrorPage方法,因此这里为
  @RequestMapping({"/ui/view/error"})    public String sendError(HttpServletRequest request, HttpServletResponse response, Map<String, Object> model) {        int errorCode = (Integer)request.getAttribute("javax.servlet.error.status_code");        String errorMessage = (String)request.getAttribute("javax.servlet.error.message");        this.logFor(errorCode, "Error status code: {}, error message: {}", errorCode, errorMessage);        String exClass = request.getAttribute("javax.servlet.error.exception_type") instanceof Class ? ((Class)request.getAttribute("javax.servlet.error.exception_type")).getCanonicalName() : "";        return this.getErrorPage(request, response, model, errorCode, errorMessage, exClass);    }
   @RequestMapping({"/error"})    public String sendUnhandledError(HttpServletRequest request, HttpServletResponse response, Map<String, Object> model) {        int errorCode = request.getAttribute("javax.servlet.error.status_code") == null ? -1 : (Integer)request.getAttribute("javax.servlet.error.status_code");        String errorMessage = request.getAttribute("javax.servlet.error.message") == null ? "" : (String)request.getAttribute("javax.servlet.error.message");        String exClass = request.getAttribute("javax.servlet.error.exception_type") instanceof Class ? ((Class)request.getAttribute("javax.servlet.error.exception_type")).getCanonicalName() : "";        String errorObj = "{"code":"" + errorCode + "", "message":"" + errorMessage + ""}";        return this.getErrorPage(request, response, model, errorCode, errorObj, exClass);    }
 private String getErrorPage(HttpServletRequest request, HttpServletResponse response, Map<String, Object> model, int errorCode, String errorMessage, String exClass) {        ...        String errorPage = (String)Optional.of(errorCode).filter(this::isErrorTypeUnauthorized).map((errCd) -> {            return this.handleUnauthorizedError(request, response, model, isAWJade, garnetAndAbove, exClass, errCd, errorMessage);        }).orElseGet(() -> {            return this.handleGenericError(request, response, model, isAWJade, garnetAndAbove, exClass, errorCode, errorMessage);        });        return StringUtils.hasText(errorPage) ? errorPage : null;    }
 private String handleUnauthorizedError(HttpServletRequest request, HttpServletResponse response, Map<String, Object> model, boolean isAWJade, boolean garnetAndAbove, String excpClass, int errorCode, String errorMessage) {        String uiRequestId = Objects.nonNull(request.getAttribute("UI_REQUEST")) ? ((UiRequest)request.getAttribute("UI_REQUEST")).getRequestId() : null;        if (isAWJade) {            ...        } else {            return (String)Optional.ofNullable(excpClass).filter(this::isSpecificUnauthError).map((excepClass) -> {                return this.handleGenericError(request, response, model, isAWJade, garnetAndAbove, excpClass, errorCode, errorMessage);            }).orElseGet(() -> {                ...                if (this.isMdmOnlyUnauthorizedAccessError(request, excpClass)) {                    return this.handleGenericError(request, response, model, isAWJade, garnetAndAbove, excpClass, errorCode, errorMessage);                } else {                    ...                }            });        }    }

但是直接访问路由,无法控制errorObj(在/ui/view/error中是errorMessage),因此还需要找到其他调用上述两条路由且能控制errorObj的路由。在com.vmware.endusercatalog.ui.web.UiApplicationExceptionResolver#resolveException中会返回/ui/view/error,且该类可以控制errorObj,如下图:



CVE-2022-22954 VmwareONEAccess漏洞复现


该类控制着如下三个类抛出的异常
CVE-2022-22954 VmwareONEAccess漏洞复现


因此可以尝试从以上三个类查找是否能够抛出异常且能控制errorObj的方法,在com.vmware.endusercatalog.ui.web.WorkspaceOauth2CodeVerificationController#authorizeError发现可以抛出异常,但是在最终构造的时候发现无法走到handleGenericError,因此继续审计。
这里可以尝试看看在UiApplication中引入的其他包:

CVE-2022-22954 VmwareONEAccess漏洞复现


以及WebConfig类中添加的interceptor,如下:

CVE-2022-22954 VmwareONEAccess漏洞复现

com.vmware.endusercatalog.auth.interceptor.AuthContextPopulationInterceptor是一个Interceptor,preHandle会构造一个authContextBuilder,在它的build方法中,最终会抛出一个异常。

CVE-2022-22954 VmwareONEAccess漏洞复现


CVE-2022-22954 VmwareONEAccess漏洞复现


其中的isValidRequest如下:

CVE-2022-22954 VmwareONEAccess漏洞复现


这里只需要让deviceId和deviceType其中一个为空,另一个不为空,即可抛出异常,且其中的参数均是可控的。
因此这里只需要访问com.vmware.endusercatalog.ui.config.WebConfig#UI_URLS中的任意一个URL,满足抛出异常的要求,设置其中的一个字段即可控制最终的errorObj,最终调试得到的errorObj如下图所示:

CVE-2022-22954 VmwareONEAccess漏洞复现


到这里,poc已经可以确定了,只需要访问
com.vmware.endusercatalog.ui.config.WebConfig#UI_URLS中的URL,保证满足抛出异常的条件,即可构造SSTI,其中需要注意一个点,host需要为localhost或域名,不能是ip。

CVE-2022-22954 VmwareONEAccess漏洞复现




04

总结

CVE-2022-22954 VmwareONEAccess漏洞复现

最终可供利用的URL在com.vmware.endusercatalog.ui.config.WebConfig#UI_URLS已经包含,只需要传入deviceUdid或deviceType的任意一个参数,然后使用freemarker的可执行代码的恶意类即可。同时,在使用GET方式传入参数时需要进行URL编码。
fofa语法:app="vmware-Workspace-ONE-Access" || app="vmware-Identity-Manager"
以下接口均可以使用,其中传入的参数可以是deviceType或者deviceUdid,参数的值即是freemarker中可执行代码的恶意类。

GET /catalog-portal/ui?deviceType=GET /catalog-portal/hub-ui?deviceType=GET /catalog-portal/hub-ui/byob?deviceType=GET /catalog-portal/ui/oauth/verify?deviceType=GET /catalog-portal/ui/oauth/verify?deviceType=



参考链接:


https://core.vmware.com/vmsa-2022-0011-questions-answers-faqhttps://nosec.org/home/detail/4988.htmlhttps://www.carlstalhood.com/vmware-access/https://kb.vmware.com/s/article/88099https://freemarker.apache.org/docs/ref_builtins_expert.html#ref_builtin_evalhttps://blog.csdn.net/qq_25215821/article/details/102490466


FRIDAY LAB

星期五实验室成立于2017年,汇集众多技术研究人员,在工业互联网安全前瞻技术研究方向上不断进取。星期五实验室由海内外知名高校的学院精英及来自于顶尖企业的行业专家组成,且大部分人员来自国际领先、国内知名的黑客战队——浙大AAA战队。作为木链科技专业的技术研发团队,星期五实验室凭借精湛的专业技术水平,为产品研发提供新思路、为行业技术革新探索新方向。
CVE-2022-22954 VmwareONEAccess漏洞复现
CVE-2022-22954 VmwareONEAccess漏洞复现
CVE-2022-22954 VmwareONEAccess漏洞复现
CVE-2022-22954 VmwareONEAccess漏洞复现
CVE-2022-22954 VmwareONEAccess漏洞复现

扫二维码|关注我们

星期五实验室
FRIDAY LAB


原文始发于微信公众号(星期五实验室):CVE-2022-22954 VmwareONEAccess漏洞复现

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月22日22:10:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2022-22954 VmwareONEAccess漏洞复现https://cn-sec.com/archives/935843.html

发表评论

匿名网友 填写信息