├── LICENSE ├── README.md └── zipeinfo /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) Hanno Böck 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 7 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 8 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 9 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 10 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 11 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 12 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | zipeinfo - ZIP encryption info 2 | ============================== 3 | 4 | parses ZIP files and tells user about used encryption method 5 | 6 | created by [Hanno Boeck](https://hboeck.de) 7 | 8 | License: 0BSD 9 | 10 | Background 11 | ========== 12 | 13 | The ZIP file format supports various methods to encrypt files. The original 14 | ZIP encryption has been broken by an attack by Biham/Kocher in 1994. 15 | 16 | Two competing formats introduced "strong" encryption into ZIP files. WinZip 17 | introduced an AES-based encryption and PKWARE, creator of the original PKZIP, 18 | created an extension supporting various encryption algorithms, some of them 19 | completely broken (DES, RC2), others still good today (AES and others). 20 | 21 | 22 | zipeinfo 23 | ======== 24 | 25 | I became curious in ZIP encryption formats and found no tool that will 26 | easily tell me which encryption was used. 27 | 28 | So I hacked together this python script. It may break on malformed files, 29 | only checks the header parts that are relevant for encryption and ignores 30 | the rest. 31 | 32 | Feedback (and patches) welcome. Especially if there are any other ZIP 33 | encryption formats out there I'd like to hear about them. 34 | 35 | 36 | Algorithm classification 37 | ======================== 38 | 39 | zipeinfo puts all encryption algorithms in three rough categories. 40 | 41 | insecure: Attacks known, can be broken on a normal home PC. 42 | Algorithms: PKZIP/Original, DES, RC2 43 | 44 | weak: No known practical attacks, but considered weak. 45 | Algorithms: RC4, 3DES with 112 bit 46 | 47 | ok: Probably not breakable by anyone. 48 | Algorithms: AES (all sizes), Blowfish, Twofish, 3DES with 168 bit 49 | 50 | More Info 51 | ========= 52 | 53 | * [PkCrack - attack on legacy ZIP encryption](https://www.unix-ag.uni-kl.de/~conrad/krypto/pkcrack.html) 54 | * [A Known Plaintext Attack on the PKZIP Stream Cipher (1994, Eli Biham, Paul C. Kocher)](https://www.unix-ag.uni-kl.de/~conrad/krypto/pkcrack/pkzip.ps.gz) 55 | * [ZIP Attacks with Reduced Known Plaintext (2001, Michael Stay)](https://link.springer.com/content/pdf/10.1007/3-540-45473-X_10.pdf) 56 | * [.ZIP File Format Specification](https://www.pkware.com/documents/casestudies/APPNOTE.TXT) 57 | * [WinZip AES Encryption Information: Encryption Specification AE-1 and AE-2](https://www.winzip.com/en/support/aes-encryption/) 58 | -------------------------------------------------------------------------------- /zipeinfo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # zipeinfo - ZIP encryption info 4 | # parses ZIP files and tells user about used encryption method 5 | 6 | # created by Hanno Boeck, https://hboeck.de 7 | # SPDX-License-Identifier: 0BSD 8 | 9 | from __future__ import print_function 10 | import struct,optparse,sys 11 | 12 | parser = optparse.OptionParser(usage="%prog [file(s)]") 13 | (options, args) = parser.parse_args() 14 | if len(args)==0: 15 | print("No arguments given!") 16 | parser.print_help() 17 | sys.exit(1) 18 | 19 | for zipfile in args: 20 | print ("* %s" % zipfile) 21 | f = open(zipfile,"rb") 22 | 23 | pos=0 24 | iszip=0 25 | while 1: 26 | head=f.read(4) 27 | if head != b"PK\x03\x04": break 28 | iszip=1 29 | version=struct.unpack("H", f.read(2))[0] 30 | bitfield=struct.unpack("H", f.read(2))[0] 31 | enc=bitfield&1 32 | ses=(bitfield&(1<<6))>>6 33 | comp=struct.unpack("H", f.read(2))[0] 34 | f.seek(pos+0x12) 35 | data_len=struct.unpack("I", f.read(4))[0] 36 | f.seek(pos+0x1a) 37 | name_len=struct.unpack("H", f.read(2))[0] 38 | extra_len=struct.unpack("H", f.read(2))[0] 39 | filename=f.read(name_len).decode("ascii") 40 | print("-> %s " % filename.ljust(12), end="") 41 | 42 | if enc==0: print("not encrypted [-]") 43 | elif comp==99: # WinZip AES 44 | f.seek(pos+0x1e+name_len) 45 | headid=struct.unpack("H", f.read(2))[0] 46 | f.seek(pos+0x1e+name_len+4) 47 | aesver=struct.unpack("B", f.read(1))[0] 48 | f.seek(pos+0x1e+name_len+8) 49 | aessize=struct.unpack("B", f.read(1))[0] 50 | if headid!=0x9901 or (aesver!=1 and aesver!=2): print("WinZip/error [ok]") 51 | elif aessize==1: print("WinZip/AES128v%i[ok]" % aesver) 52 | elif aessize==2: print("WinZip/AES192v%i[ok]" % aesver) 53 | elif aessize==3: print("WinZip/AES256v%i[ok]" % aesver) 54 | else: print("WinZip/unknown [?]") 55 | 56 | elif ses==1: # PKWARE Strong Encryption Specification 57 | f.seek(pos+0x1e+name_len) 58 | ivsize=struct.unpack("H", f.read(2))[0] 59 | f.seek(pos+0x1e+name_len+ivsize+8) 60 | algid=struct.unpack("H", f.read(2))[0] 61 | if algid==0x6601: print("PKZIP/DES [insecure]") 62 | elif algid==0x6602: print("PKZIP/RC2old [insecure]") 63 | elif algid==0x6603: print("PKZIP/3DES168 [ok]") 64 | elif algid==0x6609: print("PKZIP/3DES112 [weak]") 65 | elif algid==0x660E: print("PKZIP/AES128 [ok]") 66 | elif algid==0x660F: print("PKZIP/AES192 [ok]") 67 | elif algid==0x6610: print("PKZIP/AES256 [ok]") 68 | elif algid==0x6702: print("PKZIP/RC2new [insecure]") 69 | elif algid==0x6720: print("PKZIP/Blowfish [ok]") 70 | elif algid==0x6721: print("PKZIP/Twofish [ok]") 71 | elif algid==0x6801: print("PKZIP/RC4 [weak]") 72 | else: print("PKZIP/unknown [?]") 73 | else: print("PKZIP/legacy [insecure]") 74 | pos+=0x1e+name_len+extra_len+data_len 75 | f.seek(pos) 76 | if iszip==0: print("-> ERROR: No ZIP") 77 | f.close() 78 | --------------------------------------------------------------------------------