zipfile honors inconsistent compression method state when extracting
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 3/5
- Tempo estimado
- 1-2 dias
- Facilidade para iniciantes
- 25/100
Direção de pesquisa
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.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
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
- Linguagem predominante
- Python
- Estrelas
- 78.2k
- Forks
- 36.6k
- Merge médio
- 19h 57min
- PRs com merge (30d)
- 630
Preparar o ambiente
Inicia o contêiner de desenvolvimento do projeto no navegador, com a sua própria conta do GitHub.
- Sem Dockerfile nem arquivo Docker Compose
- Tem um modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de python/cpython
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Mantenedores costumam responder em até 1 dia
-
stdlib type-bug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 75/100
Mantenedores costumam responder em até 1 dia
-
extension-modules stdlib topic-XML type-bug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
python/cpython#161807 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
stdlib type-feature
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
python/cpython#161788 · 7 comentários ·
Mantenedores costumam responder em até 1 dia
-
extension-modules topic-IO type-bug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 75/100
Mantenedores costumam responder em até 1 dia
Todas as issues de python/cpython
Issues semelhantes
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 62/100
cpp-best-practices/project_validator#267 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
Project-N-E-K-O/N.E.K.O#3725 ·
Mantenedores costumam responder em até 1 dia
-
[Bug] Sending a Telegram message chain twice duplicates mentionsTalvez já em andamento @123XieC assumiu hoje. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
AstrBotDevs/AstrBot#11125 ·
Mantenedores costumam responder em até 1 dia
-
backend bug priority: high
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
nooriegarg/ContribIQ#73 ·
Mantenedores costumam responder em até 1 dia
-
bug p3 security
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 68/100
amd/gaia#8197 · 2 comentários ·
Mantenedores costumam responder em até 1 dia