VYPR
Critical severityNVD Advisory· Published Nov 15, 2021· Updated Aug 4, 2024

Unauthenticated remote code injection in cron-utils

CVE-2021-41269

Description

Template injection in cron-utils @Cron annotation allows unauthenticated RCE; fixed in 9.1.6.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Template injection in cron-utils @Cron annotation allows unauthenticated RCE; fixed in 9.1.6.

Vulnerability

The vulnerability is a Java EL template injection in cron-utils versions up to 9.1.2. The library parses cron expressions for validation. When using the @Cron annotation to validate untrusted Cron expressions, the expression string is incorrectly included in error messages, allowing injection of arbitrary Java EL expressions. [1]

Exploitation

An attacker can provide a malicious cron expression containing EL expressions (e.g., using ${...} syntax). If the expression fails validation, the library’s error message includes the original expression string, which gets evaluated by the template engine. This requires no authentication and only that the application uses the @Cron annotation on an untrusted input. [1][3]

Impact

Successful exploitation leads to unauthenticated Remote Code Execution (RCE) on the server, as the attacker can execute arbitrary Java code via EL injection. This compromises confidentiality, integrity, and availability. [1]

Mitigation

The issue is fixed in version 9.1.6, released on 2021-11-15. Users should upgrade to 9.1.6 or later. There are no known workarounds. The fix removed the expression from the error message, preventing injection via crafted validation failures. [1][3]

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
com.cronutils:cron-utilsMaven
< 9.1.69.1.6

Affected products

2

Patches

2
cfd2880f80e6

Merge pull request #494 from NielsDoucet/RCE-fix

https://github.com/jmrozanec/cron-utilsjmrozanecOct 30, 2021via ghsa
4 files changed · +18 9
  • src/main/java/com/cronutils/parser/CronParser.java+1 1 modified
    @@ -128,7 +128,7 @@ public Cron parse(final String expression) {
                     }
                     return new SingleCron(cronDefinition, results).validate();
                 } catch (final IllegalArgumentException e) {
    -                throw new IllegalArgumentException(String.format("Failed to parse '%s'. %s", expression, e.getMessage()), e);
    +                throw new IllegalArgumentException(String.format("Failed to parse cron expression. %s", e.getMessage()), e);
                 }
             }
         }
    
  • src/test/java/com/cronutils/Issue418Test.java+5 4 modified
    @@ -5,6 +5,7 @@
     import com.cronutils.model.definition.CronDefinitionBuilder;
     import com.cronutils.model.time.ExecutionTime;
     import com.cronutils.parser.CronParser;
    +import org.hamcrest.core.StringEndsWith;
     import org.junit.Test;
     
     import java.time.LocalDate;
    @@ -13,8 +14,8 @@
     import java.time.ZonedDateTime;
     import java.util.Optional;
     
    -import static org.junit.Assert.assertEquals;
    -import static org.junit.Assert.fail;
    +import static org.hamcrest.core.StringEndsWith.endsWith;
    +import static org.junit.Assert.*;
     
     public class Issue418Test {
     
    @@ -59,7 +60,7 @@ public void testInvalidWeekDayStart() {
                 parser.parse("0 0 2 ? * 0/7 *");
                 fail("Expected exception for invalid expression");
             } catch (IllegalArgumentException expected) {
    -            assertEquals("Failed to parse '0 0 2 ? * 0/7 *'. Value 0 not in range [1, 7]", expected.getMessage());
    +            assertThat(expected.getMessage(), endsWith("Value 0 not in range [1, 7]"));
             }
         }
     
    @@ -71,7 +72,7 @@ public void testInvalidWeekDayEnd() {
                 parser.parse("0 0 2 ? * 1/8 *");
                 fail("Expected exception for invalid expression");
             } catch (IllegalArgumentException expected) {
    -            assertEquals("Failed to parse '0 0 2 ? * 1/8 *'. Period 8 not in range [1, 7]", expected.getMessage());
    +            assertThat(expected.getMessage(), endsWith("Period 8 not in range [1, 7]"));
             }
         }
     }
    
  • src/test/java/com/cronutils/parser/CronParserQuartzIntegrationTest.java+4 3 modified
    @@ -20,16 +20,19 @@
     import com.cronutils.model.definition.CronDefinitionBuilder;
     import com.cronutils.model.field.expression.FieldExpressionFactory;
     import com.cronutils.model.time.ExecutionTime;
    +import org.hamcrest.core.StringEndsWith;
     import org.junit.Before;
     import org.junit.Rule;
     import org.junit.Test;
    +import org.junit.internal.matchers.ThrowableMessageMatcher;
     import org.junit.rules.ExpectedException;
     
     import java.time.ZonedDateTime;
     import java.util.Locale;
     import java.util.Optional;
     
     import static org.junit.Assert.*;
    +import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
     
     public class CronParserQuartzIntegrationTest {
     
    @@ -248,9 +251,7 @@ public void testReportedErrorContainsSameExpressionAsProvided() {
         public void testMissingExpressionAndInvalidCharsInErrorMessage() {
             thrown.expect(IllegalArgumentException.class);
             final String cronexpression = "* * -1 * * ?";
    -        thrown.expectMessage(
    -                String.format("Failed to parse '%s'. Invalid expression! Expression: -1 does not describe a range. Negative numbers are not allowed.",
    -                        cronexpression));
    +        thrown.expect(hasMessage(StringEndsWith.endsWith("Invalid expression! Expression: -1 does not describe a range. Negative numbers are not allowed.")));
             assertNotNull(ExecutionTime.forCron(parser.parse(cronexpression)));
         }
     
    
  • src/test/java/com/cronutils/validation/CronValidatorTest.java+8 1 modified
    @@ -4,6 +4,8 @@
     import org.junit.Test;
     import org.junit.runner.RunWith;
     import org.junit.runners.Parameterized;
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
     
     import javax.validation.ConstraintViolation;
     import javax.validation.Validation;
    @@ -16,6 +18,8 @@
     @RunWith(Parameterized.class)
     public class CronValidatorTest {
     
    +    private static final Logger LOGGER = LoggerFactory.getLogger(CronValidatorTest.class);
    +
         private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
     
         private final String expression;
    @@ -38,14 +42,17 @@ public static Object[] expressions() {
                     {"0 0 0 25 12 ?", true},
                     {"0 0 0 L 12 ?", false},
                     {"1,2, * * * * *", false},
    -                {"1- * * * * *", false}
    +                {"1- * * * * *", false},
    +                // Verification for RCE security vulnerability fix: https://github.com/jmrozanec/cron-utils/issues/461
    +                {"java.lang.Runtime.getRuntime().exec('touch /tmp/pwned'); // 4 5 [${''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js').eval(validatedValue)}]", false}
             };
         }
     
         @Test
         public void validateExamples() {
             TestPojo testPojo = new TestPojo(expression);
             Set<ConstraintViolation<TestPojo>> violations = validator.validate(testPojo);
    +        violations.stream().map(ConstraintViolation::getMessage).forEach(LOGGER::info);
     
             if (valid) {
                 assertTrue(violations.isEmpty());
    
d6707503ec2f

Merge pull request #493 from pwntester/patch-1

https://github.com/jmrozanec/cron-utilsjmrozanecOct 30, 2021via ghsa
1 file changed · +1 1
  • src/main/java/com/cronutils/validation/CronValidator.java+1 1 modified
    @@ -30,7 +30,7 @@ public boolean isValid(String value, ConstraintValidatorContext context) {
                 return true;
             } catch (IllegalArgumentException e) {
                 context.disableDefaultConstraintViolation();
    -            context.buildConstraintViolationWithTemplate(e.getMessage()).addConstraintViolation();
    +            context.buildConstraintViolationWithTemplate("Error parsing the Cron expression").addConstraintViolation();
                 return false;
             }
         }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.