April 13, 2020

GHSL-2020-012: Remote Code Execution - JavaEL Injection (high privileged accounts) in Nexus Repository Manager

Alvaro Muñoz

Summary

GHSL-2020-012 - Remote Code Execution - JavaEL Injection (high privileged accounts)

Product

Nexus Repository Manager

Tested Version

3.20.1

CVE

CVE-2020-10199

Details

It is possible for high privilege users (eg: admins), to run arbitrary code on the server (with Nexus process privileges) by injecting arbitrary Java Expression Language (EL) expressions.

The following paths share the same sink in the ConstraintViolationFactory:

Path 1

  • Source: src/main/java/org/sonatype/nexus/coreui/RepositoryComponent.groovy:232
  • Sink: src/main/java/org/sonatype/nexus/cleanup/CleanupConfigurationValidator.java:120
  • Permissions: nx-repository-admin-*-*-*

Note: There are many endpoints that may end up calling validateConfiguration(), for example coreui_Repository.create will call repositoryManager.create(config) which will call validateConfiguration

Path 2

  • Source: src/main/java/org/sonatype/nexus/coreui/ComponentComponent.groovy:188
  • Sink: src/main/java/org/sonatype/nexus/selector/SelectorFactory.java:76
  • Permissions: nx-selectors-*

Note: This is an interesting vector since we are passing either a JEXL or CSEL expression which are not exploitable (because of the sandboxed JEXL engine). However when expressions are validated, the following code gets executed:

public void validateSelector(final String type, final String expression) {
  try {
    switch (type) {
      case JexlSelector.TYPE:
        jexlEngine.parseExpression(expression);
        break;
      case CselSelector.TYPE:
        validateCselExpression(jexlEngine.parseExpression(expression));
        break;
      default:
        throw new IllegalArgumentException("Unknown selector type: " + type);
    }
  }
  catch (Exception e) {
    String detail = format("Invalid %s: %s", upper(type),
        e instanceof JexlException ? expandExceptionDetail((JexlException) e) : e.getMessage());

    log.debug(detail, e);

    throw new ConstraintViolationException(e.getMessage(),
        ImmutableSet.of(constraintViolationFactory.createViolation("expression", detail)));
  }
}

If there are any errors during the validation, the exception’s message will be passed to createViolation which will turn into Java EL evaluation. A simple way to be sure that the JavaEL expression gets into the exception’s message is to put it in single back-quotes which represents a multiline literal in JEXL.

Path 3

  • Source: src/main/java/org/sonatype/nexus/coreui/SelectorComponent.groovy:91
  • Sink: src/main/java/org/sonatype/nexus/coreui/SelectorComponent.groovy:92
  • Permissions: nx-selectors-create

Path 4

  • Source: src/main/java/org/sonatype/nexus/coreui/SelectorComponent.groovy:109
  • Sink: src/main/java/org/sonatype/nexus/coreui/SelectorComponent.groovy:110
  • Permissions: nx-selectors-update

Path 5

  • Source: src/main/java/org/sonatype/nexus/repository/rest/internal/resources/ContentSelectorsApiResource.java:89
  • Sink: src/main/java/org/sonatype/nexus/repository/rest/internal/resources/ContentSelectorsApiResource.java:90
  • Permissions: nx-selectors-create

Path 6

  • Source: src/main/java/org/sonatype/nexus/repository/rest/internal/resources/ContentSelectorsApiResource.java:110
  • Sink: src/main/java/org/sonatype/nexus/repository/rest/internal/resources/ContentSelectorsApiResource.java:113
  • Permissions: nx-selectors-update

Path 7

  • Sink: src/main/java/org/sonatype/nexus/repository/storage/StorageFacetImpl.java:176
  • Permissions: nx-blobstores-create

Path 8

  • Sink: src/main/java/org/sonatype/nexus/repository/storage/ContentSelectorUpgradeManager.java:56
  • Permissions: nx-selectors-*

The attacker needs to create a JEXL selector (not possible through UI, so either they intercept the request and change the type from CSEL to JEXL or use the REST API.

In order to trigger the vulnerability, the attacker needs to wait until lifecycle phase changes or something triggers the content selector upgrade process. If the attacker has enough permissions, they can trigger the upgrade by bouncing the lifecycle phase manually using the REST API.

Impact

This issue may lead to Remote Code execution by high-privilege users

Remediation

Apply stripJavaEL() in HelperValidator:

ConstraintViolationBuilder builder = context.buildConstraintViolationWithTemplate(getEscapeHelper().stripJavaEl(bean.getMessage()));

Coordinated Disclosure Timeline

  • 02/03/2020: Report sent to Sonatype
  • 02/03/2020: Sonatype acknowledged report
  • 02/14/2020: Sonatype raises questions about some of the issues
  • 02/17/2020: GHSL answers Sonatype questions
  • 02/19/2020: Sonatype agrees with GHSL comments

Credit

This issue was discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).

Contact

You can contact the GHSL team at securitylab@github.com, please include the GHSL-2020-012 in any communication regarding this issue.