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.
DropWizard
v2.0.1
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
This issue may lead to Remote Code execution
There are different approaches to remediate the issue:
ParameterMessageInterpolator
:Validator validator = Validation.byDefaultProvider()
.configure()
.messageInterpolator( new ParameterMessageInterpolator() )
.buildValidatorFactory()
.getValidator();
dropwizard.committers+security@gmail.com
to ask for confirmation and acknowledge of the reportThis 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.
You can contact the GHSL team at securitylab@github.com
, please include the GHSL-2020-030
in any communication regarding this issue.