SimplePasswords / src /streamlit_app.py
enacimie's picture
Update src/streamlit_app.py
803b069 verified
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)
# ----------------------------
@st.cache_resource
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."
)