High severityNVD Advisory· Published Sep 16, 2013· Updated Apr 29, 2026
CVE-2013-4315
CVE-2013-4315
Description
Directory traversal vulnerability in Django 1.4.x before 1.4.7, 1.5.x before 1.5.3, and 1.6.x before 1.6 beta 3 allows remote attackers to read arbitrary files via a file path in the ALLOWED_INCLUDE_ROOTS setting followed by a .. (dot dot) in a ssi template tag.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
DjangoPyPI | >= 1.4, < 1.4.7 | 1.4.7 |
DjangoPyPI | >= 1.5, < 1.5.3 | 1.5.3 |
Affected products
12cpe:2.3:a:djangoproject:django:1.4:*:*:*:*:*:*:*+ 11 more
- cpe:2.3:a:djangoproject:django:1.4:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.4:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.5:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.6:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5:alpha:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5:beta:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6:beta1:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6:beta2:*:*:*:*:*:*
Patches
287d2750b39f6[1.4.x] Prevented arbitrary file inclusion with {% ssi %} tag and relative paths.
2 files changed · +33 −0
django/template/defaulttags.py+2 −0 modified@@ -1,5 +1,6 @@ """Default tags used by the template system, available to all templates.""" +import os import sys import re from datetime import datetime @@ -309,6 +310,7 @@ def render(self, context): return '' def include_is_allowed(filepath): + filepath = os.path.abspath(filepath) for root in settings.ALLOWED_INCLUDE_ROOTS: if filepath.startswith(root): return True
tests/regressiontests/templates/tests.py+31 −0 modified@@ -1764,3 +1764,34 @@ def test_include_only(self): template.Template('{% include "child" only %}').render(ctx), 'none' ) + + +class SSITests(unittest.TestCase): + def setUp(self): + self.this_dir = os.path.dirname(os.path.abspath(__file__)) + self.ssi_dir = os.path.join(self.this_dir, "templates", "first") + + def render_ssi(self, path): + # the path must exist for the test to be reliable + self.assertTrue(os.path.exists(path)) + return template.Template('{%% ssi %s %%}' % path).render(Context()) + + def test_allowed_paths(self): + acceptable_path = os.path.join(self.ssi_dir, "..", "first", "test.html") + with override_settings(ALLOWED_INCLUDE_ROOTS=(self.ssi_dir,)): + self.assertEqual(self.render_ssi(acceptable_path), 'First template\n') + + def test_relative_include_exploit(self): + """ + May not bypass ALLOWED_INCLUDE_ROOTS with relative paths + + e.g. if ALLOWED_INCLUDE_ROOTS = ("/var/www",), it should not be + possible to do {% ssi "/var/www/../../etc/passwd" %} + """ + disallowed_paths = [ + os.path.join(self.ssi_dir, "..", "ssi_include.html"), + os.path.join(self.ssi_dir, "..", "second", "test.html"), + ] + with override_settings(ALLOWED_INCLUDE_ROOTS=(self.ssi_dir,)): + for path in disallowed_paths: + self.assertEqual(self.render_ssi(path), '')
988b61c550d7[1.5.x] Prevented arbitrary file inclusion with {% ssi %} tag and relative paths.
2 files changed · +33 −0
django/template/defaulttags.py+2 −0 modified@@ -1,6 +1,7 @@ """Default tags used by the template system, available to all templates.""" from __future__ import unicode_literals +import os import sys import re from datetime import datetime @@ -312,6 +313,7 @@ def render(self, context): return '' def include_is_allowed(filepath): + filepath = os.path.abspath(filepath) for root in settings.ALLOWED_INCLUDE_ROOTS: if filepath.startswith(root): return True
tests/regressiontests/templates/tests.py+31 −0 modified@@ -1737,3 +1737,34 @@ def test_include_only(self): template.Template('{% include "child" only %}').render(ctx), 'none' ) + + +class SSITests(TestCase): + def setUp(self): + self.this_dir = os.path.dirname(os.path.abspath(upath(__file__))) + self.ssi_dir = os.path.join(self.this_dir, "templates", "first") + + def render_ssi(self, path): + # the path must exist for the test to be reliable + self.assertTrue(os.path.exists(path)) + return template.Template('{%% ssi "%s" %%}' % path).render(Context()) + + def test_allowed_paths(self): + acceptable_path = os.path.join(self.ssi_dir, "..", "first", "test.html") + with override_settings(ALLOWED_INCLUDE_ROOTS=(self.ssi_dir,)): + self.assertEqual(self.render_ssi(acceptable_path), 'First template\n') + + def test_relative_include_exploit(self): + """ + May not bypass ALLOWED_INCLUDE_ROOTS with relative paths + + e.g. if ALLOWED_INCLUDE_ROOTS = ("/var/www",), it should not be + possible to do {% ssi "/var/www/../../etc/passwd" %} + """ + disallowed_paths = [ + os.path.join(self.ssi_dir, "..", "ssi_include.html"), + os.path.join(self.ssi_dir, "..", "second", "test.html"), + ] + with override_settings(ALLOWED_INCLUDE_ROOTS=(self.ssi_dir,)): + for path in disallowed_paths: + self.assertEqual(self.render_ssi(path), '')
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
12- www.djangoproject.com/weblog/2013/sep/10/security-releases-issued/nvdPatchVendor Advisory
- secunia.com/advisories/54772nvdVendor Advisory
- secunia.com/advisories/54828nvdVendor Advisory
- github.com/advisories/GHSA-vjjp-9r83-22rcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2013-4315ghsaADVISORY
- lists.opensuse.org/opensuse-updates/2013-10/msg00015.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2013-1521.htmlnvdWEB
- www.debian.org/security/2013/dsa-2755nvdWEB
- github.com/django/django/commit/87d2750b39f6f2d54b7047225521a44dcd37e896ghsaWEB
- github.com/django/django/commit/988b61c550d798f9a66d17ee0511fb7a9a7f33caghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/django/PYSEC-2013-20.yamlghsaWEB
- www.djangoproject.com/weblog/2013/sep/10/security-releases-issuedghsaWEB
News mentions
0No linked articles in our index yet.