mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	[3.2.x] Fixed CVE-2021-3281 -- Fixed potential directory-traversal via archive.extract().
Thanks Florian Apolloner, Shai Berger, and Simon Charette for reviews.
Thanks Wang Baohua for the report.
Backport of 05413afa8c from master.
			
			
This commit is contained in:
		| @@ -27,6 +27,8 @@ import stat | ||||
| import tarfile | ||||
| import zipfile | ||||
|  | ||||
| from django.core.exceptions import SuspiciousOperation | ||||
|  | ||||
|  | ||||
| class ArchiveException(Exception): | ||||
|     """ | ||||
| @@ -133,6 +135,13 @@ class BaseArchive: | ||||
|                 return False | ||||
|         return True | ||||
|  | ||||
|     def target_filename(self, to_path, name): | ||||
|         target_path = os.path.abspath(to_path) | ||||
|         filename = os.path.abspath(os.path.join(target_path, name)) | ||||
|         if not filename.startswith(target_path): | ||||
|             raise SuspiciousOperation("Archive contains invalid path: '%s'" % name) | ||||
|         return filename | ||||
|  | ||||
|     def extract(self): | ||||
|         raise NotImplementedError('subclasses of BaseArchive must provide an extract() method') | ||||
|  | ||||
| @@ -155,7 +164,7 @@ class TarArchive(BaseArchive): | ||||
|             name = member.name | ||||
|             if leading: | ||||
|                 name = self.split_leading_dir(name)[1] | ||||
|             filename = os.path.join(to_path, name) | ||||
|             filename = self.target_filename(to_path, name) | ||||
|             if member.isdir(): | ||||
|                 if filename: | ||||
|                     os.makedirs(filename, exist_ok=True) | ||||
| @@ -198,8 +207,10 @@ class ZipArchive(BaseArchive): | ||||
|             info = self._archive.getinfo(name) | ||||
|             if leading: | ||||
|                 name = self.split_leading_dir(name)[1] | ||||
|             filename = os.path.join(to_path, name) | ||||
|             if filename.endswith(('/', '\\')): | ||||
|             if not name: | ||||
|                 continue | ||||
|             filename = self.target_filename(to_path, name) | ||||
|             if name.endswith(('/', '\\')): | ||||
|                 # A directory | ||||
|                 os.makedirs(filename, exist_ok=True) | ||||
|             else: | ||||
|   | ||||
							
								
								
									
										15
									
								
								docs/releases/2.2.18.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								docs/releases/2.2.18.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| =========================== | ||||
| Django 2.2.18 release notes | ||||
| =========================== | ||||
|  | ||||
| *February 1, 2021* | ||||
|  | ||||
| Django 2.2.18 fixes a security issue with severity "low" in 2.2.17. | ||||
|  | ||||
| CVE-2021-3281: Potential directory-traversal via ``archive.extract()`` | ||||
| ====================================================================== | ||||
|  | ||||
| The ``django.utils.archive.extract()`` function, used by | ||||
| :option:`startapp --template` and :option:`startproject --template`, allowed | ||||
| directory-traversal via an archive with absolute paths or relative paths with | ||||
| dot segments. | ||||
							
								
								
									
										15
									
								
								docs/releases/3.0.12.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								docs/releases/3.0.12.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| =========================== | ||||
| Django 3.0.12 release notes | ||||
| =========================== | ||||
|  | ||||
| *February 1, 2021* | ||||
|  | ||||
| Django 3.0.12 fixes a security issue with severity "low" in 3.0.11. | ||||
|  | ||||
| CVE-2021-3281: Potential directory-traversal via ``archive.extract()`` | ||||
| ====================================================================== | ||||
|  | ||||
| The ``django.utils.archive.extract()`` function, used by | ||||
| :option:`startapp --template` and :option:`startproject --template`, allowed | ||||
| directory-traversal via an archive with absolute paths or relative paths with | ||||
| dot segments. | ||||
| @@ -2,9 +2,17 @@ | ||||
| Django 3.1.6 release notes | ||||
| ========================== | ||||
|  | ||||
| *Expected February 1, 2021* | ||||
| *February 1, 2021* | ||||
|  | ||||
| Django 3.1.6 fixes several bugs in 3.1.5. | ||||
| Django 3.1.6 fixes a security issue with severity "low" and a bug in 3.1.5. | ||||
|  | ||||
| CVE-2021-3281: Potential directory-traversal via ``archive.extract()`` | ||||
| ====================================================================== | ||||
|  | ||||
| The ``django.utils.archive.extract()`` function, used by | ||||
| :option:`startapp --template` and :option:`startproject --template`, allowed | ||||
| directory-traversal via an archive with absolute paths or relative paths with | ||||
| dot segments. | ||||
|  | ||||
| Bugfixes | ||||
| ======== | ||||
|   | ||||
| @@ -45,6 +45,7 @@ versions of the documentation contain the release notes for any later releases. | ||||
| .. toctree:: | ||||
|    :maxdepth: 1 | ||||
|  | ||||
|    3.0.12 | ||||
|    3.0.11 | ||||
|    3.0.10 | ||||
|    3.0.9 | ||||
| @@ -63,6 +64,7 @@ versions of the documentation contain the release notes for any later releases. | ||||
| .. toctree:: | ||||
|    :maxdepth: 1 | ||||
|  | ||||
|    2.2.18 | ||||
|    2.2.17 | ||||
|    2.2.16 | ||||
|    2.2.15 | ||||
|   | ||||
| @@ -4,6 +4,8 @@ import sys | ||||
| import tempfile | ||||
| import unittest | ||||
|  | ||||
| from django.core.exceptions import SuspiciousOperation | ||||
| from django.test import SimpleTestCase | ||||
| from django.utils import archive | ||||
|  | ||||
|  | ||||
| @@ -45,3 +47,22 @@ class TestArchive(unittest.TestCase): | ||||
|                 # A file is readable even if permission data is missing. | ||||
|                 filepath = os.path.join(tmpdir, 'no_permissions') | ||||
|                 self.assertEqual(os.stat(filepath).st_mode & mask, 0o666 & ~umask) | ||||
|  | ||||
|  | ||||
| class TestArchiveInvalid(SimpleTestCase): | ||||
|     def test_extract_function_traversal(self): | ||||
|         archives_dir = os.path.join(os.path.dirname(__file__), 'traversal_archives') | ||||
|         tests = [ | ||||
|             ('traversal.tar', '..'), | ||||
|             ('traversal_absolute.tar', '/tmp/evil.py'), | ||||
|         ] | ||||
|         if sys.platform == 'win32': | ||||
|             tests += [ | ||||
|                 ('traversal_disk_win.tar', 'd:evil.py'), | ||||
|                 ('traversal_disk_win.zip', 'd:evil.py'), | ||||
|             ] | ||||
|         msg = "Archive contains invalid path: '%s'" | ||||
|         for entry, invalid_path in tests: | ||||
|             with self.subTest(entry), tempfile.TemporaryDirectory() as tmpdir: | ||||
|                 with self.assertRaisesMessage(SuspiciousOperation, msg % invalid_path): | ||||
|                     archive.extract(os.path.join(archives_dir, entry), tmpdir) | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								tests/utils_tests/traversal_archives/traversal.tar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/utils_tests/traversal_archives/traversal.tar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								tests/utils_tests/traversal_archives/traversal_absolute.tar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/utils_tests/traversal_archives/traversal_absolute.tar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								tests/utils_tests/traversal_archives/traversal_disk_win.tar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/utils_tests/traversal_archives/traversal_disk_win.tar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								tests/utils_tests/traversal_archives/traversal_disk_win.zip
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/utils_tests/traversal_archives/traversal_disk_win.zip
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user