Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import time | |
| import itertools | |
| import string | |
| import requests | |
| # ---------------------------- | |
| # Configuración de la página | |
| # ---------------------------- | |
| st.set_page_config( | |
| page_title="🔐 Ataques a Contraseñas - Demo Educativa", | |
| page_icon="🔐", | |
| layout="centered" | |
| ) | |
| st.title("🔐 Ataques a Contraseñas") | |
| st.markdown(""" | |
| **Demo educativa** usando contraseñas reales del top 10,000 más comunes (SecLists). | |
| Ingresa una contraseña **simple** (máx. 6 caracteres, solo minúsculas y números). | |
| ⚠️ **Solo para fines didácticos. No uses contraseñas reales.** | |
| """) | |
| # ---------------------------- | |
| # Cargar diccionario real desde SecLists (solo una vez) | |
| # ---------------------------- | |
| def load_common_passwords(): | |
| url = "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10k-most-common.txt" | |
| try: | |
| with st.spinner("📥 Cargando diccionario real (top 10k contraseñas)..."): | |
| response = requests.get(url, timeout=10) | |
| response.raise_for_status() | |
| # Filtrar: solo contraseñas de 1 a 6 caracteres, alfanuméricas | |
| passwords = [] | |
| for line in response.text.splitlines(): | |
| p = line.strip().lower() | |
| if 1 <= len(p) <= 6 and p.isalnum(): | |
| passwords.append(p) | |
| if len(passwords) >= 5000: # Límite para velocidad | |
| break | |
| return passwords | |
| except Exception as e: | |
| st.warning("⚠️ No se pudo cargar el diccionario. Usando lista de respaldo.") | |
| return [ | |
| "123456", "12345", "password", "qwerty", "abc123", | |
| "admin", "letmein", "welcome", "monkey", "dragon", | |
| "123123", "sunshine", "iloveyou", "princess", "football" | |
| ] | |
| COMMON_PASSWORDS = load_common_passwords() | |
| # ---------------------------- | |
| # Funciones de ataque | |
| # ---------------------------- | |
| def brute_force_attack(target, max_length=6): | |
| chars = string.ascii_lowercase + string.digits | |
| start_time = time.time() | |
| attempts = 0 | |
| for length in range(1, max_length + 1): | |
| for guess_tuple in itertools.product(chars, repeat=length): | |
| guess = ''.join(guess_tuple) | |
| attempts += 1 | |
| if guess == target: | |
| return guess, attempts, time.time() - start_time | |
| return None, attempts, time.time() - start_time | |
| def dictionary_attack(target): | |
| start_time = time.time() | |
| attempts = 0 | |
| for guess in COMMON_PASSWORDS: | |
| attempts += 1 | |
| if guess == target: | |
| return guess, attempts, time.time() - start_time | |
| return None, attempts, time.time() - start_time | |
| def hybrid_attack(target): | |
| start_time = time.time() | |
| attempts = 0 | |
| base_words = ["password", "admin", "user", "test", "hello", "welcome", "qwerty", "love", "sunshine", "dragon"] | |
| suffixes = ["", "1", "123", "2023", "2024", "!", "01", "12", "789"] | |
| transformations = set() | |
| for word in base_words: | |
| for suf in suffixes: | |
| # Original y capitalizada | |
| transformations.add(word + suf) | |
| transformations.add(word.capitalize() + suf) | |
| # Reemplazos comunes | |
| w = word.replace('a', '@').replace('e', '3').replace('o', '0').replace('i', '1') | |
| transformations.add(w + suf) | |
| transformations.add(w.capitalize() + suf) | |
| for guess in transformations: | |
| attempts += 1 | |
| if guess == target: | |
| return guess, attempts, time.time() - start_time | |
| return None, attempts, time.time() - start_time | |
| # ---------------------------- | |
| # Interfaz de usuario | |
| # ---------------------------- | |
| password = st.text_input( | |
| "🔒 Ingresa una contraseña de prueba (máx. 6 caracteres, minúsculas y números):", | |
| type="password", | |
| max_chars=6 | |
| ) | |
| # Validación básica | |
| if password and not all(c in (string.ascii_lowercase + string.digits) for c in password): | |
| st.warning("⚠️ Usa solo letras minúsculas y números (ej: 'abc123').") | |
| password = "" | |
| attack_type = st.selectbox("🎯 Elige un tipo de ataque:", ["Fuerza Bruta", "Diccionario", "Híbrido"]) | |
| if st.button("🚀 Iniciar ataque", disabled=not password): | |
| if len(password) > 6: | |
| st.error("La contraseña debe tener máximo 6 caracteres.") | |
| else: | |
| with st.spinner("Buscando..."): | |
| if attack_type == "Fuerza Bruta": | |
| result, attempts, duration = brute_force_attack(password, len(password)) | |
| elif attack_type == "Diccionario": | |
| result, attempts, duration = dictionary_attack(password) | |
| else: # Híbrido | |
| result, attempts, duration = hybrid_attack(password) | |
| if result: | |
| st.success(f"✅ ¡Contraseña encontrada! `{result}`") | |
| st.metric("Tiempo", f"{duration:.2f} segundos") | |
| st.metric("Intentos", f"{attempts:,}") | |
| st.balloons() | |
| else: | |
| st.error("❌ Contraseña no encontrada con este método.") | |
| st.metric("Intentos realizados", f"{attempts:,}") | |
| st.info("💡 Prueba una contraseña más común o más corta.") | |
| # ---------------------------- | |
| # Pie de página | |
| # ---------------------------- | |
| st.markdown("---") | |
| st.caption( | |
| "🎓 Demo educativa usando el diccionario '10k-most-common.txt' de SecLists (GitHub). " | |
| "Todo se ejecuta localmente. Código ético y sin riesgos." | |
| ) |