summaryrefslogtreecommitdiffstats
path: root/Lib/zipfile
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2024-01-10 13:55:36 (GMT)
committerGitHub <noreply@github.com>2024-01-10 13:55:36 (GMT)
commit66363b9a7b9fe7c99eba3a185b74c5fdbf842eba (patch)
tree9ec743e764771d1adca1142c7165eb2090217a3a /Lib/zipfile
parent183b97bb9db075197153ad82b8ffdfce8e913250 (diff)
downloadcpython-66363b9a7b9fe7c99eba3a185b74c5fdbf842eba.zip
cpython-66363b9a7b9fe7c99eba3a185b74c5fdbf842eba.tar.gz
cpython-66363b9a7b9fe7c99eba3a185b74c5fdbf842eba.tar.bz2
gh-109858: Protect zipfile from "quoted-overlap" zipbomb (GH-110016)
Raise BadZipFile when try to read an entry that overlaps with other entry or central directory.
Diffstat (limited to 'Lib/zipfile')
-rw-r--r--Lib/zipfile/__init__.py12
1 files changed, 12 insertions, 0 deletions
diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py
index 1c415a2..1d8a607 100644
--- a/Lib/zipfile/__init__.py
+++ b/Lib/zipfile/__init__.py
@@ -395,6 +395,7 @@ class ZipInfo (object):
'compress_size',
'file_size',
'_raw_time',
+ '_end_offset',
)
def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
@@ -429,6 +430,7 @@ class ZipInfo (object):
self.external_attr = 0 # External file attributes
self.compress_size = 0 # Size of the compressed file
self.file_size = 0 # Size of the uncompressed file
+ self._end_offset = None # Start of the next local header or central directory
# Other attributes are set by class ZipFile:
# header_offset Byte offset to the file header
# CRC CRC-32 of the uncompressed file
@@ -1488,6 +1490,12 @@ class ZipFile:
if self.debug > 2:
print("total", total)
+ end_offset = self.start_dir
+ for zinfo in sorted(self.filelist,
+ key=lambda zinfo: zinfo.header_offset,
+ reverse=True):
+ zinfo._end_offset = end_offset
+ end_offset = zinfo.header_offset
def namelist(self):
"""Return a list of file names in the archive."""
@@ -1644,6 +1652,10 @@ class ZipFile:
'File name in directory %r and header %r differ.'
% (zinfo.orig_filename, fname))
+ if (zinfo._end_offset is not None and
+ zef_file.tell() + zinfo.compress_size > zinfo._end_offset):
+ raise BadZipFile(f"Overlapped entries: {zinfo.orig_filename!r} (possible zip bomb)")
+
# check for encrypted flag & handle password
is_encrypted = zinfo.flag_bits & _MASK_ENCRYPTED
if is_encrypted: