
Une vulnérabilité liée à Linux
Ce que contient ce dépôt : ma reproduction pratique et mon étude de CVE-2023-32233, un use-after-free dans le sous-système Netfilter nf_tables du noyau Linux qui permet une élévation de privilèges locale, divulgué publiquement en mai 2023 par Patryk Sondej et Piotr Krysiuk.
Crédit : le PoC (exploit.c) et l'analyse originale sont de
@Liuk3r. Mon travail dans ce dépôt
consiste à reproduire l'exploit dans mon propre laboratoire (Ubuntu 23.04,
noyau 6.2.0-20) et à documenter la cause racine ainsi que la chaîne
d'exploitation ci-dessous. Tous les tests ont été effectués sur des machines
de laboratoire dédiées.
nf_tables traite les mises à jour de configuration comme un lot atomique. La validation de chaque opération par rapport aux changements d'état des opérations précédentes du même lot est insuffisante. Concrètement :
nft_rule contenant une expression lookup sur un
nft_set anonyme qui contient certains éléments.NFT_MSG_DELRULE — supprime la règle, ce qui supprime implicitement
l'expression lookup et le nft_set anonyme ;NFT_MSG_DELSETELEM — supprime un élément du nft_set anonyme déjà
supprimé.nf_tables_commit_release() met les ressources en
file d'attente sur nf_tables_destroy_list, traitées plus tard par
nf_tables_trans_destroy_work() :
d'abord nft_commit_release() → nf_tables_rule_destroy() →
nft_lookup_destroy() → nft_set_destroy() → kvfree() libère le
nft_set ;
puis, pour NFT_MSG_DELSETELEM, nf_tables_set_elem_destroy() appelle
nft_set_elem_ext() qui déréférence le nft_set :
Si set->ops->elemsize est corrompu, un emplacement mémoire choisi par
l'attaquant est interprété comme un nft_set_ext — la primitive sur laquelle
tout le reste s'appuie.
nf_tables_trans_destroy_work() qui s'exécute
sur un thread de travail en arrière-plan : insérer une grande opération de
destruction d'ensemble comme délai contrôlé, maintenir les autres CPU
occupés, puis réallouer le bloc nft_set libéré depuis le même CPU avec un
nft_set d'un type différent (elemsize différent) → confusion de type.nft_set_ext corrompus avec des décalages hors
limites afin que nf_tables_set_elem_destroy() parcoure les blocs adjacents
comme une liste d'nft_expr à détruire.nft_log avec un NFTA_LOG_PREFIX contrôlé ;
nft_log_destroy() libère priv->prefix, donnant une primitive
d'allocation chevauchante dans kmalloc-{8..192} (limitée par octet NULL au
début).Notablement, les primitives choisies évitent tout ce que CFI bloquerait — aucun détournement d'appel indirect n'est nécessaire à aucune étape.
SLAB_FREELIST_HARDENED), INIT_ON_FREE, et restrictions de
réutilisation des slabs par CPU attaquent toutes l'étape 1 ; CFI (là où il
existe) contraint l'étape 3 et suivantes.nf_tables contenant DELRULE+DELSETELEM sur des ensembles anonymes ;
triage des crashs pour nft_set_elem_ext / nf_tables_trans_destroy_work
dans la pile.nftables exige CAP_NET_ADMIN,
mais les espaces de noms utilisateur non privilégiés l'accordent — c'est
pourquoi kernel.unprivileged_userns_clone=0 est un bouton de durcissement
significatif.Testé sous Ubuntu 23.04 (Lunar Lobster), noyau 6.2.0-20-generic.
sudo apt install gcc libmnl-dev libnftnl-dev
gcc -Wall -o exploit exploit.c -lmnl -lnftnl
Le profil intégré cible les noyaux binaires Ubuntu 23.04
(linux-image-6.2.0-20-generic 6.2.0-20.20). Pour tester d'autres noyaux,
extrayez les symboles dans un fichier profile :
modprobe nf_tables
egrep ' (nft_counter_ops|nft_counter_destroy|free_percpu|modprobe_path)(\s|$)' /proc/kallsyms > profile
La disposition du code machine de nft_counter_destroy() varie selon le
compilateur et les options ; voir ORIGIN.md pour la référence
complète des paramètres (nft_counter_destroy_call_offset/mask/check) et les
réglages de la course (race_lead_sleep etc.). La probabilité de succès
annoncée est ≥80 % sur des systèmes Intel bare-metal au repos ; certaines
microarchitectures (par ex. Alder Lake) nécessitent un réglage supplémentaire.
Le PoC laisse le noyau dans un état instable avec de la mémoire corrompue après une exécution réussie. Testez uniquement sur un système dédié, jetable ou un instantané de VM — jamais sur quoi que ce soit contenant des données auxquelles vous tenez.
exploit.c — source du PoC (par @Liuk3r)ORIGIN.md — analyse originale de la vulnérabilité et de l'exploitation (par Liuk3r)README.md — ce fichier : reproduction + mes notes d'analyseRecherche menée à des fins défensives/éducatives dans un environnement de laboratoire isolé.
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}
nft_object->udatanft_dynset pour deux types d'expressions avec
état :
nft_counter — fuite de nft_counter_ops → base de nf_tables.ko
(contre KASLR pour le module) ;nft_quota — le pointeur consumed donne une lecture arbitraire
(NFT_MSG_GETSETELEM → nft_quota_do_dump() → NFTA_QUOTA_CONSUMED)
et une écriture arbitraire (nft_overquota() ajoute skb->len à
*priv->consumed sur le trafic loopback).modprobe_path ("/sbin/modprobe" → "//tmp/modprobe") →
exécution d'un processus root avec un contenu contrôlé par l'attaquant.