John the Ripper and Password Salting

Kali LinuxBeginner
Practice Now

Introduction

In the realm of cybersecurity, protecting user credentials is paramount. Passwords, when stored, are rarely kept in plain text. Instead, they are typically hashed, meaning they are transformed into a fixed-size string of characters using a one-way cryptographic function. This prevents attackers from directly accessing passwords even if they gain access to the database. However, a simple hash is not enough. Attackers can use precomputed tables of hashes (rainbow tables) or brute-force attacks to crack passwords.

This is where "salting" comes into play. Salting is a technique where a unique, random string (the "salt") is added to a password before it is hashed. This salt is then stored alongside the hash. When a user attempts to log in, the same salt is retrieved and combined with the entered password before hashing, and the resulting hash is compared to the stored hash.

In this lab, you will explore the concept of password salting, understand its importance in enhancing security, and observe how a powerful password cracking tool like John the Ripper interacts with salted hashes. You will learn to identify salted hashes, see how John the Ripper handles them, and grasp the significant impact salting has on the difficulty of cracking passwords. Finally, you will get a glimpse into how salting is implemented in real-world password storage.

Understand the Concept of Salting

In this step, you will learn the fundamental concept of password salting and why it is crucial for enhancing password security.

When a password is "salted," a unique, random string (the salt) is added to the password before it is hashed. This means that even if two users have the exact same password, their stored hashes will be different because a different salt was used for each. This effectively defeats precomputed rainbow tables, which rely on a fixed hash for a given password. It also makes brute-force attacks more difficult because an attacker would have to compute a hash for every possible password and every possible salt, rather than just every possible password.

Let's look at an example of a salted hash. Open the salted_passwords.txt file that was created in your ~/project directory.

cat ~/project/salted_passwords.txt

You will see output similar to this:

user1:$6$salt12345$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ./:user1password
user2:5f4dcc3b5aa765d61d8327deb882cf99
user3:$6$another_salt$zyxwuvtsrqponmlkjihgfedcba9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA./:anotherpassword
user4:$6$short$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ./:shortpass

Notice the lines for user1, user3, and user4. They contain a section like $6$salt12345$ or $6$another_salt$. The $6$ indicates the hashing algorithm used (SHA-512 in this case), and the string between the second and third $ signs (salt12345 or another_salt) is the salt. The rest of the string after the third $ is the actual hash of the password combined with the salt. The line for user2 is an unsalted MD5 hash, which we will use for comparison later.

The key takeaway is that each salted hash has a unique salt, making it harder to crack multiple passwords simultaneously using the same precomputed tables.

Identify Salted Hashes

In this step, you will learn how to identify salted hashes based on their common formats. Recognizing these formats is the first step in understanding how to approach password cracking or defense.

Different hashing algorithms and systems use various formats for storing salted hashes. However, many common formats, especially those used in Linux systems, follow a pattern where the salt is embedded directly within the hash string, often delimited by special characters like $.

Let's re-examine the salted_passwords.txt file and specifically look for patterns that indicate salting.

cat ~/project/salted_passwords.txt

As you observed in the previous step:

  • user1:$6$salt12345$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ./:user1password
  • user3:$6$another_salt$zyxwuvtsrqponmlkjihgfedcba9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA./:anotherpassword
  • user4:$6$short$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ./:shortpass

These lines clearly show the $6$ prefix (indicating SHA-512 crypt) followed by the salt string (e.g., salt12345, another_salt, short), and then the actual hash. This structure is a strong indicator of a salted hash.

In contrast, the line for user2:

  • user2:5f4dcc3b5aa765d61d8327deb882cf99

This is a simple MD5 hash. It lacks any delimiters or embedded salt, making it easily identifiable as an unsalted hash.

Being able to quickly distinguish between salted and unsalted hashes is crucial for security professionals and penetration testers, as it dictates the cracking strategies that can be employed.

Observe John the Ripper's Handling of Salted Hashes

In this step, you will use John the Ripper, a popular password cracking tool, to observe how it handles both salted and unsalted hashes. This will demonstrate the practical implications of salting.

First, let's try to crack the passwords in salted_passwords.txt using a simple wordlist. We'll use a small, custom wordlist for this demonstration.

Create a wordlist file named wordlist.txt in your ~/project directory with some common passwords:

echo "password" > ~/project/wordlist.txt
echo "user1password" >> ~/project/wordlist.txt
echo "anotherpassword" >> ~/project/wordlist.txt
echo "shortpass" >> ~/project/wordlist.txt
echo "123456" >> ~/project/wordlist.txt

Now, run John the Ripper against your salted_passwords.txt file using this wordlist:

john --wordlist=~/project/wordlist.txt ~/project/salted_passwords.txt

John the Ripper will analyze the hashes. You should see output indicating that it's trying to crack the passwords. It will likely find user2's password quickly because it's an unsalted MD5 hash and "password" is in the wordlist. It will also find the salted passwords if their corresponding plaintexts are in the wordlist.

Example output might look like this:

Using default input encoding: UTF-8
Loaded 4 password hashes with no different salts to crack (crypt, generic crypt(3) [MD5/Blowfish/SHA1/SHA256/SHA512/XSHAa/XSHAab/bcrypt/scrypt])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
password         (user2)
user1password    (user1)
anotherpassword  (user3)
shortpass        (user4)
4g 0:00:00:00 DONE (2023-10-27 10:30) ...
Session completed.

After John finishes, you can view the cracked passwords using the --show option:

john --show ~/project/salted_passwords.txt

This command will display any passwords that John successfully cracked.

user2:password (user2)
user1:user1password (user1)
user3:anotherpassword (user3)
user4:shortpass (user4)

4 password hashes cracked, 0 left

John the Ripper is designed to handle various hash formats, including those with embedded salts. It automatically extracts the salt and uses it during the cracking process. However, the presence of unique salts for each password significantly increases the computational effort required for cracking, especially in large-scale attacks.

Understand the Impact of Salting on Cracking

In this step, you will solidify your understanding of why salting makes password cracking significantly harder, especially for large-scale attacks.

Consider the user2 entry from our salted_passwords.txt file: user2:5f4dcc3b5aa765d61d8327deb882cf99. This is an unsalted MD5 hash for the password "password". If an attacker obtains a database of unsalted MD5 hashes, they can:

  1. Use Rainbow Tables: Precomputed tables of hashes for common passwords. If "password" is in the table, its hash 5f4dcc3b5aa765d61d8327deb882cf99 will be there, and the plaintext can be instantly retrieved.
  2. Crack Multiple Passwords Simultaneously: If many users have the same weak password (e.g., "123456"), their unsalted hashes will be identical. An attacker only needs to crack one instance of that hash to know the password for all users sharing it.

Now, let's look at the salted hashes:

  • user1:$6$salt12345$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ./:user1password
  • user3:$6$another_salt$zyxwuvtsrqponmlkjihgfedcba9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA./:anotherpassword

Even if user1 and user3 had the exact same password, their hashes would be different because their salts (salt12345 and another_salt) are different. This has several critical implications for attackers:

  • Rainbow Tables are Useless: Because each hash is unique due to its salt, precomputed rainbow tables become ineffective. An attacker would need a rainbow table for every possible salt, which is computationally infeasible.
  • Per-Hash Cracking: An attacker must crack each salted hash individually. If 100 users have the same password, but each has a unique salt, the attacker must perform 100 separate cracking operations, one for each unique hash-salt pair. This dramatically increases the time and resources required for a successful attack.
  • Brute-Force Difficulty: For a brute-force attack, the attacker must combine each potential password with its specific salt before hashing and comparing. This adds a layer of complexity and significantly slows down the cracking process compared to unsalted hashes.

In essence, salting transforms a single cracking problem into many independent cracking problems, making large-scale password breaches much more difficult and time-consuming for attackers.

Implement Salting in Password Storage

In this step, you will get a conceptual understanding of how salting is implemented in real-world password storage systems. While we won't build a full system, you'll see the core components.

When a user sets or changes their password, a secure system typically performs the following steps:

  1. Generate a Random Salt: A cryptographically secure random salt is generated. This salt should be unique for each user and each password change.
  2. Combine Password and Salt: The user's plain-text password is concatenated with the generated salt.
  3. Hash the Combined String: The combined password and salt string is then passed through a strong, slow hashing algorithm (e.g., bcrypt, scrypt, Argon2, or SHA-512 crypt as seen in our examples). Slow hashing algorithms are preferred because they make brute-force attacks even more time-consuming.
  4. Store Hash and Salt: The resulting hash and the salt are stored together in the database. The salt does not need to be secret; its purpose is to ensure hash uniqueness.

When a user attempts to log in:

  1. Retrieve Stored Salt: The system retrieves the salt associated with the user's account from the database.
  2. Combine Entered Password and Stored Salt: The password entered by the user is combined with the retrieved salt.
  3. Hash the Combined String: This combined string is then hashed using the same hashing algorithm used during registration.
  4. Compare Hashes: The newly computed hash is compared to the stored hash. If they match, the password is correct.

Let's simulate generating a salted hash using the mkpasswd command, which is part of the whois package and can generate crypt(3) style hashes.

First, ensure whois is installed:

sudo apt install -y whois

Now, generate a SHA-512 salted hash for the password "mysecretpassword" with a custom salt "mysalt":

mkpasswd -m sha-512 "mysecretpassword" -s "mysalt"

You will see output similar to this:

$6$mysalt$some_long_hash_string_here

The output $6$mysalt$some_long_hash_string_here is the salted hash. $6$ indicates SHA-512, mysalt is the salt you provided, and the rest is the actual hash. In a real system, the salt would be randomly generated, not manually provided.

This demonstrates the fundamental process. Modern applications use libraries and frameworks that abstract away these details, but the underlying principle of generating a unique salt, combining it with the password, hashing, and storing both remains the cornerstone of secure password storage.

Summary

In this lab, you have gained a comprehensive understanding of password salting and its critical role in enhancing password security. You learned that salting involves adding a unique, random string (the salt) to a password before it is hashed, making each hash unique even for identical passwords.

You successfully identified salted hashes by their characteristic formats, often including delimiters like $ and an embedded salt string. Through practical observation with John the Ripper, you saw how this powerful tool handles both salted and unsalted hashes, and crucially, how salting forces the cracking process to be performed on a per-hash basis, significantly increasing the computational effort for attackers.

Finally, you explored the conceptual implementation of salting in password storage systems, understanding the steps involved in generating, combining, hashing, and storing salts and passwords. This knowledge is fundamental for anyone involved in cybersecurity, whether for defending systems or understanding attack methodologies.

By making each password hash unique, salting effectively mitigates the threat of rainbow table attacks and dramatically slows down brute-force attempts, making it an indispensable technique for robust password security.