Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.5.x] Prevented arbitrary file inclusion with {% ssi %} tag and rel…

…ative paths.

Thanks Rainer Koirikivi for the report and draft patch.

This is a security fix; disclosure to follow shortly.

Backport of 7fe5b65 from master
  • Loading branch information...
commit 988b61c550d798f9a66d17ee0511fb7a9a7f33ca 1 parent 693ebff
Tim Graham authored August 27, 2013
2  django/template/defaulttags.py
... ...
@@ -1,6 +1,7 @@
1 1
 """Default tags used by the template system, available to all templates."""
2 2
 from __future__ import unicode_literals
3 3
 
  4
+import os
4 5
 import sys
5 6
 import re
6 7
 from datetime import datetime
@@ -312,6 +313,7 @@ def render(self, context):
312 313
         return ''
313 314
 
314 315
 def include_is_allowed(filepath):
  316
+    filepath = os.path.abspath(filepath)
315 317
     for root in settings.ALLOWED_INCLUDE_ROOTS:
316 318
         if filepath.startswith(root):
317 319
             return True
31  tests/regressiontests/templates/tests.py
@@ -1737,3 +1737,34 @@ def test_include_only(self):
1737 1737
             template.Template('{% include "child" only %}').render(ctx),
1738 1738
             'none'
1739 1739
         )
  1740
+
  1741
+
  1742
+class SSITests(TestCase):
  1743
+    def setUp(self):
  1744
+        self.this_dir = os.path.dirname(os.path.abspath(upath(__file__)))
  1745
+        self.ssi_dir = os.path.join(self.this_dir, "templates", "first")
  1746
+
  1747
+    def render_ssi(self, path):
  1748
+        # the path must exist for the test to be reliable
  1749
+        self.assertTrue(os.path.exists(path))
  1750
+        return template.Template('{%% ssi "%s" %%}' % path).render(Context())
  1751
+
  1752
+    def test_allowed_paths(self):
  1753
+        acceptable_path = os.path.join(self.ssi_dir, "..", "first", "test.html")
  1754
+        with override_settings(ALLOWED_INCLUDE_ROOTS=(self.ssi_dir,)):
  1755
+            self.assertEqual(self.render_ssi(acceptable_path), 'First template\n')
  1756
+
  1757
+    def test_relative_include_exploit(self):
  1758
+        """
  1759
+        May not bypass ALLOWED_INCLUDE_ROOTS with relative paths
  1760
+
  1761
+        e.g. if ALLOWED_INCLUDE_ROOTS = ("/var/www",), it should not be
  1762
+        possible to do {% ssi "/var/www/../../etc/passwd" %}
  1763
+        """
  1764
+        disallowed_paths = [
  1765
+            os.path.join(self.ssi_dir, "..", "ssi_include.html"),
  1766
+            os.path.join(self.ssi_dir, "..", "second", "test.html"),
  1767
+        ]
  1768
+        with override_settings(ALLOWED_INCLUDE_ROOTS=(self.ssi_dir,)):
  1769
+            for path in disallowed_paths:
  1770
+                self.assertEqual(self.render_ssi(path), '')

0 notes on commit 988b61c

Please sign in to comment.
Something went wrong with that request. Please try again.