Apache Spark proxy-user privilege escalation from malicious configuration class
Description
In Apache Spark versions prior to 3.4.0, applications using spark-submit can specify a 'proxy-user' to run as, limiting privileges. The application can execute code with the privileges of the submitting user, however, by providing malicious configuration-related classes on the classpath. This affects architectures relying on proxy-user, for example those using Apache Livy to manage submitted applications.
Update to Apache Spark 3.4.0 or later, and ensure that spark.submit.proxyUser.allowCustomClasspathInClusterMode is set to its default of "false", and is not overridden by submitted applications.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Privilege escalation in Apache Spark <3.4.0 allows a proxy-user application to execute code as the submitting user via a malicious classpath.
Vulnerability
CVE-2023-22946 is a privilege escalation vulnerability in Apache Spark versions prior to 3.4.0. The root cause is that when spark-submit is used with a 'proxy-user' (a user identity with limited privileges), the application can still provide malicious configuration-related classes on the classpath. This allows the application to execute arbitrary code with the full privileges of the submitting user, effectively bypassing the intended privilege restriction [3].
Exploitation
To exploit this vulnerability, an attacker must be able to submit an application via spark-submit that specifies both a proxy-user and a custom classpath containing malicious classes. The attack surface includes environments relying on proxy-user delegation, such as those using Apache Livy to manage submitted applications [1]. No additional authentication is needed beyond what is required to submit a job; the submitted application can then run code with the submitting user's identity and permissions.
Impact
Successful exploitation allows the application to execute arbitrary code with the privileges of the user who submitted the Spark job, not the intended proxy-user. This can lead to unauthorized data access, privilege escalation, and potential compromise of the entire cluster if the submitting user has broad permissions.
Mitigation
The vulnerability is fixed in Apache Spark 3.4.0 and later [2]. Users should upgrade to Spark 3.4.0 or a patched 3.3.x release containing the backport (such as 3.3.3). The default configuration now disallows arbitrary custom classpaths with proxy users in cluster mode via the setting spark.submit.proxyUser.allowCustomClasspathInClusterMode, which should remain set to false [1][4]. No workarounds exist other than upgrading and ensuring the default configuration is not overridden.
- [SPARK-41958][CORE][3.3] Disallow arbitrary custom classpath with proxy user in cluster mode by degant · Pull Request #41428 · apache/spark
- [SPARK-41958][CORE] Disallow arbitrary custom classpath with proxy user in cluster mode by Ngone51 · Pull Request #39474 · apache/spark
- NVD - CVE-2023-22946
- [SPARK-41958] Disallow arbitrary custom classpath with proxy user in cluster mode
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.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.spark:spark-core_2.12Maven | < 3.3.3 | 3.3.3 |
org.apache.spark:spark-core_2.13Maven | < 3.3.3 | 3.3.3 |
pysparkPyPI | < 3.3.2 | 3.3.2 |
Affected products
5- osv-coords4 versionspkg:bitnami/sparkpkg:maven/org.apache.spark/spark-core_2.12pkg:maven/org.apache.spark/spark-core_2.13pkg:pypi/pyspark
< 3.4.0+ 3 more
- (no CPE)range: < 3.4.0
- (no CPE)range: < 3.3.3
- (no CPE)range: < 3.3.3
- (no CPE)range: < 3.3.2
- Apache Software Foundation/Apache Sparkv5Range: 0
Patches
2909da96e1471[SPARK-41958][CORE] Disallow arbitrary custom classpath with proxy user in cluster mode
2 files changed · +22 −0
core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala+15 −0 modified@@ -306,6 +306,10 @@ private[spark] class SparkSubmit extends Logging { val isKubernetesClient = clusterManager == KUBERNETES && deployMode == CLIENT val isKubernetesClusterModeDriver = isKubernetesClient && sparkConf.getBoolean("spark.kubernetes.submitInDriver", false) + val isCustomClasspathInClusterModeDisallowed = + !sparkConf.get(ALLOW_CUSTOM_CLASSPATH_BY_PROXY_USER_IN_CLUSTER_MODE) && + args.proxyUser != null && + (isYarnCluster || isMesosCluster || isStandAloneCluster || isKubernetesCluster) if (!isMesosCluster && !isStandAloneCluster) { // Resolve maven dependencies if there are any and add classpath to jars. Add them to py-files @@ -887,6 +891,13 @@ private[spark] class SparkSubmit extends Logging { sparkConf.set("spark.app.submitTime", System.currentTimeMillis().toString) + if (childClasspath.nonEmpty && isCustomClasspathInClusterModeDisallowed) { + childClasspath.clear() + logWarning(s"Ignore classpath ${childClasspath.mkString(", ")} with proxy user specified " + + s"in Cluster mode when ${ALLOW_CUSTOM_CLASSPATH_BY_PROXY_USER_IN_CLUSTER_MODE.key} is " + + s"disabled") + } + (childArgs.toSeq, childClasspath.toSeq, sparkConf, childMainClass) } @@ -940,6 +951,10 @@ private[spark] class SparkSubmit extends Logging { logInfo(s"Classpath elements:\n${childClasspath.mkString("\n")}") logInfo("\n") } + assert(!(args.deployMode == "cluster" && args.proxyUser != null && childClasspath.nonEmpty) || + sparkConf.get(ALLOW_CUSTOM_CLASSPATH_BY_PROXY_USER_IN_CLUSTER_MODE), + s"Classpath of spark-submit should not change in cluster mode if proxy user is specified " + + s"when ${ALLOW_CUSTOM_CLASSPATH_BY_PROXY_USER_IN_CLUSTER_MODE.key} is disabled") val loader = getSubmitClassLoader(sparkConf) for (jar <- childClasspath) { addJarToClasspath(jar, loader)
core/src/main/scala/org/apache/spark/internal/config/package.scala+7 −0 modified@@ -2461,4 +2461,11 @@ package object config { .version("3.4.0") .timeConf(TimeUnit.MILLISECONDS) .createWithDefaultString("5s") + + private[spark] val ALLOW_CUSTOM_CLASSPATH_BY_PROXY_USER_IN_CLUSTER_MODE = + ConfigBuilder("spark.submit.proxyUser.allowCustomClasspathInClusterMode") + .internal() + .version("3.4.0") + .booleanConf + .createWithDefault(false) }
bfba57724d25Backporting fix for SPARK-41958 to 3.3 branch from #39474
2 files changed · +22 −0
core/src/main/scala/org/apache/spark/deploy/SparkSubmit.scala+15 −0 modified@@ -310,6 +310,10 @@ private[spark] class SparkSubmit extends Logging { val isKubernetesClient = clusterManager == KUBERNETES && deployMode == CLIENT val isKubernetesClusterModeDriver = isKubernetesClient && sparkConf.getBoolean("spark.kubernetes.submitInDriver", false) + val isCustomClasspathInClusterModeDisallowed = + !sparkConf.get(ALLOW_CUSTOM_CLASSPATH_BY_PROXY_USER_IN_CLUSTER_MODE) && + args.proxyUser != null && + (isYarnCluster || isMesosCluster || isStandAloneCluster || isKubernetesCluster) if (!isMesosCluster && !isStandAloneCluster) { // Resolve maven dependencies if there are any and add classpath to jars. Add them to py-files @@ -870,6 +874,13 @@ private[spark] class SparkSubmit extends Logging { sparkConf.set("spark.app.submitTime", System.currentTimeMillis().toString) + if (childClasspath.nonEmpty && isCustomClasspathInClusterModeDisallowed) { + childClasspath.clear() + logWarning(s"Ignore classpath ${childClasspath.mkString(", ")} with proxy user specified " + + s"in Cluster mode when ${ALLOW_CUSTOM_CLASSPATH_BY_PROXY_USER_IN_CLUSTER_MODE.key} is " + + s"disabled") + } + (childArgs.toSeq, childClasspath.toSeq, sparkConf, childMainClass) } @@ -923,6 +934,10 @@ private[spark] class SparkSubmit extends Logging { logInfo(s"Classpath elements:\n${childClasspath.mkString("\n")}") logInfo("\n") } + assert(!(args.deployMode == "cluster" && args.proxyUser != null && childClasspath.nonEmpty) || + sparkConf.get(ALLOW_CUSTOM_CLASSPATH_BY_PROXY_USER_IN_CLUSTER_MODE), + s"Classpath of spark-submit should not change in cluster mode if proxy user is specified " + + s"when ${ALLOW_CUSTOM_CLASSPATH_BY_PROXY_USER_IN_CLUSTER_MODE.key} is disabled") val loader = getSubmitClassLoader(sparkConf) for (jar <- childClasspath) { addJarToClasspath(jar, loader)
core/src/main/scala/org/apache/spark/internal/config/package.scala+7 −0 modified@@ -2355,4 +2355,11 @@ package object config { .version("3.3.0") .intConf .createWithDefault(5) + + private[spark] val ALLOW_CUSTOM_CLASSPATH_BY_PROXY_USER_IN_CLUSTER_MODE = + ConfigBuilder("spark.submit.proxyUser.allowCustomClasspathInClusterMode") + .internal() + .version("3.3.3") + .booleanConf + .createWithDefault(false) }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-329j-jfvr-rhr6ghsaADVISORY
- lists.apache.org/thread/yllfl25xh5tbotjmg93zrq4bzwhqc0gvghsavendor-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2023-22946ghsaADVISORY
- github.com/apache/spark/commit/909da96e1471886a01a9e1def93630c4fd40e74aghsaWEB
- github.com/apache/spark/pull/39474ghsaWEB
- github.com/apache/spark/pull/41428ghsaWEB
- github.com/degant/spark/commit/bfba57724d2520e0fcaa7990f7257c21d11cd75aghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/pyspark/PYSEC-2023-44.yamlghsaWEB
- issues.apache.org/jira/browse/SPARK-41958ghsaWEB
News mentions
0No linked articles in our index yet.