Moderate severityNVD Advisory· Published Dec 7, 2013· Updated Apr 29, 2026
CVE-2013-6397
CVE-2013-6397
Description
Directory traversal vulnerability in SolrResourceLoader in Apache Solr before 4.6 allows remote attackers to read arbitrary files via a .. (dot dot) or full pathname in the tr parameter to solr/select/, when the response writer (wt parameter) is set to XSLT. NOTE: this can be leveraged using a separate XXE (XML eXternal Entity) vulnerability to allow access to files across restricted network boundaries.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.solr:solr-coreMaven | < 4.6.0 | 4.6.0 |
Affected products
11cpe:2.3:a:apache:solr:*:*:*:*:*:*:*:*+ 10 more
- cpe:2.3:a:apache:solr:*:*:*:*:*:*:*:*range: <=4.5.1
- cpe:2.3:a:apache:solr:4.0.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:solr:4.0.0:alpha:*:*:*:*:*:*
- cpe:2.3:a:apache:solr:4.0.0:beta:*:*:*:*:*:*
- cpe:2.3:a:apache:solr:4.1.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:solr:4.2.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:solr:4.2.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:solr:4.3.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:solr:4.3.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:solr:4.4.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:solr:4.5.0:*:*:*:*:*:*:*
Patches
1da34b18cb309SOLR-4882: Restrict SolrResourceLoader to only allow access to resource files below the instance dir
7 files changed · +99 −40
solr/CHANGES.txt+11 −0 modified@@ -71,6 +71,17 @@ New Features * SOLR-5167: Add support for AnalyzingInfixSuggester (AnalyzingInfixLookupFactory). (Areek Zillur, Varun Thacker via Robert Muir) +Security +---------------------- + +* SOLR-4882: SolrResourceLoader was restricted to only allow access to resource + files below the instance dir. The reason for this is security related: Some + Solr components allow to pass in resource paths via REST parameters + (e.g. XSL stylesheets, velocity templates,...) and load them via resource + loader. For backwards compatibility, this security feature can be disabled + by a new system property: solr.allow.unsafe.resourceloading=true + (Uwe Schindler) + Other Changes ----------------------
solr/contrib/velocity/src/java/org/apache/solr/response/VelocityResponseWriter.java+1 −20 modified@@ -67,7 +67,6 @@ public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse res } catch (ClassCastException e) { // known edge case where QueryResponse's extraction assumes "response" is a SolrDocumentList // (AnalysisRequestHandler emits a "response") - e.printStackTrace(); rsp = new SolrResponseBase(); rsp.setResponse(parsedResponse); } @@ -121,25 +120,7 @@ private VelocityEngine getEngine(SolrQueryRequest request) { SolrVelocityResourceLoader resourceLoader = new SolrVelocityResourceLoader(request.getCore().getSolrConfig().getResourceLoader()); engine.setProperty("solr.resource.loader.instance", resourceLoader); - - File fileResourceLoaderBaseDir = null; - try { - String template_root = request.getParams().get("v.base_dir"); - fileResourceLoaderBaseDir = new File(request.getCore().getResourceLoader().getConfigDir(), "velocity"); - if (template_root != null) { - fileResourceLoaderBaseDir = new File(template_root); - } - } catch (SolrException e) { - // no worries... probably in ZooKeeper mode and getConfigDir() isn't available, so we'll just ignore omit - // the file system resource loader - } - - if (fileResourceLoaderBaseDir != null) { - engine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, fileResourceLoaderBaseDir.getAbsolutePath()); - engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "params,file,solr"); - } else { - engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "params,solr"); - } + engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "params,solr"); // TODO: Externalize Velocity properties String propFile = request.getParams().get("v.properties");
solr/core/src/java/org/apache/solr/cloud/ZkSolrResourceLoader.java+3 −2 modified@@ -18,6 +18,7 @@ */ import java.io.ByteArrayInputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.List; @@ -75,15 +76,15 @@ public InputStream openResource(String resource) throws IOException { String file = collectionZkPath + "/" + resource; try { if (zkController.pathExists(file)) { - byte[] bytes = zkController.getZkClient().getData(collectionZkPath + "/" + resource, null, null, true); + byte[] bytes = zkController.getZkClient().getData(file, null, null, true); return new ByteArrayInputStream(bytes); } } catch (Exception e) { throw new IOException("Error opening " + file, e); } try { // delegate to the class loader (looking into $INSTANCE_DIR/lib jars) - is = classLoader.getResourceAsStream(resource); + is = classLoader.getResourceAsStream(resource.replace(File.separatorChar, '/')); } catch (Exception e) { throw new IOException("Error opening " + resource, e); }
solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java+33 −13 modified@@ -55,6 +55,7 @@ import java.io.InputStream; import java.lang.reflect.Constructor; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.CharacterCodingException; @@ -250,7 +251,7 @@ public String[] listConfigDir() { } public String getConfigDir() { - return instanceDir + "conf/"; + return instanceDir + "conf" + File.separator; } public String getDataDir() { @@ -299,27 +300,46 @@ public InputStream openConfig(String name) throws IOException { public InputStream openResource(String resource) throws IOException { InputStream is=null; try { - File f0 = new File(resource); - File f = f0; + File f0 = new File(resource), f = f0; if (!f.isAbsolute()) { // try $CWD/$configDir/$resource - f = new File(getConfigDir() + resource); + f = new File(getConfigDir() + resource).getAbsoluteFile(); } - if (f.isFile() && f.canRead()) { + boolean found = f.isFile() && f.canRead(); + if (!found) { // no success with $CWD/$configDir/$resource + f = f0.getAbsoluteFile(); + found = f.isFile() && f.canRead(); + } + // check that we don't escape instance dir + if (found) { + if (!Boolean.parseBoolean(System.getProperty("solr.allow.unsafe.resourceloading", "false"))) { + final URI instanceURI = new File(getInstanceDir()).getAbsoluteFile().toURI().normalize(); + final URI fileURI = f.toURI().normalize(); + if (instanceURI.relativize(fileURI) == fileURI) { + // no URI relativize possible, so they don't share same base folder + throw new IOException("For security reasons, SolrResourceLoader cannot load files from outside the instance's directory: " + f + + "; if you want to override this safety feature and you are sure about the consequences, you can pass the system property "+ + "-Dsolr.allow.unsafe.resourceloading=true to your JVM"); + } + } + // relativize() returned a relative, new URI, so we are fine! return new FileInputStream(f); - } else if (f != f0) { // no success with $CWD/$configDir/$resource - if (f0.isFile() && f0.canRead()) - return new FileInputStream(f0); } - // delegate to the class loader (looking into $INSTANCE_DIR/lib jars) - is = classLoader.getResourceAsStream(resource); - if (is == null) - is = classLoader.getResourceAsStream(getConfigDir() + resource); + // Delegate to the class loader (looking into $INSTANCE_DIR/lib jars). + // We need a ClassLoader-compatible (forward-slashes) path here! + is = classLoader.getResourceAsStream(resource.replace(File.separatorChar, '/')); + // This is a hack just for tests (it is not done in ZKResourceLoader)! + // -> the getConfigDir's path must not be absolute! + if (is == null && System.getProperty("jetty.testMode") != null && !new File(getConfigDir()).isAbsolute()) { + is = classLoader.getResourceAsStream((getConfigDir() + resource).replace(File.separatorChar, '/')); + } + } catch (IOException ioe) { + throw ioe; } catch (Exception e) { throw new IOException("Error opening " + resource, e); } if (is==null) { - throw new IOException("Can't find resource '" + resource + "' in classpath or '" + getConfigDir() + "', cwd="+System.getProperty("user.dir")); + throw new IOException("Can't find resource '" + resource + "' in classpath or '" + new File(getConfigDir()).getAbsolutePath() + "'"); } return is; }
solr/core/src/test/org/apache/solr/core/ResourceLoaderTest.java+34 −5 modified@@ -19,10 +19,10 @@ import junit.framework.Assert; -import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.analysis.core.KeywordTokenizerFactory; import org.apache.lucene.analysis.ngram.NGramFilterFactory; import org.apache.lucene.util._TestUtil; +import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; import org.apache.solr.handler.admin.LukeRequestHandler; import org.apache.solr.handler.component.FacetComponent; @@ -33,6 +33,7 @@ import java.io.File; import java.io.FileFilter; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.nio.charset.CharacterCodingException; import java.util.Arrays; @@ -41,23 +42,46 @@ import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; -public class ResourceLoaderTest extends LuceneTestCase +public class ResourceLoaderTest extends SolrTestCaseJ4 { public void testInstanceDir() throws Exception { SolrResourceLoader loader = new SolrResourceLoader(null); String instDir = loader.getInstanceDir(); assertTrue(instDir + " is not equal to " + "solr/", instDir.equals("solr/") == true); + loader.close(); loader = new SolrResourceLoader("solr"); instDir = loader.getInstanceDir(); assertTrue(instDir + " is not equal to " + "solr/", instDir.equals("solr" + File.separator) == true); + loader.close(); + } + + public void testEscapeInstanceDir() throws Exception { + File temp = _TestUtil.getTempDir("testEscapeInstanceDir"); + try { + temp.mkdirs(); + new File(temp, "dummy.txt").createNewFile(); + File instanceDir = new File(temp, "instance"); + instanceDir.mkdir(); + new File(instanceDir, "conf").mkdir(); + SolrResourceLoader loader = new SolrResourceLoader(instanceDir.getAbsolutePath()); + try { + loader.openResource("../../dummy.txt").close(); + fail(); + } catch (IOException ioe) { + assertTrue(ioe.getMessage().startsWith("For security reasons, SolrResourceLoader")); + } + loader.close(); + } finally { + _TestUtil.rmDir(temp); + } } - public void testAwareCompatibility() + public void testAwareCompatibility() throws Exception { SolrResourceLoader loader = new SolrResourceLoader( "." ); - Class clazz = ResourceLoaderAware.class; + Class<?> clazz = ResourceLoaderAware.class; // Check ResourceLoaderAware valid objects loader.assertAwareCompatibility( clazz, new NGramFilterFactory(new HashMap<String,String>()) ); loader.assertAwareCompatibility( clazz, new KeywordTokenizerFactory(new HashMap<String,String>()) ); @@ -97,6 +121,8 @@ public void testAwareCompatibility() } catch( SolrException ex ) { } // OK } + + loader.close(); } public void testBOMMarkers() throws Exception { @@ -123,18 +149,21 @@ public void testBOMMarkers() throws Exception { List<String> lines = loader.getLines(fileWithBom); assertEquals(1, lines.size()); assertEquals("BOMsAreEvil", lines.get(0)); + + loader.close(); } public void testWrongEncoding() throws Exception { String wrongEncoding = "stopwordsWrongEncoding.txt"; SolrResourceLoader loader = new SolrResourceLoader("solr/collection1"); // ensure we get our exception try { - List<String> lines = loader.getLines(wrongEncoding); + loader.getLines(wrongEncoding); fail(); } catch (SolrException expected) { assertTrue(expected.getCause() instanceof CharacterCodingException); } + loader.close(); } public void testClassLoaderLibs() throws Exception {
solr/core/src/test/org/apache/solr/schema/PrimitiveFieldTypeTest.java+7 −0 modified@@ -43,10 +43,17 @@ public void setUp() throws Exception { System.setProperty("enable.update.log", "false"); // schema12 doesn't support _version_ System.setProperty("solr.test.sys.prop1", "propone"); System.setProperty("solr.test.sys.prop2", "proptwo"); + System.setProperty("solr.allow.unsafe.resourceloading", "true"); initMap = new HashMap<String,String>(); config = new SolrConfig(new SolrResourceLoader("solr/collection1"), testConfHome + "solrconfig.xml", null); } + + @Override + public void tearDown() throws Exception { + System.clearProperty("solr.allow.unsafe.resourceloading"); + super.tearDown(); + } @SuppressWarnings("deprecation") @Test
solr/core/src/test/org/apache/solr/util/TestSystemIdResolver.java+10 −0 modified@@ -28,6 +28,16 @@ public class TestSystemIdResolver extends LuceneTestCase { + public void setUp() throws Exception { + super.setUp(); + System.setProperty("solr.allow.unsafe.resourceloading", "true"); + } + + public void tearDown() throws Exception { + System.clearProperty("solr.allow.unsafe.resourceloading"); + super.tearDown(); + } + private void assertEntityResolving(SystemIdResolver resolver, String expectedSystemId, String base, String systemId) throws Exception { final InputSource is = resolver.resolveEntity(null, null, base, systemId); try {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
13- issues.apache.org/jira/browse/SOLR-4882nvdPatchWEB
- www.agarri.fr/kom/archives/2013/11/27/compromising_an_unreachable_solr_server_with_cve-2013-6397/index.htmlnvdExploitWEB
- secunia.com/advisories/55730nvdVendor Advisory
- github.com/advisories/GHSA-j8qw-mwmv-28cgghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2013-6397ghsaADVISORY
- lucene.apache.org/solr/4_6_0/changes/Changes.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2013-1844.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2014-0029.htmlnvdWEB
- www.openwall.com/lists/oss-security/2013/11/27/1nvdWEB
- github.com/apache/lucene-solr/commit/da34b18cb3092df4972e2b6fa5178d1059923910ghsaWEB
- web.archive.org/web/20170307173358/http://www.securityfocus.com/bid/63935ghsaWEB
- secunia.com/advisories/59372nvd
- www.securityfocus.com/bid/63935nvd
News mentions
0No linked articles in our index yet.