> For the complete documentation index, see [llms.txt](https://chubu.gitbook.io/chubu/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://chubu.gitbook.io/chubu/less-than.-coding-greater-than/ejercicios-1.md).

# Ejercicios #1

diversos ejercicios en python para practicar <3

#### **1. Ejercicio Básico: Password Analyzer**

**Nivel:** Básico **Nombre del ejercicio:** password\_analyzer.py

**Objetivos de aprendizaje:**

* Manejo de cadenas, expresiones regulares (re) y archivos en Python.
* Entender qué hace que una contraseña sea débil o fuerte.
* Conceptos básicos de ataques de fuerza bruta y rate-limiting.
* Buenas prácticas de programación segura y legible.

*Solucion*:

{% code expandable="true" %}

```python
import re
import time

#Configuramos
COMMON_PASSWORDS = {"password", "admin", "123456", "qwerty", "letmein", "welcome", "abc123"}

def load_wordlist(file_path: str):
    """Carga wordlist desde archivo elegido por el usuario"""
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            return {line.strip().lower() for line in f if line.strip()}
    except FileNotFoundError:
        print(f"  No se encontró el archivo {file_path}. Usando wordlist por defecto.")
        return COMMON_PASSWORDS


def check_password_strength(password: str, wordlist: set):
    feedback = []
    score = 0

    if len(password) >= 8:
        score += 25
    else:
        feedback.append("❌ Mínimo 8 caracteres recomendados")

    if re.search(r"[A-Z]", password): score += 20
    else: feedback.append("❌ Agrega al menos una mayúscula")

    if re.search(r"[a-z]", password): score += 20
    else: feedback.append("❌ Agrega al menos una minúscula")

    if re.search(r"\d", password): score += 20
    else: feedback.append("❌ Agrega al menos un número")

    if re.search(r"[!@#$%^&*()_+\-=\[\]{};':\"\\|,.<>/?]", password): score += 15
    else: feedback.append("❌ Agrega al menos un carácter especial")

    if password.lower() in wordlist:
        score = 0
        feedback.append("❌ Esta contraseña es muy común (está en wordlist)")

    strength = "Débil" if score < 50 else "Media" if score < 80 else "Fuerte"
    return score, strength, feedback


def brute_force_simulator(target: str, wordlist: set, delay: float = 0.3):
    print(f"\n[+] Iniciando simulación de brute force contra: {target}\n")
    for pwd in list(wordlist)[:50]:   # límite para no tardar mucho
        print(f"Probando → {pwd}")
        if pwd == target or pwd == target.lower():
            print(f"\n✅ ¡Contraseña crackeada!: {pwd}")
            return pwd
        time.sleep(delay)
    print("❌ No se encontró la contraseña en esta wordlist.")
    return None


# MAIN 
if __name__ == "__main__":
    print("=== Password Analyzer - Hacking Ético ===\n")
    
    password = input("Ingresa la contraseña a analizar: ").strip()
    
    # Wordlist personalizada
    wordlist_path = input("Ruta de tu wordlist (Enter para usar por defecto): ").strip()
    if wordlist_path:
        wordlist = load_wordlist(wordlist_path)
    else:
        wordlist = COMMON_PASSWORDS
    
    score, strength, feedback = check_password_strength(password, wordlist)
    
    print(f"\nFortaleza: {strength} ({score}/100)")
    for msg in feedback:
        print(msg)
    
    if input("\n¿Simular ataque de fuerza bruta? (s/n): ").lower() == "s":
        brute_force_simulator(password, wordlist)
```

{% endcode %}

#### **2. Ejercicio Intermedio: Directory Brute Forcer**

**Nivel:** Intermedio **Nombre del ejercicio:** directory\_bruteforcer.py

**Objetivos de aprendizaje:**

* Uso de la librería requests para peticiones HTTP.
* Programación multihilo con threading.
* Manejo de código asíncrono básico y control de errores.
* Trabajo con archivos (lectura de wordlists y escritura de resultados).
* Uso de argparse (opcional) y buenas prácticas CLI.

*Solucion*:

{% code expandable="true" %}

```python
import requests
import argparse
import csv
from datetime import datetime
from urllib.parse import urljoin
from threading import Thread
import time

requests.packages.urllib3.disable_warnings()

def load_wordlist(path: str):
    try:
        with open(path, 'r', encoding='utf-8') as f:
            return [line.strip() for line in f if line.strip() and not line.startswith("#")]
    except FileNotFoundError:
        print(f"❌ Wordlist no encontrada: {path}")
        exit(1)


def check_directory(session, base_url: str, path: str, results: list):
    url = urljoin(base_url.rstrip('/') + '/', path.lstrip('/'))
    try:
        r = session.get(url, timeout=6, allow_redirects=True, verify=False)
        if r.status_code in [200, 301, 302]:
            print(f"[+] [{r.status_code}] → {url}")
            results.append({"url": url, "status": r.status_code, "size": len(r.content)})
        elif r.status_code == 403:
            print(f"[!] [403] → {url}")
    except:
        pass


def main():
    print("=== Directory Brute Forcer - Chubu Bug Bounty ===\n")
    
    target = input("URL objetivo (ej: http://testphp.vulnweb.com): ").strip()
    wordlist_path = input("Ruta de la wordlist (common.txt): ").strip() or "common.txt"
    threads = int(input("Cantidad de hilos (recomendado 10-20): ") or 15)

    session = requests.Session()
    session.headers.update({"User-Agent": "Mozilla/5.0 (Chubu-Pentester)"})

    wordlist = load_wordlist(wordlist_path)
    results = []
    threads_list = []

    print(f"\n[+] Iniciando brute force con {len(wordlist)} entradas...\n")
    
    for path in wordlist:
        t = Thread(target=check_directory, args=(session, target, path, results))
        threads_list.append(t)
        t.start()

        if len(threads_list) >= threads:
            for t in threads_list:
                t.join()
            threads_list = []

    # Esperar hilos restantes
    for t in threads_list:
        t.join()

    # Guardar resultados
    if results:
        filename = f"bruteforce_{datetime.now().strftime('%Y%m%d_%H%M')}.csv"
        with open(filename, 'w', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=["url", "status", "size"])
            writer.writeheader()
            writer.writerows(results)
        print(f"\n✅ Resultados guardados en: {filename}")
    else:
        print("\nNo se encontraron directorios.")

if __name__ == "__main__":
    main()
```

{% endcode %}

#### **3. Ejercicio Avanzado: Basic Web Vulnerability Scanner**

**Nivel:** Avanzado **Nombre del ejercicio:** basic\_vuln\_scanner.py

**Objetivos de aprendizaje:**

* Web scraping básico con BeautifulSoup.
* Inyección de payloads y análisis de respuestas.
* Diferencia entre vulnerabilidades SQLi y XSS.
* Estructuración de código usando Programación Orientada a Objetos.
* Generación de reportes y buena documentación de hallazgos.

*Solucion:*

{% code expandable="true" %}

```python
import requests
from bs4 import BeautifulSoup
import urllib.parse
import time

class WebVulnScanner:
    def __init__(self, target_url: str):
        self.target = target_url.rstrip('/')
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Chubu-VulnScanner/1.0 (Hacking Ético)"
        })
        
        self.sqli_payloads = ["'", "' OR 1=1--", "1' UNION SELECT 1,2--", "' OR sleep(3)--"]
        self.xss_payloads = ["<script>alert(1)</script>", "<img src=x onerror=alert(1)>", "javascript:alert(1)"]

    def get_forms(self):
        try:
            r = self.session.get(self.target, timeout=8)
            soup = BeautifulSoup(r.text, 'html.parser')
            forms = soup.find_all("form")
            print(f"[+] Encontrados {len(forms)} formularios")
            return forms
        except Exception as e:
            print(f"[-] Error cargando página: {e}")
            return []

    def test_parameter(self, url: str, params: dict, test_type: str):
        if test_type == "sqli":
            payloads = self.sqli_payloads
            print_name = "SQL Injection"
        else:
            payloads = self.xss_payloads
            print_name = "XSS"

        print(f"\n[*] Probando {print_name} en {url}")
        for payload in payloads:
            try:
                test_params = params.copy()
                for key in test_params:
                    test_params[key] = payload

                start = time.time()
                r = self.session.get(url, params=test_params, timeout=7)

                if test_type == "sqli":
                    if any(err in r.text.lower() for err in ["sql syntax", "mysql", "sqlite", "odbc"]) or (time.time() - start > 2.5):
                        print(f"🚨 POSIBLE {print_name} → Payload: {payload}")
                else:  # xss
                    if payload in r.text:
                        print(f"🚨 POSIBLE {print_name} → Payload: {payload}")
            except:
                pass


    def scan(self):
        print(f"\n=== Iniciando Vulnerability Scanner contra {self.target} ===\n")
        
        # Test parámetros GET
        if "?" in self.target:
            base_url, query = self.target.split("?", 1)
            params = dict(urllib.parse.parse_qsl(query))
            if params:
                self.test_parameter(base_url, params, "sqli")
                self.test_parameter(base_url, params, "xss")

        # Test formularios (básico)
        self.get_forms()
        
        print("\n[+] Escaneo finalizado.")
        print("   Recuerda: Este es un scanner básico. Siempre valida manualmente los resultados.")


if __name__ == "__main__":
    url = input("Ingresa la URL objetivo: ").strip()
    if not url.startswith("http"):
        url = "http://" + url
    
    scanner = WebVulnScanner(url)
    scanner.scan()
```

{% endcode %}
