zipfile.BadZipFile: ошибка CRC-32 при извлечении защищенного паролем .zip & .zip повреждена при извлечении - PullRequest
0 голосов
/ 05 февраля 2019

Я пытаюсь извлечь защищенный паролем файл .zip с документом .txt (скажем Congrats.txt для этого случая).Теперь Congrats.txt содержит текст, поэтому его размер не равен 0 КБ.Он помещается в .zip (ради этого потока давайте назовем этот .zip zipv1.zip) с паролем dominique для этого потока.Этот пароль хранится среди других слов и имен в другом .txt (который мы назовем его file.txt ради этого вопроса).Теперь, если я запускаю приведенный ниже код, выполнив python Program.py -z zipv1.zip -f file.txt (при условии, что все эти файлы находятся в той же папке, что и Program.py), моя программа отобразит dominique в качестве правильного пароля для zipv1.zip среди других слов / паролей в file.txt и извлекает zipv1.zip, но Congrats.txt пуст и имеет размер 0 КБ.

Теперь мой код выглядит следующим образом:

import argparse
import multiprocessing
import zipfile

parser = argparse.ArgumentParser(description="Unzips a password protected .zip", usage="Program.py -z zip.zip -f file.txt")
# Creates -z arg
parser.add_argument("-z", "--zip", metavar="", required=True, help="Location and the name of the .zip file.")
# Creates -f arg
parser.add_argument("-f", "--file", metavar="", required=True, help="Location and the name of file.txt.")
args = parser.parse_args()


def extract_zip(zip_filename, password):
    try:
        zip_file = zipfile.ZipFile(zip_filename)
        zip_file.extractall(pwd=password)
        print(f"[+] Password for the .zip: {password.decode('utf-8')} \n")
    except:
        # If a password fails, it moves to the next password without notifying the user. If all passwords fail, it will print nothing in the command prompt.
        pass


def main(zip, file):
    if (zip == None) | (file == None):
        # If the args are not used, it displays how to use them to the user.
        print(parser.usage)
        exit(0)
    # Opens the word list/password list/dictionary in "read binary" mode.
    txt_file = open(file, "rb")
    # Allows 8 instances of Python to be ran simultaneously.
    with multiprocessing.Pool(8) as pool:
        # "starmap" expands the tuples as 2 separate arguments to fit "extract_zip"
        pool.starmap(extract_zip, [(zip, line.strip()) for line in txt_file])


if __name__ == '__main__':
    main(args.zip, args.file)

Однако, если я еще один zip (zipv2.zip) тем же методом, что и zipv1.zip, с той лишь разницей, что Congrats.txt находится в папке, в которую папка упакована вместе с Congrats.txt Я получаю те же результаты, что и zipv1.zip, но на этот раз Congrats.txt извлекается вместепапка, в которой она находилась, и Congrats.txt была целой;текст в нем и его размер были целы.

Поэтому, чтобы решить эту проблему, я попытался прочитать документацию zipfile , где я обнаружил, что если пароль не совпадает с .zip, он выбрасываетRuntimeError.Поэтому я изменил except: в коде на except RuntimeError: и получил эту ошибку при попытке разархивировать zipv1.zip:

(venv) C:\Users\USER\Documents\Jetbrains\PyCharm\Program>Program.py -z zipv1.zip -f file.txt
[+] Password for the .zip: dominique

multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 121, in worker
result = (True, func(*args, **kwds))
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 47, in starmapstar
return list(itertools.starmap(args[0], args[1]))
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 16, in extract_zip
zip_file.extractall(pwd=password)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 1594, in extractall
self._extract_member(zipinfo, path, pwd)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 1649, in _extract_member
shutil.copyfileobj(source, target)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\shutil.py", line 79, in copyfileobj
buf = fsrc.read(length)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 876, in read
data = self._read1(n)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 966, in _read1
self._update_crc(data)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 894, in _update_crc
raise BadZipFile("Bad CRC-32 for file %r" % self.name)
zipfile.BadZipFile: Bad CRC-32 for file 'Congrats.txt'
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 38, in <module>
main(args.zip, args.file)
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 33, in main
pool.starmap(extract_zip, [(zip, line.strip()) for line in txt_file])
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 276, in starmap
return self._map_async(func, iterable, starmapstar, chunksize).get()
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 657, in get
raise self._value
zipfile.BadZipFile: Bad CRC-32 for file 'Congrats.txt'

Хотя те же результаты более счастливы;пароль был найден в file.txt, zipv1.zip был извлечен, но Congrats.txt был пуст и имел размер 0 КБ.Поэтому я снова запустил программу, но на этот раз zipv2.zip и получил в результате:

(venv) C:\Users\USER\Documents\Jetbrains\PyCharm\Program>Program.py -z zipv2.zip -f file.txt
[+] Password for the .zip: dominique

multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 121, in worker
result = (True, func(*args, **kwds))
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 47, in starmapstar
return list(itertools.starmap(args[0], args[1]))
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 16, in extract_zip
zip_file.extractall(pwd=password)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 1594, in extractall
self._extract_member(zipinfo, path, pwd)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 1649, in _extract_member
shutil.copyfileobj(source, target)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\shutil.py", line 79, in copyfileobj
buf = fsrc.read(length)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 876, in read
data = self._read1(n)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 966, in _read1
self._update_crc(data)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 894, in _update_crc
raise BadZipFile("Bad CRC-32 for file %r" % self.name)
zipfile.BadZipFile: Bad CRC-32 for file 'Congrats.txt'
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 38, in <module>
main(args.zip, args.file)
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 33, in main
pool.starmap(extract_zip, [(zip, line.strip()) for line in txt_file])
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 276, in starmap
return self._map_async(func, iterable, starmapstar, chunksize).get()
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 657, in get
raise self._value
zipfile.BadZipFile: Bad CRC-32 for file 'Congrats.txt'

Опять те же результаты;где папка была успешно извлечена, и Congrats.txt также был извлечен с текстом внутри нее, а ее размер не изменился.

Я также взглянул на эту аналогичную ветку, а такжекак этот поток, но они не помогли.Я также проверил документацию zipfile , но это не помогло в отношении проблемы.

Я не уверен, что является причиной моей проблемы или как ее исправить, и хотел бы получить некоторую помощь в отношении этой проблемы..

РЕДАКТИРОВАТЬ

Теперь после реализации with zipfile.ZipFile(zip_filename, 'r') as zip_file: по неизвестной и странной причине;программа может читать / обрабатывать небольшой список слов / список паролей / словарь, но не может, если он большой (?).

Под этим я подразумеваю, что текст .txt присутствует в zipv1.zip;с именем Congrats.txt с текстом You have cracked the .zip!.Тот же самый .txt присутствует и в zipv2.zip, но на этот раз помещен в папку с именем ZIP Contents, а затем защищен паролем.Пароль - dominique для обоих почтовых индексов.

Обратите внимание, что каждый ZIP-файл был создан с использованием Deflate метода сжатия и ZipCrypto шифрования в 7zip.

Теперь этот пароль в Line 35 (35/52 строк) John The Ripper Jr.txt и Line 1968 для John The Ripper.txt (1968/3106 строк).

Теперь, если вы делаете python Program.py -z zipv1 -f "John The Ripper Jr.txt" в своем CMD (или IDE по вашему выбору);он создаст папку с именем Extracted и поместит Congrats.txt с предложением, которое мы ранее установили.То же самое касается zipv2, но Congrats.txt будет в папке ZIP Contents, которая находится внутри папки Extracted.Нет проблем с извлечением .zips в этом случае.

Но если вы попробуете то же самое с John The Ripper.txt, т.е. python Program.py -z zipv1 -f "John The Ripper.txt" в вашей CMD (или IDE по вашему выбору), это создаст папку Extracted с двумя zip-файлами;точно так же, как John The Ripper Jr.txt, но на этот раз Congrats.txt будет пусто для них обоих по неизвестной причине.

Мой код и все необходимые файлы выглядят следующим образом:

import argparse
import multiprocessing
import zipfile

parser = argparse.ArgumentParser(description="Unzips a password protected .zip by performing a brute-force attack.", usage="Program.py -z zip.zip -f file.txt")
# Creates -z arg
parser.add_argument("-z", "--zip", metavar="", required=True, help="Location and the name of the .zip file.")
# Creates -f arg
parser.add_argument("-f", "--file", metavar="", required=True, help="Location and the name of the word list/password list/dictionary.")
args = parser.parse_args()


def extract_zip(zip_filename, password):
    try:
        with zipfile.ZipFile(zip_filename, 'r') as zip_file:
            zip_file.extractall('Extracted', pwd=password)
            print(f"[+] Password for the .zip: {password.decode('utf-8')} \n")
    except:
        # If a password fails, it moves to the next password without notifying the user. If all passwords fail, it will print nothing in the command prompt.
        pass


def main(zip, file):
    if (zip == None) | (file == None):
        # If the args are not used, it displays how to use them to the user.
        print(parser.usage)
        exit(0)
    # Opens the word list/password list/dictionary in "read binary" mode.
    txt_file = open(file, "rb")
    # Allows 8 instances of Python to be ran simultaneously.
    with multiprocessing.Pool(8) as pool:
        # "starmap" expands the tuples as 2 separate arguments to fit "extract_zip"
        pool.starmap(extract_zip, [(zip, line.strip()) for line in txt_file])


if __name__ == '__main__':
    # Program.py - z zipname.zip -f filename.txt
    main(args.zip, args.file)

Program.py

zipv1.zip

zipv2.zip

Джон Потрошитель Jr.txt

Джон Потрошитель.txt

Джон Потрошитель v2.txt

Я не уверен, почему это происходит, и не могу найти ответ на этот вопрос нигде.Это абсолютно неизвестно из того, что я могу сказать, и я не могу найти способ отладить или решить эту проблему.

Это продолжает происходить независимо от разных списков слов / паролей.Попытался сгенерировать больше .zips с тем же Congrats.txt, но с разными паролями из разных списков слов / списков паролей / словарей.Тот же метод;была использована более крупная и меньшая версия .txt, и были достигнуты те же результаты, что и выше.

НО Я обнаружил, что если я вырежу первые 2k слов в John The Ripper.txt исделать новый .txt;скажем John The Ripper v2.txt;ZIP-файл успешно извлечен, появляется папка Extracted и присутствует Congrats.txt с текстом внутри нее.Поэтому я считаю, что это связано со строками после ввода пароля.Так что в этом случае Line 1968;где скрипт не останавливается после Line 1968?Я не уверен, почему это работает, хотя.Это не решение, а шаг к решению, я думаю ...

Любая помощь будет принята с благодарностью.

РЕДАКТИРОВАТЬ 2

Поэтому я попытался использовать код «завершения пула»:

import argparse
import multiprocessing
import zipfile

parser = argparse.ArgumentParser(description="Unzips a password protected .zip by performing a brute-force attack using", usage="Program.py -z zip.zip -f file.txt")
# Creates -z arg
parser.add_argument("-z", "--zip", metavar="", required=True, help="Location and the name of the .zip file.")
# Creates -f arg
parser.add_argument("-f", "--file", metavar="", required=True, help="Location and the name of the word list/password list/dictionary.")
args = parser.parse_args()


def extract_zip(zip_filename, password, queue):
    try:
        with zipfile.ZipFile(zip_filename, "r") as zip_file:
            zip_file.extractall('Extracted', pwd=password)
            print(f"[+] Password for the .zip: {password.decode('utf-8')} \n")
            queue.put("Done")  # Signal success
    except:
        # If a password fails, it moves to the next password without notifying the user. If all passwords fail, it will print nothing in the command prompt.
        pass


def main(zip, file):
    if (zip == None) | (file == None):
        print(parser.usage)  # If the args are not used, it displays how to use them to the user.
        exit(0)
    # Opens the word list/password list/dictionary in "read binary" mode.
    txt_file = open(file, "rb")

    # Create a Queue
    manager = multiprocessing.Manager()
    queue = manager.Queue()

    with multiprocessing.Pool(8) as pool:  # Allows 8 instances of Python to be ran simultaneously.
        pool.starmap_async(extract_zip, [(zip, line.strip(), queue) for line in txt_file])  # "starmap" expands the tuples as 2 separate arguments to fit "extract_zip"
        pool.close()
        queue.get(True)  # Wait for a process to signal success
        pool.terminate()  # Terminate the pool
        pool.join()


if __name__ == '__main__':
    main(args.zip, args.file)  # Program.py -z zip.zip -f file.txt.

Теперь, если я использую это, оба архива извлекаются успешно, как и предыдущие экземпляры. НО на этот раз zipv1.zip Congrats.txt не поврежден;есть сообщение внутри него.Но то же самое нельзя сказать о zipv2.zip, поскольку он все еще пуст.

1 Ответ

0 голосов
/ 08 марта 2019

Извините за долгую паузу ... Кажется, вы попали в засолку.

1.Расследование

Сценарий сложный (довольно далеко от M CVE , я бы сказал), есть много вещей, которые можно обвинить в поведении.

Начиная с zipv1.zip / zipv2.zip несовпадение.При ближайшем рассмотрении оказывается, что zipv2 также испорчен .Если вещи легко обнаружить для zipv1 ( Congrats.txt является единственным файлом), для zipv2 , "ZIP Contents / Black-Large.png " 0 size .
Воспроизводится с любым файлом, и более того: применяется к 1 st записи (которая не является dir), возвращаемой zf.namelist.

Итак, все становится немного яснее:

  • Содержимое файла распаковывается, из-за dominique , присутствующего в файле паролей (не знаю, что произойдет, покаточка)
  • Позже запись .zip 1 st усекается до 0 байт

Глядя на исключения, возникающие при попытке извлечь файлы с использованием неверного пароля, существует 3 типа (из которых последние 2 могут быть сгруппированы вместе):

  1. RuntimeError: Неверный пароль для файла ...
  2. Другие:
    • zlib.error: Ошибка -3 при распаковке данных ...
    • zipfile.BadZipFile: Bad CRC-32 для файла ...

Я создал собственный архивный файл.Ради согласованности я буду использовать его с этого момента, но все будет относиться и к любому другому файлу.

  • Содержимое:
    • DummyFile0.zip ( 10 байт) - содержит: 0123456789
    • DummyFile1.zip ( 10 байт) - содержит: 0000000000
    • DummyFile2.zip ( 10 байт) - содержит: AAAAAAAAAA
  • Заархивировал 3 файла с помощью Total Commander ( 9.21a ) внутреннего упаковщика zip , защитив его паролем dominique ( zip2.0 шифрование).Результирующий архив (с именем arc0.zip (но имя не имеет значения)) имеет длину 392 байт

code.py :

#!/usr/bin/env python3

import sys
import os
import zipfile


def main():
    arc_name = sys.argv[1] if len(sys.argv) > 1 else "./arc0.zip"
    pwds = [
        #b"dominique",
        #b"dickhead",
        b"coco",
    ]
    pwds = [item.strip() for item in open("orig/John The Ripper.txt.orig", "rb").readlines()]
    print("Unpacking (password protected: dominique) {:s},"
          " using a list of predefined passwords ...".format(arc_name))
    if not os.path.isfile(arc_name):
        raise SystemExit("Archive file must exist!\nExiting.")
    faulty_pwds = list()
    good_pwds = list()
    with zipfile.ZipFile(arc_name, "r") as zip_file:
        print("Zip names: {:}\n".format(zip_file.namelist()))
        for idx, pwd in enumerate(pwds):
            try:
                zip_file.extractall("Extracted", pwd=pwd)
            except:
                exc_cls, exc_inst, exc_tb = sys.exc_info()
                if exc_cls != RuntimeError:
                    print("Exception caught when using password ({:d}): [{:}] ".format(idx, pwd))
                    print("    {:}: {:}".format(exc_cls, exc_inst))
                    faulty_pwds.append(pwd)
            else:
                print("Success using password ({:d}): [{:}] ".format(idx, pwd))
                good_pwds.append(pwd)
    print("\nFaulty passwords: {:}\nGood passwords: {:}".format(faulty_pwds, good_pwds))


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

Вывод :

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q054532010]> "e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py arc0.zip
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32

Unpacking (password protected: dominique) arc0.zip, using a list of predefined passwords ...
Zip names: ['DummyFile0.txt', 'DummyFile1.txt', 'DummyFile2.txt']

Exception caught when using password (1189): [b'mariah']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid code lengths set
Exception caught when using password (1446): [b'zebra']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid block type
Exception caught when using password (1477): [b'1977']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid block type
Success using password (1967): [b'dominique']
Exception caught when using password (2122): [b'hank']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid code lengths set
Exception caught when using password (2694): [b'solomon']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid distance code
Exception caught when using password (2768): [b'target']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid block type
Exception caught when using password (2816): [b'trish']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid code lengths set
Exception caught when using password (2989): [b'coco']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid stored block lengths

Faulty passwords: [b'mariah', b'zebra', b'1977', b'hank', b'solomon', b'target', b'trish', b'coco']
Good passwords: [b'dominique']

Глядя на код ZipFile.extractall, он пытается извлечь все элементы.1 st вызывает исключение, поэтому становится понятнее, почему он ведет себя так, как он.Но почему разница в поведении при попытке извлечь элементы с использованием 2 неправильных паролей?
Как видно из трассировок 2 различных типов исключений, ответ лежит где-то в конце ZipFile.open.

После дополнительных исследований выясняется, что это из-за

2.Столкновение определяется по zip слабости шифрования

Согласно [UT.CS]: dmitri-report-f15-16.pdf - Шифрование на основе пароля в файлах ZIP ((последнее) выделение принадлежит мне):

3.1 Традиционное шифрование PKWARE

Исходная схема шифрования, обычно называемая PKZIPШифр, был разработан Роджером Шаффели [1].В [5] Бихам и Кохер показали, что шифр слабый, и продемонстрировали атаку, требующую 13 байт открытого текста.Были разработаны дальнейшие атаки, некоторые из которых вообще не требуют открытого текста [6].Шифр PKZIP, по сути, является потоковым шифром, т. Е. Ввод шифруется путем генерации потока псевдослучайного ключа и его XOR с открытым текстом.Внутреннее состояние шифра состоит из трех 32-битных слов: key0 , key1 и key2 .Они инициализируются в 0x12345678 , 0x23456789 и 0x34567890 соответственно.Основной шаг алгоритма включает обновление трех ключей с использованием одного байта ввода ...

...

Перед шифрованием файла в архиве сначала добавляется 12 случайных байтов.его сжатое содержимое и результирующий поток данных затем шифруются.После расшифровки первые 12 байтов должны быть отброшены.Согласно спецификации, это делается для того, чтобы сделать атаку открытым текстом на данные неэффективной.В спецификации также указывается, что из 12 добавленных байтов только первые 11 являются фактически случайными, последний байт равен старшему байту CRC-32 несжатого содержимого файла.Это дает возможность быстро проверить, является ли данный пароль правильным, сравнивая последний байт расшифрованного 12-байтового заголовка с старшим байтом фактического значения CRC-32, которое включено в локальный заголовок файла .Это можно сделать перед расшифровкой остальной части файла.

Другие ссылки:

Слабость алгоритма: из-за того, что дифференцирование выполняется на только один байт , для 256 отличается (и тщательно выбранный) неправильных паролей, будет один (как минимум), который будет генерировать то же число, что и правильный пароль .

Алгоритм отбрасывает большинство неправильных паролей,но есть некоторые, которые этого не делают.

Возвращаясь назад: при попытке извлечь файл с помощью пароля:

  • Если "хэш" вычислен на шифре файлапоследний байт отличается от старшего байта файла CRC , генерируется исключение
  • Но , если они равны:
    • НовыйПоток файла открыт для записи (очистка файла, если он уже существует)
    • Распаковкапопытка:
      • При неправильных паролях (прошедших вышеуказанную проверку) распаковка завершится неудачно (но файл уже очищен)

Как видно из приведенного выше вывода, для моего ( .zip ) файла есть пароли 8 , которые запутывают его.Обратите внимание, что:

  • Для каждого файла архива результат отличается
  • member имя файла и его содержимое имеют отношение (по крайней мере, для 1 st один).Изменение любого из них приведет к другим результатам (для «одного и того же» архивного файла)

Вот тест, основанный на данных из моего .zip файла:

>>> import zipfile
>>>
>>> zd_coco = zipfile._ZipDecrypter(b"coco")
>>> zd_dominique = zipfile._ZipDecrypter(b"dominique")
>>> zd_other = zipfile._ZipDecrypter(b"other")
>>> cipher = b'\xd1\x86y ^\xd77gRzZ\xee'  # Member (1st) file cipher: 12 bytes starting from archive offset 44
>>>
>>> crc = 2793719750  # Member (1st) file CRC - archive bytes: 14 - 17
>>> hex(crc)
'0xa684c7c6'
>>> for zd in (zd_coco, zd_dominique, zd_other):
...     print(zd, [hex(zd(c)) for c in cipher])
...
<zipfile._ZipDecrypter object at 0x0000021E8DA2E0F0> ['0x1f', '0x58', '0x89', '0x29', '0x89', '0xe', '0x32', '0xe7', '0x2', '0x31', '0x70', '0xa6']
<zipfile._ZipDecrypter object at 0x0000021E8DA2E160> ['0xa8', '0x3f', '0xa2', '0x56', '0x4c', '0x37', '0xbb', '0x60', '0xd3', '0x5e', '0x84', '0xa6']
<zipfile._ZipDecrypter object at 0x0000021E8DA2E128> ['0xeb', '0x64', '0x36', '0xa3', '0xca', '0x46', '0x17', '0x1a', '0xfb', '0x6d', '0x6c', '0x4e']
>>>  # As seen, the last element of the first 2 arrays (coco and dominique) is 0xA6 (166), which is the same as the first byte of the CRC

Я провел несколько тестов с другими механизмами распаковки (с аргументами по умолчанию):

  1. WinRar : для неправильного пароля файл не тронут, нодля неисправного он усекается (так же, как здесь)
  2. 7-Zip : пользователь запрашивает, перезаписать ли файл, и перезаписывает его независимо от результата распаковки
  3. Total Commander * Внутренний распаковщик ( zip ): такой же, как # 2.

3.Заключение

  • Я вижу это как zipfile bug.Указание такого неверного (и неправильного) пароля не должно перезаписывать существующий файл (если есть).Или, по крайней мере, поведение должно быть согласованным (для всех неправильных паролей)
  • Быстрый просмотр не выявил каких-либо ошибок в Python
  • Я не вижу легкихисправить, как:
    • Алгоритм zip не может быть улучшен (чтобы лучше проверить, в порядке ли пароль)
    • Я подумал о паре исправлений, но онилибо отрицательно повлияет на производительность, либо может привести к регрессии в некоторых (угловых) случаях

@ EDIT0 :

Я отправил [GitHub]: python / cpython - [3.6] bpo-36247: zipfile - извлекать усеченный (существующий) файл, если предоставлен неверный пароль (слабость шифрования zip) который был закрыт для филиала 3.6 (который находится в режиме только для исправлений безопасности ).Не уверен, каким будет результат (в других ветках), но в любом случае он не будет доступен в ближайшее время (скажем, в ближайшие месяцы).

В качестве альтернативы вы можете загрузитьпатч и применить изменения локально.Проверьте [SO]: запускать / отлаживать UnitTests приложения Django из контекстного меню, вызываемого правой кнопкой мыши, в PyCharm Community Edition?(@ Ответ CristiFati) ( Patching utrunner section) о том, как применять патчи к Win (в основном, каждая строка, начинающаяся с one)"+" знак входит, и каждая строка, которая начинается с one "-" знак гаснет).Я использую Cygwin , кстати .
Вы можете скопировать zipfile.py из Python dir в ваш проект (или какой-нибудь "персональный") и пропатчить этот файл, если вы хотите сохранить свой Python установка нетронутая.

...