zipfile honors inconsistent compression method state when extracting
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 25/100
Direzione di ricerca
Start from the reproduction script in the issue, which rewrites only the local header compression method and then reads the member through zipfile. The fix belongs in the zipfile module's code that reads local file headers and checks them against central directory entries, where the existing file-name mismatch check is the pattern to follow. Done means zipfile rejects an entry whose local and central compression methods differ, and a regression test covers it. The body links PR gh-161948, so check that work before starting.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Bug report
Moving from GHSA to public after discussion consensus.
Summary
An user can contrive a ZIP input such that Python's zipfile attempts to decompress a member of the file with a different decompression method than other parsers will select. This can result in an extraction differential.
Details
ZIP entries record their compression method in two places: the local header and the central directory.
Python honors the central directory entry's compression method, even if it differs from the local file entry's stated method. Consequently, Python will attempt to decompress an entry with an inconsistent method.
The correct behavior is to reject the discrepancy, similarly to how Python already rejects file name discrepancies between the local header and central directory.
PoC
import binascii
import io
import struct
import zipfile
original = b"central-directory interpretation\n" * 4
# Produce a valid DEFLATED archive and close it so the central directory exists.
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w", compression=zipfile.ZIP_DEFLATED) as z:
z.writestr("test.txt", original)
raw = bytearray(buf.getvalue())
with zipfile.ZipFile(io.BytesIO(raw), "r") as z:
info = z.getinfo("test.txt")
local = info.header_offset
assert raw[local : local + 4] == b"PK\x03\x04"
name_len, extra_len = struct.unpack_from("<HH", raw, local + 26)
data_start = local + 30 + name_len + extra_len
compressed = bytes(raw[data_start : data_start + info.compress_size])
# Rewrite ONLY the local header to describe a valid STORED member.
#
# Local-header offsets:
# +8: compression method (uint16)
# +14: CRC-32 (uint32)
# +18: compressed size (uint32)
# +22: uncompressed size (uint32)
struct.pack_into("<H", raw, local + 8, zipfile.ZIP_STORED)
struct.pack_into(
"<III",
raw,
local + 14,
binascii.crc32(compressed) & 0xFFFFFFFF,
len(compressed),
len(compressed),
)
central = raw.index(b"PK\x01\x02")
local_method = struct.unpack_from("<H", raw, local + 8)[0]
central_method = struct.unpack_from("<H", raw, central + 10)[0]
assert local_method == zipfile.ZIP_STORED
assert central_method == zipfile.ZIP_DEFLATED
print(f"local={local_method}, central={central_method}")
# Python trusts the central directory and successfully decompresses.
with zipfile.ZipFile(io.BytesIO(raw), "r") as z:
python_contents = z.read("test.txt")
assert python_contents == original
print("zipfile:", repr(python_contents))
# Simulate a parser that trusts the local header.
local_crc, local_csize, local_usize = struct.unpack_from("<III", raw, local + 14)
local_contents = bytes(raw[data_start : data_start + local_csize])
assert len(local_contents) == local_usize
assert binascii.crc32(local_contents) & 0xFFFFFFFF == local_crc
assert local_contents != python_contents
print("local-header parser:", repr(local_contents))
print("different outputs:", local_contents != python_contents)
Impact
The impact is a fail-open differential: zipfile extracts the malformed ZIP as though it's valid. Other parsers may pick the other incorrect fail-open half of the differential, which is to extract using the local file entry's compression method.
A strict parser should reject a ZIP file if it contains a local file entry/central directory entry pair that has an inconsistent compression method.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux, Windows, macOS
Linked PRs
- gh-161948
- Lingua principale
- Python
- Stelle
- 78.2k
- Fork
- 36.6k
- Merge medio
- 19h 57m
- PR unite (30g)
- 630
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di python/cpython
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
stdlib type-bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno
-
extension-modules stdlib topic-XML type-bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
python/cpython#161807 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
stdlib type-feature
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
python/cpython#161788 · 7 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
extension-modules topic-IO type-bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di python/cpython
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
cpp-best-practices/project_validator#267 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
Project-N-E-K-O/N.E.K.O#3725 ·
I maintainer di solito rispondono entro 1 giorno
-
[Bug] Sending a Telegram message chain twice duplicates mentionsForse già presa @123XieC l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
AstrBotDevs/AstrBot#11125 ·
I maintainer di solito rispondono entro 1 giorno
-
backend bug priority: high
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
nooriegarg/ContribIQ#73 ·
I maintainer di solito rispondono entro 1 giorno
-
bug p3 security
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno