Summary
Remote Code Execution - JavaEL Injection (low privileged accounts)
CVE
CVE-2020-10199
Product
Nexus Repository Manager
Tested Version
3.20.1
Details
It is possible for any authenticated user, no matter the permissions granted, to run arbitrary code on the server (with Nexus process privileges) by injecting arbitrary Java Expression Language (EL) expressions.
We conducted a CodeQL-based variant analysis of CVE-2018-16621 and found that the applied mitigation (stripJavaEL()
) was not applied to org.sonatype.nexus.validation.ConstraintViolationFactory
. Therefore when user-controlled data flows into createViolation(final String path, final String message)
it will get evaluated as a Java EL by buildConstraintViolationWithTemplate()
:
private static class HelperValidator extends ConstraintValidatorSupport<HelperAnnotation, HelperBean> {
@Override
public boolean isValid(final HelperBean bean, final ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
// build a custom property path
ConstraintViolationBuilder builder = context.buildConstraintViolationWithTemplate(bean.getMessage());
NodeBuilderCustomizableContext nodeBuilder = null;
for (String part : bean.getPath().split("\\.")) {
if (nodeBuilder == null) {
nodeBuilder = builder.addPropertyNode(part);
}
else {
nodeBuilder = nodeBuilder.addPropertyNode(part);
}
}
if (nodeBuilder != null) {
nodeBuilder.addConstraintViolation();
}
return false;
}
}
}
We found multiple paths flowing into ConstraintViolationFactory.createViolation(1)
. This issue (GHSL-2020-011) focuses on the endpoints which are accessible to any authenticated user, no matter the permissions granted.
On src/main/java/org/sonatype/nexus/repository/rest/api/AbstractGroupRepositoriesApiResource.java:97
group format and members are validated and failures are reported using the insecure createViolation
method:
private void validateGroupMembers(T request) {
String groupFormat = request.getFormat();
Set<ConstraintViolation<?>> violations = Sets.newHashSet();
Collection<String> memberNames = request.getGroup().getMemberNames();
for (String repositoryName : memberNames) {
Repository repository = repositoryManager.get(repositoryName);
if (nonNull(repository)) {
String memberFormat = repository.getFormat().getValue();
if (!memberFormat.equals(groupFormat)) {
violations.add(constraintViolationFactory.createViolation("memberNames",
"Member repository format does not match group repository format: " + repositoryName));
}
}
else {
violations.add(constraintViolationFactory.createViolation("memberNames",
"Member repository does not exist: " + repositoryName));
}
}
maybePropagate(violations, log);
}
This sink can be reached from any class extending AbstractGroupRepositoriesApiResource
. At the moment of reporting, there is only one class that meets this requirement: GolangGroupRepositoriesApiResource
.
These paths can be exercised by creating or updating a GoLang group repository which enforces authentication but since authorization checks are performed after bean validation, the RCE sink can be reached by any authenticated user.
Impact
This issue may lead to Remote Code execution by any low-privilege user
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
Vendor advisories
CVE-2020-10199 Nexus Repository Manager 3 - Remote Code Execution - 2020-03-31
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-011
in any communication regarding this issue.