Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

method testzip to check password on encrypted zip #14

Closed
beamzer opened this issue Oct 31, 2020 · 4 comments
Closed

method testzip to check password on encrypted zip #14

beamzer opened this issue Oct 31, 2020 · 4 comments

Comments

@beamzer
Copy link

beamzer commented Oct 31, 2020

Hi,
I was working on a sort of brute force script, this is for malware analysis where attachments often use zip archive with encryption to evade detection. The script uses a dictionary file on a zip archive and with the use of testzip tells when the right password is found.
This seemed to work, untill by chance i got a hit on a second password which was correct according to the method.

import pyzipper
import sys

secret_password = bytes(sys.argv[1].encode("utf-8"))
print(secret_password)

with pyzipper.AESZipFile('test.zip') as zf:
    try:
        zf.setpassword(secret_password)
        zf.testzip()
        print("right password: %s" % secret_password.decode("utf-8"))
    except Exception as e:
        # raise e
        print("wrong password: %s" % secret_password.decode("utf-8"))
% python3.7 ziptest.py 0272
b'0272'
right password: 0272
python3.7 ziptest.py 0272 0273
b'0273'
wrong password: 0273
python3.7 ziptest.py 0272 254 
b'254'
right password: 254

If i change the method to extractall it work OK, but the wrong passwords generate empty files, so i would first like to establish the right password and only then move on to extracting.

with pyzipper.AESZipFile('test.zip') as zf:
    try:
        zf.setpassword(secret_password)
        zf.extractall()
        print("right password: %s" % secret_password.decode("utf-8"))
    except Exception as e:
        # raise e
        print("wrong password: %s" % secret_password.decode("utf-8"))
python3.7 ziptest.py 0272
b'0272'
right password: 0272
python3.7 ziptest.py 0273
b'0273'
wrong password: 0273
python3.7 ziptest.py 254 
b'254'
wrong password: 254

Am i doing something wrong?

thanks in advance,
Ewald...

@danifus
Copy link
Owner

danifus commented Oct 31, 2020

Hi,

You're not doing anything wrong. There are two places in the winzip aes format where the password is verified:

  • a cheap password check which can give false positives
  • hmac check after decrypting the entire file.

The cheap password check is described in https://www.winzip.com/win/en/aes_info.html:

This two-byte value is produced as part of the process that derives the encryption and decryption keys from the password. When encrypting, a verification value is derived from the encryption password and stored with the encrypted file. Before decrypting, a verification value can be derived from the decryption password and compared to the value stored with the file, serving as a quick check that will detect most, but not all, incorrect passwords. There is a 1 in 65,536 chance that an incorrect password will yield a matching verification value; therefore, a matching verification value cannot be absolutely relied on to indicate a correct password.

So you can use your current script to filter out potential candidates, but then you will need to read the file contents to see if it was the actual password or one of these false positives.

@lclevy
Copy link

lclevy commented Oct 31, 2020

@danifus danifus closed this as completed Nov 2, 2020
@beamzer
Copy link
Author

beamzer commented Nov 4, 2020

@danifus thanks for the clarification, pitty there is no real password check one could do after the "cheap check". When doing the real extraction with the wrong passwords, it writes out zero-byte files, so it overwrites previous files which might have been written with the correct key. Of course you can solve this with separate output directories and testing the length of the generated files, but that's all extra code.
@lclevy looks interesting, thanks for the pointer.

@danifus
Copy link
Owner

danifus commented Nov 4, 2020

I've never used testzip() before and it turns out that it is doing both cheap and expensive password check. testzip() returns None if everything is ok or the name of the first file that raises an error. Checking the return value of testzip() will fix this for you:

with pyzipper.AESZipFile('test.zip') as zf:
    try:
        zf.setpassword(secret_password)
        error_filename = zf.testzip()
        if error_filename is None:
            print("right password: %s" % secret_password.decode("utf-8"))
        else:
            print("wrong password: %s" % secret_password.decode("utf-8"))
    except Exception as e:
        # raise e
        print("wrong password: %s" % secret_password.decode("utf-8"))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants