VYPR
Moderate severityNVD Advisory· Published Apr 9, 2024· Updated Feb 13, 2025

Apache Zeppelin: Cron arbitrary user impersonation with improper privileges

CVE-2024-31865

Description

Apache Zeppelin before 0.11.1 has an improper input validation in the cron API, allowing attackers to run notebooks with elevated privileges.

AI Insight

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

Apache Zeppelin before 0.11.1 has an improper input validation in the cron API, allowing attackers to run notebooks with elevated privileges.

Vulnerability

CVE-2024-31865 is an improper input validation vulnerability in Apache Zeppelin's cron scheduling API. The API fails to enforce proper permission checks when updating cron information, allowing unauthorized modification of notebook schedules [1][2]. The fix, introduced in pull request #4631 and commit 49e2740, adds explicit permission verification before allowing cron updates [1][3].

Exploitation

An attacker can call the updating cron API with invalid or improper privileges, meaning they may lack the required authorization but can still set or modify cron schedules for notebooks [2]. This does not require administrative access; any user who can reach the API endpoint may exploit it. The vulnerability affects Zeppelin versions from 0.8.2 up to (but not including) 0.11.1 [2][4].

Impact

Successful exploitation allows an attacker to schedule notebooks to run with the privileges of another user, effectively impersonating that user [4]. This can lead to arbitrary code execution, data exfiltration, or privilege escalation within the Zeppelin environment, depending on the notebook's content and the privileges of the impersonated user.

Mitigation

Users should upgrade to Apache Zeppelin 0.11.1, which contains the fix [2][3]. No workarounds are documented; upgrading is the recommended action.

AI Insight generated on May 20, 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
org.apache.zeppelin:zeppelin-serverMaven
>= 0.8.2, < 0.11.10.11.1

Affected products

2

Patches

1
49e2740a1d83

[HOTFIX] Check permission when updating cron information (#4631)

https://github.com/apache/zeppelinJongyoul LeeFeb 16, 2024via ghsa
4 files changed · +61 7
  • zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java+5 1 modified
    @@ -596,6 +596,10 @@ public String getShiroPath() {
         return new File(shiroPath).exists() ? shiroPath : StringUtils.EMPTY;
       }
     
    +  public boolean isAuthenticationEnabled() {
    +    return !StringUtils.isBlank(getShiroPath());
    +  }
    +
       public String getInterpreterRemoteRunnerPath() {
         return getAbsoluteDir(ConfVars.ZEPPELIN_INTERPRETER_REMOTE_RUNNER);
       }
    @@ -770,7 +774,7 @@ public String getZeppelinNotebookGitRemoteOrigin() {
       }
     
       public boolean isZeppelinNotebookCronEnable() {
    -    return getBoolean(ConfVars.ZEPPELIN_NOTEBOOK_CRON_ENABLE);
    +    return getBoolean(ConfVars.ZEPPELIN_NOTEBOOK_CRON_ENABLE) && isAuthenticationEnabled();
       }
     
       public String getZeppelinNotebookCronFolders() {
    
  • zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java+49 6 modified
    @@ -879,17 +879,60 @@ public void updateNote(String noteId,
               return null;
             }
     
    -        if (!(Boolean) note.getConfig().get("isZeppelinNotebookCronEnable")) {
    +        if (!zConf.isZeppelinNotebookCronEnable()) {
    +          boolean hasCronSettings = false;
               if (config.get("cron") != null) {
    -            config.remove("cron");
    +            LOGGER.warn("cron should be null when cron is disabled");
    +            hasCronSettings = true;
    +          }
    +          if (config.get("cronExecutingUser") != null) {
    +            LOGGER.warn("cronExecutingUser should be null when cron is disabled");
    +            hasCronSettings = true;
    +          }
    +          if (config.get("cronExecutingRoles") != null) {
    +            LOGGER.warn("cronExecutingRoles should be null when cron is disabled");
    +            hasCronSettings = true;
    +          }
    +          if (hasCronSettings) {
    +            callback.onFailure(new IllegalArgumentException("Wrong configs"), context);
    +            return null;
    +          }
    +        } else {
    +          String requestCronUser = (String) config.get("cronExecutingUser");
    +          Set<String> requestCronRoles = (Set<String>) config.get("cronExecutingRoles");
    +
    +          if (!authorizationService.hasRunPermission(Collections.singleton(requestCronUser), note.getId())) {
    +            LOGGER.error("Wrong cronExecutingUser: {}", requestCronUser);
    +            callback.onFailure(new IllegalArgumentException(requestCronUser), context);
    +            return null;
    +          } else {
    +            // This part should be restarted but we need to prepare to notice who can be a cron user in advance
    +            if (!context.getUserAndRoles().contains(requestCronUser)) {
    +              LOGGER.error("Wrong cronExecutingUser: {}", requestCronUser);
    +              callback.onFailure(new IllegalArgumentException(requestCronUser), context);
    +              return null;
    +            }
    +
    +            if (!context.getUserAndRoles().containsAll(requestCronRoles)) {
    +              LOGGER.error("Wrong cronExecutingRoles: {}", requestCronRoles);
    +              callback.onFailure(new IllegalArgumentException(requestCronRoles.toString()), context);
    +              return null;
    +            }
    +          }
    +
    +          if (!(Boolean) note.getConfig().get("isZeppelinNotebookCronEnable")) {
    +            if (config.get("cron") != null) {
    +              config.remove("cron");
    +            }
    +          }
    +          boolean cronUpdated = isCronUpdated(config, note.getConfig());
    +          if (cronUpdated) {
    +            schedulerService.refreshCron(note.getId());
               }
             }
    -        boolean cronUpdated = isCronUpdated(config, note.getConfig());
    +
             note.setName(name);
             note.setConfig(config);
    -        if (cronUpdated) {
    -          schedulerService.refreshCron(note.getId());
    -        }
     
             notebook.updateNote(note, context.getAutheInfo());
             callback.onSuccess(note, context);
    
  • zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java+3 0 modified
    @@ -29,6 +29,7 @@
     import org.junit.jupiter.api.AfterAll;
     import org.junit.jupiter.api.BeforeAll;
     import org.junit.jupiter.api.BeforeEach;
    +import org.junit.jupiter.api.Disabled;
     import org.junit.jupiter.api.MethodOrderer;
     import org.junit.jupiter.api.Test;
     import org.junit.jupiter.api.TestMethodOrder;
    @@ -657,6 +658,7 @@ void testRunParagraphWithParams() throws Exception {
         }
       }
     
    +  @Disabled // TODO(ZEPPELIN-5994): Fix and enable this test
       @Test
       void testJobs() throws Exception {
         // create a note and a paragraph
    @@ -719,6 +721,7 @@ void testJobs() throws Exception {
         }
       }
     
    +  @Disabled // TODO(ZEPPELIN-5994): Fix and enable this test
       @Test
       void testCronDisable() throws Exception {
         String noteId = null;
    
  • zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java+4 0 modified
    @@ -57,6 +57,7 @@
     import java.io.FileWriter;
     import java.io.IOException;
     import java.nio.charset.StandardCharsets;
    +import java.nio.file.Files;
     import java.util.ArrayList;
     import java.util.Arrays;
     import java.util.Collections;
    @@ -111,6 +112,9 @@ public void setUp() throws Exception {
         schedulerService = new QuartzSchedulerService(conf, notebook);
         notebook.initNotebook();
         notebook.waitForFinishInit(1, TimeUnit.MINUTES);
    +
    +    // create empty shiro.ini file under confDir
    +    Files.createFile(new File(confDir, "shiro.ini").toPath());
       }
     
       @Override
    

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.