Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 02e6592

Browse files
committed
[3.1.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 05413af from master.
1 parent 03a8678 commit 02e6592

File tree

10 files changed

+77
-5
lines changed

10 files changed

+77
-5
lines changed

django/utils/archive.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import tarfile
2828
import zipfile
2929

30+
from django.core.exceptions import SuspiciousOperation
31+
3032

3133
class ArchiveException(Exception):
3234
"""
@@ -133,6 +135,13 @@ def has_leading_dir(self, paths):
133135
return False
134136
return True
135137

138+
def target_filename(self, to_path, name):
139+
target_path = os.path.abspath(to_path)
140+
filename = os.path.abspath(os.path.join(target_path, name))
141+
if not filename.startswith(target_path):
142+
raise SuspiciousOperation("Archive contains invalid path: '%s'" % name)
143+
return filename
144+
136145
def extract(self):
137146
raise NotImplementedError('subclasses of BaseArchive must provide an extract() method')
138147

@@ -155,7 +164,7 @@ def extract(self, to_path):
155164
name = member.name
156165
if leading:
157166
name = self.split_leading_dir(name)[1]
158-
filename = os.path.join(to_path, name)
167+
filename = self.target_filename(to_path, name)
159168
if member.isdir():
160169
if filename:
161170
os.makedirs(filename, exist_ok=True)
@@ -198,8 +207,10 @@ def extract(self, to_path):
198207
info = self._archive.getinfo(name)
199208
if leading:
200209
name = self.split_leading_dir(name)[1]
201-
filename = os.path.join(to_path, name)
202-
if filename.endswith(('/', '\\')):
210+
if not name:
211+
continue
212+
filename = self.target_filename(to_path, name)
213+
if name.endswith(('/', '\\')):
203214
# A directory
204215
os.makedirs(filename, exist_ok=True)
205216
else:

docs/releases/2.2.18.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
===========================
2+
Django 2.2.18 release notes
3+
===========================
4+
5+
*February 1, 2021*
6+
7+
Django 2.2.18 fixes a security issue with severity "low" in 2.2.17.
8+
9+
CVE-2021-3281: Potential directory-traversal via ``archive.extract()``
10+
======================================================================
11+
12+
The ``django.utils.archive.extract()`` function, used by
13+
:option:`startapp --template` and :option:`startproject --template`, allowed
14+
directory-traversal via an archive with absolute paths or relative paths with
15+
dot segments.

docs/releases/3.0.12.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
===========================
2+
Django 3.0.12 release notes
3+
===========================
4+
5+
*February 1, 2021*
6+
7+
Django 3.0.12 fixes a security issue with severity "low" in 3.0.11.
8+
9+
CVE-2021-3281: Potential directory-traversal via ``archive.extract()``
10+
======================================================================
11+
12+
The ``django.utils.archive.extract()`` function, used by
13+
:option:`startapp --template` and :option:`startproject --template`, allowed
14+
directory-traversal via an archive with absolute paths or relative paths with
15+
dot segments.

docs/releases/3.1.6.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22
Django 3.1.6 release notes
33
==========================
44

5-
*Expected February 1, 2021*
5+
*February 1, 2021*
66

7-
Django 3.1.6 fixes several bugs in 3.1.5.
7+
Django 3.1.6 fixes a security issue with severity "low" and a bug in 3.1.5.
8+
9+
CVE-2021-3281: Potential directory-traversal via ``archive.extract()``
10+
======================================================================
11+
12+
The ``django.utils.archive.extract()`` function, used by
13+
:option:`startapp --template` and :option:`startproject --template`, allowed
14+
directory-traversal via an archive with absolute paths or relative paths with
15+
dot segments.
816

917
Bugfixes
1018
========

docs/releases/index.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ versions of the documentation contain the release notes for any later releases.
3838
.. toctree::
3939
:maxdepth: 1
4040

41+
3.0.12
4142
3.0.11
4243
3.0.10
4344
3.0.9
@@ -56,6 +57,7 @@ versions of the documentation contain the release notes for any later releases.
5657
.. toctree::
5758
:maxdepth: 1
5859

60+
2.2.18
5961
2.2.17
6062
2.2.16
6163
2.2.15

tests/utils_tests/test_archive.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import tempfile
55
import unittest
66

7+
from django.core.exceptions import SuspiciousOperation
8+
from django.test import SimpleTestCase
79
from django.utils import archive
810

911

@@ -45,3 +47,22 @@ def test_extract_file_permissions(self):
4547
# A file is readable even if permission data is missing.
4648
filepath = os.path.join(tmpdir, 'no_permissions')
4749
self.assertEqual(os.stat(filepath).st_mode & mask, 0o666 & ~umask)
50+
51+
52+
class TestArchiveInvalid(SimpleTestCase):
53+
def test_extract_function_traversal(self):
54+
archives_dir = os.path.join(os.path.dirname(__file__), 'traversal_archives')
55+
tests = [
56+
('traversal.tar', '..'),
57+
('traversal_absolute.tar', '/tmp/evil.py'),
58+
]
59+
if sys.platform == 'win32':
60+
tests += [
61+
('traversal_disk_win.tar', 'd:evil.py'),
62+
('traversal_disk_win.zip', 'd:evil.py'),
63+
]
64+
msg = "Archive contains invalid path: '%s'"
65+
for entry, invalid_path in tests:
66+
with self.subTest(entry), tempfile.TemporaryDirectory() as tmpdir:
67+
with self.assertRaisesMessage(SuspiciousOperation, msg % invalid_path):
68+
archive.extract(os.path.join(archives_dir, entry), tmpdir)
10 KB
Binary file not shown.
10 KB
Binary file not shown.
10 KB
Binary file not shown.
312 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)