
Les canaux microarchitecturaux sont un aspect crucial, mais souvent mal compris, de la cybersécurité moderne. Contrairement aux canaux latéraux traditionnels en réseau, les canaux microarchitecturaux exploitent les comportements matériels de bas niveau des processeurs modernes pour divulguer des informations sensibles. Cet article de blog examine en profondeur ce que sont ces canaux, comment ils fonctionnent, pourquoi ils sont dangereux et quelles mesures pratiques peuvent être prises pour les détecter et les prévenir, avec des exemples concrets et des exemples de code.
Table des Matières
- Introduction aux Canaux Microarchitecturaux
- Canaux Latéraux vs. Canaux Couverts : Définitions et Différences
- Comment Fonctionnent les Canaux Couverts Microarchitecturaux
- Exploits et Études de Cas du Monde Réel
- Techniques de Détection des Canaux Couverts Microarchitecturaux
- Stratégies de Prévention et d'Atténuation
- Pratique: Scanner et Détecter les Attaques Microarchitecturales
- Exemples Python & Bash: Analyse des Événements Matériels et du Timing
- Conclusion
- Références
Les processeurs modernes sont des merveilles d'ingénierie, optimisées pour la vitesse, l'efficacité et le parallélisme. Cependant, sous l'abstraction des logiciels se cache un système complexe où les processus partagent des ressources matérielles telles que les caches, les contrôleurs de mémoire et les unités d'exécution. Les canaux microarchitecturaux sont des voies de communication qui exploitent ces ressources partagées, qui, si elles ne sont pas atténuées, peuvent être utilisées pour divulguer des secrets à travers les limites des processus, violant ainsi les hypothèses fondamentales de sécurité.
Un canal latéral est une voie de fuite d'information qui n'exploite pas directement des vulnérabilités logicielles, mais observe des différences dans le comportement du système (comme l'utilisation de puissance, le timing, les hits/misses de cache) pour inférer des secrets. Par exemple, un attaquant peut déduire des clés cryptographiques en observant le temps pris pour certaines opérations.
Un canal couvert est un chemin de communication utilisé pour transférer des informations d'une manière qui viole la politique de sécurité du système. Contrairement aux canaux latéraux, les canaux couverts sont généralement utilisés délibérément pour échanger discrètement des informations entre deux parties collusoires.
Principale Différence :
Les canaux microarchitecturaux fonctionnent profondément au sein du matériel, exploitant la manière dont les ressources sont partagées entre les processus, alors que les canaux réseau utilisent les supports de transmission de données.
Voyons comment les attaquants construisent des canaux couverts (et latéraux) microarchitecturaux en utilisant les ressources CPU partagées.
// Étapes simplifiées pour Prime+Probe
1. Prime : Accéder aux lignes de cache pour remplir un ensemble de cache spécifique
2. Laisser la victime s'exécuter
3. Probe : Accéder aux mêmes lignes de cache et chronométrer les accès
4. Les manques de cache impliquent une éviction, probablement due à l'accès de la victime
Celles-ci exploitent l'exécution spéculative, où les processeurs devinent les futures instructions pour les performances. Les mauvaises prédictions sont annulées, mais les effets secondaires dans la microarchitecture (comme l'état du cache) restent, divulguant des secrets.
En utilisant l'historique dans les tables de prédiction de branches, les attaquants peuvent déduire des décisions de flux de contrôle dans le code de la victime.
Si deux processus partagent des unités d'exécution physiques, la contestation cause des changements de timing mesurables.
Supposons que le Processus A (attaquant) et le Processus B (victime) s'exécutent sur la même machine, partageant des ressources de cache.
Les canaux couverts microarchitecturaux ne sont pas seulement théoriques — ils ont permis certaines des attaques pratiques les plus importantes sur l'infrastructure informatique moderne.
Dans les environnements cloud, les attaquants co-résidant sur le même matériel qu'une cible peuvent utiliser des attaques de cache pour divulguer des informations depuis des VMs voisines.
Le code JavaScript peut mesurer les intervalles de temps et utiliser l'état du cache partagé, exfiltrant des bits de données sensibles du contexte du navigateur de l'utilisateur.
La détection de ces canaux est difficile, mais plusieurs approches pratiques existent.
Les processeurs modernes fournissent des compteurs pour des événements tels que les manques de cache, les prédictions de branches manquées, les blocages d'exécution. Des pics ou des modèles inhabituels peuvent suggérer une attaque.
Exemples d'événements pertinents :
cache-referencescache-missesbranch-instructionsbranch-missesLes systèmes d'exploitation avancés (et hyperviseurs) peuvent surveiller, enregistrer et analyser les anomalies de timing des processus pour identifier des comportements suspects.
Des recherches récentes exploitent le machine learning pour différencier les utilisations bénignes et malveillantes des ressources matérielles, modelant les modèles normaux et signalant les écarts.
La prévention des canaux couverts microarchitecturaux implique souvent des approches matérielles et logicielles.
Réduire la granularité du minuteur disponible ou ajouter du bruit/jitter réduit l'efficacité des attaques, en particulier pour les attaques basées sur JavaScript.
Écrire du code dont le temps d'exécution ne dépend pas des données secrètes déjoue de nombreux canaux de timing.
Certains processeurs incluent maintenant des conceptions "résistantes aux canaux cachés", avec des caches partitionnés ou des atténuations d'exécution spéculative.
Voyons comment les praticiens peuvent scanner les anomalies microarchitecturales en utilisant des outils open-source et du code.
perf pour Collecter des Données de Performanceperf list | grep cache
<pid> par le PID réel)sudo perf stat -e cache-references,cache-misses -p <pid>
104,212 cache-references
12,342 cache-misses
sudo perf record -e cache-misses -p <pid> -- sleep 10
sudo perf report
sudo perf stat -a --per-socket -e cache-misses sleep 5 | grep "cache-misses"
Ou par processus:
ps -eo pid,comm | while read pid comm; do
sudo perf stat -p $pid -e cache-misses -I 1000 -- sleep 1 2>&1 | grep cache-misses
done
Supposons que vous souhaitez automatiser le scan de plusieurs processus ou analyser les résultats de perf pour la détection d'anomalies.
perf stat pour des Manques de Cache Suspectsimport subprocess
import re
def get_cache_misses(pid):
cmd = ["perf", "stat", "-p", str(pid), "-e", "cache-misses", "--", "sleep", "2"]
result = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
match = re.search(r"([\d,]+)\s+cache-misses", result.stderr)
if match:
count = int(match.group(1).replace(",", ""))
return count
else:
return None
# Scanner tous les processus
import psutil
for proc in psutil.process_iter(attrs=['pid', 'name']):
pid = proc.info['pid']
name = proc.info['name']
try:
misses = get_cache_misses(pid)
if misses and misses > 100000:
print(f"Suspect : PID {pid} ({name}) a {misses} manques de cache")
except Exception:
continue
#!/bin/bash
THRESHOLD=100000
for pid in $(ps -e -o pid=); do
MISS=$(sudo perf stat -p $pid -e cache-misses -- sleep 1 2>&1 | grep cache-misses | awk '{print $1}' | tr -d ',')
if [ ! -z "$MISS" ] && [ "$MISS" -gt "$THRESHOLD" ]; then
echo "Avertissement: PID $pid manques de cache élevés ($MISS)"
fi
done
Pour des raisons éthiques et légales, expérimentez uniquement dans un environnement sécurisé, isolé, et avec permission.
// AVERTISSEMENT: À titre de démonstration uniquement. Ne pas utiliser sur des systèmes de production.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <x86intrin.h>
#include <unistd.h>
#define CACHE_LINE_SIZE 64
#define PROBE_COUNT 100000
volatile char *array;
void prime_cache() {
for (int i = 0; i < 4096; i += CACHE_LINE_SIZE)
_mm_clflush(&array[i]);
}
int probe_cache() {
int sum = 0;
uint64_t start, end;
for (int i = 0; i < 4096; i += CACHE_LINE_SIZE) {
start = __rdtscp(&i);
volatile char x = array[i];
end = __rdtscp(&i);
sum += (end - start);
}
return sum;
}
int main() {
array = malloc(4096);
for (int i = 0; i < PROBE_COUNT; i++) {
prime_cache();
usleep(1); // Laisser la "victime" s'exécuter
int timing = probe_cache();
printf("%d\n", timing);
}
free((void *)array);
return 0;
}
Explication :
Ce code mesure les temps d'accès au cache avant et après qu'une "victime" puisse s'exécuter, en inférant si le cache a été utilisé par un autre processus — l'idée de base derrière de nombreuses attaques microarchitecturales.
Les canaux microarchitecturaux représentent une menace unique et de plus en plus critique pour la sécurité de l'information à l'ère du multi-core et du cloud computing. Contrairement aux canaux latéraux traditionnels basés sur le réseau, ces attaques exploitent le partage complexe des ressources matérielles physiques. Le domaine évolue rapidement, et chaque praticien doit comprendre les bases et les implications avancées alike.
Points à Retenir :
perf), le durcissement du OS, et les pratiques de codage pour détecter et se défendre.Pour les environnements critiques à la sécurité — clouds, navigateurs, applications cryptographiques — prêter attention à la sécurité microarchitecturale n'est plus optionnel.
Cet article est destiné à des fins éducatives. Suivez toujours les directives éthiques et les réglementations locales lors de recherches en sécurité.
Si vous avez trouvé ce contenu utile, imaginez ce que vous pourriez accomplir avec notre programme de formation élite complet de 47 semaines. Rejoignez plus de 1 200 étudiants qui ont transformé leur carrière grâce aux techniques de l'Unité 8200.