Skip to main content

Chapitre 2.1 Quiz - Analyse de paquets et dissection de protocoles

Mode Quiz - Toutes les reponses sont masquees. Tentez chaque question avant de reveler la reponse.


Question 1

Vous analysez une capture de paquets et observez les combinaisons de drapeaux TCP suivantes provenant de l'adresse IP source 10.0.0.88 vers plusieurs destinations. Identifiez le type de scan pour chacun, expliquez ce que l'attaquant cherche a determiner, et ecrivez un filtre tcpdump pour capturer chacun :

Paquet A: Drapeaux [F] (FIN uniquement)
Paquet B: Drapeaux [FPU] (FIN + PSH + URG)
Paquet C: Drapeaux [] (Aucun drapeau)
Paquet D: Drapeaux [S] vers 65535 ports de destination uniques en 30 secondes
Reveler la reponse et l'explication

Reponse : Scan FIN, scan Xmas, scan NULL, scan SYN vertical.

Paquet A - Scan FIN (Nmap -sF)

Un paquet FIN est envoye sans connexion prealable. Selon la RFC 793 :

  • Port ouvert : abandonne silencieusement le FIN inattendu (pas de reponse)
  • Port ferme : repond avec RST

Cela permet l'enumeration de l'etat des ports. Les scans FIN contournent les filtres de paquets sans etat qui bloquent uniquement les paquets SYN, car il n'y a pas de SYN a bloquer.

# Filtre tcpdump :
tcpdump -i eth0 'tcp[tcpflags] == tcp-fin'
# Wireshark : tcp.flags == 0x001

Paquet B - Scan Xmas (Nmap -sX)

Definit FIN, PSH et URG simultanement - ces drapeaux ne coexistent pas dans le trafic normal. Nomme "Xmas" car les drapeaux s'allument comme un arbre de Noel. Meme comportement ouvert/ferme que le scan FIN (RFC 793).

# Filtre tcpdump :
tcpdump -i eth0 'tcp[tcpflags] & (tcp-fin|tcp-push|tcp-urg) == (tcp-fin|tcp-push|tcp-urg)'
# Wireshark : tcp.flags == 0x029

Paquet C - Scan NULL (Nmap -sN)

Aucun drapeau TCP defini - completement invalide selon la RFC 793. Meme comportement de reponse RFC 793 ouvert/ferme.

# Filtre tcpdump :
tcpdump -i eth0 'tcp[tcpflags] == 0'
# Wireshark : tcp.flags == 0x000

Mise en garde importante pour A, B, C : Ces scans ne fonctionnent de maniere fiable que contre les piles TCP conformes a la RFC 793 (Linux). Windows ignore la regle RFC et repond toujours avec RST quel que soit l'etat du port - rendant ces scans peu fiables pour les cibles Windows.

Paquet D - Scan SYN vertical / Scan de ports

Des paquets SYN vers 65535 ports differents en 30 secondes est un scan de ports classique. L'attaquant cartographie tous les services ouverts sur un hote cible.

# Filtre tcpdump :
tcpdump -i eth0 'src host 10.0.0.88 and tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'
# Wireshark : ip.src==10.0.0.88 && tcp.flags.syn==1 && tcp.flags.ack==0

Detection et reponse :

# Alerter sur tout hote envoyant plus de 100 SYNs en 60 secondes (regle Suricata) :
# alert tcp any any -> $HOME_NET any (msg:"Scan de ports detecte";
# flags:S,12; threshold:type both, track by_src, count 100, seconds 60;
# sid:1000001;)

# Bloquer la source de scan avec iptables (temporaire, peut aussi bloquer des scanners legitimes)
iptables -A INPUT -s 10.0.0.88 -p tcp --tcp-flags ALL FIN -j DROP
iptables -A INPUT -s 10.0.0.88 -p tcp --tcp-flags ALL FIN,PSH,URG -j DROP
iptables -A INPUT -s 10.0.0.88 -p tcp --tcp-flags ALL NONE -j DROP

Question 2

Analysez cet extrait de sortie tshark et identifiez toutes les anomalies de securite :

Temps      IP source     IP dest       Proto  Info
0.000 10.0.1.55 8.8.8.8 DNS Requete standard A aGVsbG8ud29ybGQ.updates.corp-patch.net
30.012 10.0.1.55 8.8.8.8 DNS Requete standard A dGhpcyBpcyBhIHRlc3Q.updates.corp-patch.net
60.001 10.0.1.55 8.8.8.8 DNS Requete standard A c2VjcmV0IGRhdGE.updates.corp-patch.net
90.003 10.0.1.55 8.8.8.8 DNS Requete standard A ZXhmaWx0cmF0aW5n.updates.corp-patch.net
0.000 10.0.1.77 10.0.0.1 TCP SYN vers port 22
0.001 10.0.1.77 10.0.0.1 TCP SYN vers port 23
0.002 10.0.1.77 10.0.0.1 TCP SYN vers port 25
0.003 10.0.1.77 10.0.0.1 TCP SYN vers port 80
0.300 10.0.1.77 10.0.0.2 TCP SYN vers port 22
0.301 10.0.1.77 10.0.0.2 TCP SYN vers port 23
200.000 10.0.0.5 203.45.67.89 TCP [SYN] Seq=0 Win=1024
200.030 203.45.67.89 10.0.0.5 TCP [SYN, ACK]
200.031 10.0.0.5 203.45.67.89 TCP [ACK]
200.032 10.0.0.5 203.45.67.89 TCP [PSH,ACK] len=1 (payload: "\x00")
230.035 10.0.0.5 203.45.67.89 TCP [PSH,ACK] len=1 (payload: "\x00")
260.038 10.0.0.5 203.45.67.89 TCP [PSH,ACK] len=1 (payload: "\x00")

Identifiez toutes les anomalies de securite presentes dans ce trafic.

Reveler la reponse et l'explication

Reponse : Trois attaques distinctes - tunneling DNS/exfiltration depuis .55, scan reseau interne depuis .77, et balise C2 depuis .5.

Anomalie 1 - Tunneling DNS / Exfiltration de donnees (10.0.1.55)

# Decoder les labels de sous-domaines :
echo "aGVsbG8ud29ybGQ" | base64 -d # -> "hello.world"
echo "dGhpcyBpcyBhIHRlc3Q" | base64 -d # -> "this is a test"
echo "c2VjcmV0IGRhdGE" | base64 -d # -> "secret data"
echo "ZXhmaWx0cmF0aW5n" | base64 -d # -> "exfiltrating"
  • Les sous-domaines sont des donnees encodees en Base64 - tunneling DNS confirme
  • Intervalles de 30 secondes - timing de balise C2 / exfiltration classique
  • Domaine de destination corp-patch.net - typosquatting du domaine legitime de l'entreprise
  • Donnees decodees : "hello.world", "this is a test", "secret data", "exfiltrating" - l'attaquant teste le canal puis exfiltre des donnees

Anomalie 2 - Scan reseau interne / Mouvement lateral (10.0.1.77)

  • Paquets SYN vers les ports 22, 23, 25, 80 sur 10.0.0.1 en 3 ms - scan de ports automatise
  • Se repete immediatement contre 10.0.0.2 - scan horizontal sur plusieurs hotes
  • Scan des ports 22 (SSH), 23 (Telnet), 25 (SMTP), 80 (HTTP) - enumeration de services courants
  • Le timing en millisecondes est une vitesse machine - pas manuel, outil automatise (Nmap, Masscan)

Anomalie 3 - Battement de coeur de balise C2 (10.0.0.5 -> 203.45.67.89)

  • Taille de fenetre TCP 1024 - caracteristique de Nmap et de certains malwares (non standard, la plupart des OS utilisent 64240+)
  • Apres la poignee de main : envoie exactement 1 octet (payload \x00)
  • Intervalle entre les envois de 1 octet : exactement 30 secondes (200.032 -> 230.035 -> 260.038)
  • Battement de coeur de 1 octet a 30s d'intervalle = maintien de connexion de balise C2 classique
  • 203.45.67.89 est externe - malware maintenant une connexion avec le serveur C2 de l'attaquant

Actions de reponse :

# 1. Isoler 10.0.1.55 -- exfiltration de donnees confirmee
iptables -A FORWARD -s 10.0.1.55 -j DROP

# 2. Bloquer le domaine de tunnel DNS au niveau du resolveur
# Ajouter a /etc/bind/named.conf : zone "corp-patch.net" { type redirect; ... }

# 3. Bloquer l'IP C2 au niveau du pare-feu
iptables -A OUTPUT -d 203.45.67.89 -j DROP

# 4. Identifier l'hote a 10.0.0.5 et initier le processus de reponse aux incidents
# Verifier les journaux DHCP pour l'adresse MAC
grep "10.0.0.5" /var/log/dhcp.log | tail -5

# 5. Rechercher les mouvements lateraux depuis 10.0.1.77
# Verifier les journaux d'authentification sur 10.0.0.1 et 10.0.0.2 pour les connexions subsequentes
grep "10.0.1.77" /var/log/auth.log

Question 3

Ecrivez un script Python utilisant Scapy qui detecte les attaques d'usurpation ARP en temps reel en surveillant le trafic ARP. Le script doit alerter lorsqu'un changement d'adresse MAC est detecte pour une adresse IP existante et l'enregistrer avec un horodatage.

Reveler la reponse et l'explication

Reponse : Voici un detecteur d'usurpation ARP de qualite production.

#!/usr/bin/env python3
"""
Detecteur d'usurpation ARP
Surveille le trafic ARP en temps reel et alerte sur les changements d'adresse MAC
pour les correspondances IP->MAC existantes.

Utilisation : sudo python3 arp_detector.py -i eth0
"""

from scapy.all import ARP, Ether, sniff, get_if_hwaddr
from datetime import datetime
import argparse
import json
import sys

# Table ARP : mappe IP -> MAC (de confiance, apprise depuis le premier ARP vu)
arp_table = {}
alerts = []

# Hotes connus/critiques -- alerter immediatement (pas seulement sur changement)
CRITICAL_IPS = {
"10.0.0.1": "Passerelle",
"10.0.0.254": "Passerelle secondaire",
}


def get_mac_vendor(mac):
"""Retourner le fabricant depuis les 3 premiers octets du MAC (recherche OUI)"""
# En production : interroger l'API macvendors ou utiliser une base de donnees OUI locale
known_oui = {
"00:50:56": "VMware",
"08:00:27": "VirtualBox",
"52:54:00": "QEMU/KVM",
"00:0c:29": "VMware",
"aa:bb:cc": "Inconnu (suspect)",
}
oui = mac[:8].lower()
return known_oui.get(oui, "Inconnu")


def process_arp(pkt):
"""Traiter chaque paquet ARP et detecter l'usurpation"""
if ARP not in pkt:
return

arp = pkt[ARP]

# Traiter uniquement les reponses ARP (op=2) et les ARP gratuits
# Gratuit : IP emetteur == IP cible, utilise pour annoncer les changements MAC
if arp.op not in [1, 2]: # 1=who-has (requete), 2=is-at (reponse)
return

src_ip = arp.psrc # IP de l'emetteur
src_mac = arp.hwsrc # MAC de l'emetteur

# Ignorer les MACs de diffusion et les IPs vides
if src_mac in ("ff:ff:ff:ff:ff:ff", "00:00:00:00:00:00"):
return
if src_ip in ("0.0.0.0", "255.255.255.255"):
return

timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]

# Premiere fois que l'on voit cette IP -- l'apprendre
if src_ip not in arp_table:
arp_table[src_ip] = src_mac
vendor = get_mac_vendor(src_mac)
print(f"[{timestamp}] INFO Appris {src_ip:18s} -> {src_mac} ({vendor})")

# Si c'est une IP critique, le journaliser en evidence
if src_ip in CRITICAL_IPS:
print(f"[{timestamp}] NOTE Hote critique {CRITICAL_IPS[src_ip]} a {src_mac}")
return

# IP deja connue -- verifier si le MAC a change
known_mac = arp_table[src_ip]

if src_mac != known_mac:
severity = "CRITIQUE" if src_ip in CRITICAL_IPS else "AVERTISSEMENT"
role = CRITICAL_IPS.get(src_ip, "")

alert = {
"timestamp": timestamp,
"severity": severity,
"src_ip": src_ip,
"role": role,
"old_mac": known_mac,
"new_mac": src_mac,
"old_vendor": get_mac_vendor(known_mac),
"new_vendor": get_mac_vendor(src_mac),
}
alerts.append(alert)

# Afficher l'alerte
print(f"\n{'!'*60}")
print(f"[{timestamp}] {severity}: USURPATION ARP DETECTEE")
print(f" IP: {src_ip} {('(' + role + ')') if role else ''}")
print(f" ANCIEN MAC: {known_mac} ({get_mac_vendor(known_mac)})")
print(f" NOUVEAU MAC: {src_mac} ({get_mac_vendor(src_mac)})")
print(f" Op ARP: {'Requete' if arp.op==1 else 'Reponse (ARP gratuit)'}")
print(f"{'!'*60}\n")

# Mettre a jour la table pour suivre l'etat actuel
arp_table[src_ip] = src_mac

# Ecrire l'alerte dans le fichier journal
with open("arp_alerts.jsonl", "a") as f:
f.write(json.dumps(alert) + "\n")


def main():
parser = argparse.ArgumentParser(description="Detecteur d'usurpation ARP")
parser.add_argument("-i", "--interface", default="eth0",
help="Interface reseau a surveiller")
parser.add_argument("-c", "--count", type=int, default=0,
help="Nombre de paquets (0 = infini)")
args = parser.parse_args()

print(f"Detecteur d'usurpation ARP - surveillance de {args.interface}")
print(f"IPs critiques : {CRITICAL_IPS}")
print(f"Alertes journalisees dans : arp_alerts.jsonl\n")

try:
sniff(
iface=args.interface,
filter="arp", # Filtre BPF -- uniquement ARP
prn=process_arp, # Rappel pour chaque paquet
store=0, # Ne pas stocker les paquets en memoire
count=args.count
)
except KeyboardInterrupt:
print(f"\n\nArrete. {len(alerts)} alertes generees.")
print(f"Table ARP finale ({len(arp_table)} entrees) :")
for ip, mac in sorted(arp_table.items()):
print(f" {ip:18s} -> {mac}")


if __name__ == "__main__":
if sys.platform != "win32":
import os
if os.geteuid() != 0:
print("Erreur : doit etre execute en tant que root (sudo)")
sys.exit(1)
main()

Fonctionnement :

  • Renifle uniquement les paquets ARP en utilisant BPF (efficace, sans traitement d'autre trafic)
  • Construit une table IP->MAC de confiance depuis le premier ARP observe pour chaque IP
  • Tout ARP subsequent revendiquant un MAC different pour une IP connue declenche une alerte
  • Les ARP gratuits (op=1 ou l'IP emetteur == IP cible) sont egalement traites - les attaquants les utilisent pour empoisonner tout le sous-reseau a la fois
  • Les IPs critiques (passerelles, serveurs) generent des alertes de gravite superieure

Execution :

sudo python3 arp_detector.py -i eth0
# Exemple de sortie lors d'une detection :
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# [2024-01-15 14:32:07.442] CRITIQUE: USURPATION ARP DETECTEE
# IP: 10.0.0.1 (Passerelle)
# ANCIEN MAC: aa:bb:cc:dd:ee:ff (Cisco)
# NOUVEAU MAC: 11:22:33:44:55:66 (Inconnu (suspect))
# Op ARP: Reponse (ARP gratuit)
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Question 4

Une capture pcap prise lors d'un incident montre des connexions HTTPS sortantes regulieres depuis un hote interne (10.0.0.99) vers 185.220.101.45:443. Vous ne pouvez pas dechiffrer le trafic. En utilisant uniquement les metadonnees (timing, tailles de paquets, empreintes TLS), decrivez une methodologie pour determiner avec un haut degre de confiance s'il s'agit d'une balise C2, et quels outils/commandes vous utiliseriez a chaque etape.

Reveler la reponse et l'explication

Reponse : Analyse de metadonnees en cinq etapes - regularite du timing, distribution des tailles de paquets, empreinte JA3, reputation IP, et analyse du SNI TLS.

Etape 1 - Analyse de l'intervalle de balise

# Extraire les horodatages de connexion pour le flux specifique
tshark -r capture.pcap \
-Y "ip.src==10.0.0.99 && ip.dst==185.220.101.45 && tcp.flags.syn==1 && tcp.flags.ack==0" \
-T fields \
-e frame.time_epoch | \
python3 -c "
import sys, statistics
times = [float(t) for t in sys.stdin if t.strip()]
times.sort()
intervals = [times[i+1]-times[i] for i in range(len(times)-1)]
if intervals:
avg = statistics.mean(intervals)
std = statistics.stdev(intervals) if len(intervals)>1 else 0
print(f'Connexions : {len(times)}')
print(f'Intervalle moyen : {avg:.1f}s ({avg/60:.1f}m)')
print(f'Ecart-type : {std:.1f}s')
print(f'Ratio de gigue : {std/avg:.3f}')
print(f'Verdict balise : {\"BALISE PROBABLE\" if std/avg < 0.15 else \"IRREGULIER\"}')
"
# Navigation humaine : irreguliere, gigue elevee (ratio de gigue > 0.5)
# Balise C2 : faible gigue (< 0.1), intervalle regulier toutes les N secondes/minutes

Etape 2 - Distribution des tailles de paquets

# Les balises C2 envoient souvent de petits paquets (maintien de connexion) vs grandes reponses
tshark -r capture.pcap \
-Y "ip.addr==10.0.0.99 && ip.addr==185.220.101.45" \
-T fields \
-e frame.len \
-e ip.src | \
python3 -c "
import sys
from collections import Counter
sortant = []
entrant = []
for line in sys.stdin:
parts = line.strip().split()
if len(parts) == 2:
size, src = int(parts[0]), parts[1]
if src == '10.0.0.99':
sortant.append(size)
else:
entrant.append(size)
print(f'Sortant : moy={sum(sortant)/len(sortant):.0f}o, n={len(sortant)}')
print(f'Entrant : moy={sum(entrant)/len(entrant):.0f}o, n={len(entrant)}')
# C2 typique : petit sortant (100-400o d'enregistrement) + grand entrant occasionnel (livraison de taches)
"

Etape 3 - Analyse de l'empreinte JA3

# Extraire le hachage JA3 depuis le ClientHello TLS
tshark -r capture.pcap \
-Y "tls.handshake.type==1 && ip.src==10.0.0.99" \
-T fields \
-e tls.handshake.ja3 # Necessite tshark >= 3.0

# Ou utiliser Zeek (anciennement Bro) qui calcule JA3 nativement
zeek -r capture.pcap
cat ssl.log | grep "185.220.101.45" | cut -f15 # champ ja3

# Comparer JA3 avec les hachages de malwares connus :
KNOWN_C2_JA3=(
"72a589da586844d7f0818ce684948eea" # Cobalt Strike par defaut
"d4e457bda4060ac3a1e0ce55df394aa6" # Metasploit
"a0e9f5d64349fb13191bc781f81f42e1" # Sliver
"51c64c77e60f3980eea90869b68c58a8" # Malware courant
)
OBSERVED_JA3="<hash depuis tshark>"
for h in "${KNOWN_C2_JA3[@]}"; do
if [ "$OBSERVED_JA3" == "$h" ]; then
echo "CORRESPONDANCE : Hachage JA3 C2 connu $h"
fi
done

# Calculer egalement JARM (empreinte du serveur) pour le serveur C2
python3 jarm.py 185.220.101.45 443
# Comparer avec le renseignement sur les menaces : hachages JARM Cobalt Strike connus

Etape 4 - Reputation IP/Domaine

# Verifier la reputation IP (plusieurs sources)
# VirusTotal
curl "https://www.virustotal.com/api/v3/ip_addresses/185.220.101.45" \
-H "x-apikey: VOTRE_CLE" | python3 -m json.tool | grep -E "malicious|suspicious|asn"

# AbuseIPDB
curl "https://api.abuseipdb.com/api/v2/check" \
-d "ipAddress=185.220.101.45&maxAgeInDays=90" \
-H "Key: VOTRE_CLE" | python3 -m json.tool

# Shodan : qu'est-ce qui tourne sur cette IP ?
shodan host 185.220.101.45
# Noeud de sortie Tor ? Fournisseur d'hebergement pare-balles connu ?
# Certificat inhabituel ? Auto-signe ? Validite courte ?

# Verifier l'ASN
whois 185.220.101.45 | grep -E "OrgName|ASName|country"
# ASNs d'hebergement pare-balles connus : AS62282, AS206728, AS209588

Etape 5 - Analyse du SNI TLS et du certificat

# Extraire le SNI depuis le ClientHello TLS (quel nom d'hote a ete demande)
tshark -r capture.pcap \
-Y "tls.handshake.type==1 && ip.src==10.0.0.99" \
-T fields \
-e tls.handshake.extensions_server_name

# Si SNI absent -> suspect (HTTPS legitime envoie toujours SNI)
# Si SNI ne correspond pas a l'enregistrement PTR de l'IP -> suspect
# Si SNI est une adresse IP au lieu d'un nom d'hote -> signal d'alarme

# Extraire les details du certificat serveur (meme sans dechiffrer le contenu)
tshark -r capture.pcap \
-Y "tls.handshake.type==11 && ip.src==185.220.101.45" \
-T fields \
-e tls.handshake.certificate

# Verifier le certificat :
# - Auto-signe ? (Emetteur == Sujet)
# - Le CN correspond au domaine/IP ?
# - Emis recemment ? (Les certificats C2 sont souvent rotates rapidement)
# - Periode de validite (inhabituellement longue/courte ?)
# - Emis par une AC gratuite comme Let's Encrypt ? (courant pour les malwares)

Methodologie de verdict :

SignalBeninIndicateur malveillant
Ratio de gigue d'intervalle> 0.5 (irregulier)< 0.1 (tres regulier)
Taille de paquet sortantVariable, grandePetite, uniforme (enregistrement)
Hachage JA3Hachage de navigateur courantCorrespond a un malware connu
Hachage JARMHachage serveur courantCorrespond a un C2 connu (CS, Meterpreter)
Reputation IPPropreScore d'abus eleve
ASNGrand cloud/CDNHebergement pare-balles
SNI presentOuiAbsent ou base sur IP
CertificatAC de confiance, organisation valideAuto-signe, emis recemment

Si 4+ signaux correspondent aux indicateurs malveillants -> balise C2 avec un haut degre de confiance.