
Exploit du noyau pour CVE-2016-6187 (Élévation de privilèges locale)
Exploit du noyau pour CVE-2016-6187 (élévation de privilèges locale) (versions concernées : < 4.6.5)
Exploit développé contre un noyau 4.6 compilé sur mesure + buildroot (kconfig bientôt disponible)
et contourne KASLR, SMEP, SMAP (KPTI non disponible pour 4.6)
La vulnérabilité exploitée est un débordement d'un seul octet nul basé sur le tas dans l'un des hooks LSM d'AppArmor (setprocattr). Voir https://nvd.nist.gov/vuln/detail/CVE-2016-6187
Les premières mesures indiquent une probabilité de 94,44 % d'obtenir un shell root. Dans 1 cas sur 18, l'exploit provoquera un oops du noyau. Des travaux supplémentaires sont nécessaires pour stabiliser complètement le noyau après l'obtention de l'accès root.
Nous abusons du débordement d'octet nul pour corrompre l'octet de poids faible d'un pointeur de freelist dans kmalloc-128, ce qui lui permet
de pointer vers un objet déjà alloué. Ensuite, nous libérons cet objet alloué, provoquant un scénario de double libération.
Nous pouvons maintenant allouer deux structures qui se chevauchent depuis le cache kmalloc-128. La première structure est un objet
dont le contenu peut être relu par l'espace utilisateur. Pour cela, j'ai choisi ip6_sf_socklist. La seconde structure,
qui sera allouée par-dessus ip6_sf_socklist, doit contenir des pointeurs noyau - rfkill_data est un bon candidat.
Elle contient un pointeur de tas (liste vide rfkill_data.events) et un pointeur de données global vers rfkill_fds (rfkill_data.list.prev).
Une fois rfkill_data alloué par-dessus ip6_sf_socklist, nous pouvons récupérer les pointeurs noyau en appelant
getsockopt(sock, IPPROTO_IPV6, MCAST_MSFILTER, gsf, &optlen). La base du texte du noyau peut ensuite être dérivée de rfkill_fds.
Nous provoquons ensuite un second scénario de double libération, mais cette fois nous abusons du processus de chaînage de la file de messages pour écraser le pointeur de freelist
et tromper l'allocateur pour qu'il nous retourne un bloc de kmalloc-96 alors que nous demandons en réalité un bloc de kmalloc-128.
Nous pouvons alors exploiter la différence de taille pour déborder dans le pointeur de freelist d'un bloc kmalloc-96, ce qui nous donne la primitive pour
allouer des adresses arbitraires. Dans notre cas, nous visons la structure ptmx_fops et écrasons le rappel ptmx_fops.unlocked_ioctl
avec un pointeur vers notre gadget de pivotement de pile.
L'appel de ioctl sur /dev/ptmx déclenche le gadget de pivotement et nous faisons pivoter la pile vers une fausse pile dans kmalloc-128 où nous
avons déjà préparé notre chaîne ROP de premier étage.
Comme nous disposons d'un espace limité sur la fausse pile kmalloc-128 (nous ne savons pas si le bloc suivant sera sur le même slab), la tâche
de la chaîne ROP de premier étage est de copier la chaîne ROP de second étage depuis l'espace utilisateur dans le tampon du journal dmesg du noyau (__log_buf)
et de faire pivoter la pile vers __log_buf.
La chaîne ROP de second étage tente ensuite de réparer le noyau (en restaurant ptmx_fops, ...) avant d'appeler commit_creds(prepare_kernel_cred(NULL))
pour élever les privilèges.
Et enfin, elle quitte l'espace noyau via iretq et nous revenons, avec des privilèges élevés, à notre sous-routine execve("/bin/sh", 0, 0).
Les utilisateurs non privilégiés doivent pouvoir au moins ouvrir /dev/rfkill. Certaines distributions peuvent
ne pas le permettre. J'ai vérifié Ubuntu et Fedora : les deux permettent aux utilisateurs non privilégiés d'ouvrir /dev/rfkill
(bien que Fedora utilise SELinux au lieu d'AppArmor)
La section suivante explique comment reproduire l'élévation de privilèges locale dans qemu-x86_64.
Tout d'abord, vous devez obtenir un initrd et un noyau bzImage. Il y a deux méthodes :
La méthode la plus simple et la plus rapide. Vous trouverez tous les composants nécessaires dans prebuilt/.
Ou, alternativement, vous pouvez aussi construire les composants vous-même. Il suffit de récupérer le
noyau 4.6 et d'utiliser
le x86_64_vuln_kern_defconfig fourni dans config/ pour compiler le bzImage.
Pour le initrd, vous pouvez par exemple utiliser buildroot. Assurez-vous simplement
de définir les permissions correctes pour /dev/rfkill dans votre init
chmod 664 /dev/rfkill
C'est la même chose que dans Ubuntu-16.04 et beaucoup d'autres distributions. N'oubliez pas non plus de compiler
l'exploit et de le placer dans votre initrd
make
Une fois les composants obtenus, démarrez la machine vulnérable
./boot.sh <path/to/bzImage> <path/to/rootfs.cpio>
et exécutez l'exploit
./exploit
Vous devrez peut-être exécuter l'exploit plusieurs fois, selon que nous avons mis à zéro un LSB qui était déjà nul. Notez également qu'il y a encore une légère chance (peut-être 10 % à 20 % ?) de crash après l'obtention de l'accès root - j'y travaille.
Amusez-vous bien.
ptmx_fops pour augmenter la stabilitékmalloc-128 pour augmenter la stabilité