le deuxième lien décrit assez bien le problème.
Un problème de "fuite(leak)" d'un compteur dans du code kernel contrôlable facilement par syscall (tu peux incrémenter un compteur sans que les objets qu'est sensé compter se compteur existent). Le compteur est codé sur 32 bits (quelque soit l'architecture 32 ou 64 bit), donc si tu arrive à "overflow" le compteur (tu fait un tour de boucle et donc passe ton compteur à 0), le kernel croit qu'il n'y a plus d'objet référencé et va donc dés-allouer un emplacement mémoire. (use after free attack). Si le compteur ne fuyait pas tu pourrais pas l'overflow car tu te mangerai le Out of Memory Killer bien avant ça.
Suffit alors de ré-allouer l'emplacement mémoire par du code user-space qui va appeler la fonction kernel suivante :
commit_creds(prepare_kernel_cred(0));
Cette fonction aura pour but de te donner l'uid et guid 0 à ton process. (ret2usr attack)
Ré-allouer le même emplacement mémoire est assez simple, l'allocateur mémoire SLAB du kernel linux est facilement prédictible. Pour des raisons de performance si tu demande une allocation d'un bloc mémoire de même taille juste après un dés-allocation, par soucis de performance (et pour pas se prendre la tête et éviter de la fragmentation mémoire), le noyau va te redonner le même emplacement mémoire.
Reste plus qu'a déclencher un syscall, (pour rappel syscall = exeption, passage en ring 0 (kernel)), qui va alors utilisé ton code userspace à toi, qui va appeller la fontion commit_creds.
Voila pour l'explication grosse maille, dans les faits l'exploit est un peu plus élaborer, puisque du code décrementant le compteur utilise le mécanisme de RCU (read copy update) et il est donc plus difficile de savoir quand on a réellement fait l'overflow du compteur. ça demande des synchro a grand coup de sleep. (donc leur exploit prend environ 30 min sur un coeur I7 pour passer root).
Il y a pas mal de mécanisme qu'on peut mettre en place pour se protéger de se genre d'attaque (se protéger du ret2user attack). https://www.usenix.org/sites/default/files/conference/protected-files/sec14_slides_kemerlis.pdf
1) La proposition impossible car complétement anti-performante :
Faire une séparation entre la mémoire user et kernel (arreté de mapper la mémoire kernel dans tous les processus user) et faire un context switch à chaque syscall.
Facile à faire mais complétement irréaliste car ça tuerai toute performance.
2) Identifier les zones mémoires ne devant pas s'exécuter en ring 0 :
Plusieurs techniques soft et aussi hard tels que PaX's KERNEXEC and UDEREF ou PXN (Priviligied Never Execute) sur ARM64.
3) empêcher l'appel à la fonction kernel commit_creds (l'appel ce fait par symbole).
Le symbole (l'emplacement mémoire de la fonction) est facilement connaissable (définie statiquement à la compilation et dépend uniquement de l'architecture utilisée et du compilateur (gcc ici)). Suffit de mettre de l'aléatoire en place à travers la technique de KASLR (Kernel Address Space Layout Randomisation). Dans ce cas il faut alors éviter de laisser des informations fuitées sur l'emplacement des symboles (voir par example kptr_restrict pour censurer /proc/kallsyms).
Voila ce que j'en ai compris (ps: je suis pas expert en sécurité donc il y a certainement quelques raccourcies).
[^] # Re: Journal bookmark inutile.
Posté par Tangi Colin . En réponse au journal faille Linux 0 day du 19 janvier 2016. Évalué à 10. Dernière modification le 20 janvier 2016 à 21:05.
le deuxième lien décrit assez bien le problème.
Un problème de "fuite(leak)" d'un compteur dans du code kernel contrôlable facilement par syscall (tu peux incrémenter un compteur sans que les objets qu'est sensé compter se compteur existent). Le compteur est codé sur 32 bits (quelque soit l'architecture 32 ou 64 bit), donc si tu arrive à "overflow" le compteur (tu fait un tour de boucle et donc passe ton compteur à 0), le kernel croit qu'il n'y a plus d'objet référencé et va donc dés-allouer un emplacement mémoire. (use after free attack). Si le compteur ne fuyait pas tu pourrais pas l'overflow car tu te mangerai le Out of Memory Killer bien avant ça.
Suffit alors de ré-allouer l'emplacement mémoire par du code user-space qui va appeler la fonction kernel suivante :
commit_creds(prepare_kernel_cred(0));
Cette fonction aura pour but de te donner l'uid et guid 0 à ton process. (ret2usr attack)
Ré-allouer le même emplacement mémoire est assez simple, l'allocateur mémoire SLAB du kernel linux est facilement prédictible. Pour des raisons de performance si tu demande une allocation d'un bloc mémoire de même taille juste après un dés-allocation, par soucis de performance (et pour pas se prendre la tête et éviter de la fragmentation mémoire), le noyau va te redonner le même emplacement mémoire.
Reste plus qu'a déclencher un syscall, (pour rappel syscall = exeption, passage en ring 0 (kernel)), qui va alors utilisé ton code userspace à toi, qui va appeller la fontion commit_creds.
Voila pour l'explication grosse maille, dans les faits l'exploit est un peu plus élaborer, puisque du code décrementant le compteur utilise le mécanisme de RCU (read copy update) et il est donc plus difficile de savoir quand on a réellement fait l'overflow du compteur. ça demande des synchro a grand coup de sleep. (donc leur exploit prend environ 30 min sur un coeur I7 pour passer root).
Il y a pas mal de mécanisme qu'on peut mettre en place pour se protéger de se genre d'attaque (se protéger du ret2user attack). https://www.usenix.org/sites/default/files/conference/protected-files/sec14_slides_kemerlis.pdf
1) La proposition impossible car complétement anti-performante :
Faire une séparation entre la mémoire user et kernel (arreté de mapper la mémoire kernel dans tous les processus user) et faire un context switch à chaque syscall.
Facile à faire mais complétement irréaliste car ça tuerai toute performance.
2) Identifier les zones mémoires ne devant pas s'exécuter en ring 0 :
Plusieurs techniques soft et aussi hard tels que PaX's KERNEXEC and UDEREF ou PXN (Priviligied Never Execute) sur ARM64.
Cette protection peut malheureusement aussi être contourner si on arrive à utiliser la zone mémoire physmap (voir https://www.usenix.org/sites/default/files/conference/protected-files/sec14_slides_kemerlis.pdf). Mais on complique déjà la vie des attaquant avec ces protections.
3) empêcher l'appel à la fonction kernel commit_creds (l'appel ce fait par symbole).
Le symbole (l'emplacement mémoire de la fonction) est facilement connaissable (définie statiquement à la compilation et dépend uniquement de l'architecture utilisée et du compilateur (gcc ici)). Suffit de mettre de l'aléatoire en place à travers la technique de KASLR (Kernel Address Space Layout Randomisation). Dans ce cas il faut alors éviter de laisser des informations fuitées sur l'emplacement des symboles (voir par example kptr_restrict pour censurer /proc/kallsyms).
Voila ce que j'en ai compris (ps: je suis pas expert en sécurité donc il y a certainement quelques raccourcies).