Skip to content

Repository files navigation

Coverity Scan Build Status Build Status

Overview

yazc (Yet Another Zip Cracker) is a simple command-line application for recovering passwords and encryption keys from legacy ZIP archives.

The project builds and installs a single executable, yazc. Its attack modes and archive-inspection support are available as subcommands; there is no separate library or collection of helper executables to install.

Dependencies

On Ubuntu, install the required packages with:

sudo apt install -y autoconf automake bison flex zlib1g-dev pkg-config

The Vulkan brute-force backend is built automatically when the Vulkan headers and loader development package are available. On Ubuntu, install libvulkan-dev. Use ./configure --disable-vulkan to build without it. The runtime also needs a Vulkan driver exposing a compute-capable device.

Building the unit tests also requires Check.

Installation

Clone, configure, compile, and install the project:

git clone https://github.com/mferland/libzc.git
cd libzc
./autogen.sh
./configure CFLAGS='-O3 -ffast-math -march=native -mtune=native'
make
sudo make install

This installs yazc, its manual page, and this README. Run yazc --help to list the available subcommands.

Usage

There are currently four attack modes available:

Brute force

This mode tries every password that can be generated from the given character set. It supports multithreading.

Options

-c, --charset specifies the character set. For example, -c abc123 tries every combination of a, b, c, 1, 2, and 3 up to the maximum password length, which defaults to eight characters.

-i, --initial specifies the first password to try. By default, the initial password is the first character in the character set. For example, with the character set abc, the search begins with a, then b, c, aa, and so on. This option is useful for skipping part of the password search space.

-l, --length specifies the maximum password length. The program stops after testing every password through length.

--min-length specifies the minimum password length in character-set mode. It defaults to one and must not exceed --length. When --initial is also provided, the initial password may start later than this minimum but not earlier.

-a, --alpha uses lowercase ASCII letters (a-z).

-A, --alpha-caps uses uppercase ASCII letters (A-Z).

-n, --numeric uses digits (0-9).

-s, --special uses printable special ASCII characters.

-t, --threads=N specifies the number of worker threads. Use --threads=auto to select the number of online CPUs reported by sysconf(_SC_NPROCESSORS_ONLN). This is the default.

-S, --stats prints runtime statistics, the estimated number of password candidates tested, and the estimated password rate. Candidate accounting is performed at completed worker leaf and vector-batch boundaries to avoid synchronization or per-password timing overhead in the cracking loops.

Mask options

A mask defines the allowed characters separately for each password position. Using -m, --mask selects mask mode instead of the character-set mode described above. Quote masks on the command line so the shell does not interpret characters such as ?, [, or ].

-m, --mask=MASK specifies the password mask. Without either of the length options below, the program tests passwords whose length exactly matches the mask. Passwords cannot exceed 16 characters.

-k, --mask-minlen=N also tests shorter prefixes of the mask, starting at length N. The minimum length cannot exceed the number of positions in the mask.

-x, --mask-maxlen=N also tests longer passwords, up to length N, by repeating the final mask position. The maximum length cannot be shorter than the mask.

Each mask position can be a literal character, a character list such as [abc], an alphanumeric range such as [a-z], or one of these placeholders:

Placeholder Characters
?l Lowercase ASCII letters (a-z)
?u Uppercase ASCII letters (A-Z)
?d Decimal digits (0-9)
?s Printable special ASCII characters, including space
?a All printable ASCII characters (0x20–0x7e)
?B Upper-half byte values (0x80–0xff)
?b All non-NUL byte values (0x01–0xff)
?h Lowercase hexadecimal characters (a-f, 0-9)
?H Uppercase hexadecimal characters (A-F, 0-9)

Lists can combine characters and ranges. For example, [a-f0-9_] matches a lowercase hexadecimal character or an underscore. Use a backslash to include a mask metacharacter literally, such as \? for a question mark or \\ for a backslash. A byte can also be written as \xNN, where NN is its two-digit hexadecimal value. Unescaped whitespace in a mask is ignored; use \x20 when a position must contain a space.

For example, try a literal pass- prefix followed by four digits:

yazc bruteforce --mask='pass-?d?d?d?d' archive.zip

Try either Pass0 or pass0 through Pass9 or pass9:

yazc bruteforce --mask='[Pp]ass?d' archive.zip

The following mask first tests one lowercase letter, then a lowercase letter followed by one digit, and finally passwords with that prefix followed by up to two more digits:

yazc bruteforce --mask='?l?d' --mask-minlen=1 --mask-maxlen=4 archive.zip

-i, --initial can also be used in mask mode. The initial password must have the minimum generated length and every character must match its corresponding mask position.

Examples

Try all passwords in a-z0-9 up to eight characters using four worker threads:

yazc bruteforce -a -n -l8 -t4 archive.zip

Try all password combinations using the characters abc123 up to a maximum of ten characters, using the default number of worker threads:

yazc bruteforce -c abc123 -l10 archive.zip

Vulkan brute force

This experimental mode generates and filters password candidates using a Vulkan compute device. It supports a custom character set and an inclusive password-length range. Each fixed-length subspace is exhausted before the next length begins.

-c, --charset specifies the character set.

The predefined character classes are the same as for the CPU brute-force command: -a, --alpha adds lowercase ASCII letters, -A, --alpha-caps adds uppercase ASCII letters, -n, --numeric adds digits, and -s, --special adds printable special ASCII characters. The class options can be combined. An explicit --charset takes precedence over them.

-l, --length specifies the maximum password length. It is optional and defaults to eight, matching the CPU brute-force command.

--min-length specifies the minimum password length and must not exceed --length. It is optional and defaults to one.

-d, --device selects an indexed compute device. Device zero is the default. Use --list-devices to print the available indices.

-S, --stats prints the selected device, search configuration, runtime, estimated number of password candidates tested, and estimated password rate. When supported by the selected compute queue, it also reports GPU-only compute runtime and throughput using Vulkan timestamp queries. Candidate accounting is performed once per GPU dispatch.

Debug builds started with ZC_LOG=debug also report implementation-provided pipeline executable statistics when the Vulkan driver supports them. These can include compiled instruction, register, scratch-memory, and subgroup details; the exact fields are driver-specific.

GPU searches process up to 64 million candidates per dispatch, clamped to the selected device's compute workgroup limit. This amortizes command submission, fence waits, and result readback without changing search order. Within a dispatch, each shader invocation derives the keys for one password prefix and reuses them across every final-character candidate. The backend also creates and caches a compute pipeline specialized for each password length it encounters. Password length, character-set size, and ZIP header count become compile-time constants for that pipeline; if a driver rejects specialization, the search continues with the generic pipeline.

For example, list devices and search every lowercase password from six through eight characters on device zero:

yazc vulkan --list-devices
yazc vulkan -a --min-length=6 --length=8 --device=0 archive.zip

The command reports an error instead of silently falling back to the CPU when Vulkan support or the selected device is unavailable.

Dictionary

This mode tries every password from the given dictionary file. If no dictionary file is provided, the program reads passwords from standard input (stdin).

Options

-d, --dictionary reads passwords from the specified file.

-S, --stats prints runtime statistics.

Examples

Try all passwords from words.dict:

cat words.dict | yazc dictionary archive.zip
yazc dictionary -d words.dict archive.zip

Use John the Ripper to generate more passwords:

john --wordlist=words.dict --rules --stdout | yazc dictionary archive.zip

Plaintext

This mode uses a known vulnerability in the PKZIP stream cipher to find the internal representation of the encryption key. Once the internal representation has been recovered, the program tries to find the actual password or an equivalent one.

Three methods are available for mapping plaintext bytes to ciphertext bytes: file (-f), offset (-o), and ZIP entry (the default).

If no mapping option is given, the program reads the plaintext and ciphertext from ZIP archives. Provide the corresponding entry name from each archive. For example:

yazc plaintext notencrypted.zip file.exe encrypted.zip file.exe

Options

-o, --offset uses offsets instead of ZIP entry names. This mode can map plaintext bytes from anywhere in one file to ciphertext bytes in another file. The number of mapped bytes must match. This option is useful when only part of a ZIP entry can be used. The following example tries to find the password for archive.zip by mapping bytes 100–650 of plain.bin to bytes 112–662 of archive.zip; the first ciphertext byte is at offset 64:

yazc plaintext -o plain.bin 100 650 archive.zip 112 662 64

-f, --file uses plaintext bytes from plaintextfile and maps them to bytes in cipherfile. The program assumes that the first 12 bytes of cipherfile are the encryption header. Bytes that cannot be mapped are ignored, which can happen when either file is shorter. For example:

yazc plaintext -f plaintextfile cipherfile

-i, --password-from-internal-rep finds a password from the provided internal representation. See section 3.6 of the Biham and Kocher paper for more information. For example:

yazc plaintext -i 0x777095c0 0xc1764180 0xf5d5b494

-p, --internal-rep-from-password calculates the internal representation of a password. For example:

yazc plaintext -p pAssW0Rd

-t, --threads=N specifies the number of worker threads. Use --threads=auto to select the number of online CPUs reported by sysconf(_SC_NPROCESSORS_ONLN). This is the default.

-S, --stats prints runtime statistics.

Info

The info subcommand lists the contents of a ZIP archive. It provides information useful for plaintext and other attack modes. For example:

yazc info data/noradi.zip

Result:

INDEX NAME      OFFSETS     SIZE CSIZE ENCRYPTED HEADER
    0 TEXT1.TXT 39  51  155 110  116   875dee36d843e98819faae48
    1 TEXT2.TXT 194 206 302 99   108   4fa3648cd55cdbdc071bfae1
    2 TEXT3.TXT 341 353 439 88   98    0d9507f1cd95d217c8cadb11
  • The first column (INDEX) is the index of the file in the archive.
  • The second column (NAME) is the name of the file taken from the ZIP header.
  • The third column (OFFSETS) contains offsets useful for the plaintext attack when using the -o option. The first number is the offset of the first byte of the encrypted header; the second is the offset of the first byte of the compressed data; and the third is the offset of the last byte of the compressed data.
  • The fourth column (SIZE) is the original file size in bytes.
  • The fifth column (CSIZE) is the compressed file size including the encrypted header (always 12 bytes).
  • The sixth column (ENCRYPTED HEADER) is the encrypted header.

This subcommand makes it easier to inspect the contents of ZIP archives. Another tool you can use is zipinfo.

Performance benchmarks

Run the CPU attack benchmarks with:

scripts/benchmark-attacks.sh

Set VULKAN_DEVICE to append the fixed-length Vulkan workload to the report:

VULKAN_DEVICE=0 scripts/benchmark-attacks.sh

For dedicated Vulkan compute measurements at password lengths 6, 7, and 8, run:

scripts/benchmark-vulkan.sh

The three workloads exhaust the lowercase search spaces of 308,915,776, 8,031,810,176, and 208,827,064,576 candidates, respectively. Their passwords are all z characters, placing the match at the end of each search space. The length-eight workload is intentionally long-running. Select a GPU and take multiple samples with:

VULKAN_DEVICE=0 RUNS=3 scripts/benchmark-vulkan.sh

data/bruteforce-7char.zip is the stable brute-force workload. It contains five traditionally encrypted entries and uses the password zzzzzzz, so an alphabetic search through length seven tests all 8,353,082,582 candidates. Use BRUTEFORCE_THREADS to select a fixed worker count when comparing builds:

BRUTEFORCE_THREADS=12 RUNS=3 scripts/benchmark-attacks.sh

The brute-force header-filter batch size defaults to 64 candidates. To compare other sizes, define ZC_BRUTEFORCE_BATCH_SIZE while configuring the build. The supported range is 1 through 1024:

CPPFLAGS=-DZC_BRUTEFORCE_BATCH_SIZE=32 ./configure

License

Distributed under the GPLv3+ license. See COPYING for more information.

Contact

Marc Ferland - marc.ferland@gmail.com

About

Crack legacy zip files using plaintext, bruteforce or dictionary attacks.

Topics

Resources

Stars

51 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages