Análisis de Cabeceras de Correo para Investigación de Phishing
Cuándo Usarlo
- Al investigar un correo sospechoso de phishing para determinar su verdadero origen
- Para verificar la autenticidad del remitente y detectar suplantación de correo
- Durante la respuesta a incidentes cuando un usuario ha hecho clic en un enlace de phishing
- Al rastrear la ruta de entrega y los servidores relay de un correo sospechoso
- Para validar la alineación SPF, DKIM y DMARC e identificar falsificaciones
Requisitos Previos
- Cabeceras de correo sin procesar del mensaje sospechoso (formato EML o MSG)
- Comprensión del protocolo SMTP y de los campos de cabecera del correo
- Acceso a herramientas de consulta DNS (dig, nslookup) para verificación SPF/DKIM/DMARC
- Herramientas de análisis de cabeceras (conceptos de MHA, emailheaders.net)
- Python con librerías de análisis de correo para análisis automatizado
- Acceso a plataformas de inteligencia de amenazas para reputación de IP/dominio
Flujo de Trabajo
Paso 1: Extraer Cabeceras de Correo Sin Procesar
# Exportar desde Outlook: Abrir correo > Archivo > Propiedades > Cabeceras de Internet
# Exportar desde Gmail: Abrir correo > Tres puntos > Mostrar original
# Exportar desde Thunderbird: Ver > Código fuente del mensaje
# Si se trabaja con un archivo EML de una imagen forense
cp /mnt/evidence/Users/sospechoso/AppData/Local/Microsoft/Outlook/phishing_email.eml \
/casos/caso-2024-001/correo/
# Si se trabaja con un archivo PST, extraer mensajes individuales
pip install pypff
python3 << 'PYEOF'
import pypff
pst = pypff.file()
pst.open("/casos/caso-2024-001/correo/outlook.pst")
raiz = pst.get_root_folder()
def extraer_mensajes(carpeta, ruta=""):
for i in range(carpeta.get_number_of_sub_messages()):
msg = carpeta.get_sub_message(i)
cabeceras = msg.get_transport_headers()
asunto = msg.get_subject()
if cabeceras:
archivo = f"/casos/caso-2024-001/correo/msg_{i}_{asunto[:30]}.txt"
with open(archivo, 'w') as f:
f.write(cabeceras)
for i in range(carpeta.get_number_of_sub_folders()):
extraer_mensajes(carpeta.get_sub_folder(i))
extraer_mensajes(raiz)
PYEOF
Paso 2: Analizar la Cadena de Cabeceras
# Analizar cabeceras usando la librería email de Python
python3 << 'PYEOF'
import email
from email import policy
with open('/casos/caso-2024-001/correo/phishing_email.eml', 'r') as f:
msg = email.message_from_file(f, policy=policy.default)
print("=== CAMPOS DE CABECERA CLAVE ===")
print(f"De: {msg['From']}")
print(f"Para: {msg['To']}")
print(f"Asunto: {msg['Subject']}")
print(f"Fecha: {msg['Date']}")
print(f"Message-ID: {msg['Message-ID']}")
print(f"Responder a: {msg['Reply-To']}")
print(f"Return-Path: {msg['Return-Path']}")
print(f"X-Mailer: {msg['X-Mailer']}")
print(f"X-Originating-IP: {msg['X-Originating-IP']}")
print("\n=== CABECERAS RECEIVED (de abajo arriba = cronológico) ===")
cabeceras_recibidas = msg.get_all('Received')
if cabeceras_recibidas:
for i, cabecera in enumerate(reversed(cabeceras_recibidas)):
print(f"\nSalto {i+1}: {cabecera.strip()}")
print("\n=== RESULTADOS DE AUTENTICACIÓN ===")
resultados_auth = msg.get_all('Authentication-Results')
if resultados_auth:
for resultado in resultados_auth:
print(resultado)
print(f"\nARC-Authentication-Results: {msg.get('ARC-Authentication-Results', 'No presente')}")
print(f"Received-SPF: {msg.get('Received-SPF', 'No presente')}")
print(f"DKIM-Signature: {msg.get('DKIM-Signature', 'No presente')}")
PYEOF
Paso 3: Validar Registros SPF, DKIM y DMARC
# Extraer el dominio del remitente del sobre
DOMINIO_REMITENTE="example-corp.com"
# Consultar registro SPF
dig TXT $DOMINIO_REMITENTE +short | grep "v=spf1"
# Ejemplo: "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
# Consultar registro DKIM (selector del campo DKIM-Signature, p.ej., "s=selector1")
SELECTOR_DKIM="selector1"
dig TXT ${SELECTOR_DKIM}._domainkey.${DOMINIO_REMITENTE} +short
# Consultar registro DMARC
dig TXT _dmarc.${DOMINIO_REMITENTE} +short
# Ejemplo: "v=DMARC1; p=reject; rua=mailto:[email protected]; pct=100"
# Verificar la IP de envío contra SPF
# Extraer IP de la primera cabecera Received
IP_ORIGEN="203.0.113.45"
# Verificación SPF manual con Python
python3 << 'PYEOF'
import spf # pip install pyspf
resultado, explicacion = spf.check2(
i='203.0.113.45',
s='[email protected]',
h='mail.example-corp.com'
)
print(f"Resultado SPF: {resultado}")
print(f"Explicación: {explicacion}")
# Resultados: pass, fail, softfail, neutral, none, temperror, permerror
PYEOF
# Comprobar si la IP de envío está en listas de IPs maliciosas conocidas
# Consultar AbuseIPDB o VirusTotal
curl -s "https://api.abuseipdb.com/api/v2/check?ipAddress=${IP_ORIGEN}" \
-H "Key: TU_API_KEY" -H "Accept: application/json" | python3 -m json.tool
Paso 4: Analizar el Dominio del Remitente y la Infraestructura
# Consulta WHOIS del dominio del remitente
whois $DOMINIO_REMITENTE | grep -iE '(registrar|creation|expiration|registrant|nameserver)'
# Comprobar antigüedad del dominio (dominios registrados recientemente son sospechosos)
# Investigación de registros DNS
dig A $DOMINIO_REMITENTE +short
dig MX $DOMINIO_REMITENTE +short
dig NS $DOMINIO_REMITENTE +short
# DNS inverso de la IP de envío
dig -x $IP_ORIGEN +short
# Comprobar dominios similares/typosquatting
# Comparar con el dominio legítimo usando similitud visual
python3 << 'PYEOF'
import Levenshtein # pip install python-Levenshtein
legitimo = "microsoft.com"
sospechoso = "micr0soft.com"
distancia = Levenshtein.distance(legitimo, sospechoso)
ratio = Levenshtein.ratio(legitimo, sospechoso)
print(f"Distancia de edición: {distancia}")
print(f"Ratio de similitud: {ratio:.2%}")
if ratio > 0.8:
print("¡ADVERTENCIA: Posible typosquatting/dominio similar!")
PYEOF
# Consultar reputación del dominio en VirusTotal
curl -s "https://www.virustotal.com/api/v3/domains/${DOMINIO_REMITENTE}" \
-H "x-apikey: TU_API_KEY_VT" | python3 -m json.tool
# Comprobar si Reply-To difiere de From (indicador común de phishing)
python3 -c "
import email
with open('/casos/caso-2024-001/correo/phishing_email.eml') as f:
msg = email.message_from_file(f)
direccion_from = email.utils.parseaddr(msg['From'])[1]
direccion_reply = email.utils.parseaddr(msg.get('Reply-To', msg['From']))[1]
if direccion_from != direccion_reply:
print(f'ADVERTENCIA: From ({direccion_from}) != Reply-To ({direccion_reply})')
else:
print('From y Reply-To coinciden')
"
Paso 5: Examinar el Cuerpo del Correo y los Archivos Adjuntos
# Extraer URLs del cuerpo del correo
python3 << 'PYEOF'
import email
import re
from email import policy
with open('/casos/caso-2024-001/correo/phishing_email.eml', 'r') as f:
msg = email.message_from_file(f, policy=policy.default)
cuerpo = msg.get_body(preferencelist=('html', 'plain'))
if cuerpo:
contenido = cuerpo.get_content()
urls = re.findall(r'https?://[^\s<>"\']+', contenido)
print("=== URLs ENCONTRADAS EN EL CUERPO DEL CORREO ===")
for url in set(urls):
print(f" {url}")
# Comprobar ofuscación de URLs (texto mostrado != href)
patron_href = re.findall(r'<a[^>]*href=[\"\']([^\"\']+)[\"\'][^>]*>(.*?)</a>', contenido, re.DOTALL)
print("\n=== ANÁLISIS DE HIPERVÍNCULOS ===")
for href, texto in patron_href:
url_mostrada = re.findall(r'https?://[^\s<]+', texto)
if url_mostrada and url_mostrada[0] != href:
print(f" DISCREPANCIA: Mostrado='{url_mostrada[0]}' -> Real='{href}'")
# Extraer y hashear archivos adjuntos
print("\n=== ARCHIVOS ADJUNTOS ===")
for parte in msg.walk():
if parte.get_content_disposition() == 'attachment':
nombre = parte.get_filename()
contenido = parte.get_payload(decode=True)
import hashlib
sha256 = hashlib.sha256(contenido).hexdigest()
print(f" Archivo: {nombre}, Tamaño: {len(contenido)}, SHA-256: {sha256}")
with open(f'/casos/caso-2024-001/correo/adjuntos/{nombre}', 'wb') as af:
af.write(contenido)
PYEOF
# Enviar hashes de archivos adjuntos a VirusTotal
# Enviar URLs a URLhaus o PhishTank para verificar reputación
Conceptos Clave
| Concepto | Descripción |
|---|---|
| SPF (Sender Policy Framework) | Registro DNS que especifica los servidores de correo autorizados para un dominio |
| DKIM (DomainKeys Identified Mail) | Firma criptográfica que verifica la integridad del contenido del correo |
| DMARC | Marco de políticas que combina SPF y DKIM para autenticación del remitente |
| Cabeceras Received | Cabeceras añadidas por el servidor que muestran cada salto en la cadena de entrega (leer de abajo arriba) |
| Return-Path | Dirección del remitente del sobre utilizada para mensajes de rebote; puede diferir de From |
| Message-ID | Identificador único asignado por el servidor de correo de origen |
| X-Originating-IP | Dirección IP original del remitente (añadida por algunos servicios de correo) |
| Falsificación de cabeceras | Los atacantes pueden falsificar From, Reply-To y otras cabeceras, pero no las cadenas Received |
Herramientas y Sistemas
| Herramienta | Propósito |
|---|---|
| MXToolbox | Analizador de cabeceras de correo y consultas DNS en línea |
| dig/nslookup | Consultas de registros DNS para verificación SPF, DKIM, DMARC |
| pyspf | Librería Python de validación de registros SPF |
| dkimpy | Librería Python de verificación de firmas DKIM |
| PhishTool | Plataforma especializada en análisis de correos de phishing |
| VirusTotal | Servicio de verificación de reputación de URLs y archivos |
| AbuseIPDB | Base de datos de reputación de direcciones IP |
| whois | Consulta de información de registro de dominios |
Escenarios Comunes
Escenario 1: Fraude del CEO / Compromiso de Correo Empresarial El correo dice ser del CEO, pero Reply-To apunta a una dirección de Gmail, SPF falla porque la IP de envío no está autorizada para el dominio suplantado, DKIM no está presente y el dominio From es un similar (ceo-company.com vs company.com).
Escenario 2: Phishing de Captura de Credenciales El correo contiene un enlace que muestra "login.microsoft.com" pero el href apunta a un dominio similar, el archivo adjunto es un archivo HTML que contiene una página de inicio de sesión falsa con JavaScript de exfiltración de credenciales, el dominio de envío se registró hace 3 días.
Escenario 3: Entrega de Malware mediante Archivo Adjunto Correo con un documento Office que contiene macros, el dominio del remitente pasa SPF pero la cuenta está comprometida, la firma DKIM es válida (enviada desde infraestructura legítima), el SHA-256 del archivo adjunto coincide con malware conocido en VirusTotal.
Escenario 4: Spear Phishing con Servicio Legítimo El atacante utiliza un servicio de marketing por correo legítimo para enviar phishing, SPF y DKIM pasan porque el servicio está autorizado, el phishing está en el contenido, no en la infraestructura, requiere análisis de URLs y contenido en lugar de verificaciones de autenticación de cabeceras.
Formato de Salida
Informe de Análisis de Cabeceras:
Asunto: "Urgente: Pago de Factura Requerido"
De: [email protected] (SUPLANTADO)
Responder a: [email protected] (DISCREPANCIA)
Return-Path: <[email protected]>
Fecha: 2024-01-15 09:23:45 UTC
Ruta de Entrega (4 saltos):
Salto 1: mail-server.xyz [203.0.113.45] -> relay1.isp.com
Salto 2: relay1.isp.com -> mx.empresa-destino.com
Salto 3: mx.empresa-destino.com -> filtro-interno.empresa.com
Salto 4: filtro-interno.empresa.com -> buzón
Autenticación:
SPF: FALLO (203.0.113.45 no autorizado para examp1e-corp.com)
DKIM: NINGUNO (no hay firma presente)
DMARC: FALLO (p=none, sin cumplimiento)
Indicadores de Phishing:
- Dominio similar (examp1e-corp.com vs example-corp.com, 96% similar)
- Discrepancia From/Reply-To
- Dominio registrado 2 días antes del envío del correo
- URL en el cuerpo apunta a página de captura de credenciales
- Archivo adjunto: invoice.xlsm (SHA-256: a3f2...) - Malware conocido en VT
Nivel de Riesgo: ALTO