/    

VMware Workspace ONE Access OAuth2TokenResourceController Auth Vulnerability

The class has two exposed endpoints. The first will generate an activation code for an existing oauth2 client:com.vmware.horizon.rest.controller.oauth2.OAuth2TokenResourceController

/*     */   @RequestMapping(value = {"/generateActivationToken/{id}"}, method = {RequestMethod.POST})
/*     */   @ResponseBody
/*     */   @ApiOperation(value = "Generate and update activation token for an existing oauth2 client", response = OAuth2ActivationTokenMedia.class)
/*     */   @ApiResponses({@ApiResponse(code = 500, message = "Generation failed, unknown error."), @ApiResponse(code = 400, message = "Generation failed, client is invalid or not specified.")})
/*     */   public OAuth2ActivationTokenMedia generateActivationToken(@ApiParam(value = "OAuth 2.0 Client identifier", example = "\"my-auth-grant-client1\"", required = true) @PathVariable("id") String clientId, HttpServletRequest request) throws MyOneLoginException {
/* 128 */     OrganizationRuntime orgRuntime = getOrgRuntime(request);
/* 129 */     OAuth2Client client = this.oAuth2ClientService.getOAuth2Client(orgRuntime.getOrganizationId().intValue(), clientId);
/* 130 */     if (client == null || client.getIdUser() == null) {
/* 131 */       throw new BadRequestException("invalid.client", new Object[0]);
/*     */   

The second will activate the device OAuth2 client by exchanging the activation code for a client ID and client secret:

/*     */   @RequestMapping(value = {"/activate"}, method = {RequestMethod.POST})
/*     */   @ResponseBody
/*     */   @AllowExecutionWhenReadOnly
/*     */   @ApiOperation(value = "Activate the device client by exchanging an activation code for a client ID and client secret.", notes = "This endpoint is used in the dynamic mobile registration flow. The activation code is obtained by calling the /SAAS/auth/device/register endpoint. The client_secret and client_id returned in this call will be used in the call to the /SAAS/auth/oauthtoken endpoint.", response = OAuth2ClientActivationDetails.class)
/*     */   @ApiResponses({@ApiResponse(code = 500, message = "Activation failed, unknown error."), @ApiResponse(code = 404, message = "Activation failed, organization not found."), @ApiResponse(code = 400, message = "Activation failed, activation code is invalid or not specified.")})
/*     */   public OAuth2ClientActivationDetails activateOauth2Client(@ApiParam(value = "the activation code", required = true) @RequestBody String activationCode, HttpServletRequest request) throws MyOneLoginException {
/* 102 */     OrganizationRuntime organizationRuntime = getOrgRuntime(request);
/*     */     try {
/* 104 */       return this.activationTokenService.activateAndGetOAuth2Client(organizationRuntime.getOrganization(), activationCode);
/* 105 */     } catch (EncryptionException e) {
/* 106 */       throw new BadRequestException("invalid.activation.code", e, new Object[0]);
/* 107 */     } catch (MyOneLoginException e) {
/*     */
/* 109 */       if (e.getCode() == 80480 || e.getCode() == 80476 || e.getCode() == 80440 || e.getCode() == 80558) {
/* 110 */         throw new BadRequestException("invalid.activation.code", e, new Object[0]);
/*     */       }
/* 112 */       throw e;
/*     */     } 
/*     */   }

This is enough for an attacker to then exchange the and for an OAuth2 token to achieve a complete authentication bypass. Now, this wouldn’t have been so easily exploitable if no default OAuth2 clients were present, but as it turns out. There are two internal clients installed by default:client_id``client_secret

These clients are created in several locations, one of them is in the class. I won’t bore you will the full stack trace, but it essentially leads to a call to in the class:com.vmware.horizon.rest.controller.system.BootstrapController``createTenant``com.vmware.horizon.components.authentication.OAuth2RemoteAccessServiceImpl

/*     */   public boolean createTenant(int orgId, String tenantId) {
/*     */     try {
/* 335 */       createDefaultServiceOAuth2Client(orgId); // 1
/* 336 */     } catch (Exception e) {
/* 337 */       log.warn("Failed to create the default service oauth2 client for org " + tenantId, e);
/* 338 */       return false;
/*     */     }
/* 340 */     return true;
/*     */   }

At [1] the code calls :createDefaultServiceOAuth2Client

/*     */   @Nonnull
/*     */   @Transactional(rollbackFor = {MyOneLoginException.class})
/*     */   @ReadWriteConnection
/*     */   public OAuth2Client createDefaultServiceOAuth2Client(int orgId) throws MyOneLoginException {
/* 116 */     OAuth2Client oAuth2Client = this.oauth2ClientService.getOAuth2Client(orgId, "Service__OAuth2Client");
/* 117 */     if (oAuth2Client == null) {
/* 118 */       Organizations firstOrg = this.organizationService.getFirstOrganization();
/* 119 */       if (firstOrg.getId().intValue() == orgId) {
/* 120 */         log.info("Creating service_oauth2 client for root tenant.");
/* 121 */         return createSystemScopedServiceOAuth2Client(firstOrg, "Service__OAuth2Client", null, "admin system"); // 2
/*     */       }
/*     */     //...
/* 131 */     return oAuth2Client;
/*     */   }

The code at [2] calls which, as the name suggests creates a system scoped oauth2 client using the “Service__OAuth2Client”. I actually found another authentication bypass documented as SRC-2022-0007 using variant analysis, however it impacts only the cloud environment due to the on-premise version not loading the Spring profile by default.createSystemScopedServiceOAuth2Client``clientId``authz

From : IAM Whoever I Say IAM :: Infiltrating VMWare Workspace ONE Access Using a 0-Click Exploit (srcincite.io)

Access OAuth2TokenResourceController Auth Bypass From : CVE-2022-22955 VMware Workspace ONE Access OAuth2TokenResourceController Auth Bypass - Y4er的博客