Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
vsock_poc — Enquête sur le bug derrière CVE-2021-26708 | Kitploit
Outils/GitHubGitHub/jordan9001/vsock_poc
Analyse des VulnérabilitésExploitationDébogueursArticles et RechercheApprentissage et ÉducationExploitation de Binaires
GitHubjordan9001/vsock_poc

vsock_poc

Enquête sur le bug derrière CVE-2021-26708

Voir le dépôt
282il y a 5 ansVérifié par Kitploit

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

vsock_poc

Enquête sur le bogue derrière CVE-2021-26708


Ce dépôt contient un petit compte-rendu sur CVE-2021-26708, et comment ce bogue peut être transformé en primitive d'écriture Use-After-Free. Le PoC ici n'est pas un exploit complet, mais simplement mon harnais que j'ai utilisé pour enquêter sur ce bogue. Il peut utiliser avec succès une entrée du cache kmalloc-64 après qu'elle ait été libérée, mais ne contient aucun code pour préparer la mémoire et placer quelque chose d'intéressant dans l'emplacement.

C'est un bogue amusant signalé par @a13xp0p0v. Il a attiré mon attention car le correctif était si simple, juste empêcher d'obtenir une référence à vsk->transport en dehors du verrou dans 5 endroits différents. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c518adafa39f37858697ac9309c6cf1805581446

Ci-dessous un petit guide du processus pour passer du correctif à une primitive use-after-free qui pourrait être utilisée pour l'exploitation. Ce guide devrait être utile à ceux qui souhaitent explorer ce bogue.

Configuration de l'environnement

J'ai téléchargé le noyau Linux 5.10.13 et annulé manuellement le correctif montré ci-dessus. Pour plus d'informations sur la construction et l'exécution du noyau, la référence suivante est bonne.

https://fedoraproject.org/wiki/Building_a_custom_kernel

J'ai également modifié les paramètres de démarrage pour activer le débogage du noyau avec kgdb. En utilisant gdb avec le fichier vmlinux que j'avais construit plus tôt, j'avais tous les symboles du noyau pour le noyau principal, mais pas pour les modules chargeables. Le code associé à la vulnérabilité n'était pas chargé par défaut, mais serait chargé dans le noyau lorsque la famille PF_VSOCK est utilisée (selon la façon dont vous avez construit votre noyau).

Pour obtenir les symboles dans kgdb pour les modules chargés, je me suis assuré d'utiliser la socket vsock au moins une fois, puis j'ai utilisé sudo cat /proc/modules | grep vsock pour obtenir les adresses de base des modules associés. Dans gdb, je faisais ensuite quelque chose comme (gdb) add-symbol-file ./net/vmw_vsock/vsock.ko 0xffffffffc0567000 pour informer gdb de l'emplacement en mémoire des symboles de ce fichier ko. vsock.ko et vmw_vsock_virtio_transport_common.ko étaient les deux plus pertinents.

Chasse à la primitive

C'est amusant de travailler à rebours à partir des correctifs car, contrairement à de nombreuses chasses aux vulnérabilités, vous savez avec certitude que vous regardez au bon endroit. Dans ce cas, nous savons d'après le correctif qu'une référence au transport est sauvegardée avant l'obtention du sock_lock. Nous pouvons raisonnablement supposer que la vulnérabilité est due au changement de transport, mais que l'ancienne référence est utilisée.

Dans ce genre de scénario, nous espérerions que le transport lui-même soit un objet alloué dynamiquement qui pourrait être libéré et remplacé par un autre objet entre l'obtention de la référence et la prise du verrou. Malheureusement, lorsque nous suivons les durées de vie des transports pertinents implémentés par les autres modules, ils semblent tous être en mémoire globale. Nous allons donc devoir chercher un niveau plus profond pour des éléments utilisés lorsqu'ils sont hors de portée.

La libération, partie 1

En regardant dans af_vsock.c, nous pouvons trouver deux endroits où vsk->transport est modifié. Dans vsock_assign_transport et vsock_deassign_transport. Dans vsock_assign_transport, nous pouvons voir que s'il existe un transport différent, vsock_deassign_transport est appelé avant de placer le nouveau transport. Si nous regardons les possibilités pour l'appel vsk->transport->destruct(vsk) ici, nous voyons que les transports loopback et virtio vont simplement kfree le paramètre vsk->trans ici. Bingo ! Si nous pouvons trouver (1) un chemin vers cet appel qui peut entrer en compétition avec (2) une fonction vulnérable qui utilise une référence de transport d'avant sa destruction pour accéder à vsk->trans, alors nous aurons notre primitive.

En cherchant un chemin vers vsock_deassign_transport, nous le voyons appelé depuis vsock_sk_destruct ou vsock_assign_transport. vsock_sk_destruct est défini comme fonction de sock->destruct, donc des appels à __sys_close, ou d'autres appels disponibles le long du chemin de destruction comme sock_put, sock_close, ou vsock_release peuvent aboutir ici.

Le chemin le plus pertinent vers vsock_assign_transport passe par vsock_stream_connect, mais nécessite que la socket soit dans quelques états spécifiques, et n'appelle vsock_deassign_transport que si le transport change. Et il remplacerait le paramètre vsk->trans si nous n'obtenons pas un nouveau transport NULL.

L'utilisation

Avant d'aller trop loin dans la recherche du chemin vers la libération qui nous convient, nous voulons déterminer qu'il existe un chemin valide qui utilise le membre vsk->trans avec une référence invalide à un transport détruit. Nous pouvons vérifier méthodiquement chaque endroit où le transport est utilisé avec une référence potentiellement invalide. En traçant ces trous, nous pouvons trouver ceux où vsk->trans est utilisé. Le meilleur chemin semble être via vsock_stream_setsockopt ici, lorsque transport->notify_buffer_size écrit à un décalage à l'intérieur de vsk->trans pour les transports loopback et virtio juste ici. Si le trans est déjà libéré lorsqu'il est utilisé là, nous obtenons une belle écriture d'un u32 à un décalage de 0x28 dans une allocation kmalloc-64.

La course

L'utilisation de vsock_stream_setsockopt comme primitive dépend d'une course où, entre l'obtention de la référence au transport et l'obtention du sock_lock, il y a une petite fenêtre et beaucoup d'instructions pour y arriver. Ici, nous pouvons utiliser une fonctionnalité intéressante de Linux appelée userfaultfd pour améliorer nos chances. Ce mécanisme nous permet de gérer les défauts de page en mode utilisateur à notre guise. Voir https://man7.org/linux/man-pages/man2/ioctl_userfaultfd.2.html et https://man7.org/linux/man-pages/man2/userfaultfd.2.html.

Avec cela, nous pouvons avoir un thread (le bloqueur) qui obtiendra le sock_lock puis accédera à une mémoire utilisateur et provoquera un défaut de page. Nous pouvons maintenir ce thread en pause (avec le verrou toujours détenu) aussi longtemps que nous le voulons. Les autres threads qui tentent d'obtenir ce verrou resteront là jusqu'à ce que nous laissions le bloqueur partir et relâcher le verrou. Nous pouvons aligner notre thread qui finira par faire la destruction, et notre thread qui utilisera la référence invalide. Les deux attendront le sock_lock, et maintenant nous avons une excellente chance de gagner la course. Si le thread qui fait la destruction est choisi pour obtenir le verrou en premier, alors notre appel setsockopt s'exécutera après. Il utilisera le pointeur vsk->trans après qu'il ait été libéré (et remplacé).

Si l'appel setsockopt passe en premier à la place, nous perdons la course, mais nous pouvons réessayer en toute sécurité tout le processus.

La libération, partie 2

Lorsque j'ai essayé de construire cela, j'ai passé du temps à suivre une mauvaise piste. J'ai essayé d'obtenir l'appel de vsock_deassign_transport via close et des timeouts, mais je suis tombé sur beaucoup de vérifications de comptes de référence qui retardaient la destruction effective jusqu'à ce qu'il soit trop tard.

En aparté, le débogage de ces chemins peut être difficile ; comme vous pouvez l'imaginer, un point d'arrêt sur l'appel système close sera atteint très souvent. Même si vous utilisez des points d'arrêt conditionnels pour ne vous arrêter que dans le thread approprié, la machine ralentira considérablement. Un chemin intéressant pour contourner cela est d'utiliser ebpf avec des tracepoints qui n'appelleront bpf_trace_printk que si les conditions sont correctes. Ensuite, un point d'arrêt kgdb peut être placé sur bpf_trace_printk, ce qui nous amènera près du bon endroit. Cela ne fonctionne pas avec kprobes car vous êtes déjà dans un gestionnaire de point d'arrêt. Je pense qu'ajouter un appel bpf_trace_kgdb_break à ebpf pourrait être un bel ajout au noyau.

Lorsque je suis finalement passé à l'examen du chemin vsock_assign_transport, tout s'est mis en place rapidement. Pour satisfaire les exigences, nous nous connectons d'abord à VM_ADDR_CID_LOCAL, lorsqu'il n'y a pas de serveur à l'écoute. Cela nous donnera le transport loopback, mais lorsque notre connexion expirera ou échouera, notre état reviendra à SS_UNCONNECTED. Cela nous permet de faire une autre connexion à une adresse supérieure à VM_ADDR_CID_HOST, ce qui entraînera un changement de transport, détruisant notre transport existant et provoquant la libération. Il est important de le faire lorsqu'il n'y a en fait aucun transport_g2h ou transport_h2g enregistré, afin que notre nouveau transport soit NULL, et que notre référence libérée reste dans vsk->trans.

Exploitation

Avec tout cela aligné, nous obtenons un use-after-free fiable qui pourrait être utilisé pour une escalade de privilèges.

Ce dépôt ne concerne que notre progression jusqu'au use-after-free initial. Mais maintenant nous avons une primitive pour écrire une valeur à un décalage dans le cache kmalloc-64 là où virtio_vsock_sock était précédemment alloué. J'ai décidé d'arrêter le guide ici parce que, quoi, est-ce que je dois tout faire ici ?

Télécharger l’outil