When working with security-related issues, one common
challenge is dealing with passwords that we don't know. For example, during an
authorized security assessment, we may obtain a password hash from a database
or another source.
But there is one problem. We don't have the original
password.
The password is usually not stored directly. Instead,
applications should store passwords using a password-hashing mechanism.
So, if all we have is the hash, we cannot simply read the
original password from it.
We have to test possible passwords and determine whether any
of them produce a matching result. This is where password-auditing and password-recovery tools
can help.
Hello everyone, and welcome to a new video. In this article,
we're going to learn about a popular password-auditing tool called John the
Ripper, commonly known as John.
We'll understand:
- what this tool does
- how they work and
- how
we can use it safely in a controlled laboratory environment.
So, let’s get
started.
Introduction to JTR
John the Ripper is a widely used, open-source password auditing and password recovery tool. Its main purpose is to test the strength of passwords by working with password hashes and other supported password-protected formats.
Features and capabilities of
John the Ripper
Let's explore the remarkable features and capabilities of
John the Ripper!
- John the Ripper can handle
various password hash formats,
making it compatible with different systems and databases.
- It offers
multiple attack modes, including brute-force,
dictionary, and hybrid attacks, providing versatile options for cracking
passwords.
- John the Ripper is
optimized for speed, utilizing multiple
CPU cores and GPUs
to accelerate the password-cracking process.
- Users can easily use their
own wordlists and apply custom
rules
to generate password variations efficiently.
- Being an open-source tool, John the Ripper benefits from
continuous support and contributions
from the vibrant cybersecurity community.
Now we have a basic understanding
of what John the Ripper is and why security professionals use it. But simply understanding the
theory isn't enough.
To properly understand how a
security tool works, we need to use that knowledge in a practical environment.
Supported Platforms and Environments
If we talk about Supported Platforms and Environments, John
the Ripper is compatible with various operating systems, including Windows,
macOS, Linux, and other Unix-like systems.
It also works on different
architectures, making it highly versatile and widely accessible.
For this demonstration, we'll be
using Kali Linux.
Kali Linux is a Linux-based
operating system specifically designed for penetration testing, security
research, digital forensics, and other cybersecurity tasks. It comes with a large collection
of security tools, including John the Ripper.
If you're using Kali Linux, John
is generally available by default, so we don't need to spend time installing
it. For Windows users, I'll cover the
installation and usage of John the Ripper in a separate article.
Now, before we start our
practical demonstration, let's understand a few important options that we'll be
using:
┌──(kali㉿kali)-[~]
└─$ john
John the Ripper 1.9.0-jumbo-1+bleeding-aec1328d6c 2021-11-02 10:45:52 +0100 OMP [linux-gnu 64-bit x86_64 SSE2 AC]
Copyright (c) 1996-2021 by Solar Designer and others
Homepage: https://www.openwall.com/john/
Usage: john [OPTIONS] [PASSWORD-FILES]
Use --help to list available options.
┌──(kali㉿kali)-[~]
└─$
The basic command structure looks
like this:
john[OPTIONS][PASSWORD-FILES]
Here:
- john starts the program.
- The OPTIONS tell John how we
want it to perform the password-auditing task.
- And PASSWORD-FILES represents the
file containing the password hashes or other supported data that we want John
to process.
About the OPTIONS, John provides
many different options for different situations. Run john with the help flag; it
displays all options to use:
┌──(kali㉿kali)-[~]
└─$ john --help
John the Ripper 1.9.0-jumbo-1+bleeding-aec1328d6c 2021-11-02 10:45:52 +0100 OMP [linux-gnu 64-bit x86_64 SSE2 AC]
Copyright (c) 1996-2021 by Solar Designer and others
Homepage: https://www.openwall.com/john/
Usage: john [OPTIONS] [PASSWORD-FILES]
--help Print usage summary
--single[=SECTION[,..]] "Single crack" mode, using default or named rules
--single=:rule[,..] Same, using "immediate" rule(s)
--single-seed=WORD[,WORD] Add static seed word(s) for all salts in single mode
--single-wordlist=FILE *Short* wordlist with static seed words/morphemes
--single-user-seed=FILE Wordlist with seeds per username (user:password[s]
format)
--single-pair-max=N Override max. number of word pairs generated (6)
--no-single-pair Disable single word pair generation
--[no-]single-retest-guess Override config for SingleRetestGuess
--wordlist[=FILE] --stdin Wordlist mode, read words from FILE or stdin
--pipe like --stdin, but bulk reads, and allows rules
--rules[=SECTION[,..]] Enable word mangling rules (for wordlist or PRINCE
modes), using default or named rules
--rules=:rule[;..]] Same, using "immediate" rule(s)
--rules-stack=SECTION[,..] Stacked rules, applied after regular rules or to
modes that otherwise don't support rules
--rules-stack=:rule[;..] Same, using "immediate" rule(s)
--rules-skip-nop Skip any NOP ":" rules (you already ran w/o rules)
--loopback[=FILE] Like --wordlist, but extract words from a .pot file
--mem-file-size=SIZE Size threshold for wordlist preload (default 2048 MB)
--dupe-suppression Suppress all dupes in wordlist (and force preload)
--incremental[=MODE] "Incremental" mode [using section MODE]
--incremental-charcount=N Override CharCount for incremental mode
--external=MODE External mode or word filter
--mask[=MASK] Mask mode using MASK (or default from john.conf)
--markov[=OPTIONS] "Markov" mode (see doc/MARKOV)
--mkv-stats=FILE "Markov" stats file
--prince[=FILE] PRINCE mode, read words from FILE
--prince-loopback[=FILE] Fetch words from a .pot file
--prince-elem-cnt-min=N Minimum number of elements per chain (1)
--prince-elem-cnt-max=[-]N Maximum number of elements per chain (negative N is
relative to word length) (8)
--prince-skip=N Initial skip
--prince-limit=N Limit number of candidates generated
--prince-wl-dist-len Calculate length distribution from wordlist
--prince-wl-max=N Load only N words from input wordlist
--prince-case-permute Permute case of first letter
--prince-mmap Memory-map infile (not available with case permute)
--prince-keyspace Just show total keyspace that would be produced
(disregarding skip and limit)
--subsets[=CHARSET] "Subsets" mode (see doc/SUBSETS)
--subsets-required=N The N first characters of "subsets" charset are
the "required set"
--subsets-min-diff=N Minimum unique characters in subset
--subsets-max-diff=[-]N Maximum unique characters in subset (negative N is
relative to word length)
--subsets-prefer-short Prefer shorter candidates over smaller subsets
--subsets-prefer-small Prefer smaller subsets over shorter candidates
--make-charset=FILE Make a charset, FILE will be overwritten
--stdout[=LENGTH] Just output candidate passwords [cut at LENGTH]
--session=NAME Give a new session the NAME
--status[=NAME] Print status of a session [called NAME]
--restore[=NAME] Restore an interrupted session [called NAME]
--[no-]crack-status Emit a status line whenever a password is cracked
--progress-every=N Emit a status line every N seconds
--show[=left] Show cracked passwords [if =left, then uncracked]
--show=formats Show information about hashes in a file (JSON)
--show=invalid Show lines that are not valid for selected format(s)
--test[=TIME] Run tests and benchmarks for TIME seconds each
(if TIME is explicitly 0, test w/o benchmark)
--stress-test[=TIME] Loop self tests forever
--test-full=LEVEL Run more thorough self-tests
--no-mask Used with --test for alternate benchmark w/o mask
--skip-self-tests Skip self tests
--users=[-]LOGIN|UID[,..] [Do not] load this (these) user(s) only
--groups=[-]GID[,..] Load users [not] of this (these) group(s) only
--shells=[-]SHELL[,..] Load users with[out] this (these) shell(s) only
--salts=[-]COUNT[:MAX] Load salts with[out] COUNT [to MAX] hashes, or
--salts=#M[-N] Load M [to N] most populated salts
--costs=[-]C[:M][,...] Load salts with[out] cost value Cn [to Mn]. For
tunable cost parameters, see doc/OPTIONS
--fork=N Fork N processes
--node=MIN[-MAX]/TOTAL This node's number range out of TOTAL count
--save-memory=LEVEL Enable memory saving, at LEVEL 1..3
--log-stderr Log to screen instead of file
--verbosity=N Change verbosity (1-5 or 6 for debug, default 3)
--no-log Disables creation and writing to john.log file
--bare-always-valid=Y Treat bare hashes as valid (Y/N)
--catch-up=NAME Catch up with existing (paused) session NAME
--config=FILE Use FILE instead of john.conf or john.ini
--encoding=NAME Input encoding (eg. UTF-8, ISO-8859-1). See also
doc/ENCODINGS.
--input-encoding=NAME Input encoding (alias for --encoding)
--internal-codepage=NAME Codepage used in rules/masks (see doc/ENCODINGS)
--target-encoding=NAME Output encoding (used by format)
--force-tty Set up terminal for reading keystrokes even if we're
not the foreground process
--field-separator-char=C Use 'C' instead of the ':' in input and pot files
--[no-]keep-guessing Try finding plaintext collisions
--list=WHAT List capabilities, see --list=help or doc/OPTIONS
--length=N Shortcut for --min-len=N --max-len=N
--min-length=N Request a minimum candidate length in bytes
--max-length=N Request a maximum candidate length in bytes
--max-candidates=[-]N Gracefully exit after this many candidates tried.
(if negative, reset count on each crack)
--max-run-time=[-]N Gracefully exit after this many seconds (if negative,
reset timer on each crack)
--mkpc=N Request a lower max. keys per crypt
--no-loader-dupecheck Disable the dupe checking when loading hashes
--pot=NAME Pot file to use
--regen-lost-salts=N Brute force unknown salts (see doc/OPTIONS)
--reject-printable Reject printable binaries
--tune=HOW Tuning options (auto/report/N)
--subformat=FORMAT Pick a benchmark format for --format=crypt
--format=[NAME|CLASS][,..] Force hash of type NAME. The supported formats can
be seen with --list=formats and --list=subformats.
See also doc/OPTIONS for more advanced selection of
format(s), including using classes and wildcards.
┌──(kali㉿kali)-[~]
└─$
However, I am not going through
every single option one by one, it will take time, there is a chance it looks
more theoretic. The best way to understand these
options is to see them in action.
So, instead of spending too much
time on theory, we'll move into our lab and learn the important commands
through practical examples.
Let's start with our Practical demonstration.
Practical Demonstration
So, what are we going to do here?
- First, we'll create a password
hash ourselves.
- Then, we'll use John the Ripper
to test password candidates against that hash and see whether the original
password can be recovered.
Everything in this demonstration
is created inside our own lab environment.
We'll use the mkdir command to
create a new directory, and then we change the directory to it:
┌──(kali㉿kali)-[~]
└─$ mkdir john-lab
┌──(kali㉿kali)-[~]
└─$ cd john-lab
┌──(kali㉿kali)-[~/john-lab]
└─$
We are now inside our laboratory
directory.
Next, we'll create a test MD5
hash.
For this demonstration, I'll use
a simple test password. We can generate its MD5 hash by
using the md5sum command-line tool:
┌──(kali㉿kali)-[~/john-lab]
└─$ echo -n 'password' | md5sum
5f4dcc3b5aa765d61d8327deb882cf99 -
┌──(kali㉿kali)-[~/john-lab]
└─$
Here,
- the echo -n command outputs
the password without adding a new line.
- The pipe symbol, or | sends that output
directly to md5sum.
- The md5sum command then calculates the MD5 hash of that
text.
The result is the hash value that
we'll use for our demonstration. Now, let's save that hash into a
file called hash.txt.
We can use the echo command
again, followed by the hash, and then use the redirection operator to write it
into the file.
┌──(kali㉿kali)-[~/john-lab]
└─$ echo '5f4dcc3b5aa765d61d8327deb882cf99' > hash.txt
┌──(kali㉿kali)-[~/john-lab]
└─$ ls
hash.txt
┌──(kali㉿kali)-[~/john-lab]
└─$
As you can see, the hash has been
stored inside hash.txt.
Now we have our test hash, so
let's see what happens when we give it to John. For the first attempt, we won't
specify any additional options.
┌──(kali㉿kali)-[~/john-lab]
└─$ john hash.txt
Warning: detected hash type "LM", but the string is also recognized as "dynamic-md5($p)"
Use the-format-dynamic-md5($p) option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "HAVAL-128-4"
Use the-format-HAVAL-128-4" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "MD2"
Use the-format-MD2" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "mdc2"
Use the-format-mdc2" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "mscash"
Use the-format-mscash" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "mscash2" Use the-format mscash2" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "NT"
Use the-format-NT" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "Raw-MD4" Use the format Raw-MD4" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "Raw-MD5" Use the-format Raw-MD5" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "Raw-MD5u
Use the format Raw-MD5u" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "Raw-SHA1-AxCrypt"
Use the format Raw-SHA1-AxCrypt option to force loading these as that type instead Warning: detected hash type "LM", but the string is also recognized as "ripemd-128
Use the-format-ripemd-128" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "Snefru-128"
Use the format Snefru-128" option to force loading these as that type instead
Warning: detected hash type "LM", but the string is also recognized as "ZipMonster"
Use the-format-ZipMonster" option to force loading these as that type instead
Using default input encoding: UTF-8
Using default target encoding: CP850
Loaded 2 password hashes with no different salts (LM [DES 256/256 AVX2])
Warning: poor OpenMP scalability for this hash type, consider fork-2
Will run 2 OpenMP threads.
Proceeding with single, rules: Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/usr/share/john/password.lst
Proceeding with incremental:LM_ASCII
John will try to identify the
hash format automatically and start using its configured default settings. Depending on the John build and
the input, you may see a warning or a message indicating that the hash format
was not identified as expected.
This is a good example of why
understanding the hash format is important.
In our case, we already know that
the hash we created is an MD5 hash.
So instead of relying on
automatic detection, we can explicitly tell John which format to use. We'll use the --format option for
this.
- Here, John knows that the value should be treated as a raw MD5 hash. If the correct password is present in the candidates John is testing, it can find a matching value.
When that happens, John reports the recovered password.
┌──(kali㉿kali)-[~/john-lab]
└─$ john --format=Raw-MD5 hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8×3])
Warning: no OpenMP support for this hash type, consider -fork-2
Proceeding with single, rules: Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/usr/share/john/password.lst
password (?)
1g 0:00:00:00 DONE 2/3 (2026-09-21 14:17) 2.500g/s 960.0p/s 960.0c/s 960.0C/s 123456.. larry
Use the "--show --format-Raw-MD5" options to display all of the cracked passwords reliably
Session completed.
┌──(kali㉿kali)-[~/john-lab]
└─$
So, what exactly happened here?
- John did not decrypt the MD5
hash.
- Instead, it tested password
candidates, calculated their MD5 values, and compared those results with our
target hash.
- When one of the candidates
produced the same hash, John reported a successful match.
Now let's consider a different
situation.
What happens if the password is more complicated?
What if the password is not included in the candidates that
John is currently using?
This is where a wordlist becomes useful.
We can provide our own wordlist by using the --wordlist
option.
Let's create a second test password and generate its MD5
hash using the same method. And we'll then append that new hash to our existing file.
┌──(kali㉿kali)-[~/john-lab]
└─$ echo 'Cybersecmastery' | md5sum
593ed296028cd517e7e68b5f534cc8c5 -
┌──(kali㉿kali)-[~/john-lab]
└─$
To add new content without replacing the existing data, we can use the >> redirection operator. After that, we'll use cat to verify whether both hashes are present or not.
┌──(kali㉿kali)-[~/john-lab]
└─$ echo '593ed296028cd517e7e68b5f534cc8c5' >> hash.txt
┌──(kali㉿kali)-[~/john-lab]
└─$ cat hash.txt
5f4dcc3b5aa765d61d8327deb882cf99
593ed296028cd517e7e68b5f534cc8c5
┌──(kali㉿kali)-[~/john-lab]
└─$
Now, let's run John again with our specified format.
┌──(kali㉿kali)-[~/john-lab]
└─$ john --format=Raw-MD5 hash.txt
Using default input encoding: UTF-8
Loaded 2 password hashes with no different salts (Raw-MD5 [MD5 256/256 AVX2 8×3])
Remaining 1 password hash
Warning: no OpenMP support for this hash type, consider fork+2
Proceeding with single, rules: Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/usr/share/john/password.lst
Proceeding with incremental: ASCII
0g 0:00:00:26 3/3 0g/s 6312Kp/s 6312Kc/s 6312KC/s vwa..marl131
Session aborted
┌──(kali㉿kali)-[~/john-lab]
└─$
You may notice that John does not report a new password. That can happen for several reasons.
- One possibility is that the password isn't in the
candidates being tested.
- Another possibility is that John has already processed that
hash and stored the result.
So, let's provide our own wordlist.
A common example on Kali Linux is the rockyou.txt wordlist.
We can use it with rockyou.txt. The rockyou.txt file contains a large collection of password candidates. John will process those candidates and compare them against the hashes in our file.
┌──(kali㉿kali)-[~/john-lab]
└─$ john --format=Raw-MD5 --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Using default input encoding: UTF-8
Loaded 2 password hashes with no different salts (Raw-MD5 [MD5 256/256 AVX2 8×3])
Remaining 1 password hash
Warning: no OpenMP support for this hash type, consider fork-2 Press q or Ctrl-C to abort, almost any other key for status
0g 0:00:00:03 DONE (2026-09-21 14:20) 0g/s 4672Kp/s 4672Kc/s 4672KC/s fuckyooh21.. 7; Vamos!
Session completed.
┌──(kali㉿kali)-[~/john-lab]
└─$
But in our demonstration, the password we selected
isn't in that wordlist. In that case, John is not able to recover it. This brings us to another useful technique.
We can create our own small wordlist containing the password
candidates that we want to test.
Let's create a file called passwords.txt.
┌──(kali㉿kali)-[~/john-lab]
└─$ cat > passwords.txt << 'EOF'
heredoc> password
heredoc> admin
heredoc> 123456
heredoc> CyberSecurity
heredoc> Cybersecmastery
heredoc> EOF
┌──(kali㉿kali)-[~/john-lab]
└─$
In real security assessments, candidate lists can be created
from many different sources, including password policies, previously known
information, or password-generation and transformation techniques.
The important security lesson is that people often choose
passwords based on information that is easy to remember. That can include names, dates, favorite words, phone
numbers, or other predictable patterns.
Now our wordlist is ready. Let's use it with John to test the candidates from our custom
wordlist against the hashes.
┌──(kali㉿kali)-[~/john-lab]
└─$ john --format=Raw-MD5 --wordlist=passwords.txt hash.txt
Using default input encoding: UTF-8
Loaded 2 password hash with no different salts (Raw-MD5 [MD5 256/256 AVX2 8×3])
Remaining 1 password hash
Warning: no OpenMP support for this hash type, consider --fork-2
Press 'q' or Ctrl-C to abort, almost any other key for status
Warning: Only 5 candidates left, minimum 24 needed for performance.
Cybersecmastery (?)
1g 0:00:00:00 DONE (2026-09-21 14:21) 2.380g/s 11.90p/s 11.90c/s 11.90C/s 123456.. Cybersecmastery
Use the "--show --format-Raw-MD5" options to display all of the cracked passwords reliably
Session completed.
┌──(kali㉿kali)-[~/john-lab]
└─$
As you can see, the password has been recovered.
Notice something interesting here.
Our hash.txt file contains two hashes. However, during the previous run, John reported only one newly recovered password.
So, what happened to the other hash?
The answer is that John keeps track of passwords it has already recovered. It stores these results in a password database, commonly known as the pot file.
So, when John sees a hash that it has already successfully recovered, it doesn't need to perform the same password testing again.
This is why you may see fewer new results, even though multiple hashes are present in the file.
Instead, we can use the --show option. The --show option displays passwords that John has already
recovered for the hashes in the file.
┌──(kali㉿kali)-[~/john-lab]
└─$ john --format=Raw-MD5 --show hash.txt
?:password
?:Cybersecmastery
2 password hashes cracked, 0 left
┌──(kali㉿kali)-[~/john-lab]
└─$
This is usually the better way to review previously
recovered results. If you remove or reset John's password database, those
results will no longer be available to John, and he may need to perform the
work again.
The exact location of the pot file depends on the John
installation and configuration.
Let me explain this using our previous example:
Now, when we run the john command with our wordlist and hash file, John displays a message like this:
“Loaded 2 password hashes with no different salts. No password hashes left to crack.”
So, what does this message mean?
It means John has already processed both hashes and has recovered their corresponding passwords. Because those results are already stored in John's pot file, there are no remaining hashes for John to process.
So, what happens if we delete the pot file?
Let's find out.
First, we need to locate John's pot file.
On a typical Linux installation, John's configuration and data are stored under a hidden directory within the .john directory inside the current user's home directory.
The tilde (|) symbol is a shortcut that represents the current user's home directory.
Run ls command along with the path to the .john directory.
┌──(kali㉿kali)-[~/john-lab]
└─$ ls ~/.john/
john.logjohn.pot
┌──(kali㉿kali)-[~/john-lab]
└─$
Here, you see files such as john.log and john.pot.
The john.pot file is particularly important for this demonstration because it stores passwords that John has already recovered. Now, let's remove the john.pot file. This removes John's stored recovery history for the passwords it has already found.
┌──(kali㉿kali)-[~/john-lab]
└─$ rm ~/.john/john.pot
┌──(kali㉿kali)-[~/john-lab]
└─$
Now, let's run the same John command again. This time, John no longer has the previous recovery results stored in its pot file.
So, it processes the hashes again. As you can see, the two passwords are now reported again. This demonstrates why John keeps a pot file: it prevents the tool from repeating work that it has already completed.
Now let's move to another important capability.
John the Ripper is not limited to simple hash files. It can also work with password-protected files and other
supported formats.
John includes a collection of utilities whose names commonly
end with *2john. These utilities convert password-protected files into a
representation that John can process.
Let's see this with a ZIP archive.
First, we'll create a simple text file.
┌──(kali㉿kali)-[~/john-lab]
└─$ echo "This is a password-protected laboratory file -- Cybersecmastery' > secret.txt
┌──(kali㉿kali)-[~/john-lab]
└─$
The secret.txt file has now been created.
Next, we'll create a password-protected ZIP archive. We'll use the zip command with the -e option.
┌──(kali㉿kali)-[~/john-lab]
└─$ zip -e protected.zip secret.txt
Enter password:
Verify password:
adding: secret.txt (stored 0%)
┌──(kali㉿kali)-[~/john-lab]
└─$ ls
hash.txt passwords.txt protected.zip secret.txt
┌──(kali㉿kali)-[~/john-lab]
└─$
The command asks us to enter a password for the archive.
After we provide the password, the protected.zip file is
created.
Let's verify it.
Now we have our password-protected ZIP archive.
If we try to extract it using the normal unzip command, it
asks for the password.
┌──(kali㉿kali)-[~/john-lab]
└─$ unzip protected.zip
Archive: protected.zip
[protected.zip] secret.zip password:
skipping: secret.txtincorrect password
┌──(kali㉿kali)-[~/john-lab]
└─$
Without the correct password, we cannot extract the
protected contents. But John the Ripper cannot simply take every protected file
directly as input.
This is where a format-specific*2john utility comes into the
workflow.
For ZIP archives, the utility is called zip2john. Its job is to extract the password-verification data from
the ZIP archive and convert it into a format that John can process.
Let's first check whether the zip2john utility can be run directly from the terminal.
We can use the command:
┌──(kali㉿kali)-[~/john-lab]
└─$ which zip2john
/usr/sbin/zip2john
┌──(kali㉿kali)-[~/john-lab]
└─$
As you can see, the utility is available in the system's executable path, which means we can run it directly from the terminal without specifying the full path.
Now, we'll use it against our protected archive. Let's use it zip2john with our protected ZIP file.
Here,
protected.zip is our password-protected ZIP archive.- The greater-than (>) symbol redirects the output from
zip2john into a file called protected.hash.
When we run the command, zip2john extracts the password-verification data from the ZIP archive and writes that data into protected.hash.
┌──(kali㉿kali)-[~/john-lab]
└─$ zip2john protected.zip > protected.hash
ver 1.0 efh 5455 efh 7875 protected.zip/secret.txt PKZIP Encr: 2b chk, TS_chk, cmplen-73, decmplen-61, crc-60098CA7 ts=7454 cs-745
4 type-0
┌──(kali㉿kali)-[~/john-lab]
└─$
The output is redirected into a new file called
protected.hash. Let's inspect that file.
┌──(kali㉿kali)-[~/john-lab]
└─$ cat protected.hash
protected.zip/secret.txt:Spkzip$1*2*2*0*49*30*60d98ca7+0*44*0*49*7454-12c3ee50b5dbcbe3ac80d85b5778a59b5c3bb80053ee6c0476129e3c2309 d3cdfcfe72f61e412c818d2b051608a83d2caaeb86c67efed22b591bb4e908c080c03bb4336d414f473080+$/pkzip$:secret.txt:protected.zip::protecte
d.zip
┌──(kali㉿kali)-[~/john-lab]
└─$
As you can see, the file now contains the extracted
verification data in a format that John understands.
Now, we can provide that file to John.
┌──(kali㉿kali)-[~/john-lab]
└─$ john --wordlist=passwords.txt protected.hash
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Cybersecmastery(protected.zip/secret.txt)
1g 0:00:00:00 DONE (2029-09-21 14:39) 33.33g/s 166.6p/s 166.6c/s 166.6C/s password.. Cybersecmastery
Use the show" option to display all of the cracked passwords reliably
Session completed.
┌──(kali㉿kali)-[~/john-lab]
└─$
If the correct password is present in our wordlist, John can
identify it.
And there we have it. We now have the password that can be
used to extract the password-protected zip file.
We've now demonstrated the complete workflow.
- First, we created a password-protected file.
- Then, we converted the file into a format that John could
process.
- Finally, John tested the password candidates and recovered
the password from our controlled laboratory example.
There are many other *2john utilities for different file
formats and password-protected data.
The overall workflow is generally similar:
- Convert the supported file into a John-readable format.
- Then provide that output to John for password auditing.
We're not going to cover every supported format in this
video, because the basic concept remains the same.
You can experiment with the other utilities safely in your
own laboratory environment. And that's where we'll stop for this lesson.