JNDI injection into Apache sling-org-apache-sling-jcr-base
Description
Apache Sling JCR Base <3.1.12 on JDK ≤1.8.191 has a JNDI injection via RepositoryAccessor allowing remote code execution.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Apache Sling JCR Base <3.1.12 on JDK ≤1.8.191 has a JNDI injection via RepositoryAccessor allowing remote code execution.
CVE-2023-25141 is a critical injection vulnerability in Apache Sling JCR Base versions prior to 3.1.12, specifically in the RepositoryAccessor utility class. The functions getRepository and getRepositoryFromURL allow JNDI and RMI lookups that can be exploited on JDK versions 1.8.191 or earlier [3].
An attacker can craft malicious JNDI or RMI URLs to trigger remote code execution or access remote resources. The vulnerability arises because the code uses InitialContext without proper validation, allowing injection of arbitrary JNDI lookups [1][2]. Prerequisites include running on an older JDK (≤1.8.191) and the attacker being able to supply input to these functions.
Successful exploitation could allow an attacker to execute arbitrary code or access data stored on remote locations, potentially leading to full system compromise [3].
The fix in version 3.1.12 deprecates the vulnerable functions and removes the JNDI/RMI lookup capability [1][2]. Users are recommended to upgrade to Apache Sling JCR Base 3.1.12 or later, or to run on a more recent JDK ( >1.8.191 ) [3]. The related Jira issue SLING-11770 tracks this cleanup [4].
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.sling:org.apache.sling.jcr.baseMaven | < 3.1.12 | 3.1.12 |
Affected products
2- Range: 2.0.6
Patches
2779d8a7dd043Merge pull request #8 from apache/SLING-11770
1 file changed · +27 −114
src/main/java/org/apache/sling/jcr/base/util/RepositoryAccessor.java+27 −114 modified@@ -18,26 +18,26 @@ */ package org.apache.sling.jcr.base.util; -import java.util.Hashtable; - -import javax.jcr.Repository; -import javax.naming.InitialContext; - -import org.apache.jackrabbit.rmi.client.ClientAdapterFactory; import org.apache.jackrabbit.rmi.client.ClientRepositoryFactory; import org.apache.jackrabbit.rmi.client.LocalAdapterFactory; -import org.apache.jackrabbit.rmi.remote.RemoteRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -/** Access a Repository via JNDI or RMI. */ +import javax.jcr.Repository; +import java.util.Hashtable; + +/** + * Access a Repository via JNDI or RMI. + * + * @deprecated No longer supported + */ +@Deprecated public class RepositoryAccessor { - private final Logger log = LoggerFactory.getLogger(getClass()); /** Prefix for RMI Repository URLs */ + @Deprecated public static final String RMI_PREFIX = "rmi://"; /** Prefix for JNDI Repository URLs */ + @Deprecated public static final String JNDI_PREFIX = "jndi://"; /** @@ -55,79 +55,13 @@ public class RepositoryAccessor { * the Repository * @param jndiContext if null, JNDI is not tried * @return a Repository, or null if not found + * @throws UnsupportedOperationException Always throws {@code UnsupportedOperationException} + * @deprecated No longer supported */ + @Deprecated public Repository getRepository(String repositoryName, Hashtable<String, Object> jndiContext) { - - Repository result = null; - String tried = ""; - - if (jndiContext == null || jndiContext.size() == 0) { - log.info("jndiContext is null or empty, not trying JNDI"); - } else { - log.debug("Trying to acquire Repository '" + repositoryName - + "' via JNDI, context=" + jndiContext); - tried += "JNDI "; - final ClassLoader old = Thread.currentThread().getContextClassLoader(); - try { - Thread.currentThread().setContextClassLoader( - this.getClass().getClassLoader()); - InitialContext initialContext = new InitialContext(jndiContext); - Object repoObject = initialContext.lookup(repositoryName); - if (repoObject instanceof Repository) { - result = (Repository) repoObject; - log.info("Acquired Repository '" + repositoryName - + "' via JNDI"); - - } else if (repoObject instanceof RemoteRepository) { - RemoteRepository remoteRepo = (RemoteRepository) repoObject; - LocalAdapterFactory laf = getLocalAdapterFactory(); - result = laf.getRepository(remoteRepo); - log.info("Acquired RemoteRepository '" + repositoryName - + "' via JNDI"); - - } else { - log.info("Repository '" + repositoryName - + "' acquired via JDNI " - + "does not implement the required interfaces, class=" - + repoObject.getClass().getName()); - } - - } catch (Throwable t) { - log.info("Unable to acquire Repository '" + repositoryName - + "' via JNDI, context=" + jndiContext, t); - } finally { - Thread.currentThread().setContextClassLoader(old); - } - } - - if (result == null) { - if (repositoryName == null - || !repositoryName.startsWith(RMI_PREFIX)) { - log.info("Repository name does not start with '" + RMI_PREFIX - + "', not trying RMI"); - } else { - try { - tried += "RMI "; - log.debug("Trying to acquire Repository '" + repositoryName - + "' via RMI"); - ClientRepositoryFactory crf = getClientRepositoryFactory(); - result = crf.getRepository(repositoryName); - log.info("Acquired Repository '" + repositoryName - + "' via RMI"); - } catch (Throwable t) { - log.info("Unable to acquire Repository '" + repositoryName - + "' via RMI", t); - } - } - } - - if (result == null) { - log.info("Unable to acquire Repository '" + repositoryName - + "', tried " + tried); - } - - return result; + throw new UnsupportedOperationException("Repository access via JNDI-context is no longer supported."); } /** @@ -142,39 +76,12 @@ public Repository getRepository(String repositoryName, * </pre> * * @return the repository for the given url - * @throws NullPointerException If <code>url</code> is <code>null</code>. + * @throws UnsupportedOperationException Always throws {@code UnsupportedOperationException} + * @deprecated No longer supported */ + @Deprecated public Repository getRepositoryFromURL(String url) { - - if (url == null) { - throw new NullPointerException("url"); - } - - if (url.startsWith(JNDI_PREFIX)) { - // Parse JNDI URL to extract repository name and context - String name = null; - final Hashtable<String, Object> jndiContext = new Hashtable<String, Object>(); - final String urlNoPrefix = url.substring(JNDI_PREFIX.length()); - final int colonPos = urlNoPrefix.indexOf(':'); - if (colonPos < 0) { - name = urlNoPrefix; - } else { - name = urlNoPrefix.substring(0, colonPos); - for (String entryStr : urlNoPrefix.substring(colonPos + 1).split( - ",")) { - final String[] entry = entryStr.split("="); - if (entry.length == 2) { - jndiContext.put(entry[0], entry[1]); - } - } - } - - return getRepository(name, jndiContext); - - } - - // Use URL as is - return getRepository(url, null); + throw new UnsupportedOperationException("Repository access via URL is no longer supported."); } /** @@ -188,9 +95,12 @@ public Repository getRepositoryFromURL(String url) { * * @return the <code>LocalAdapterFactory</code> used to convert Jackrabbit * JCR RMI remote objects to local JCR API objects. + * @throws UnsupportedOperationException Always throws {@code UnsupportedOperationException} + * @deprecated No longer supported */ + @Deprecated protected LocalAdapterFactory getLocalAdapterFactory() { - return new ClientAdapterFactory(); + throw new UnsupportedOperationException("Repository access via RMI is no longer supported."); } /** @@ -206,8 +116,11 @@ protected LocalAdapterFactory getLocalAdapterFactory() { * * @return the <code>ClientRepositoryFactory</code> to access the remote * JCR repository over RMI. + * @throws UnsupportedOperationException Always throws {@code UnsupportedOperationException} + * @deprecated No longer supported */ + @Deprecated protected ClientRepositoryFactory getClientRepositoryFactory() { - return new ClientRepositoryFactory(getLocalAdapterFactory()); + throw new UnsupportedOperationException("Repository access via RMI is no longer supported."); } }
6ed0a030fd5fSLING-11770 : Cleanup in sling-jcr-base
1 file changed · +27 −114
src/main/java/org/apache/sling/jcr/base/util/RepositoryAccessor.java+27 −114 modified@@ -18,26 +18,26 @@ */ package org.apache.sling.jcr.base.util; -import java.util.Hashtable; - -import javax.jcr.Repository; -import javax.naming.InitialContext; - -import org.apache.jackrabbit.rmi.client.ClientAdapterFactory; import org.apache.jackrabbit.rmi.client.ClientRepositoryFactory; import org.apache.jackrabbit.rmi.client.LocalAdapterFactory; -import org.apache.jackrabbit.rmi.remote.RemoteRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -/** Access a Repository via JNDI or RMI. */ +import javax.jcr.Repository; +import java.util.Hashtable; + +/** + * Access a Repository via JNDI or RMI. + * + * @deprecated No longer supported + */ +@Deprecated public class RepositoryAccessor { - private final Logger log = LoggerFactory.getLogger(getClass()); /** Prefix for RMI Repository URLs */ + @Deprecated public static final String RMI_PREFIX = "rmi://"; /** Prefix for JNDI Repository URLs */ + @Deprecated public static final String JNDI_PREFIX = "jndi://"; /** @@ -55,79 +55,13 @@ public class RepositoryAccessor { * the Repository * @param jndiContext if null, JNDI is not tried * @return a Repository, or null if not found + * @throws UnsupportedOperationException Always throws {@code UnsupportedOperationException} + * @deprecated No longer supported */ + @Deprecated public Repository getRepository(String repositoryName, Hashtable<String, Object> jndiContext) { - - Repository result = null; - String tried = ""; - - if (jndiContext == null || jndiContext.size() == 0) { - log.info("jndiContext is null or empty, not trying JNDI"); - } else { - log.debug("Trying to acquire Repository '" + repositoryName - + "' via JNDI, context=" + jndiContext); - tried += "JNDI "; - final ClassLoader old = Thread.currentThread().getContextClassLoader(); - try { - Thread.currentThread().setContextClassLoader( - this.getClass().getClassLoader()); - InitialContext initialContext = new InitialContext(jndiContext); - Object repoObject = initialContext.lookup(repositoryName); - if (repoObject instanceof Repository) { - result = (Repository) repoObject; - log.info("Acquired Repository '" + repositoryName - + "' via JNDI"); - - } else if (repoObject instanceof RemoteRepository) { - RemoteRepository remoteRepo = (RemoteRepository) repoObject; - LocalAdapterFactory laf = getLocalAdapterFactory(); - result = laf.getRepository(remoteRepo); - log.info("Acquired RemoteRepository '" + repositoryName - + "' via JNDI"); - - } else { - log.info("Repository '" + repositoryName - + "' acquired via JDNI " - + "does not implement the required interfaces, class=" - + repoObject.getClass().getName()); - } - - } catch (Throwable t) { - log.info("Unable to acquire Repository '" + repositoryName - + "' via JNDI, context=" + jndiContext, t); - } finally { - Thread.currentThread().setContextClassLoader(old); - } - } - - if (result == null) { - if (repositoryName == null - || !repositoryName.startsWith(RMI_PREFIX)) { - log.info("Repository name does not start with '" + RMI_PREFIX - + "', not trying RMI"); - } else { - try { - tried += "RMI "; - log.debug("Trying to acquire Repository '" + repositoryName - + "' via RMI"); - ClientRepositoryFactory crf = getClientRepositoryFactory(); - result = crf.getRepository(repositoryName); - log.info("Acquired Repository '" + repositoryName - + "' via RMI"); - } catch (Throwable t) { - log.info("Unable to acquire Repository '" + repositoryName - + "' via RMI", t); - } - } - } - - if (result == null) { - log.info("Unable to acquire Repository '" + repositoryName - + "', tried " + tried); - } - - return result; + throw new UnsupportedOperationException("Repository access via JNDI-context is no longer supported."); } /** @@ -142,39 +76,12 @@ public Repository getRepository(String repositoryName, * </pre> * * @return the repository for the given url - * @throws NullPointerException If <code>url</code> is <code>null</code>. + * @throws UnsupportedOperationException Always throws {@code UnsupportedOperationException} + * @deprecated No longer supported */ + @Deprecated public Repository getRepositoryFromURL(String url) { - - if (url == null) { - throw new NullPointerException("url"); - } - - if (url.startsWith(JNDI_PREFIX)) { - // Parse JNDI URL to extract repository name and context - String name = null; - final Hashtable<String, Object> jndiContext = new Hashtable<String, Object>(); - final String urlNoPrefix = url.substring(JNDI_PREFIX.length()); - final int colonPos = urlNoPrefix.indexOf(':'); - if (colonPos < 0) { - name = urlNoPrefix; - } else { - name = urlNoPrefix.substring(0, colonPos); - for (String entryStr : urlNoPrefix.substring(colonPos + 1).split( - ",")) { - final String[] entry = entryStr.split("="); - if (entry.length == 2) { - jndiContext.put(entry[0], entry[1]); - } - } - } - - return getRepository(name, jndiContext); - - } - - // Use URL as is - return getRepository(url, null); + throw new UnsupportedOperationException("Repository access via URL is no longer supported."); } /** @@ -188,9 +95,12 @@ public Repository getRepositoryFromURL(String url) { * * @return the <code>LocalAdapterFactory</code> used to convert Jackrabbit * JCR RMI remote objects to local JCR API objects. + * @throws UnsupportedOperationException Always throws {@code UnsupportedOperationException} + * @deprecated No longer supported */ + @Deprecated protected LocalAdapterFactory getLocalAdapterFactory() { - return new ClientAdapterFactory(); + throw new UnsupportedOperationException("Repository access via RMI is no longer supported."); } /** @@ -206,8 +116,11 @@ protected LocalAdapterFactory getLocalAdapterFactory() { * * @return the <code>ClientRepositoryFactory</code> to access the remote * JCR repository over RMI. + * @throws UnsupportedOperationException Always throws {@code UnsupportedOperationException} + * @deprecated No longer supported */ + @Deprecated protected ClientRepositoryFactory getClientRepositoryFactory() { - return new ClientRepositoryFactory(getLocalAdapterFactory()); + throw new UnsupportedOperationException("Repository access via RMI is no longer supported."); } }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-gvg3-83q4-rfhqghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-25141ghsaADVISORY
- sling.apache.org/news.htmlghsavendor-advisoryWEB
- github.com/apache/sling-org-apache-sling-jcr-base/commit/6ed0a030fd5f13774aff0073c55cbe3ace0153cbghsaWEB
- github.com/apache/sling-org-apache-sling-jcr-base/commit/779d8a7dd0437a4f31de02c0d995afcf83b9904bghsaWEB
- issues.apache.org/jira/browse/SLING-11770ghsaWEB
News mentions
0No linked articles in our index yet.