[3.0.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:
parent
74ca3cac32
commit
52e409ed17
|
@ -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:
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
|
@ -25,6 +25,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
|
||||
|
@ -43,6 +44,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)
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue