Crear tu propio escáner de vulnerabilidades te otorga un control absoluto sobre tus auditorías. A diferencia de soluciones comerciales pesadas (como Nessus o Qualys), un escáner en Python puede ser ligero, sigiloso y estar programado para buscar exactamente los vectores de ataque que te interesan. En esta guía, construiremos la arquitectura de un escáner modular.
El núcleo de cualquier escáner es descubrir qué puertas están abiertas. Para ello, usamos la librería nativa socket de Python, intentando realizar el TCP 3-Way Handshake.
import socket
from concurrent.futures import ThreadPoolExecutor
def scan_port(ip, port):
# AF_INET para IPv4, SOCK_STREAM para TCP
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(1.0) # Evitar quedarse colgado infinitamente
if s.connect_ex((ip, port)) == 0:
print(f"[+] Puerto {port} ABIERTO")
return port
except Exception:
pass
return None
# Usar hilos paralelos para escanear rápido
target = "10.10.10.50"
with ThreadPoolExecutor(max_workers=50) as executor:
for port in range(1, 1024):
executor.submit(scan_port, target, port)
Saber que el puerto 22 está abierto no es suficiente. Necesitamos saber qué versión de SSH corre ahí para cruzarla con bases de datos de vulnerabilidades. El Banner Grabbing lee los primeros bytes que el servidor envía al conectarnos.
def grab_banner(ip, port):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(2.0)
s.connect((ip, port))
# Algunos servicios requieren que enviemos datos primero (como HTTP)
if port == 80:
s.send(b"HEAD / HTTP/1.1\r\nHost: {ip}\r\n\r\n")
banner = s.recv(1024).decode().strip()
print(f"[+] Banner en {port}: {banner}")
except Exception as e:
print(f"[-] No se pudo obtener banner en {port}")
Si el escáner detecta servicios HTTP/HTTPS, delegamos el análisis a un módulo específico. Usaremos requests para interactuar con la web y buscar cabeceras inseguras o directorios expuestos.
import requests
def analyze_headers(url):
try:
# verify=False útil en entornos locales con certificados autofirmados (cuidado en prod)
response = requests.get(url, timeout=5, verify=False)
headers = response.headers
# Comprobar cabeceras de seguridad críticas
security_headers = [
'Strict-Transport-Security',
'X-Frame-Options',
'X-Content-Type-Options',
'Content-Security-Policy'
]
for header in security_headers:
if header not in headers:
print(f"[!] ALERTA: Falta la cabecera {header}")
# Detectar tecnologías expuestas
if 'Server' in headers:
print(f"[*] Servidor detectado: {headers['Server']}")
except requests.exceptions.RequestException as e:
print(f"Error conectando a {url}: {e}")
El módulo HTTP de nuestro escáner escrito en Python está fallando en producción. Como Lead Developer, debes debugear el código, identificar los parámetros correctos de la librería requests y de sockets para asegurar que la herramienta audita correctamente.
Podemos dotar a nuestro escáner Python de las mismas capacidades que Gobuster, leyendo un diccionario y paralelizando peticiones GET para encontrar paneles ocultos (como /admin o .env).
def fuzz_directories(base_url, wordlist_path):
with open(wordlist_path, 'r') as file:
directories = file.read().splitlines()
def check_dir(directory):
url = f"{base_url}/{directory}"
res = requests.get(url)
if res.status_code != 404:
print(f"[+] Encontrado: {url} (Status: {res.status_code})")
with ThreadPoolExecutor(max_workers=20) as executor:
executor.map(check_dir, directories)
El paso final para un escáner profesional es automatizar la búsqueda de exploits. Una vez que nuestro Banner Grabbing detecta, por ejemplo, "Apache 2.4.49", el escáner hace una petición a la API del National Vulnerability Database (NVD) para listar los CVEs críticos (como el CVE-2021-41773 de Path Traversal).
ThreadPoolExecutor es fácil de usar, la librería asyncio junto con aiohttp es infinitamente más rápida para escaneos masivos en red porque evita el bloqueo de hilos (I/O Bound).try/except genéricos alrededor de las peticiones para que el escáner no se cierre (crash) a la mitad del proceso.User-Agent aleatorio en la librería requests para evitar ser bloqueado inmediatamente por los WAF.