mxcheck es una herramienta de escaneo de información y seguridad diseñada para evaluar servidores de correo electrónico

 

https://www.onlinetis.com

mxcheck es una herramienta de escaneo de información y seguridad diseñada para evaluar servidores de correo electrónico

mxcheck es una herramienta de escaneo de información y seguridad diseñada específicamente para evaluar servidores de correo electrónico. Fue añadida a los repositorios de Kali Linux (a partir de la versión 2024.3) y está escrita en Go, lo que la hace muy rápida y eficiente para auditorías.

Su objetivo principal es ayudar a los pentesters o administradores de sistemas a descubrir malas configuraciones que podrían facilitar campañas de phishing, suplantación de identidad (spoofing) o envío de spam.

¿Qué analiza mxcheck?

Cuando ejecutas la herramienta contra un dominio, realiza de forma automatizada las siguientes verificaciones:

  • Registros DNS de correo: Verifica la existencia y configuración de registros clave de seguridad y enrutamiento (A, MX, PTR, SPF, MTA-STS, DKIM y DMARC).

  • Fugas de Información: Comprueba si el servidor revela datos a través de su banner de bienvenida o mediante comandos SMTP como VRFY (que permite a un atacante enumerar usuarios válidos).

  • Open Relay: Comprueba si el servidor permite enviar correos a cualquier destino sin autenticación (una vulnerabilidad clásica aprovechada para enviar spam).

  • Cifrado (StartTLS): Valida si el servidor soporta StartTLS y analiza su certificado SSL/TLS.

  • Escaneo de Puertos: Busca si los puertos estándar de correo (25, 465, 587) están abiertos.

  • Listas Negras (Blacklists): Revisa si la IP del servidor de correo ha sido reportada por enviar spam.

  • SMTP Smuggling: Evalúa si el servidor es vulnerable al contrabando SMTP, una técnica de evasión que permite falsificar remitentes burlando protecciones como SPF.

  • Geolocalización y Red: Identifica el número de Sistema Autónomo (ASN) y el país donde se aloja el servidor.

Cómo instalarlo en Kali Linux

Si utilizas una versión reciente de Kali, es probable que ya se encuentre en los repositorios oficiales. Puedes instalarla desde la terminal:

Bash
sudo apt update
sudo apt install mxcheck

Uso básico y ejemplos

La sintaxis fundamental consiste en usar la opción -s (service) seguida del dominio del servidor que deseas analizar.

Escaneo rápido a un dominio:

Bash
mxcheck -s ejemplo.com
Escaneo completo (con listas negras y prueba de SMTP Smuggling):
Si quieres una auditoría profunda que incluya revisión de blacklists (-b) y un test de vulnerabilidad Smuggling (-g):

Bash
mxcheck -s ejemplo.com -b -g
Escaneo sin interactividad exportando los resultados:
Ideal para automatizaciones en pentesting. Anula las preguntas en pantalla (-n) y guarda la salida en un archivo con formato TSV (-w):
Bash
mxcheck -s ejemplo.com -n -w reporte_ejemplo.tsv

Opciones más comunes (Flags)

FlagDescripción
-s, --serviceEspecifica el dominio o host objetivo.
-b, --blacklistHabilita la consulta en listas negras de spam.
-g, --smuggleActiva la prueba para la vulnerabilidad SMTP Smuggling.
-S, --dkim-selectorVerifica el registro DKIM (requiere que indiques el selector, por ejemplo default).
-p, --disable-port-scanDeshabilita el escaneo de puertos (útil para pruebas más sigilosas o rápidas).
-d, --dnsserverEspecifica un servidor DNS personalizado (por defecto usa 8.8.8.8).

El SMTP Smuggling (Contrabando SMTP) es una técnica de ataque descubierta a finales de 2023 (principalmente por investigadores de SEC Consult) que explota las discrepancias en cómo diferentes servidores de correo interpretan el protocolo SMTP, específicamente la secuencia que marca el final de un mensaje.

Esta vulnerabilidad permite a los atacantes "contrabandear" un segundo correo electrónico malicioso dentro del cuerpo de un correo legítimo, logrando falsificar (spoofear) direcciones de remitentes y evadiendo por completo los mecanismos de autenticación como SPF, DKIM y DMARC.

La Raíz del Problema: La Secuencia de Fin de Datos

Según el estándar RFC 5321, cuando un servidor envía el contenido de un correo (después de ejecutar el comando DATA), debe indicar que el mensaje ha terminado enviando una secuencia muy específica: un salto de línea, un punto y otro salto de línea. A nivel de caracteres invisibles, esto es:
<CR><LF>.<CR><LF>

El problema de seguridad surge porque no todos los servidores son estrictos con este estándar. Algunos aceptan secuencias alternativas o malformadas (como <LF>.<LF> o <CR>.<CR>) como un fin de mensaje válido.

Cómo funciona el ataque paso a paso

Para evadir los controles, el atacante aprovecha un servidor de salida (Outbound) "permisivo" y un servidor de entrada (Inbound) "estricto" de la siguiente manera:

  1. La Configuración Inicial: El atacante usa una cuenta en un proveedor de correo legítimo y confiable para enviar un correo desde su propio dominio autenticado (atacante@dominio-controlado.com).

  2. La Inyección del Payload: Dentro del cuerpo de este correo, el atacante inyecta comandos SMTP adicionales simulando el inicio de un nuevo correo, precedidos por una secuencia de fin de datos no estándar (por ejemplo: <LF>.<LF>).

  3. El Servidor Saliente lo ignora: El servidor saliente procesa el correo original. Como solo reconoce el estándar estricto <CR><LF>.<CR><LF> para finalizar, asume que el <LF>.<LF> y los comandos inyectados son simplemente texto normal dentro del cuerpo del mensaje. Lo envía todo como un solo bloque hacia el servidor de destino.

  4. El Servidor Entrante lo divide: El servidor de destino recibe la transmisión. Este servidor sí interpreta <LF>.<LF> como un fin de mensaje válido. Al leer esa secuencia, cierra el primer correo y procesa los comandos que le siguen (MAIL FROM: <ceo@empresa-objetivo.com>) como un segundo correo completamente nuevo.

¿Por qué evade SPF, DKIM y DMARC?

El éxito crítico del SMTP Smuggling radica en que el servidor entrante valida la autenticación basándose en la conexión inicial:

  • SPF (Sender Policy Framework): La conexión TCP proviene de los servidores legítimos del proveedor original (una infraestructura confiable). Como la IP origen pertenece a un servidor de correo autorizado, el chequeo SPF del mensaje contrabandeado pasa exitosamente al heredar la confianza de la sesión.

  • DKIM (DomainKeys Identified Mail): El primer correo (el envoltorio) lleva una firma DKIM válida del dominio del atacante. El servidor entrante, al dividir la carga útil a nivel de sesión SMTP, procesa el segundo mensaje (el contrabandeado) dentro de esa misma sesión autenticada.

  • DMARC: Al pasar SPF gracias a la IP del servidor de salida y heredar el estado de la conexión, las políticas DMARC de rechazo del dominio suplantado no se activan.

El resultado final es que la víctima recibe un correo en su bandeja de entrada que parece provenir legítimamente de una dirección interna o de un dominio crítico, superando todas las validaciones de seguridad perimetrales.
 


La mitigación definitiva para el SMTP Smuggling a nivel de administración de sistemas y Blue Teaming consiste en forzar a los servidores a cumplir estrictamente con el RFC 5321. Esto significa configurar los demonios SMTP para que rechacen o normalicen cualquier secuencia de fin de datos que no sea exactamente <CR><LF>.<CR><LF>.

Dado que las configuraciones permisivas son la raíz del problema, aquí tienes las estrategias de endurecimiento (hardening) para las plataformas más comunes en entornos de producción.

Mitigación en Postfix (Entornos Linux)

Wietse Venema parcheó Postfix a finales de diciembre de 2023. Las versiones actualizadas introdujeron parámetros específicos para manejar los saltos de línea desnudos (bare newlines).

Para parchear e implementar la política estricta en la infraestructura:

1.Verificar y actualizar la versión de Postfix:
Asegúrate de ejecutar una versión que contenga el parche (Postfix 3.8.4, 3.7.9, 3.6.13, 3.5.23 o superior). Si utilizas Red Hat, Debian o Ubuntu, actualiza los repositorios e instala el paquete más reciente:

Bash
sudo apt update && sudo apt install postfix
# o para sistemas basados en RHEL:
# sudo dnf update postfix
2.Configurar el rechazo de saltos de línea desnudos:Modificación en main.cf.
Edita el archivo de configuración principal de Postfix (/etc/postfix/main.cf) y añade la siguiente directiva para rechazar conexiones que usen <LF> sin el <CR> correspondiente:

Ini, TOML
smtpd_forbid_bare_newline = yes
Nota: Si esta configuración rompe la compatibilidad con clientes internos muy antiguos que envían correos malformados, puedes usar smtpd_forbid_bare_newline = normalize, lo que anexará el <CR> faltante en lugar de rechazar la conexión, mitigando el smuggling sin interrumpir el servicio.

3.Excluir redes de confianza (Opcional):Para evitar bloqueos en el tráfico interno.
Si aplicas el rechazo estricto (yes), puedes excluir tu red interna (por ejemplo, aplicaciones locales que envían alertas) para que no sean filtradas por esta regla:

Ini, TOML
smtpd_forbid_bare_newline_exclusions = $mynetworks
4.Aplicar los cambios y reiniciar el servicio:
Recarga la configuración para que las nuevas directivas de seguridad entren en vigor de inmediato:

Bash
sudo systemctl reload postfix

Mitigación en Microsoft Exchange

El enfoque para Microsoft Exchange varía dependiendo de si la infraestructura es On-Premises o en la nube.

Exchange Online (Microsoft 365):
Microsoft ya ha implementado los parches necesarios en su infraestructura en la nube. Los servidores de Exchange Online ahora descartan automáticamente los correos que intentan explotar discrepancias en la secuencia de fin de datos. No se requiere acción manual por parte del administrador.

Exchange Server (On-Premises):
Para los servidores alojados localmente, la mitigación requiere aplicar las actualizaciones de seguridad más recientes proporcionadas por Microsoft, ya que el comportamiento del parser SMTP está incrustado en el código base del servicio de transporte.

  1. Instalar el último CU/SU: Debes aplicar el Cumulative Update (CU) más reciente compatible con tu versión de Exchange y, sobre este, instalar el Security Update (SU) correspondiente (marzo de 2024 en adelante).

  2. Verificación de enrutamiento: Asegúrate de que cualquier dispositivo intermedio (como balanceadores de carga proxy o firewalls de aplicaciones web, como Nginx o F5, si inspeccionan tráfico SMTP) no esté modificando o despojando los caracteres <CR> antes de que el tráfico llegue al servidor Exchange.

Auditoría y Comprobación

Una vez aplicados los parches, es crucial auditar la infraestructura para garantizar que la vulnerabilidad haya sido erradicada.

Puedes realizar una prueba manual utilizando netcat o telnet simulando el ataque:

Bash
nc servidor_correo.com 25
EHLO midominio.com
MAIL FROM:<test@midominio.com>
RCPT TO:<victima@objetivo.com>
DATA
Subject: Prueba de Smuggling

Este es el primer mensaje.
<LF>.<LF>
MAIL FROM:<spoof@objetivo.com>
RCPT TO:<victima@objetivo.com>
DATA
Subject: Mensaje contrabandeado
Este mensaje no debería pasar.
.
QUIT
(Nota: debes presionar Enter en Linux para generar un <LF> estándar, o usar un script en Python/Bash que envíe los bytes exactos \n.\n).

Si el servidor está correctamente fortificado, rechazará la secuencia intermedia o la tratará como parte del cuerpo del primer mensaje, en lugar de enrutar el segundo correo.
 
 
 

Para automatizar esta auditoría a gran escala, la mejor aproximación es utilizar Python. Las bibliotecas estándar como smtplib no nos sirven para este caso específico, ya que automáticamente "corrigen" y estandarizan los saltos de línea a <CR><LF>, impidiendo que enviemos la secuencia malformada necesaria para la prueba.

La solución es interactuar directamente a bajo nivel utilizando la biblioteca socket de Python para inyectar los bytes exactos (\n.\n).

Aquí tienes una herramienta de automatización diseñada para flujos de trabajo de pentesting. Soporta la evaluación concurrente de rangos de red (CIDR) y listas de IPs.

Script en Python: smtp_smuggle_scanner.py

Este script intenta iniciar una sesión SMTP y enviar un cuerpo de mensaje finalizado únicamente con saltos de línea desnudos (<LF>.<LF>).

Python
#!/usr/bin/env python3
import socket
import argparse
import ipaddress
from concurrent.futures import ThreadPoolExecutor

def audit_smuggling(target_ip, port, timeout):
    """
    Se conecta al servidor SMTP y envía una secuencia de fin de datos
    malformada (bare LFs) para comprobar si el servidor es permisivo.
    """
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        s.connect((target_ip, port))
        
        # Leer el banner inicial
        s.recv(1024)

        # Iniciar sesión SMTP
        s.sendall(b"EHLO audit.local\r\n")
        s.recv(1024)

        s.sendall(b"MAIL FROM:<auditor@audit.local>\r\n")
        s.recv(1024)

        s.sendall(f"RCPT TO:<admin@{target_ip}>\r\n".encode())
        s.recv(1024)

        s.sendall(b"DATA\r\n")
        s.recv(1024)

        # INYECCIÓN DEL PAYLOAD (El final de datos es \n.\n en lugar de \r\n.\r\n)
        payload = b"Subject: Auditoria SMTP Smuggling\r\n\r\nCuerpo del mensaje de prueba.\n.\n"
        s.sendall(payload)

        # Leer cómo responde el servidor a la secuencia malformada
        response = s.recv(1024).decode('utf-8', errors='ignore').strip()

        s.sendall(b"QUIT\r\n")
        s.close()

        # Analizar la respuesta
        if response.startswith("250"):
            return f"[!] VULNERABLE / PERMISIVO: {target_ip}:{port} aceptó el mensaje (Respuesta: {response})"
        elif "bare newline" in response.lower() or response.startswith("552") or response.startswith("500"):
            return f"[+] SEGURO: {target_ip}:{port} rechazó la secuencia (Respuesta: {response})"
        else:
            return f"[*] INCIERTO: {target_ip}:{port} comportamiento no estándar (Respuesta: {response})"

    except socket.timeout:
        return f"[-] TIMEOUT: {target_ip}:{port} no respondió a tiempo."
    except ConnectionRefusedError:
        return f"[-] CERRADO: {target_ip}:{port} rechazó la conexión."
    except Exception as e:
        return f"[-] ERROR: {target_ip}:{port} - {type(e).__name__}"

def main():
    parser = argparse.ArgumentParser(description="Escáner concurrente para SMTP Smuggling (Bare LF)")
    parser.add_argument("-t", "--target", help="IP objetivo o rango CIDR (ej. 192.168.1.0/24)", required=True)
    parser.add_argument("-p", "--port", type=int, default=25, help="Puerto SMTP (por defecto: 25)")
    parser.add_argument("--timeout", type=int, default=5, help="Timeout de conexión en segundos (por defecto: 5)")
    parser.add_argument("-w", "--workers", type=int, default=10, help="Número de hilos concurrentes (por defecto: 10)")
    
    args = parser.args()

    # Generar la lista de IPs a partir del CIDR o IP individual
    try:
        if '/' in args.target:
            targets = [str(ip) for ip in ipaddress.IPv4Network(args.target, strict=False)]
        else:
            targets = [args.target]
    except ValueError as e:
        print(f"Error parseando el objetivo: {e}")
        return

    print(f"[*] Iniciando auditoría SMTP Smuggling contra {len(targets)} objetivo(s)...")
    print(f"[*] Hilos: {args.workers} | Puerto: {args.port}\n")

    # Ejecución concurrente
    with ThreadPoolExecutor(max_workers=args.workers) as executor:
        futures = {executor.submit(audit_smuggling, ip, args.port, args.timeout): ip for ip in targets}
        
        for future in futures:
            result = future.result()
            if not result.startswith("[-] CERRADO") and not result.startswith("[-] TIMEOUT"): 
                # Filtramos los puertos cerrados para limpiar la salida en escaneos de subredes grandes
                print(result)

if __name__ == "__main__":
    main()

Cómo utilizar el script

Guárdalo como smtp_smuggle_scanner.py y otórgale permisos de ejecución:

Bash
chmod +x smtp_smuggle_scanner.py
Escaneo de un solo objetivo:

Bash
./smtp_smuggle_scanner.py -t 10.10.50.15
Auditoría de una subred entera con 20 hilos simultáneos:
Esta es la función más útil durante la fase de descubrimiento de infraestructura externa o evaluación de redes internas.

Bash
./smtp_smuggle_scanner.py -t 10.10.50.0/24 -w 20
Nota operativa: Para no generar ruido excesivo en los logs de la consola, el script está configurado para omitir silenciosamente las IPs donde el puerto 25 está cerrado (Connection Refused) o no responde (Timeout), mostrando únicamente los servidores SMTP que devuelven un banner válido.


Para acoplar esta herramienta directamente en tus pipelines de auditoría de infraestructura, la opción más limpia y nativa es utilizar la biblioteca estándar xml.etree.ElementTree de Python. Esto evita depender de paquetes externos, manteniendo el script ágil para despliegues rápidos en cualquier entorno.

El siguiente código modifica el script anterior integrando un analizador que extrae únicamente los hosts vivos que tengan puertos SMTP (25, 465, 587 o 2525) en estado open.

Script Actualizado: smtp_smuggle_nmap.py

1.Generar el reporte XML con Nmap:
Antes de ejecutar el script en Python, necesitas realizar el escaneo de red exportando los resultados con el flag -oX:

Bash
nmap -p 25,465,587,2525 -sV -T4 -oX escaneo_correo.xml 10.10.50.0/24
2.Actualizar el código Python:
Guarda el siguiente código. Se ha añadido la función parse_nmap_xml y se ha modificado argparse para aceptar el archivo de entrada.

Python
#!/usr/bin/env python3
import socket
import argparse
import ipaddress
import xml.etree.ElementTree as ET
from concurrent.futures import ThreadPoolExecutor

def parse_nmap_xml(xml_file):
    """
    Parsea el archivo XML de Nmap y devuelve una lista de tuplas (IP, Puerto)
    solo para los hosts activos y puertos SMTP abiertos.
    """
    targets = []
    puertos_smtp = {25, 465, 587, 2525}
    
    try:
        tree = ET.parse(xml_file)
        root = tree.getroot()
        
        for host in root.findall('host'):
            # Verificar que el host esté "up"
            status = host.find('status')
            if status is None or status.get('state') != 'up':
                continue
                
            # Extraer la dirección IPv4
            ip = None
            for address in host.findall('address'):
                if address.get('addrtype') == 'ipv4':
                    ip = address.get('addr')
                    break
            if not ip:
                continue
                
            # Buscar puertos abiertos coincidentes con SMTP
            ports = host.find('ports')
            if ports is not None:
                for port in ports.findall('port'):
                    state = port.find('state')
                    if state is not None and state.get('state') == 'open':
                        port_id = int(port.get('portid'))
                        if port_id in puertos_smtp:
                            targets.append((ip, port_id))
    except Exception as e:
        print(f"[-] Error al procesar el archivo XML: {e}")
        
    return targets

def audit_smuggling(target_ip, port, timeout):
    """
    Intenta inyectar la secuencia de saltos de línea desnudos (bare LFs).
    """
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        s.connect((target_ip, port))
        
        s.recv(1024)
        s.sendall(b"EHLO audit.local\r\n")
        s.recv(1024)

        s.sendall(b"MAIL FROM:<auditor@audit.local>\r\n")
        s.recv(1024)

        s.sendall(f"RCPT TO:<admin@{target_ip}>\r\n".encode())
        s.recv(1024)

        s.sendall(b"DATA\r\n")
        s.recv(1024)

        payload = b"Subject: Auditoria SMTP Smuggling\r\n\r\nCuerpo del mensaje.\n.\n"
        s.sendall(payload)

        response = s.recv(1024).decode('utf-8', errors='ignore').strip()
        s.sendall(b"QUIT\r\n")
        s.close()

        if response.startswith("250"):
            return f"[!] VULNERABLE: {target_ip}:{port} (Respuesta: {response})"
        elif "bare newline" in response.lower() or response.startswith("552") or response.startswith("500"):
            return f"[+] SEGURO: {target_ip}:{port} (Respuesta: {response})"
        else:
            return f"[*] INCIERTO: {target_ip}:{port} (Respuesta: {response})"

    except Exception as e:
        return f"[-] ERROR: {target_ip}:{port} - {type(e).__name__}"

def main():
    parser = argparse.ArgumentParser(description="Escáner SMTP Smuggling integrado con Nmap")
    
    # Grupo mutuamente excluyente: o escaneas por IP/CIDR o usas el XML
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument("-t", "--target", help="IP objetivo o rango CIDR")
    group.add_argument("-x", "--xml", help="Ruta al archivo XML generado por Nmap")
    
    parser.add_argument("-p", "--port", type=int, default=25, help="Puerto por defecto si se usa -t")
    parser.add_argument("--timeout", type=int, default=5, help="Timeout en segundos")
    parser.add_argument("-w", "--workers", type=int, default=10, help="Hilos concurrentes")
    
    args = parser.args()
    targets = []

    # Cargar objetivos desde XML o CIDR
    if args.xml:
        targets = parse_nmap_xml(args.xml)
        print(f"[*] Extraídos {len(targets)} servicios SMTP abiertos del XML.")
    else:
        try:
            if '/' in args.target:
                ips = [str(ip) for ip in ipaddress.IPv4Network(args.target, strict=False)]
            else:
                ips = [args.target]
            targets = [(ip, args.port) for ip in ips]
        except ValueError as e:
            print(f"[-] Error parseando el objetivo: {e}")
            return

    if not targets:
        print("[-] No hay objetivos válidos para escanear. Saliendo.")
        return

    print("[*] Iniciando auditoría...")

    with ThreadPoolExecutor(max_workers=args.workers) as executor:
        # Se pasa la IP y el puerto específico extraído de Nmap
        futures = {executor.submit(audit_smuggling, ip, port, args.timeout): (ip, port) for ip, port in targets}
        
        for future in futures:
            result = future.result()
            if not result.startswith("[-] ERROR"): 
                print(result)

if __name__ == "__main__":
    main()

Ejecución con el archivo XML

Ahora solo tienes que pasarle el archivo generado al parámetro -x:

Bash
./smtp_smuggle_nmap.py -x escaneo_correo.xml -w 15
Esto procesará el archivo en milisegundos, lanzará los hilos de ataque solo contra las IPs que tengan el servicio expuesto, y aplicará dinámicamente el número de puerto correcto para la inyección del payload.
 


Para integrar la exportación de resultados a formatos profesionales como JSON (ideal para automatizaciones o ingestionar en plataformas de gestión de vulnerabilidades) y CSV (perfecto para hojas de cálculo en informes ejecutivos), podemos utilizar las bibliotecas nativas json y csv de Python.

El script actualizado incorpora dos opciones nuevas mediante argumentos de consola: --json y --csv, las cuales guardarán únicamente los hallazgos relevantes (servidores vulnerables o con comportamientos inciertos) estructurados de manera limpia.

Script Completo Actualizado: smtp_smuggle_report.py

Python
#!/usr/bin/env python3
import socket
import argparse
import ipaddress
import json
import csv
import xml.etree.ElementTree as ET
from concurrent.futures import ThreadPoolExecutor

def parse_nmap_xml(xml_file):
    """
    Parsea el archivo XML de Nmap y devuelve una lista de tuplas (IP, Puerto)
    solo para los hosts activos y puertos SMTP abiertos.
    """
    targets = []
    puertos_smtp = {25, 465, 587, 2525}
    
    try:
        tree = ET.parse(xml_file)
        root = tree.getroot()
        
        for host in root.findall('host'):
            status = host.find('status')
            if status is None or status.get('state') != 'up':
                continue
                
            ip = None
            for address in host.findall('address'):
                if address.get('addrtype') == 'ipv4':
                    ip = address.get('addr')
                    break
            if not ip:
                continue
                
            ports = host.find('ports')
            if ports is not None:
                for port in ports.findall('port'):
                    state = port.find('state')
                    if state is not None and state.get('state') == 'open':
                        port_id = int(port.get('portid'))
                        if port_id in puertos_smtp:
                            targets.append((ip, port_id))
    except Exception as e:
        print(f"[-] Error al procesar el archivo XML: {e}")
        
    return targets

def audit_smuggling(target_ip, port, timeout):
    """
    Intenta inyectar la secuencia de saltos de línea desnudos (bare LFs).
    Retorna un diccionario estructurado con los detalles del hallazgo.
    """
    resultado = {
        "ip": target_ip,
        "port": port,
        "status": "ERROR",
        "description": "",
        "response": ""
    }
    
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        s.connect((target_ip, port))
        
        s.recv(1024)
        s.sendall(b"EHLO audit.local\r\n")
        s.recv(1024)

        s.sendall(b"MAIL FROM:<auditor@audit.local>\r\n")
        s.recv(1024)

        s.sendall(f"RCPT TO:<admin@{target_ip}>\r\n".encode())
        s.recv(1024)

        s.sendall(b"DATA\r\n")
        s.recv(1024)

        payload = b"Subject: Auditoria SMTP Smuggling\r\n\r\nCuerpo del mensaje.\n.\n"
        s.sendall(payload)

        response = s.recv(1024).decode('utf-8', errors='ignore').strip()
        s.sendall(b"QUIT\r\n")
        s.close()

        resultado["response"] = response

        if response.startswith("250"):
            resultado["status"] = "VULNERABLE"
            resultado["description"] = f"[!] VULNERABLE: {target_ip}:{port} aceptó el mensaje."
        elif "bare newline" in response.lower() or response.startswith("552") or response.startswith("500"):
            resultado["status"] = "SEGURO"
            resultado["description"] = f"[+] SEGURO: {target_ip}:{port} rechazó la secuencia."
        else:
            resultado["status"] = "INCIERTO"
            resultado["description"] = f"[*] INCIERTO: {target_ip}:{port} comportamiento no estándar."

    except socket.timeout:
        resultado["status"] = "TIMEOUT"
        resultado["description"] = f"[-] TIMEOUT: {target_ip}:{port} no respondió a tiempo."
    except ConnectionRefusedError:
        resultado["status"] = "CERRADO"
        resultado["description"] = f"[-] CERRADO: {target_ip}:{port} rechazó la conexión."
    except Exception as e:
        resultado["status"] = "ERROR"
        resultado["description"] = f"[-] ERROR: {target_ip}:{port} - {type(e).__name__}"

    return resultado

def export_results(results, json_file, csv_file):
    """
    Exporta los resultados recolectados a archivos JSON o CSV.
    """
    # Filtrar solo los resultados relevantes o guardar todos según preferencia. 
    # Aquí guardaremos todos los que tengan estado distinto de CERRADO y TIMEOUT para limpiar el reporte.
    filtered_results = [r for r in results if r["status"] not in ["CERRADO", "TIMEOUT", "ERROR"]]

    if json_file:
        try:
            with open(json_file, 'w', encoding='utf-8') as f:
                json.dump(filtered_results, f, indent=4, ensure_ascii=False)
            print(f"\n[+] Resultados exportados exitosamente a JSON: {json_file}")
        except Exception as e:
            print(f"[-] Error al exportar JSON: {e}")

    if csv_file:
        try:
            with open(csv_file, 'w', newline='', encoding='utf-8') as f:
                writer = csv.writer(f)
                # Escribir cabeceras
                writer.writerow(["IP", "Puerto", "Estado", "Respuesta SMTP", "Descripción"])
                # Escribir filas
                for r in filtered_results:
                    writer.writerow([r["ip"], r["port"], r["status"], r["response"], r["description"]])
            print(f"\n[+] Resultados exportados exitosamente a CSV: {csv_file}")
        except Exception as e:
            print(f"[-] Error al exportar CSV: {e}")

def main():
    parser = argparse.ArgumentParser(description="Escáner SMTP Smuggling con opciones de exportación para reportes")
    
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument("-t", "--target", help="IP objetivo o rango CIDR")
    group.add_argument("-x", "--xml", help="Ruta al archivo XML generado por Nmap")
    
    parser.add_argument("-p", "--port", type=int, default=25, help="Puerto por defecto si se usa -t")
    parser.add_argument("--timeout", type=int, default=5, help="Timeout en segundos")
    parser.add_argument("-w", "--workers", type=int, default=10, help="Hilos concurrentes")
    
    # Nuevos argumentos para reportes
    parser.add_argument("--json", help="Ruta del archivo de salida en formato JSON")
    parser.add_argument("--csv", help="Ruta del archivo de salida en formato CSV")
    
    args = parser.parse_args()
    targets = []

    if args.xml:
        targets = parse_nmap_xml(args.xml)
        print(f"[*] Extraídos {len(targets)} servicios SMTP abiertos del XML.")
    else:
        try:
            if '/' in args.target:
                ips = [str(ip) for ip in ipaddress.IPv4Network(args.target, strict=False)]
            else:
                ips = [args.target]
            targets = [(ip, args.port) for ip in ips]
        except ValueError as e:
            print(f"[-] Error parseando el objetivo: {e}")
            return

    if not targets:
        print("[-] No hay objetivos válidos para escanear. Saliendo.")
        return

    print("[*] Iniciando auditoría...")
    all_results = []

    with ThreadPoolExecutor(max_workers=args.workers) as executor:
        futures = {executor.submit(audit_smuggling, ip, port, args.timeout): (ip, port) for ip, port in targets}
        
        for future in futures:
            res = future.result()
            all_results.append(res)
            # Mostrar en consola solo si no está cerrado
            if res["status"] not in ["CERRADO", "TIMEOUT"]:
                print(res["description"])

    # Ejecutar la exportación si se especificaron los flags correspondientes
    if args.json or args.csv:
        export_results(all_results, args.json, args.csv)

if __name__ == "__main__":
    main()

Ejemplos de Uso con los Nuevos Reportes

1. Exportar los resultados directamente a un archivo CSV para adjuntar a la hoja de cálculo del informe:

Bash
./smtp_smuggle_report.py -x escaneo_correo.xml --csv reporte_auditoria.csv -w 15
2. Exportar en formato JSON estructurado para procesos automatizados:

Bash
./smtp_smuggle_report.py -t 192.168.1.0/24 --json resultados.json -w 20
3. Generar ambos formatos de forma simultánea:

Bash
./smtp_smuggle_report.py -x escaneo_correo.xml --json hallazgos.json --csv hallazgos.csv

¿Qué se incluye en la exportación?

  • Filtro inteligente de ruido: El script descarta automáticamente los puertos cerrados o timeouts al momento de escribir el archivo final, asegurando que tu informe CSV o JSON contenga únicamente datos limpios y procesables (servidores vulnerables, seguros o con comportamientos inciertos).

  • Trazabilidad: Tanto el CSV como el JSON registran la dirección IP, el puerto analizado, el veredicto del estado de seguridad y la respuesta exacta enviada por el servidor SMTP (response), lo cual sirve como evidencia técnica de respaldo para el cliente o equipo de infraestructura.

https://www.onlinetis.com

Entradas populares de este blog

Hacking y ciberseguridad en kali linux con Fping

Como utilizar Fierce en kali linux

Hacking y ciberseguridad en kali linux con atk6-thcping6