Analyser les en-têtes d'email pour une investigation de phishing
Quand utiliser
- Lors de l'investigation d'un email suspecté de phishing pour déterminer sa véritable origine.
- Pour vérifier l'authenticité de l'expéditeur et détecter l'usurpation d'email.
- Pendant la réponse à incident lorsqu'un utilisateur a cliqué sur un lien de phishing.
- Pour retracer le chemin de livraison et les serveurs relais d'un email suspect.
- Pour valider l'alignement SPF, DKIM et DMARC et identifier les falsifications.
Prérequis
- Les en-têtes bruts du message suspect (format EML ou MSG).
- Compréhension du protocole SMTP et des champs d'en-tête d'email.
- Accès aux outils de requête DNS (dig, nslookup) pour la vérification SPF/DKIM/DMARC.
- Outils d'analyse d'en-têtes d'email (MHA, concepts d'emailheaders.net).
- Python avec les bibliothèques d'analyse d'email pour une analyse automatisée.
- Accès aux plateformes de renseignement sur les menaces pour la réputation des IP/domaines.
Flux de travail
Étape 1 : Extraire les en-têtes bruts
# Export depuis Outlook : Ouvrir l'email > Fichier > Propriétés > En-têtes Internet
# Export depuis Gmail : Ouvrir l'email > Trois points > Afficher l'original
# Export depuis Thunderbird : Affichage > Source du message
# Si vous travaillez avec un fichier EML provenant d'une image forensique
cp /mnt/evidence/Users/suspect/AppData/Local/Microsoft/Outlook/phishing_email.eml \
/cases/case-2024-001/email/
# Si vous travaillez avec un fichier PST, extrayez les messages individuels
pip install pypff
python3 << 'PYEOF'
import pypff
pst = pypff.file()
pst.open("/cases/case-2024-001/email/outlook.pst")
root = pst.get_root_folder()
def extract_messages(folder, path=""):
for i in range(folder.get_number_of_sub_messages()):
msg = folder.get_sub_message(i)
headers = msg.get_transport_headers()
subject = msg.get_subject()
if headers:
filename = f"/cases/case-2024-001/email/msg_{i}_{subject[:30]}.txt"
with open(filename, 'w') as f:
f.write(headers)
for i in range(folder.get_number_of_sub_folders()):
extract_messages(folder.get_sub_folder(i))
extract_messages(root)
PYEOF
Étape 2 : Analyser la chaîne d'en-têtes
# Analyser les en-têtes avec la bibliothèque email de Python
python3 << 'PYEOF'
import email
from email import policy
with open('/cases/case-2024-001/email/phishing_email.eml', 'r') as f:
msg = email.message_from_file(f, policy=policy.default)
print("=== CHAMPS D'EN-TÊTE PRINCIPAUX ===")
print(f"From : {msg['From']}")
print(f"To : {msg['To']}")
print(f"Subject : {msg['Subject']}")
print(f"Date : {msg['Date']}")
print(f"Message-ID : {msg['Message-ID']}")
print(f"Reply-To : {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=== EN-TÊTES RECEIVED (de bas en haut = chronologique) ===")
received_headers = msg.get_all('Received')
if received_headers:
for i, header in enumerate(reversed(received_headers)):
print(f"\nSaut {i+1} : {header.strip()}")
print("\n=== RÉSULTATS D'AUTHENTIFICATION ===")
auth_results = msg.get_all('Authentication-Results')
if auth_results:
for result in auth_results:
print(result)
print(f"\nARC-Authentication-Results : {msg.get('ARC-Authentication-Results', 'Non présent')}")
print(f"Received-SPF : {msg.get('Received-SPF', 'Non présent')}")
print(f"DKIM-Signature : {msg.get('DKIM-Signature', 'Non présent')}")
PYEOF
Étape 3 : Valider les enregistrements SPF, DKIM et DMARC
# Extraire le domaine de l'expéditeur (enveloppe)
SENDER_DOMAIN="example-corp.com"
# Vérifier l'enregistrement SPF
dig TXT $SENDER_DOMAIN +short | grep "v=spf1"
# Exemple : "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
# Vérifier l'enregistrement DKIM (sélecteur depuis l'en-tête DKIM-Signature, ex. "s=selector1")
DKIM_SELECTOR="selector1"
dig TXT ${DKIM_SELECTOR}._domainkey.${SENDER_DOMAIN} +short
# Vérifier l'enregistrement DMARC
dig TXT _dmarc.${SENDER_DOMAIN} +short
# Exemple : "v=DMARC1; p=reject; rua=mailto:[email protected]; pct=100"
# Vérifier l'IP d'envoi par rapport au SPF
# Extraire l'IP du premier en-tête Received
SENDING_IP="203.0.113.45"
# Vérification manuelle SPF avec Python
python3 << 'PYEOF'
import spf # pip install pyspf
result, explanation = spf.check2(
i='203.0.113.45',
s='[email protected]',
h='mail.example-corp.com'
)
print(f"Résultat SPF : {result}")
print(f"Explication : {explanation}")
# Résultats : pass, fail, softfail, neutral, none, temperror, permerror
PYEOF
# Vérifier si l'IP d'envoi figure dans les listes d'IP malveillantes connues
# Interroger AbuseIPDB ou VirusTotal
curl -s "https://api.abuseipdb.com/api/v2/check?ipAddress=${SENDING_IP}" \
-H "Key: YOUR_API_KEY" -H "Accept: application/json" | python3 -m json.tool
Étape 4 : Analyser le domaine et l'infrastructure de l'expéditeur
# Requête WHOIS sur le domaine de l'expéditeur
whois $SENDER_DOMAIN | grep -iE '(registrar|creation|expiration|registrant|nameserver)'
# Vérifier l'âge du domaine (les domaines récemment enregistrés sont suspects)
# Investigation des enregistrements DNS
dig A $SENDER_DOMAIN +short
dig MX $SENDER_DOMAIN +short
dig NS $SENDER_DOMAIN +short
# DNS inverse sur l'IP d'envoi
dig -x $SENDING_IP +short
# Vérifier les domaines de type homoglyphe/typosquatting
# Comparer avec le domaine légitime en utilisant la similarité visuelle
python3 << 'PYEOF'
import Levenshtein # pip install python-Levenshtein
legitimate = "microsoft.com"
suspicious = "micr0soft.com"
distance = Levenshtein.distance(legitimate, suspicious)
ratio = Levenshtein.ratio(legitimate, suspicious)
print(f"Distance d'édition : {distance}")
print(f"Taux de similarité : {ratio:.2%}")
if ratio > 0.8:
print("ATTENTION : Il s'agit probablement d'un domaine de typosquatting/homoglyphe !")
PYEOF
# Vérifier la réputation du domaine sur VirusTotal
curl -s "https://www.virustotal.com/api/v3/domains/${SENDER_DOMAIN}" \
-H "x-apikey: YOUR_VT_API_KEY" | python3 -m json.tool
# Vérifier si Reply-To diffère de From (indicateur courant de phishing)
python3 -c "
import email
with open('/cases/case-2024-001/email/phishing_email.eml') as f:
msg = email.message_from_file(f)
from_addr = email.utils.parseaddr(msg['From'])[1]
reply_to = email.utils.parseaddr(msg.get('Reply-To', msg['From']))[1]
if from_addr != reply_to:
print(f'ATTENTION : From ({from_addr}) != Reply-To ({reply_to})')
else:
print('From et Reply-To correspondent')
"
Étape 5 : Examiner le corps de l'email et les pièces jointes
# Extraire les URL du corps de l'email
python3 << 'PYEOF'
import email
import re
from email import policy
with open('/cases/case-2024-001/email/phishing_email.eml', 'r') as f:
msg = email.message_from_file(f, policy=policy.default)
body = msg.get_body(preferencelist=('html', 'plain'))
if body:
content = body.get_content()
urls = re.findall(r'https?://[^\s<>\"\']+', content)
print("=== URL TROUVÉES DANS LE CORPS DE L'EMAIL ===")
for url in set(urls):
print(f" {url}")
# Vérifier l'obfuscation d'URL (texte affiché != href)
href_pattern = re.findall(r'<a[^>]*href=[\"\']([^\"\']+)[\"\'][^>]*>(.*?)</a>', content, re.DOTALL)
print("\n=== ANALYSE DES HYPERLIENS ===")
for href, text in href_pattern:
display_url = re.findall(r'https?://[^\s<]+', text)
if display_url and display_url[0] != href:
print(f" NON CONCORDANT : Affiché='{display_url[0]}' -> Réel='{href}'")
# Extraire et hacher les pièces jointes
print("\n=== PIÈCES JOINTES ===")
for part in msg.walk():
if part.get_content_disposition() == 'attachment':
filename = part.get_filename()
content = part.get_payload(decode=True)
import hashlib
sha256 = hashlib.sha256(content).hexdigest()
print(f" Fichier : {filename}, Taille : {len(content)}, SHA-256 : {sha256}")
with open(f'/cases/case-2024-001/email/attachments/{filename}', 'wb') as af:
af.write(content)
PYEOF
# Soumettre les hachages de pièces jointes à VirusTotal
# Soumettre les URL à URLhaus ou PhishTank pour vérification de réputation
Concepts clés
| Concept | Description |
|---|---|
| SPF (Sender Policy Framework) | Enregistrement DNS spécifiant les serveurs de messagerie autorisés pour un domaine |
| DKIM (DomainKeys Identified Mail) | Signature cryptographique vérifiant l'intégrité du contenu de l'email |
| DMARC | Cadre de stratégie combinant SPF et DKIM pour l'authentification de l'expéditeur |
| En-têtes Received | En-têtes ajoutés par les serveurs montrant chaque saut dans la chaîne de livraison (à lire de bas en haut) |
| Return-Path | Adresse de l'expéditeur de l'enveloppe utilisée pour les rebonds ; peut différer de From |
| Message-ID | Identifiant unique attribué par le serveur de messagerie d'origine |
| X-Originating-IP | Adresse IP de l'expéditeur d'origine (ajoutée par certains services de messagerie) |
| Falsification d'en-tête | Les attaquants peuvent falsifier From, Reply-To et d'autres en-têtes, mais pas les chaînes Received |
Outils et systèmes
| Outil | Objectif |
|---|---|
| MXToolbox | Analyseur d'en-têtes d'email en ligne et requête DNS |
| dig/nslookup | Requêtes d'enregistrements DNS pour la vérification SPF, DKIM, DMARC |
| pyspf | Bibliothèque Python de validation d'enregistrements SPF |
| dkimpy | Bibliothèque Python de vérification des signatures DKIM |
| PhishTool | Plateforme spécialisée dans l'analyse d'emails de phishing |
| VirusTotal | Service de vérification de réputation des URL et des fichiers |
| AbuseIPDB | Base de données de réputation des adresses IP |
| whois | Recherche d'informations d'enregistrement de domaine |
Scénarios courants
Scénario 1 : Fraude au PDG / Compromission d'email professionnel (BEC) L'email prétend provenir du PDG mais Reply-To pointe vers une adresse Gmail, SPF échoue car l'IP d'envoi n'est pas autorisée pour le domaine usurpé, DKIM est absent, et le domaine From est un homoglyphe (ceo-company.com vs company.com).
Scénario 2 : Phishing de récupération d'identifiants L'email contient un lien qui affiche "login.microsoft.com" mais href pointe vers un domaine homoglyphe, la pièce jointe est un fichier HTML contenant une fausse page de connexion avec JavaScript d'exfiltration d'identifiants, le domaine d'envoi a été enregistré il y a 3 jours.
Scénario 3 : Livraison de malware via pièce jointe Email avec une pièce jointe de document Office contenant des macros, le domaine de l'expéditeur passe SPF mais le compte a été compromis, la signature DKIM est valide (envoyé depuis une infrastructure légitime), le hachage SHA-256 de la pièce jointe correspond à un malware connu sur VirusTotal.
Scénario 4 : Spear phishing via un service légitime L'attaquant utilise un service de marketing par email légitime pour envoyer un phishing, SPF et DKIM réussissent car le service est autorisé, le phishing réside dans le contenu et non dans l'infrastructure, nécessitant une analyse des URL et du contenu plutôt que des vérifications d'authentification des en-têtes.
Format de sortie
Rapport d'analyse des en-têtes d'email :
Subject : "Urgent : Paiement de facture requis"
From : [email protected] (USURPÉ)
Reply-To : [email protected] (NON CONCORDANT)
Return-Path : <[email protected]>
Date : 2024-01-15 09:23:45 UTC
Chemin de livraison (4 sauts) :
Saut 1 : mail-server.xyz [203.0.113.45] -> relay1.isp.com
Saut 2 : relay1.isp.com -> mx.target-company.com
Saut 3 : mx.target-company.com -> internal-filter.target.com
Saut 4 : internal-filter.target.com -> mailbox
Authentification :
SPF : ÉCHEC (203.0.113.45 non autorisé pour examp1e-corp.com)
DKIM : AUCUN (aucune signature présente)
DMARC : ÉCHEC (p=none, aucune application)
Indicateurs de phishing :
- Domaine homoglyphe (examp1e-corp.com vs example-corp.com, 96 % de similarité)
- Non-concordance From/Reply-To
- Domaine enregistré 2 jours avant l'envoi de l'email
- URL dans le corps pointant vers une page de récupération d'identifiants
- Pièce jointe : invoice.xlsm (SHA-256 : a3f2...) – Malware connu sur VT
Niveau de risque : ÉLEVÉ