Skip to main content

Chapitre 2.3 - Forensique reseau et analyse des journaux

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


Table des matieres

  1. Ce que la forensique reseau repond reellement
  2. Sources de preuves et hierarchie de collecte
  3. Capture de paquets complete : architecture et stockage
  4. NetFlow / IPFIX : telemetrie au niveau session
  5. Zeek : le cadre d'observation reseau
  6. Sources de journaux : pare-feu, DNS, proxy, authentification
  7. Correlation de journaux et reconstruction de chronologie
  8. Identification des patterns d'attaque dans les journaux
  9. Chaine de traçabilite et integrite forensique
  10. Correspondance MITRE ATT&CK

1. Ce que la forensique reseau repond reellement

La forensique reseau est la capture, la preservation et l'analyse du trafic reseau et des journaux associes pour reconstruire ce qui s'est passe, quand, comment et depuis ou. Contrairement a la detection d'intrusions en direct - qui se declenche sur des evenements en cours - la forensique reseau opere retroactivement sur des preuves stockees pour repondre aux questions d'enquete :

  • Qu'est-ce qui a ete exfiltre ? Reconstruire les fichiers transferes depuis les captures de paquets.
  • Comment l'attaquant est-il entre ? Identifier le vecteur d'acces initial depuis les journaux HTTP/SMTP/VPN.
  • Quels systemes ont ete touches ? Cartographier le mouvement lateral depuis les journaux d'authentification et NetFlow.
  • Quand l'intrusion a-t-elle commence ? Etablir une chronologie precise depuis les horodatages de journaux correles.
  • L'attaquant est-il toujours present ? Detecter la balise C2 en cours dans les donnees de flux.

Ces questions ont des exigences de preuves differentes. La reconstruction de fichiers necessite une capture complete de paquets (FPC). La cartographie du mouvement lateral requiert des journaux d'evenements d'authentification et NetFlow. La precision de la chronologie exige que toutes les sources soient synchronisees - un enqueteur qui ignore la derive NTP construira des chronologies avec des lacunes et des chevauchements fantomes.

La hierarchie de qualite des preuves est importante : la FPC est l'etalon-or (chaque octet est preserve), mais elle est couteuse a grande echelle. NetFlow est economique et evolutif mais perd le payload. Les journaux ont un signal eleve mais dependent entierement de ce que l'application generatrice a choisi d'enregistrer.


2. Sources de preuves et hierarchie de collecte

Estimations de stockage et de retention

SourceVolume a 1 Gbps soutenuRetention typique
Capture de paquets complete~80-100 Go/h (apres compression)24-72 heures (tournant)
NetFlow (echantillonne 1:100)~50-200 Mo/h30-90 jours
Journaux Zeek~1-3 Go/h30-90 jours
Journaux de refus du pare-feu~100-500 Mo/h90-365 jours
Journaux de requetes DNS~50-200 Mo/h90-365 jours
Journaux proxy~500 Mo-2 Go/h90-365 jours

L'implication pratique : la FPC est une fenetre courte qui capture les incidents actifs ; NetFlow et les journaux applicatifs sont les principales sources de preuves pour investiguer les incidents decouverts des jours ou semaines apres les faits.


3. Capture de paquets complete

Capture a grande echelle

Un tcpdump sur interface unique echoue au-dela de quelques centaines de Mbps en raison de la surcharge de copie du noyau et de la contention de verrous. La FPC de production utilise :

  • AF_PACKET avec PACKET_MMAP - tampon en anneau du noyau, zero copie vers l'espace utilisateur
  • PF_RING / DPDK - contournement du noyau ; gere des debits de 10-100 Gbps
  • Appliances dedieees - Endace, cPacket, materiel ExtraHop avec garanties de capture sans perte

Pour les travaux de laboratoire et de reponse aux incidents, tcpdump et tshark sont adequats :

# Capturer vers des fichiers pcap rotatifs : 1 Go par fichier, conserver les 50 derniers fichiers
tcpdump \
-i eth0 \ # interface de capture
-s 0 \ # snaplen complet (0 = paquet entier, sans troncature)
-w /data/capture/capture-%Y%m%d-%H%M%S.pcap \
-G 3600 \ # rotation toutes les 3600 secondes
-C 1000 \ # rotation aussi a 1000 Mo
-W 50 \ # conserver au maximum 50 fichiers (tampon circulaire)
-z gzip \ # compresser les fichiers rotes
not port 22 # exclure SSH (votre propre trafic de gestion)

# Capture haute performance avec PF_RING zero-copy
tcpdump \
--immediate-mode \
-i zc:eth0 \ # prefixe zc: = interface PF_RING zero-copy
-s 0 \
-w /nvme/capture/live.pcap

Capture ciblee pendant la reponse aux incidents

Quand vous connaissez l'hote ou la connexion suspecte, limitez la portee de capture pour reduire le stockage et le bruit d'analyse :

# Capturer tout le trafic vers/depuis un hote specifique
tcpdump -i eth0 -s 0 -w hote_suspect.pcap \
host 192.168.1.55

# Capturer uniquement les ports indicatifs de C2 depuis un hote suspect
tcpdump -i eth0 -s 0 -w c2_suspect.pcap \
"host 192.168.1.55 and (port 443 or port 4444 or port 8080)"

# Capturer les requetes + reponses DNS pour un domaine specifique
tcpdump -i eth0 -s 0 -w dns_malveillant.pcap \
"port 53 and (udp or tcp)"

# Capturer le trafic SMB pour l'analyse du mouvement lateral
tcpdump -i eth0 -s 0 -w smb_lateral.pcap \
"port 445 or port 139"

Extraction de fichiers depuis le PCAP

La forensique reseau necessite frequemment de reconstruire les fichiers transferes sur le reseau - charges utiles de maliciels, documents exfiltres, implants C2.

# Extraire les fichiers transferes par HTTP depuis pcap avec NetworkMiner (mode CLI)
mono /opt/NetworkMiner/NetworkMiner.exe \
--capture-file suspect.pcap \
--output-dir /tmp/fichiers_extraits

# Extraction avec tcpflow : reassemble les flux TCP, ecrit chaque flux dans un fichier
tcpflow \
-r capture.pcap \ # lire depuis pcap
-o /tmp/sortie_tcpflow \ # repertoire de sortie
-ae # -a : signaler tous les paquets ; -e : capturer nommee par regex

# Extraire les objets HTTP avec tshark (CLI Wireshark)
tshark \
-r capture.pcap \
--export-objects http,/tmp/objets_http # ecrit chaque objet HTTP comme fichier separe

# Extraire les fichiers transferes par SMB
tshark \
-r capture.pcap \
--export-objects smb,/tmp/objets_smb

# Extraire et hacher tous les fichiers extraits pour la correspondance IoC
find /tmp/objets_http -type f \
| xargs -I{} sh -c 'md5sum {}; sha256sum {}'

Reconstruction des sessions pour l'examen de l'analyste

# Suivre un flux TCP specifique dans tshark (indice de flux depuis l'analyse initiale)
tshark -r capture.pcap \
-z follow,tcp,ascii,0 \ # indice de flux 0 ; mode ascii (texte lisible)
-q # supprimer la sortie par defaut des paquets

# Suivre une conversation HTTP
tshark -r capture.pcap \
-Y "http" \
-T fields \
-e frame.time \
-e ip.src \
-e http.request.method \
-e http.request.uri \
-e http.response.code \
-e http.content_length \
-E header=y \
-E separator=,

# Reconstruire un corps de reponse HTTP complet (ex. maliciel telecharge)
tshark -r capture.pcap \
-Y "tcp.stream eq 5" \ # isoler le flux 5
-T fields -e data \ # donnees hexadecimales brutes
| tr -d '\n' \
| xxd -r -p > /tmp/payload_flux5.bin
file /tmp/payload_flux5.bin # identifier le type de fichier

4. NetFlow / IPFIX : telemetrie au niveau session

NetFlow (Cisco), IPFIX (standard IETF, RFC 7011) et sFlow (echantillonne) decrivent tous les sessions reseau sans preserver le payload. Un enregistrement de flux capture le 5-tuple (IP src, IP dst, port src, port dst, protocole) plus les compteurs (octets, paquets, duree, drapeaux TCP) et les metadonnees (numeros AS, DSCP, interface entree/sortie).

Ce que vous pouvez et ne pouvez pas determiner depuis les flux

Question d'investigationReponse NetFlow
L'hote A a-t-il communique avec l'hote B ?Oui - src/dst/port/heure precis
Quelle quantite de donnees a ete transferee ?Oui - compteurs octets/paquets par direction
Quel protocole a ete utilise ?Oui - port + champ protocole (L4 uniquement)
Le trafic etait-il chiffre ?Oui - deduit (TLS = port 443 typiquement)
Quel fichier a ete telecharge ?Non - pas de payload, pas de nom de fichier
Quelle requete SQL a ete executee ?Non - pas de contenu couche applicative
La session a-t-elle reussi ?Partiel - les drapeaux TCP indiquent la poignee de main SYN-ACK
Des donnees ont-elles ete exfiltrees ?Partiel - deduit des grands compteurs d'octets en upload

Collecte et interrogation des flux

# nfdump : analyser les donnees NetFlow/IPFIX stockees par nfcapd
# Afficher les principaux emetteurs par octets dans les 6 dernieres heures
nfdump \
-R /var/cache/nfdump/2024/11/15/ \
-t 2024-11-15.08:00:00-2024-11-15.14:00:00 \
-s srcip/bytes \ # tri : principales IP sources par octets
-n 20 \ # afficher le top 20
-o long

# Trouver toutes les connexions depuis un hote specifique
nfdump \
-R /var/cache/nfdump/ \
-t 2024-11-15.00:00:00-2024-11-16.00:00:00 \
'src ip 192.168.1.55' \
-o long

# Detecter les grands transferts de donnees (exfiltration potentielle) -- uploads > 50 Mo
nfdump \
-R /var/cache/nfdump/ \
'src ip in [10.0.0.0/8] and bytes > 52428800 and not dst ip in [10.0.0.0/8]' \
-o long \
-s dstip/bytes

# Identifier les balises : connexions d'un hote vers la meme IP externe a intervalles reguliers
nfdump \
-R /var/cache/nfdump/ \
'src ip 192.168.1.55 and dst ip 203.0.113.44' \
-o "fmt:%ts %td %byt %pkt" \
-q

Detection de balises avec Python

La detection automatisee de balises opere sur les donnees de duree/timing des flux :

#!/usr/bin/env python3
"""
beacon_detect.py - detecter les balises C2 periodiques depuis l'export CSV NetFlow
Usage: nfdump -R /data/flows/ -o csv | python3 beacon_detect.py
"""
import sys
import statistics
from collections import defaultdict
from datetime import datetime

flux = defaultdict(list)

for ligne in sys.stdin:
ligne = ligne.strip()
if not ligne or ligne.startswith('#') or ligne.startswith('Date'):
continue
parties = ligne.split(',')
if len(parties) < 7:
continue
try:
ts_str = parties[0] # champ horodatage
ip_src = parties[3] # IP source
ip_dst = parties[5] # IP destination
port_dst = parties[6] # port destination
ts = datetime.strptime(ts_str, '%Y-%m-%d %H:%M:%S')
cle = (ip_src, ip_dst, port_dst)
flux[cle].append(ts.timestamp()) # stocker horodatage unix
except (ValueError, IndexError):
continue

# Analyser chaque paire src->dst:port pour un comportement periodique
print(f"{'Source':<20} {'Destination':<20} {'Port':<8} {'Total':>6} {'IntervalleMoy':>14} {'Gigue(CV)':>12}")
print("-" * 85)

for (src, dst, port), horodatages in flux.items():
if len(horodatages) < 5: # besoin d'au moins 5 echantillons pour une analyse significative
continue
horodatages.sort()
intervalles = [horodatages[i+1] - horodatages[i] for i in range(len(horodatages)-1)]
moy_intervalle = statistics.mean(intervalles)
if moy_intervalle < 10: # ignorer les intervalles sous 10 secondes (trop bruyants)
continue
ecart = statistics.stdev(intervalles) if len(intervalles) > 1 else 0
cv = ecart / moy_intervalle # coefficient de variation ; CV faible = balise reguliere

if cv < 0.3 and len(horodatages) >= 10: # seuil balise : CV < 0.3, 10+ echantillons
print(f"{src:<20} {dst:<20} {port:<8} {len(horodatages):>6} "
f"{moy_intervalle:>12.1f}s {cv:>12.4f} <- CANDIDAT BALISE")
elif cv < 0.5:
print(f"{src:<20} {dst:<20} {port:<8} {len(horodatages):>6} "
f"{moy_intervalle:>12.1f}s {cv:>12.4f}")

5. Zeek : le cadre d'observation reseau

Zeek (anciennement Bro) occupe l'espace entre la capture complete de paquets et le NetFlow brut - il effectue une analyse approfondie des protocoles et emet des journaux structures, interrogeables, sans stocker les paquets bruts. Pour la forensique reseau, les journaux Zeek sont souvent l'artefact le plus precieux : ils contiennent la semantique de la couche applicative (URI HTTP, noms de requetes DNS, certificats TLS, hachages de fichiers) a une fraction du cout de stockage du PCAP.

Fichiers de journaux principaux

Fichier journalContenuChamps forensiques cles
conn.logToutes les sessions TCP/UDP/ICMPid.orig_h, id.resp_h, service, orig_bytes, resp_bytes, conn_state, history
dns.logRequetes et reponses DNSquery, qtype_name, answers, TTL
http.logRequetes HTTPmethod, host, uri, referrer, user_agent, status_code, resp_body_len
ssl.logSessions TLSserver_name (SNI), subject, issuer, validation_status, ja3, ja3s
files.logTransferts de fichiers (tout protocole)source, filename, mime_type, md5, sha1, sha256
x509.logCertificats TLScertificate.subject, certificate.issuer, certificate.not_valid_after, san.dns
smtp.logSessions emailfrom, to, subject, path, user_agent
notice.logAlertes de strategie Zeeknote, msg, src, dst, sub
weird.logAnomalies de protocolename, msg - paquets malformes, violations de protocole

CLI Zeek : analyse hors ligne

# Traiter un pcap hors ligne, ecrire tous les journaux dans le repertoire courant
zeek -r trafic_suspect.pcap \
local \ # charger les scripts de strategie par defaut
LogAscii::use_json=T # sortir les journaux en JSON au lieu de TSV

# Activer des scripts de detection specifiques
zeek -r capture.pcap \
/opt/zeek/share/zeek/policy/protocols/ssl/validate-certs.zeek \
/opt/zeek/share/zeek/policy/protocols/dns/detect-external-names.zeek \
/opt/zeek/share/zeek/policy/frameworks/intel/do_notice.zeek \
Intel::read_files=/etc/zeek/intel/indicateurs.dat # charger le flux IoC

# Mode capture en direct (deploiement en production)
zeekctl deploy # deployer avec ZeekControl (gere les workers, logger, manager)
zeekctl status # verifier la sante des noeuds
zeekctl stop

Interrogation des journaux Zeek avec zeek-cut et jq

Les journaux TSV de Zeek sont rapides a interroger avec zeek-cut (extracteur de colonnes) ; les journaux JSON utilisent jq :

# Extraire toutes les destinations externes uniques et compteurs d'octets depuis conn.log
cat conn.log \
| zeek-cut id.orig_h id.resp_h orig_bytes resp_bytes conn_state \
| awk '$5 == "SF"' \ # SF = connexion etablie et fermee normalement
| sort -k3 -rn \ # trier par orig_bytes decroissant
| head -20

# Trouver toutes les requetes DNS vers un TLD suspect
cat dns.log \
| zeek-cut ts id.orig_h query answers \
| grep -E '\.(ru|cn|tk|pw|xyz)$' \
| sort -u

# Trouver les requetes HTTP avec des agents utilisateurs suspects (ou agent manquant)
cat http.log \
| zeek-cut ts id.orig_h host uri user_agent status_code \
| awk -F'\t' '$5 == "-" || $5 ~ /python|curl|wget|go-http/' \
| sort

# Trouver tous les fichiers transferes avec leurs hachages (necessite l'analyse de fichiers activee)
cat files.log \
| zeek-cut ts source fuid mime_type filename md5 sha256 \
| grep -v '-' \ # filtrer les entrees sans hachages
| sort -k4 # trier par type MIME

# Croiser les hachages de fichiers avec VirusTotal (manuel -- necessite vt-cli)
cat files.log \
| zeek-cut sha256 \
| grep -v '^-$' \
| sort -u \
| xargs -I{} vt file {}

# Identifier les connexions TLS avec des certificats auto-signes
cat ssl.log \
| zeek-cut ts id.orig_h id.resp_h server_name validation_status \
| awk '$5 == "self signed certificate in certificate chain"' \
| sort -k3 # grouper par IP destination

Ecriture d'un script Zeek personnalise

Le langage de scripting de Zeek permet une logique de detection personnalisee qui opere sur les evenements de protocole :

# /etc/zeek/site/detecter_tunneling_dns.zeek
# Declenche une notice quand un nom de requete DNS depasse 60 chars ou contient des patterns base64

@load base/frameworks/notice

module TunnelDNS;

export {
redef enum Notice::Type += {
DNS_Requete_Longue,
DNS_Requete_HighEntropie
};
}

event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count)
{
# Verification de la longueur de requete
if ( |query| > 60 )
{
NOTICE([$note=DNS_Requete_Longue,
$conn=c,
$msg=fmt("Requete DNS longue: %s (%d chars)", query, |query|),
$identifier=cat(c$id$orig_h, query)]);
}

# Verification de pattern base64 : longues sequences alphanumeriques avant le premier point
local etiquette = split_string(query, /\./)[0];
if ( |etiquette| > 30 && /^[A-Za-z0-9+\/]{30,}/ in etiquette )
{
NOTICE([$note=DNS_Requete_HighEntropie,
$conn=c,
$msg=fmt("Etiquette DNS possiblement encodee base64: %s", etiquette),
$identifier=cat(c$id$orig_h, etiquette)]);
}
}

6. Sources de journaux

Journaux de pare-feu

Les journaux de pare-feu enregistrent les decisions autoriser/refuser avec le 5-tuple plus l'horodatage, l'interface, le nom de regle et l'etat de traduction NAT. Ce qu'ils revelent et ce qu'ils ratent :

  • Reverent : tentatives de connexion bloquees (reconnaissance), connexions autorisees (chemins de communication), mappages NAT (hote interne derriere une adresse traduite)
  • Ratent : contenu de la couche applicative, payload chiffre, trafic autorise par des regles trop larges
# Analyser le journal iptables/nftables depuis syslog
# iptables doit etre configure avec la regle -j LOG avant DROP/ACCEPT
grep "IN=eth0" /var/log/kern.log \
| grep -E "DPT=(4444|8080|1337)" \ # ports de destination suspects
| awk '{
for(i=1;i<=NF;i++){
if($i~/^SRC=/) printf "src=%s ", substr($i,5);
if($i~/^DST=/) printf "dst=%s ", substr($i,5);
if($i~/^DPT=/) printf "dport=%s ", substr($i,5);
if($i~/^SPT=/) printf "sport=%s\n", substr($i,5);
}
}'

# pfSense/OPNsense : format CSV filterlog
# Champs : regle,sous,ancre,tracker,interface,raison,action,direction,version_ip,...
grep ",block," /var/log/filter.log \
| awk -F',' '{print $11, $13, $14, $16}' \
| sort | uniq -c | sort -rn

# Cisco ASA : analyser les journaux de refus
grep "Deny" /var/log/asa.log \
| grep -oP "src\s+\K[\d.]+" \ # extraire les IP sources
| sort | uniq -c | sort -rn \
| head -20

Journaux DNS

Le DNS est une mine d'or forensique. Chaque resolution de nom d'hote est enregistree - y compris les requetes de domaine C2, les balises DGA et les canaux d'exfiltration de donnees. Les journaux DNS correlent l'activite des hotes internes avec les destinations externes meme quand la connexion reelle est chiffree.

# Journal de requetes BIND (named) -- activer dans named.conf :
# logging { channel journal_requetes { file "/var/log/named/requetes.log"; severity info; };
# category queries { journal_requetes; }; };

# Extraire tous les domaines uniques interroges par un hote specifique
grep "192.168.1.55" /var/log/named/query.log \
| grep -oP "query: \K\S+" \ # extraire le nom de domaine
| sort | uniq -c | sort -rn

# Trouver les reponses NX (NXDOMAIN) -- hotes interrogeant des domaines inexistants
# Taux NX eleve depuis un hote = DGA ou tunneling DNS
grep "NXDOMAIN" /var/log/named/query.log \
| awk '{print $5}' \ # champ IP client
| sort | uniq -c | sort -rn

# Journal de debogage DNS Windows (activer via dnsmgmt.msc -> proprietes serveur -> journalisation de debogage)
Get-Content C:\Windows\System32\dns\dns.log |
Where-Object { $_ -match "QUERY" } |
Select-String -Pattern "[A-Za-z0-9]{20,}\.(com|net|org)" |
Sort-Object | Get-Unique

# DNS passif -- utilisant dnstap (format de capture DNS binaire, fidelite superieure aux journaux texte)
fstrm_capture \
-t protobuf:dnstap.Dnstap \
-u /var/run/named/dnstap.sock \
-w /var/log/dnstap/dns.fstrm

# Decoder les trames dnstap
dnstap-read /var/log/dnstap/dns.fstrm \
| grep -i "evil\.com"

Journaux proxy

Les proxies HTTP/HTTPS (Squid, Bluecoat, Zscaler) journalisent chaque requete web avec l'URL complete, le code de reponse, les octets transferes et l'identite du client. Dans les organisations qui acheminent tout le trafic web via un proxy, les journaux proxy sont souvent la source de preuves la plus riche pour les attaques basees sur le web.

# Format access.log Squid :
# horodatage duree ip_client resultat/statut octets methode url hierarchie type_mime

# Trouver toutes les URLs accedees par un hote suspect avec de grandes reponses
awk '$3 == "192.168.1.55" && $5 > 1048576' /var/log/squid/access.log \
| awk '{print $3, $5, $7}' \ # client, octets, URL
| sort -k2 -rn

# Trouver les requetes vers des domaines nouvellement enregistres (heuristique : SLD court)
awk '{print $7}' /var/log/squid/access.log \
| grep -oP 'https?://\K[^/]+' \ # extraire le nom d'hote
| sort | uniq -c | sort -rn \
| awk '$2 ~ /^[a-z0-9]{4,8}\.(com|net|cc|pw|xyz)$/'

# Trouver les requetes POST (donnees envoyees) avec de grands corps
awk '$6 == "POST" && $5 > 10000' /var/log/squid/access.log \
| awk '{print $3, $7, $5}'

# Zscaler / proxy cloud : journal NSS transmis via syslog -- analyser JSON
cat /var/log/zscaler/nss.log \
| jq 'select(.action == "Allowed") | select(.bytes_sent > 100000) | {user, url, bytes_sent}'

Journaux d'authentification

Les evenements d'authentification vous indiquent qui s'est authentifie depuis ou et quand - essentiels pour suivre le mouvement lateral (T1021) et l'abus d'identifiants.

# Linux : echecs de connexion SSH (detection de force brute)
grep "Failed password" /var/log/auth.log \
| awk '{print $(NF-3)}' \ # extraire l'IP source
| sort | uniq -c | sort -rn \
| head -20

# Connexions reussies apres des echecs precedents depuis la meme IP (force brute reussie)
awk '/Failed password/{echec[$NF]++} /Accepted password/{if(echec[$NF]>5) print "SUCCES BRUTE:", $NF, echec[$NF], "echecs"}' \
/var/log/auth.log

# Journal d'utilisation sudo -- escalade de privileges apres compromission
grep "sudo:" /var/log/auth.log \
| grep "COMMAND" \
| awk '{print $1, $2, $3, $5, $NF}'

# Journal d'evenements de securite Windows -- utiliser Get-WinEvent (PowerShell) ou evtx_dump
# IDs d'evenements :
# 4624 = Ouverture de session reussie
# 4625 = Echec d'ouverture de session
# 4648 = Ouverture de session avec des identifiants explicites (indicateur pass-the-hash)
# 4768 = TGT Kerberos demande
# 4769 = Ticket de service Kerberos demande
# 4776 = Authentification NTLM
# 4771 = Pre-authentification Kerberos echouee (indicateur Kerberoasting)

# PowerShell : trouver tous les evenements 4624 avec type d'ouverture 3 (reseau) dans les 24 dernieres heures
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
Id = 4624
StartTime = (Get-Date).AddHours(-24)
} | Where-Object {
$_.Properties[8].Value -eq 3 # LogonType 3 = ouverture de session reseau
} | Select-Object TimeCreated,
@{N='Utilisateur'; E={$_.Properties[5].Value}},
@{N='IPSrc'; E={$_.Properties[18].Value}},
@{N='Station'; E={$_.Properties[11].Value}} |
Format-Table -AutoSize

# Ouvertures de session rapides sur plusieurs systemes = mouvement lateral
# Python : trouver les IP sources s'authentifiant vers >3 hotes uniques en 1 heure
python3 << 'EOF'
from collections import defaultdict
import csv

hotes_par_src = defaultdict(set)

with open('evenements_auth.csv') as f:
lecteur = csv.DictReader(f)
for ligne in lecteur:
if ligne['EventId'] == '4624' and ligne['LogonType'] == '3':
hotes_par_src[ligne['SrcIP']].add(ligne['TargetHost'])

for ip_src, hotes in hotes_par_src.items():
if len(hotes) > 3:
print(f"CANDIDAT MOUVEMENT LATERAL: {ip_src} authentifie vers {len(hotes)} hotes: {hotes}")
EOF

7. Correlation de journaux et reconstruction de chronologie

Les sources de journaux individuelles repondent a des questions isolees. L'histoire complete de l'attaque emerge de la correlation d'evenements entre sources alignees sur une chronologie commune. La synchronisation du temps n'est pas optionnelle - les systemes Windows se synchronisent par defaut avec les controleurs de domaine, les systemes Linux doivent utiliser chronyd ou ntpd, et les equipements reseau necessitent une configuration NTP explicite. Une derive d'horloge de 2 minutes rend la correlation peu fiable.

Methodologie de reconstruction de chronologie

1. Definir la fenetre d'incident (ex. horodatage alerte IDS initiale +/- 2 heures)
2. Extraire toutes les sources de journaux pertinentes pour cette fenetre
3. Normaliser les horodatages en UTC ISO-8601
4. Fusionner et trier par horodatage
5. Identifier le premier evenement observable
6. Avancer : chaque evenement doit etre causalement explicable par les evenements precedents
7. Identifier les lacunes -- preuves manquantes qui devraient exister si votre hypothese est correcte

Fusion automatisee de chronologie

# Convertir les EVTX Windows en JSON pour la correlation
evtx_dump -f json /chemin/vers/Security.evtx \
| jq '.[] | {ts: .Event.System.TimeCreated."#attributes"."SystemTime",
id: .Event.System.EventID."#text",
data: .Event.EventData}' \
> evenements_windows.jsonl

# Fusionner plusieurs sources de journaux dans une seule chronologie CSV
python3 << 'EOF'
import json, csv, sys
from datetime import datetime, timezone

evenements = []

# Zeek conn.log
with open('conn.log') as f:
for ligne in f:
if ligne.startswith('#'): continue
parties = ligne.split('\t')
if len(parties) < 10: continue
ts = datetime.fromtimestamp(float(parties[0]), tz=timezone.utc)
evenements.append({
'horodatage': ts.isoformat(),
'source': 'zeek_conn',
'src': parties[2],
'dst': parties[4],
'detail': f"{parties[6]}/{parties[7]} {parties[9]} octets"
})

# Journal DNS
with open('dns.log') as f:
for ligne in f:
if ligne.startswith('#'): continue
parties = ligne.split('\t')
if len(parties) < 10: continue
ts = datetime.fromtimestamp(float(parties[0]), tz=timezone.utc)
evenements.append({
'horodatage': ts.isoformat(),
'source': 'zeek_dns',
'src': parties[2],
'dst': parties[4],
'detail': f"query={parties[9]} answers={parties[21] if len(parties)>21 else '-'}"
})

# Journal HTTP
with open('http.log') as f:
for ligne in f:
if ligne.startswith('#'): continue
parties = ligne.split('\t')
if len(parties) < 16: continue
ts = datetime.fromtimestamp(float(parties[0]), tz=timezone.utc)
evenements.append({
'horodatage': ts.isoformat(),
'source': 'zeek_http',
'src': parties[2],
'dst': parties[4],
'detail': f"{parties[7]} {parties[8]}{parties[9]} -> {parties[15]}"
})

evenements.sort(key=lambda e: e['horodatage'])

writer = csv.DictWriter(sys.stdout, fieldnames=['horodatage','source','src','dst','detail'])
writer.writeheader()
writer.writerows(evenements)
EOF

Chronologie d'attaque : a quoi ressemble une reconstruction reelle

La chronologie suivante est synthetique mais representative d'un incident de hameconnage cible vers mouvement lateral vers exfiltration, reconstruite depuis la correlation de journaux :

[09:14:33 UTC] Journal SMTP: serveur mail recoit email vers user@corp.com
De: attacker@lookalike.com, Sujet: "Facture T4", Piece jointe: Facture.doc

[09:15:01 UTC] Journal proxy: 192.168.5.22 GET http://evil.com/stage1.exe (200, 84 Ko)
User-Agent: Microsoft Office/15.0 (execution de macro via document)

[09:15:03 UTC] Journal DNS: 192.168.5.22 requete: evil.com (resolu en 203.0.113.44)

[09:15:04 UTC] Zeek conn.log: 192.168.5.22:54291 -> 203.0.113.44:443 ETABLI
2847 octets envoyes, 84129 octets recus (telechargement d'implant)

[09:15:07 UTC] Zeek ssl.log: SNI=evil.com, cert auto-signe, JA3=51c64c77...
validation_status="self signed certificate in certificate chain"

[09:15:10 UTC] Alerte IDS: SID 2014702 "ET TROJAN Possible Cobalt Strike Beacon"

[09:18:44 UTC] Zeek conn.log: 192.168.5.22:54312 -> 203.0.113.44:443 (balise 60 sec)
[09:19:44 UTC] Zeek conn.log: 192.168.5.22:54318 -> 203.0.113.44:443 (balise 60 sec)
[pattern continue toutes les 60 secondes -- enregistrement C2]

[09:32:15 UTC] Journal auth (Windows): 4624 LogonType=3 src=192.168.5.22 dst=192.168.5.10
Compte: corp\svcaccount (identifiant depuis la memoire via Mimikatz)

[09:32:21 UTC] Zeek conn.log: 192.168.5.22:62544 -> 192.168.5.10:445 (SMB)
orig_bytes=8812, resp_bytes=2941, duree=0.44s

[09:32:25 UTC] Journal auth (Windows): 4624 LogonType=3 src=192.168.5.22 dst=192.168.5.31

[09:33:00 UTC] Zeek files.log: fichier extrait depuis SMB vers 192.168.5.10
filename=donnees_financieres_2024.xlsx, sha256=a3f9c2..., taille=4.2 Mo

[09:47:12 UTC] NetFlow: 192.168.5.22 -> 203.0.113.44:443 upload=4.3 Mo en 12 secondes
(exfiltration des donnees financieres via canal C2)

Cette chronologie n'est possible que parce que chaque horodatage est synchronise en UTC et que les evenements de SMTP, proxy, DNS, Zeek, IDS, authentification et NetFlow sont fusionnes. Chaque source seule donne une image incomplete.


8. Identification des patterns d'attaque

Detection de balises C2 dans les journaux proxy/Zeek

# Trouver les connexions vers la meme IP externe a intervalles suspicieusement reguliers
cat conn.log \
| zeek-cut ts id.orig_h id.resp_h id.resp_p proto orig_bytes \
| awk '$4 == "443" && $3 !~ /^10\.|^192\.168\.|^172\./' \
| awk '{
cle=$2"-"$3;
if(dernier_ts[cle]) {
intervalle = $1 - dernier_ts[cle];
somme[cle] += intervalle;
compteur[cle]++;
if(intervalle < min[cle] || min[cle]==0) min[cle]=intervalle;
if(intervalle > max[cle]) max[cle]=intervalle;
}
dernier_ts[cle]=$1;
}
END {
for(k in compteur) {
if(compteur[k]>=8) {
moy=somme[k]/compteur[k];
gigue=(max[k]-min[k])/moy;
if(gigue<0.3) printf "BALISE: %s count=%d avg=%.1fs gigue=%.2f\n", k, compteur[k], moy, gigue;
}
}
}'

Detection de l'exfiltration DNS

# Volume eleve de requetes vers un seul domaine autoritaire + grandes reponses TXT
cat dns.log \
| zeek-cut query qtype_name answers \
| awk '{
n=split($1, parties, ".");
if(n>=2) apex=parties[n-1]"."parties[n];
else apex=$1;
compteur[apex]++;
}
END {
for(d in compteur) if(compteur[d]>100) print compteur[d], d
}' \
| sort -rn \
| head -20

# Grandes reponses d'enregistrements TXT = donnees encodees dans le DNS
cat dns.log \
| zeek-cut ts id.orig_h query qtype_name answers \
| awk '$4 == "TXT" && length($5) > 100' \
| sort -k3 # grouper par domaine de requete

Detection du mouvement lateral dans les journaux d'authentification

# Windows : trouver les comptes s'authentifiant vers plusieurs hotes avec LogonType 3
awk -F',' '
NR==1 {next}
$3 == "4624" && $5 == "3" {
cle = $6; # nom utilisateur
hotes[cle][$7] = 1; # hote destination
sources[cle][$8] = 1; # IP source
}
END {
for(user in hotes) {
n=0; for(h in hotes[user]) n++;
m=0; for(s in sources[user]) m++;
if(n > 3) {
printf "LATERAL: user=%s hotes_dst_uniques=%d ips_source=%d\n", user, n, m;
}
}
}' evenements_auth.csv

# Detecter pass-the-hash : Event 4624 LogonType=3 avec auth NTLM (4776) mais sans 4768 (TGT Kerberos)
# L'absence de TGT Kerberos pour une ouverture de session reseau reussie suggere un rejeu d'identifiants

Detection de l'exfiltration de donnees

# NetFlow : hotes internes avec upload significativement superieur au download (inhabituel)
nfdump \
-R /var/cache/nfdump/today/ \
'src ip in [10.0.0.0/8] and not dst ip in [10.0.0.0/8]' \
-A srcip,dstip \
-s flows/bytes \
-o "fmt:%sa %da %byt %pkt" \
-q \
| awk '$3 > 10485760' \ # paires ou > 10 Mo ont ete transferes
| sort -k3 -rn

# Rechercher une exfiltration par etapes : donnees se deplacant d'abord vers un hote de transit interne
nfdump \
-R /var/cache/nfdump/today/ \
'dst ip 192.168.5.99 and bytes > 1048576' \
-o long
# Puis verifier les connexions externes de l'hote de transit
nfdump \
-R /var/cache/nfdump/today/ \
'src ip 192.168.5.99 and not dst ip in [10.0.0.0/8]' \
-o long

9. Chaine de traçabilite et integrite forensique

Les preuves collectees lors d'une investigation forensique reseau peuvent etre utilisees dans des procedures judiciaires ou des actions disciplinaires internes. Si l'integrite ne peut pas etre demontree, les preuves peuvent etre contestees ou rejetees. Les exigences fondamentales :

Integrite : prouver que les preuves n'ont pas ete modifiees depuis la collecte. Utiliser le hachage cryptographique :

# Hacher un fichier pcap immediatement apres acquisition
sha256sum evidence.pcap > evidence.pcap.sha256
md5sum evidence.pcap >> evidence.pcap.sha256

# Verifier l'integrite plus tard
sha256sum -c evidence.pcap.sha256

# Pour les grandes archives de captures : hacher chaque fichier et un manifeste
find /evidence/ -name "*.pcap.gz" -exec sha256sum {} \; \
| tee /evidence/MANIFESTE.sha256 \
| gpg --clearsign > /evidence/MANIFESTE.sha256.asc # signer le manifeste avec GPG

Protection en ecriture : ne jamais analyser directement les preuves originales. Travailler sur des copies. Bloqueurs d'ecriture physiques pour les supports de stockage ; pour les captures reseau, le fichier pcap doit etre rendu immediatement en lecture seule :

# Rendre le fichier de capture immuable (ne peut pas etre modifie meme par root sans supprimer l'attribut)
chattr +i /evidence/capture_2024-11-15.pcap

# Verifier
lsattr /evidence/capture_2024-11-15.pcap
# ----i--------e-- /evidence/capture_2024-11-15.pcap

Documentation : maintenir un journal de chaine de traçabilite enregistrant qui a manipule les preuves, quand, quelles actions ont ete effectuees et les informations systeme :

# Enregistrer l'etat du systeme au moment de la capture
cat << 'FIN_GARDE' > /evidence/notes_collecte.txt
Date de collecte : [enregistree au moment de la collecte]
Collecte par : [nom de l'enqueteur]
Systeme : [nom d'hote et version du noyau]
Interface : eth0
Commande : tcpdump -i eth0 -s 0 -w /evidence/capture.pcap
Objectif : Reponse incident - C2 Cobalt Strike suspecte
Ticket : INC-2024-1115-001
FIN_GARDE
# Puis ajouter les valeurs calculees :
echo "Hachage (SHA256): $(sha256sum /evidence/capture.pcap | awk '{print $1}')" >> /evidence/notes_collecte.txt
echo "Decalage NTP: $(chronyc tracking | grep 'System time')" >> /evidence/notes_collecte.txt

Precision de l'heure : documenter l'etat de synchronisation NTP au moment de la capture. Si l'horloge du systeme de capture etait derivee, tous les horodatages necessitent un ajustement avant l'analyse :

# Verifier l'etat de synchronisation NTP
chronyc tracking # affiche l'heure de reference, le decalage et le decalage RMS
timedatectl status # systemd : affiche l'etat de synchronisation NTP et le decalage actuel

10. Correspondance MITRE ATT&CK

TechniqueID ATT&CKSource de journaux de detectionObservable cle
Hameconnage : piece jointe cibleeT1566.001Journaux SMTP, passerelle mailPiece jointe executable/macro depuis expediteur externe
Interpreteur de commandes et scriptsT1059Journaux proxyProcessus Office effectuant des requetes HTTP (execution de macro)
C2 via HTTPST1071.001Zeek ssl.log, NetFlowBalise periodique, cert auto-signe, JA3 inhabituel
Tunneling DNST1071.004Journaux DNS, Zeek dns.logTaux NXDOMAIN eleve, grandes reponses TXT, etiquettes a entropie elevee
Mouvement lateral : partages SMB/Admin WindowsT1021.002Journaux auth (4624 Type 3), Zeek conn.logOuvertures de session reseau vers port 445 sur plusieurs hotes
Mouvement lateral : Pass the HashT1550.002Journaux auth (4624 + 4776, sans 4768)Auth NTLM sans TGT Kerberos precedent
Extraction d'identifiants : LSASST1003.001EDR/Sysmon + Auth (utilisation subsequente)Suivi d'un mouvement lateral rapide
Exfiltration via canal C2T1041NetFlow, Zeek conn.logGrands octets upload vers IP C2, sans download correspondant
Exfiltration vers stockage cloudT1567Journaux proxyGrand POST vers API stockage cloud (dropbox.com, drive.google.com)
Donnees stockees localementT1074.001Zeek files.log, NetFlowHote interne accumulant de grands transferts de fichiers depuis plusieurs sources
Suppression d'indicateurs : journaux WindowsT1070.001Journaux auth (Evenement 1102 : journal d'audit efface)Evenement 1102 ou 104 genere quand le journal est efface