Skip to main content

Chapitre 2.4 - Analyse du trafic chiffre et inspection TLS

Module 2 : Analyse du trafic et detection des intrusions Prerequis : Chapitre 2.1 (Analyse de paquets), 2.2 (IDS/IPS), 2.3 (Forensique reseau)


Table des matieres

  1. Pourquoi l'analyse du trafic chiffre est importante
  2. Mecanismes TLS - Poignee de main, couche d'enregistrement et materiel de cles
  3. Empreinte passive - JA3, JA3S, JARM et HASSH
  4. Analyse du trafic chiffre sans dechiffrement
  5. Interception TLS - Proxies MITM et inspection SSL
  6. Dechiffrement avec les secrets pre-master et SSLKEYLOGFILE
  7. Intelligence sur les certificats et abus de PKI
  8. Presentation des outils - Zeek, Wireshark, mitmproxy, Suricata
  9. Evasion - Domain fronting, ESNI/ECH et mimicry de certificats
  10. Correspondance MITRE ATT&CK

1. Pourquoi l'analyse du trafic chiffre est importante

Le chiffrement est le principal obstacle a la visibilite au niveau de la couche reseau. En 2024, plus de 95 % du trafic web est protege par TLS, et les memes protocoles utilises pour securiser les sessions bancaires sont employes par les logiciels malveillants pour exfiltrer des donnees, recevoir des instructions C2 et tunneliser des protocoles arbitraires. L'equipe de securite qui traite tout le trafic chiffre comme une boite noire a abandonne sa visibilite sur la majorite de son reseau.

La reponse n'est pas binaire - le dechiffrement complet n'est pas toujours faisable ni legal, et il introduit sa propre surface d'attaque. La discipline de l'Analyse du trafic chiffre (ETA) couvre un spectre :

  • Empreinte passive - identifier le logiciel client/serveur, detecter les anomalies dans les parametres TLS sans toucher au texte en clair
  • Analyse des metadonnees - duree du flux, patterns d'octets, timing des paquets, champs des certificats - tous disponibles sans dechiffrement
  • Dechiffrement selectif - intercepter des categories specifiques de trafic (navigation d'entreprise sortante) en un point de controle maitrise
  • Dechiffrement par journal de cles - utiliser les cles de session exportees depuis le terminal pour dechiffrer les captures dans Wireshark ou Zeek a posteriori
  • Intelligence sur les certificats - identifier l'infrastructure malveillante par les attributs de certificat, les anomalies d'AC ou les certificats auto-signes sur des ports inattendus

Chaque technique a un profil juridique, operationnel et architectural different. Ce chapitre couvre l'ensemble de ces techniques.


2. Mecanismes TLS - Poignee de main, couche d'enregistrement et materiel de cles

Comprendre ce que TLS expose en clair - et ce qu'il ne revele pas - est le fondement de l'ETA. Selon la version TLS et la configuration, une quantite substantielle de metadonnees reste visible meme sur des sessions chiffrees.

Poignee de main TLS 1.2 (partiellement visible)

En TLS 1.2, les messages ClientHello et ServerHello sont envoyes en clair. Le message Certificate n'est egalement pas chiffre, exposant la chaine de certificats du serveur. Seuls les enregistrements Finished et Application Data sont chiffres.

Client                                         Serveur
| |
|--- ClientHello (texte clair) -------------->| # Suites de chiffrement, extensions, SNI, alea
|<-- ServerHello (texte clair) ---------------| # Chiffrement choisi, ID de session, alea
|<-- Certificate (texte clair) ---------------| # Chaine de certificats serveur - ENTIEREMENT VISIBLE
|<-- ServerHelloDone (texte clair) -----------|
|--- ClientKeyExchange (texte clair) -------->| # Premaster chiffre RSA OU partage de cle ECDH
|--- ChangeCipherSpec (texte clair) --------->|
|--- Finished (chiffre) -------------------->|
|<-- ChangeCipherSpec (texte clair) ----------|
|<-- Finished (chiffre) ---------------------|
|<=== Application Data (chiffre) ============>|

Poignee de main TLS 1.3 (davantage chiffree)

TLS 1.3 reduit les allers-retours et chiffre significativement plus d'etapes de la poignee de main. Le message Certificate est chiffre. Cependant, le SNI est toujours envoye en clair dans le ClientHello a moins que ECH (Encrypted Client Hello) ne soit utilise - traite dans la section 9.

Client                                         Serveur
| |
|--- ClientHello (texte clair) -------------->| # key_share, SNI (texte clair !), supported_versions
|<-- ServerHello (texte clair) ---------------| # reponse key_share
|<-- {EncryptedExtensions} (chiffre) ---------| # ALPN, etc.
|<-- {Certificate} (chiffre) ----------------| # Plus visible passivement
|<-- {CertificateVerify} (chiffre) ----------|
|<-- {Finished} (chiffre) -------------------|
|--- {Finished} (chiffre) ------------------>|
|<=== Application Data (chiffre) ============>|

Ce qui reste visible sur le reseau (les deux versions TLS)

ChampTLS 1.2TLS 1.3Notes
SNI (Server Name Indication)Texte clairTexte clairSauf si ECH est utilise
Certificat serveurTexte clairChiffreRegression majeure de visibilite en 1.3
Certificat clientTexte clairChiffreIdentite mTLS cachee en 1.3
Liste des suites de chiffrementTexte clairTexte clairSource d'empreinte
Extensions TLSTexte clairTexte clairSource cle pour JA3
ALPN (protocole negocie)Texte clairPartielh2, http/1.1, etc.
Taille des paquets et timingToujoursToujoursAttribut ML
Duree du flux et octetsToujoursToujoursAnalytique comportementale

Inspection TLS avec OpenSSL

# Inspection complete de la poignee de main TLS - voir le certificat, le chiffrement, la version du protocole
openssl s_client \\
-connect target.example.com:443 \\\\ # Hote:port a connecter
-servername target.example.com \\\\ # Valeur SNI a envoyer (peut differer de la cible de connexion)
-showcerts \\\\ # Afficher la chaine de certificats complete
-tlsextdebug \\\\ # Afficher toutes les extensions TLS brutes
-msg 2>&1 | head -100 # Afficher les messages bruts du protocole

# Forcer une version TLS specifique pour tester la compatibilite du serveur
openssl s_client -connect target.example.com:443 \\
-tls1_2 \\\\ # Forcer TLS 1.2 uniquement
-cipher 'ECDHE-RSA-AES256-GCM-SHA384' # Forcer un chiffrement specifique

# Extraire uniquement les details du certificat
openssl s_client -connect target.example.com:443 \\
-servername target.example.com 2>/dev/null | \\
openssl x509 -noout -text # Analyser le certificat

# Verifier les dates de validite du certificat
openssl s_client -connect target.example.com:443 2>/dev/null | \\
openssl x509 -noout -dates

# Verifier les noms alternatifs du sujet (SAN) du certificat - critique pour la detection du domain fronting
openssl s_client -connect target.example.com:443 2>/dev/null | \\
openssl x509 -noout -ext subjectAltName

# Tester si le serveur accepte les certificats clients (mTLS)
openssl s_client -connect api.internal:8443 \\
-cert /path/to/client.crt \\
-key /path/to/client.key \\
-CAfile /path/to/ca-chain.pem \\
-verify_return_error


3. Empreinte passive - JA3, JA3S, JARM et HASSH

JA3 - Empreinte TLS du client

JA3 calcule un hachage MD5 a partir de cinq champs extraits du ClientHello TLS :

JA3 = MD5( SSLVersion, Ciphers, Extensions, EllipticCurves, EllipticCurvePointFormats )

Ces champs sont determines par la bibliotheque TLS utilisee par l'application, et non par la logique applicative. Un beacon Cobalt Strike compile sous Linux avec sa construction OpenSSL par defaut produira le meme JA3 quelle que soit la destination. Cela fait de JA3 une empreinte fiable du logiciel client - navigateur, curl, Python requests, framework malveillant.

# Extraire les hachages JA3 d'un PCAP avec tshark
tshark -r capture.pcap \\
-Y 'tls.handshake.type == 1' \\\\ # Filtre : ClientHello uniquement
-T fields \\
-e ip.src \\\\ # IP source
-e tls.handshake.ja3 \\\\ # Hachage JA3
-e tls.handshake.extensions_server_name \\\\ # SNI
-E header=y -E separator=,

# Utiliser l'outil Python ja3 sur une interface en direct
pip install pyja3
python3 -m pyja3 -i eth0 # Capture en direct
python3 -m pyja3 -r /captures/malware.pcap # Mode PCAP

# zeek calcule nativement JA3 - consulter ssl.log
zeek -r capture.pcap -C
cat ssl.log | zeek-cut ts id.orig_h id.resp_h ja3 ja3s server_name | head -20

Hachages JA3 malveillants connus (liste partielle a titre d'illustration) :

Hachage JA3Outil associe
72a589da586844d7f0818ce684948eeaBeacon Cobalt Strike par defaut
a0e9f5d64349fb13191bc781f81f42e1Metasploit Meterpreter
e7d705a3286e19ea42f587b07571b559Logiciel malveillant Dridex
7dd80081a2a0b18aeed51b2b4a1f5f40curl (courant - benin)
cd08e31494f9531f560d64c695473da9Bibliotheque Python requests

Note operationnelle : JA3 est un outil de chasse, pas une regle de blocage. Un JA3 malveillant de Cobalt Strike peut etre facilement modifie en recompilant l'implant. Cependant, la plupart des operateurs ne le font pas - la correspondance avec les JA3 connus comme malveillants attrape une part significative des attaquants utilisant des outils generiques.

JA3S - Empreinte TLS du serveur

JA3S cree l'empreinte du ServerHello du serveur :

JA3S = MD5( SSLVersion, Cipher, Extensions )

En combinant JA3 (client) + JA3S (serveur), on peut crer l'empreinte de la paire de communication - un framework C2 specifique communiquant avec son serveur specifique, independamment du domaine ou de l'IP.

JARM - Empreinte active du serveur

JARM envoie 10 paquets ClientHello specialement forges a un serveur et hache la maniere dont le serveur repond. Cela cree l'empreinte de la pile TLS cote serveur. Des hachages JARM identiques sur differentes IP/domaines indiquent le meme logiciel et la meme configuration de serveur - utile pour decouvrir l'infrastructure C2 a grande echelle.

# Installer JARM
git clone https://github.com/salesforce/jarm
cd jarm

# Creer l'empreinte d'un seul serveur
python3 jarm.py target.example.com -p 443

# Creer l'empreinte de plusieurs IP (chasse a l'infrastructure C2)
for ip in $(cat suspicious_ips.txt); do
echo -n "$ip: "
python3 jarm.py $ip -p 443 2>/dev/null | awk '{print $NF}'
done

# Exemple de format de sortie JARM :
# 2ad2ad0002ad2ad00042d42d000000ad9bf51cc3f5a1e29eecb81d0c7b06eb
# Ce hachage est deterministe pour une configuration TLS de serveur donnee

Hachages JARM connus :

Hachage JARM (prefixe)Logiciel serveur
2ad2ad0002ad2ad00042d42d000000...Cobalt Strike Team Server
07d14d16d21d21d07c42d41d00041d...Ecouteur Metasploit
29d29d00029d29d21c29d29d29d29d...nginx par defaut

HASSH - Empreinte client/serveur SSH

HASSH est l'equivalent SSH de JA3 - il hache les champs du message Key Exchange Init :

# Extraire HASSH d'un PCAP avec zeek
zeek -r capture.pcap -C /opt/zeek/share/zeek/site/hassh/
cat ssh.log | zeek-cut ts id.orig_h id.resp_h hassh hasshServer

# Ou utiliser l'outil hassh autonome
pip install hassh
hassh -r /captures/ssh_session.pcap

4. Analyse du trafic chiffre sans dechiffrement

Meme lorsque le dechiffrement de la charge utile est impossible, le trafic chiffre revele des informations comportementales importantes via les metadonnees de canal lateral.

Attributs des metadonnees de flux

Chaque session TCP/TLS expose :

  • Duree du flux - les beacons C2 ont des durees caracteristiques (souvent de courtes verifications)
  • Octets envoyes / recus - les flux asymetriques suggerent une exfiltration (telechargement important) ou un telechargement de fichier
  • Timing inter-arrivee - le beaconing regulier a une faible gigue ; la navigation humaine a une gigue elevee
  • Distribution de la taille des paquets - la video en streaming a des tailles de paquets differentes du transfert de fichiers
  • Nombre de paquets - une session TLS avec 3 paquets est une sonde ; une avec 10 000 est un transfert de donnees
  • Nombre de sessions par destination - de nombreuses sessions courtes vers une seule IP = pattern de beaconing

Detection du beaconing

Les frameworks C2 envoient des beacons a intervalles reguliers. Meme via TLS, la signature temporelle est visible dans conn.log. Le script suivant calcule la gigue inter-beacon pour tous les hotes externes :

#!/usr/bin/env python3
# beacon_detect.py - Detecter les connexions TLS sortantes periodiques (beaconing C2)
# Entree : conn.log de Zeek (separe par des tabulations)

import pandas as pd
import numpy as np
import sys

# Charger conn.log - ignorer les lignes de commentaire commencant par #
df = pd.read_csv('conn.log', sep='\\t', comment='#',
names=['ts','uid','src_ip','src_port','dst_ip','dst_port',
'proto','service','duration','src_bytes','dst_bytes',
'state','local_orig','local_resp','missed_bytes',
'history','orig_pkts','orig_ip_bytes','resp_pkts',
'resp_ip_bytes','tunnel_parents'],
low_memory=False)

df['ts'] = pd.to_numeric(df['ts'], errors='coerce')
df = df.dropna(subset=['ts'])

# Se concentrer sur les connexions TLS sortantes (port 443)
tls = df[(df['dst_port'] == 443) & (df['proto'] == 'tcp')].copy()

# Regrouper par paire IP source + IP destination
for (src, dst), group in tls.groupby(['src_ip', 'dst_ip']):
if len(group) < 5: # Il faut au moins 5 connexions pour evaluer le pattern
continue

times = sorted(group['ts'].values)
intervals = np.diff(times) # Temps entre des connexions consecutives

if len(intervals) == 0:
continue

mean_interval = np.mean(intervals)
std_interval = np.std(intervals)
jitter_ratio = std_interval / mean_interval if mean_interval > 0 else 999

# Faible gigue + intervalle regulier = signature de beacon
# La navigation legitime a une gigue elevee ; les beacons ont une faible gigue
if mean_interval < 600 and jitter_ratio < 0.2 and len(group) > 10:
print(f"[CANDIDAT BEACON]")
print(f" {src} -> {dst}:443")
print(f" Connexions : {len(group)}")
print(f" Intervalle moyen : {mean_interval:.1f}s ({mean_interval/60:.1f} min)")
print(f" Ratio de gigue : {jitter_ratio:.3f} (< 0.2 = suspect)")
print(f" Octets envoyes : {group['src_bytes'].sum():,}")
print()
# Executer la detection de beaconing
python3 beacon_detect.py

# Exemple de sortie :
# [CANDIDAT BEACON]
# 192.168.1.105 -> 185.220.101.42:443
# Connexions : 48
# Intervalle moyen : 60.2s (1.0 min)
# Ratio de gigue : 0.031 (< 0.2 = suspect)
# Octets envoyes : 24,576

NetworkML - ETA basee sur le ML

NetworkML open-source de Cisco applique des classificateurs ML aux attributs de flux pour identifier le type d'application et les anomalies - sans dechiffrement :

# Installer NetworkML
pip install networkml

# Executer sur un PCAP - produit une classification d'application par flux
networkml -p /captures/suspicious.pcap

# Attributs cles utilises en interne par NetworkML :
# - Moyenne, ecart-type, min, max de la taille des paquets
# - Statistiques du temps inter-arrivee
# - Ratio d'octets du flux (telechargement montant/descendant)
# - Distribution des indicateurs (comptages SYN, ACK, PSH, FIN)
# - Duree et total des octets

5. Interception TLS - Proxies MITM et inspection SSL

Lorsque l'analyse des metadonnees est insuffisante, les organisations deploient des proxies d'inspection TLS - egalement appeles inspection SSL, dechiffrement SSL/TLS ou proxies MITM. Le proxy termine la session TLS du client, inspecte le texte en clair et rechiffre vers le serveur en utilisant son propre certificat signe par une AC d'entreprise approuvee par tous les terminaux.

Fonctionnement de l'inspection SSL

Client --[TLS vers cert proxy]--> Proxy --[TLS vers cert serveur reel]--> Serveur
|
Texte clair
inspecte ici

Le proxy presente un certificat forge pour la destination, signe par l'AC d'entreprise. Les clients font confiance a cette AC (deployee via MDM/GPO), donc aucune erreur de certificat n'est affichee. Le vrai certificat du serveur est valide par le proxy.

Considerations juridiques et operationnelles

L'inspection SSL sur les appareils d'entreprise pour le trafic d'entreprise est generalement permise lorsque :

  • Les employes sont notifies (politique d'utilisation acceptable)
  • Le trafic vers des points d'extremite sensibles connus (banque, sante, messagerie personnelle) est exclu
  • Les exigences de conformite (HIPAA, PCI-DSS, confidentialite avocat-client) sont respectees via des listes de contournement

L'inspection SSL sur des appareils personnels ou sans divulgation cree une exposition juridique significative.

mitmproxy - Proxy TLS complet

# Installer mitmproxy
pip install mitmproxy

# Demarrer mitmproxy en mode transparent (necessite une redirection iptables)
mitmproxy --mode transparent \\
--listen-host 0.0.0.0 \\
--listen-port 8080 \\
--save-stream-file /captures/decrypted.mitm # Sauvegarder tous les flux

# Mode console interactif (appuyer sur ? pour l'aide)
mitmproxy

# Mode dump non interactif (tous les flux vers stdout)
mitmdump -w /captures/flows.mitm \\\\ # Ecrire les flux dans un fichier
--flow-detail 3 \\\\ # Verbiosite : 3 = en-tetes complets + corps
-q # Silencieux (pas de sortie console)

# Rejouer un fichier de flux sauvegarde
mitmproxy -r /captures/flows.mitm

# Executer un module/script mitmproxy pour extraire des identifiants
mitmdump -s extract_creds.py -r /captures/flows.mitm

# Regles iptables pour rediriger le trafic vers mitmproxy (mode transparent)
# Executer sur la passerelle/routeur
iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 443 -j REDIRECT --to-port 8080
# Permettre a mitmproxy lui-meme de se connecter en sortie
iptables -t nat -A OUTPUT -p tcp -m owner --uid-owner mitmproxyuser -j RETURN

Module mitmproxy - extraire les en-tetes et corps HTTP vers JSON :

# addon_logger.py - journaliser toutes les requetes/reponses HTTPS en NDJSON
import json
import mitmproxy.http

class HTTPLogger:
def response(self, flow: mitmproxy.http.HTTPFlow):
entry = {
"url": flow.request.pretty_url,
"method": flow.request.method,
"req_headers": dict(flow.request.headers),
"req_body": flow.request.content.decode("utf-8", errors="replace")[:2048],
"status": flow.response.status_code,
"resp_headers":dict(flow.response.headers),
"resp_body": flow.response.content.decode("utf-8", errors="replace")[:4096],
}
with open("/tmp/mitm_log.ndjson", "a") as f:
f.write(json.dumps(entry) + "\\n")

addons = [HTTPLogger()]
# Executer avec le module
mitmdump -s addon_logger.py --mode transparent

Squid + SSL-Bump (Proxy d'entreprise)

Pour les environnements d'entreprise a haut debit, Squid avec ssl-bump est l'approche standard :

# /etc/squid/squid.conf (sections d'inspection TLS pertinentes)

# Definir le certificat AC d'entreprise utilise pour forger les certificats
tls_outgoing_options cafile=/etc/squid/ssl/corporate-ca.pem

# Etapes SSL bump
ssl_bump peek step1 all # Lire le SNI avant de decider
ssl_bump bump step2 !no_ssl_bump # Intercepter sauf dans l'ACL d'exclusion
ssl_bump splice step2 no_ssl_bump # Laisser passer pour les sites exclus

# Liste d'exclusion - ne pas inspecter ces sites (banque, sante, etc.)
acl no_ssl_bump dstdomain .chase.com .bankofamerica.com .mychart.org

# Generation de certificats TLS
sslcrtd_program /usr/lib/squid/security_file_certgen \\
-s /var/lib/squid/ssl_db -M 4MB

http_port 3128 ssl-bump \\
generate-host-certificates=on \\
dynamic_cert_mem_cache_size=4MB \\
tls-cert=/etc/squid/ssl/corporate-ca.pem \\
tls-key=/etc/squid/ssl/corporate-ca.key

6. Dechiffrement avec les secrets pre-master et SSLKEYLOGFILE

Pour le dechiffrement forensique a posteriori de sessions TLS capturees, un proxy n'est pas necessaire. Si les cles de session peuvent etre extraites du terminal ayant participe a la session, tout trafic precedemment capture pour cette session peut etre dechiffre.

SSLKEYLOGFILE - L'approche standard

La plupart des bibliotheques TLS (NSS, OpenSSL via des enveloppes, BoringSSL) supportent la variable d'environnement SSLKEYLOGFILE. Lorsqu'elle est definie, la bibliotheque ecrit le secret pre-master de chaque session dans un fichier au format NSS Key Log. Wireshark et Zeek supportent tous deux ce format nativement.

# Activer la journalisation des cles dans Firefox ou Chrome
SSLKEYLOGFILE=/tmp/tls_keys.log firefox &
SSLKEYLOGFILE=/tmp/tls_keys.log google-chrome --no-sandbox &

# Activer pour curl (utilise NSS ou OpenSSL selon la compilation)
SSLKEYLOGFILE=/tmp/tls_keys.log curl https://target.example.com

# Activer pour Python (requests/urllib3)
# Note : le module ssl de Python ne supporte pas nativement SSLKEYLOGFILE
# Utiliser l'approche keylog_callback :
import ssl, os

def keylog_callback(conn, line):
with open("/tmp/tls_keys.log", "a") as f:
f.write(line.decode() + "\\n")

ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.keylog_filename = "/tmp/tls_keys.log" # Python 3.8+
# ou :
ctx.set_keylog_callback(keylog_callback) # bas niveau

# Activer pour Node.js
NODE_OPTIONS='--tls-keylog=/tmp/tls_keys.log' node app.js

Format du fichier journal de cles (format NSS Key Log) :

# Fichier journal des secrets SSL/TLS, genere par NSS
CLIENT_RANDOM a3f2...8b1c <secret-premaster-48-octets-hex>
CLIENT_HANDSHAKE_TRAFFIC_SECRET a3f2...8b1c <secret>
SERVER_HANDSHAKE_TRAFFIC_SECRET a3f2...8b1c <secret>
CLIENT_TRAFFIC_SECRET_0 a3f2...8b1c <secret>
SERVER_TRAFFIC_SECRET_0 a3f2...8b1c <secret>

Dechiffrement dans Wireshark

Edit -> Preferences -> Protocols -> TLS -> (Pre)-Master-Secret log filename
-> pointer vers /tmp/tls_keys.log

Apres chargement du journal de cles, Wireshark dechiffrera automatiquement les sessions TLS dont le Client Random correspond a une entree. Les donnees d'application dechiffrees apparaissent dans la dissection des paquets.

# Dechiffrement en ligne de commande avec tshark
tshark -r /captures/encrypted.pcap \\
-o "tls.keylog_file:/tmp/tls_keys.log" \\\\ # Fichier journal de cles
-Y 'http' \\\\ # Afficher uniquement HTTP dechiffre apres suppression TLS
-T fields \\
-e http.request.method \\
-e http.request.uri \\
-e http.request.full_uri

# Exporter les objets HTTP dechiffres (fichiers, telechargements)
tshark -r /captures/encrypted.pcap \\
-o "tls.keylog_file:/tmp/tls_keys.log" \\
--export-objects "http,/tmp/exported_objects/"

# Suivre un flux TLS dechiffre
tshark -r /captures/encrypted.pcap \\
-o "tls.keylog_file:/tmp/tls_keys.log" \\
-q \\
-z "follow,tls,ascii,0" # Suivre le flux d'index 0

Dechiffrement dans Zeek

# Configurer Zeek pour utiliser le fichier journal de cles
zeek -r /captures/encrypted.pcap \\
-C \\
"Ssl::keylog_file=/tmp/tls_keys.log" \\\\ # Passer le journal de cles via la variable Zeek
local

# Apres dechiffrement, http.log contiendra les URI complets, les en-tetes et les hachages de fichiers
# files.log contiendra les fichiers extraits avec MD5/SHA1
cat http.log | zeek-cut ts id.orig_h method host uri status_code
cat files.log | zeek-cut ts source mime_type filename md5 sha1

Dechiffrement par cle RSA (TLS 1.2 uniquement, sans PFS)

Si le serveur utilise l'echange de cles RSA (sans confidentialite persistante), la cle privee du serveur seule suffit a dechiffrer toutes les sessions capturees :

# Dechiffrer avec la cle privee du serveur dans Wireshark
# Edit -> Preferences -> Protocols -> TLS -> Liste de cles RSA -> Ajouter :
# IP : server_ip, Port : 443, Protocole : http, Fichier de cle : /path/to/server.key

# Ligne de commande avec ssldump
ssldump -r /captures/encrypted.pcap \\
-k /path/to/server-private.key \\\\ # Cle privee RSA du serveur
-d \\\\ # Decoder les donnees d'application
-A # Afficher tous les enregistrements

# Important : Cela fonctionne UNIQUEMENT si la session capturee utilisait l'echange de cles RSA.
# Les sessions ECDHE/DHE (PFS) ne peuvent pas etre dechiffrees a partir de la cle privee seule.
# Verifier la suite de chiffrement : si "ECDHE" ou "DHE" dans le nom -> besoin du fichier journal de cles.

7. Intelligence sur les certificats et abus de PKI

Meme sans dechiffrement de la charge utile, les certificats TLS sont une riche source de renseignements - et un point de preparation courant pour les attaquants.

Ce que les certificats revelent

# Extraire et analyser le certificat d'une connexion en direct
openssl s_client -connect suspicious-domain.xyz:443 \\
-servername suspicious-domain.xyz 2>/dev/null | \\
openssl x509 -noout -text 2>/dev/null | \\
grep -E "Subject:|Issuer:|Not Before|Not After|DNS:|IP Address:"

# Inspection de certificats en masse depuis Zeek ssl.log
# Champs : ts, uid, src_ip, dst_ip, version, cipher, subject, issuer, validity
cat ssl.log | zeek-cut ts id.orig_h id.resp_h \\
subject issuer validation_status | \\
grep -v "Let's Encrypt\\\\|DigiCert\\\\|GlobalSign\\\\|Comodo" | \\\\ # Filtrer les AC legit courantes
head -50 # Reste = auto-signe ou inhabituel

# Trouver les certificats auto-signes dans Zeek ssl.log
cat ssl.log | zeek-cut ts id.orig_h id.resp_h subject issuer | \\
awk '$4 == $5' # Sujet == Emetteur -> auto-signe

# Trouver les certificats avec une validite tres courte (courant dans l'infrastructure C2)
cat ssl.log | zeek-cut ts id.orig_h id.resp_h \\
notvalidbefore notvalidafter | \\
awk '{
split($4, a, "T"); split($5, b, "T");
diff = mktime(b[1]) - mktime(a[1]);
if (diff < 86400*30) print $0, "VALIDITE:", diff/86400, "jours" # < 30 jours
}'

Journaux de transparence des certificats (CT) pour le renseignement sur les menaces

Les attaquants enregistrent des certificats avant de lancer des attaques. Les journaux CT enregistrent tous les certificats approuves publiquement - les surveiller pour vos domaines et les domaines similaires fournit un avertissement precoce de l'infrastructure de phishing.

# Interroger crt.sh pour les certificats emis pour un domaine (reconnaissance passive)
curl -s "https://crt.sh/?q=%.target-corp.com&output=json" | \\
jq -r '.[] | [.logged_at, .name_value, .issuer_name] | @csv' | \\
sort -r | head -50

# Trouver des domaines sosies/typosquats recemment certifies
curl -s "https://crt.sh/?q=target-c0rp.com&output=json" | jq -r '.[].name_value'

# certstream - surveillance en temps reel des journaux CT (detecte les certificats de phishing a l'emission)
pip install certstream
certstream --json | \\
python3 -c "
import sys, json, re
for line in sys.stdin:
try:
d = json.loads(line)
if d.get('message_type') != 'certificate_update': continue
domains = d['data']['leaf_cert']['all_domains']
for domain in domains:
if re.search(r'(paypal|amazon|microsoft|google|facebook)', domain, re.I):
if not domain.endswith(('.paypal.com','.amazon.com','.microsoft.com')):
print('[CANDIDAT PHISHING]', domain)
except: pass
"

Identification des C2 via les attributs de certificat

Les certificats generes par les attaquants, notamment ceux auto-generes par Cobalt Strike, Metasploit ou des implants personnalises, ont souvent des champs caracteristiques :

# Sujet du certificat par defaut de Cobalt Strike (IOC tres courant)
# Sujet : C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=microsoft.com
# Mais emis par : Let's Encrypt ou auto-signe - incoherence !

# Detecter l'usurpation de sujet de certificat Microsoft/Apple/Google
cat ssl.log | zeek-cut ts id.orig_h id.resp_h subject issuer | \\
grep -i "microsoft\\\\|apple\\\\|google\\\\|amazon" | \\\\ # Le sujet pretend etre une grande tech
grep -vi "digicert\\\\|verisign\\\\|baltimore\\\\|amazon trust" # Mais non signe par leur AC

# Extraire l'empreinte du certificat par defaut de Cobalt Strike
# SHA1 : 6aea28b9ba0e274f4f9a5c8d4c1a9e16e7c9a7f2 (varie selon la version, verifier les IOC actuels)
cat ssl.log | zeek-cut ts id.orig_h id.resp_h cert_chain_fps | \\
grep "known_cs_sha1_here"

8. Presentation des outils - Zeek, Wireshark, mitmproxy, Suricata

Zeek - Pipeline d'analyse TLS/SSL

# Pipeline complet d'analyse TLS avec Zeek sur un PCAP
zeek -r /captures/suspicious.pcap -C local

# Champs de ssl.log pertinents pour l'analyse TLS :
# ts, uid, id.orig_h, id.resp_h, version, cipher, curve, server_name (SNI),
# resumed, last_alert, next_protocol (ALPN), established, cert_chain_fps,
# client_cert_chain_fps, subject, issuer, validation_status, ja3, ja3s

# Trouver tous les hachages JA3 uniques avec les SNI associes
cat ssl.log | zeek-cut ja3 server_name | sort | uniq -c | sort -rn

# Trouver les sessions TLS ayant echoue a la validation (auto-signe, expire, mauvais nom d'hote)
cat ssl.log | zeek-cut ts id.orig_h id.resp_h server_name validation_status | \\
grep -v "^-\\\\|ok"

# Corroler ssl.log avec conn.log pour ajouter les octets/la duree
join -1 2 -2 2 \\
<(sort -k2 ssl.log) \\
<(sort -k2 conn.log) | \\
awk '{print $1, $3, $4, $14, $15, $7}' # ts, src, dst, duree, octets, SNI

# Trouver les connexions ou le SNI ne correspond pas au sujet du certificat (indicateur de domain fronting)
cat ssl.log | zeek-cut server_name subject | \\
awk '{
sni=$1;
sub(/CN=/, "", $2); cert_cn=$2;
if (sni != "" && cert_cn != "" && index(cert_cn, sni) == 0)
print "SNI:", sni, "CERT CN:", cert_cn
}'

Wireshark - Dissection TLS et filtres d'affichage

# Capturer uniquement le trafic TLS
tcpdump -i eth0 -w /tmp/tls.pcap 'tcp port 443 or tcp port 8443'

# Filtres d'affichage Wireshark utiles pour l'analyse TLS :
# tls.handshake.type == 1 -> ClientHello uniquement
# tls.handshake.type == 2 -> ServerHello uniquement
# tls.handshake.extensions_server_name -> Contient le champ SNI
# tls.alert_message -> Alertes TLS (echecs de connexion)
# tls.handshake.ciphersuite == 0x009c -> AES-128-GCM (verifier les suites faibles)
# tls.record.version == 0x0301 -> SSLv3/TLS1.0 (obsolete, a signaler)

# Ligne de commande : extraire toutes les valeurs SNI d'un PCAP
tshark -r /captures/traffic.pcap \\
-Y 'tls.handshake.extensions_server_name' \\
-T fields \\
-e ip.src \\
-e ip.dst \\
-e tls.handshake.extensions_server_name \\
-E separator=, | sort | uniq -c | sort -rn

# Extraire les suites de chiffrement proposees par les clients (empreinte)
tshark -r /captures/traffic.pcap \\
-Y 'tls.handshake.type == 1' \\
-T fields \\
-e ip.src \\
-e tls.handshake.ciphersuites \\
-E separator="|"

Suricata - Regles TLS adaptees

# Detecter TLS sur un port non standard (possible tunnel C2)
alert tls $HOME_NET any -> $EXTERNAL_NET !443 (
msg:"TLS sur port non standard - Possible tunnel C2";
flow:established,to_server;
tls.sni; content:!"."; # SNI absent ou inhabituel
classtype:policy-violation;
sid:9002001; rev:1;
)

# Detecter un certificat TLS expire
alert tls any any -> $HOME_NET any (
msg:"Certificat TLS expire";
tls.cert_subject;
tls_cert_expired; # Mot-cle Suricata : signale les certificats expires
classtype:policy-violation;
sid:9002002; rev:1;
)

# Detecter TLS vers un JA3 connu comme malveillant (profil par defaut Cobalt Strike)
alert tls $HOME_NET any -> $EXTERNAL_NET any (
msg:"JA3 malveillant connu - Cobalt Strike par defaut";
ja3.hash; content:"72a589da586844d7f0818ce684948eea";
classtype:command-and-control;
sid:9002003; rev:1;
metadata:mitre_tactic Command_And_Control, mitre_technique T1071.001;
)

# Detecter le domain fronting : le SNI ne correspond pas a l'en-tete Host (HTTP/2 dans TLS)
# Necessite une inspection TLS ou l'analyse HTTP/2
alert http $HOME_NET any -> $EXTERNAL_NET any (
msg:"Domain fronting possible - Incoherence SNI/Host";
flow:established,to_server;
http.host; content:!"cloudfront.net"; # Exemple : le Host reel est different du SNI
# Combiner avec la chasse JA3 pour une meilleure fidelite
classtype:policy-violation;
sid:9002004; rev:1;
)

9. Evasion - Domain fronting, ESNI/ECH et mimicry de certificats

Les attaquants n'acceptent pas passivement l'inspection TLS. Les techniques suivantes sont activement utilisees pour echapper a la detection, a l'inspection et au blocage.

Domain fronting

Le domain fronting exploite l'infrastructure CDN (Cloudflare, Fastly, Akamai) ou le SNI dans le ClientHello TLS pointe vers un domaine autorise (par ex. allowed.cloudflare.com) mais l'en-tete HTTP Host dans la charge utile chiffree pointe vers le vrai domaine C2. Le CDN route selon l'en-tete Host, pas le SNI.

Le defenseur voit : SNI = allowed.cloudflare.com -> semble legitime
Realite : Host: c2.attacker.com -> le CDN route vers l'origine de l'attaquant

Detection :

  • Corroler le SNI avec les requetes DNS - si l'IP resolue appartient a un CDN et que le pattern de contenu est inhabituel, enqueter
  • Surveiller les flux d'octets asymetriques importants vers les IP CDN (exfiltration de donnees)
  • Apres inspection TLS : comparer le SNI directement a l'en-tete HTTP Host
# Detecter le domain fronting dans mitmproxy (apres dechiffrement)
# addon_fronting.py
import mitmproxy.http

class DomainFrontingDetector:
def request(self, flow: mitmproxy.http.HTTPFlow):
sni = flow.client_conn.tls_extensions.get("server_name", "")
host = flow.request.headers.get("host", "")
if sni and host and sni.lower() != host.lower():
print(f"[DOMAIN FRONTING] SNI={sni} HOST={host} URL={flow.request.url}")

ESNI / ECH - Encrypted Client Hello

ESNI (SNI chiffre) et son successeur ECH (Encrypted Client Hello) chiffrent l'ensemble du ClientHello, cachant le SNI aux observateurs passifs. Le client recupere un ECHConfig via DNS (type d'enregistrement HTTPS), puis chiffre le ClientHello interne en utilisant la cle publique du serveur.

# Verifier si un serveur supporte ECH
dig HTTPS cloudflare.com # Rechercher le parametre "ech=" dans la reponse
dig HTTPS crypto.cloudflare.com +short

# Tester ECH avec curl (necessite une compilation recente avec support ECH)
curl --ech hard https://crypto.cloudflare.com/ # Echec si pas de support ECH
curl --ech grease https://target.com/ # Envoyer un faux ECH (GREASE)

# Detecter l'utilisation d'ECH dans Wireshark :
# TLS ClientHello -> Extensions -> encrypted_client_hello (type 0xFE0D)
tshark -r capture.pcap \\
-Y 'tls.handshake.extensions.type == 65037' \\\\ # 0xFE0D = ECH
-T fields -e ip.src -e ip.dst

Implication defensive : ECH brise le filtrage passif base sur le SNI. Les reseaux qui s'appuient uniquement sur le SNI pour bloquer des categories de trafic (jeux d'argent, reseaux sociaux) seront contournes. Le seul point d'application fiable devient le filtrage au niveau DNS (bloquer l'enregistrement DNS HTTPS) ou l'inspection TLS complete.

Mimicry de certificats

Les frameworks C2 sophistiques generent des certificats qui imitent les services legitimes :

  • Emettre un certificat avec CN=microsoft.com signe par une AC auto-signee nommee "Microsoft IT TLS CA"
  • Enregistrer un domaine typosquat (micros0ft.com) et obtenir un certificat Let's Encrypt legitime
  • Utiliser un domaine legitime compromis comme serveur de preparation
# Detecter l'usurpation de sujet de certificat
# Rechercher les certificats pretendant etre Microsoft/Google/Apple mais signes par des AC inconnues
cat ssl.log | zeek-cut subject issuer | \\
awk '
/[Mm]icrosoft/ && !/DigiCert|Baltimore|GlobalSign/ { print "USURPATION:", $0 }
/[Gg]oogle/ && !/Google Trust Services|GTS/ { print "USURPATION:", $0 }
/[Aa]pple/ && !/Apple/ { print "USURPATION:", $0 }
'

# Trouver les certificats Let's Encrypt sur des ports non standard (inhabituel pour les services legitimes)
cat ssl.log | zeek-cut id.resp_p issuer | \\
awk '$1 != 443 && /Let.s Encrypt/ { print "LE_PORT_NON_STANDARD:", $0 }'

Tunnelisation de protocoles via TLS

Les attaquants tunnelisent des protocoles arbitraires (SSH, RDP, DNS, binaire personnalise) via TLS sur le port 443 pour contourner les pare-feux :

# Detecter le trafic non HTTP sur le port 443 via l'analyse ALPN
cat ssl.log | zeek-cut id.orig_h id.resp_h next_protocol server_name | \\
awk '$3 != "h2" && $3 != "http/1.1" && $3 != "-" { print "ALPN INHABITUEL:", $0 }'

# Detecter les charges utiles TLS a haute entropie (exfil de donnees chiffreees ou protocole personnalise)
# Utiliser l'analyse d'entropie sur les enregistrements TLS Application Data dans tshark :
tshark -r /captures/suspicious.pcap \\
-Y 'tls.app_data' \\
-T fields \\
-e ip.src -e ip.dst -e tls.app_data.len \\
| awk '$3 > 16000' # Grands enregistrements TLS (> ~16 Ko max) = transfert de donnees en masse

10. Correspondance MITRE ATT&CK

TechniqueIDDescriptionMethode de detection
Canal chiffre : Cryptographie asymetriqueT1573.002TLS pour les communications C2Empreinte JA3/JARM, metadonnees de flux
Protocole de couche applicative : Protocoles WebT1071.001HTTPS C2Detection de beaconing, anomalies de certificat
Domain FrontingT1090.004Evasion via CDNIncoherence SNI/en-tete Host post-inspection
Proxy : Proxy externeT1090.002Router le C2 via proxyTLS enchaine, ALPN inhabituel
Exfiltration via canal C2T1041Exfil de donnees en TLSAnalyse du flux d'octets asymetrique
Vol de cookie de session webT1539Via MITM TLSDetecter les anomalies du proxy MITM
Adversaire au milieuT1557Interception TLSAnomalies d'emetteur de certificat
DNS sur HTTPST1071.004DoH pour C2Trafic vers 1.1.1.1/dns-query, 8.8.8.8/dns-query

Fin du Chapitre 2.4 - Analyse du trafic chiffre et inspection TLS

Suivant : Module 3 - Securite offensive et exploitation Chapitre 3.1 : Reconnaissance, balayage et enumeration