hacktricks/binary-exploitation/heap/bins-and-memory-allocations.md

211 lines
19 KiB
Markdown
Raw Normal View History

# Bins & Aloakcije memorije
<details>
<summary><strong>Naučite hakovanje AWS-a od nule do heroja sa</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
Drugi načini podrške HackTricks-u:
* Ako želite da vidite **vašu kompaniju reklamiranu na HackTricks-u** ili **preuzmete HackTricks u PDF formatu** Proverite [**PLANOVE ZA PRIJAVU**](https://github.com/sponsors/carlospolop)!
* Nabavite [**zvanični PEASS & HackTricks swag**](https://peass.creator-spring.com)
* Otkrijte [**Porodicu PEASS**](https://opensea.io/collection/the-peass-family), našu kolekciju ekskluzivnih [**NFT-ova**](https://opensea.io/collection/the-peass-family)
* **Pridružite se** 💬 [**Discord grupi**](https://discord.gg/hRep4RUj7f) ili [**telegram grupi**](https://t.me/peass) ili nas **pratite** na **Twitteru** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Podelite svoje hakovanje trikove slanjem PR-ova na** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repozitorijume.
</details>
## Osnovne informacije
Kako bi se poboljšala efikasnost čuvanja delova, svaki deo nije smešten samo u jednoj povezanoj listi, već postoje različite vrste. To su binovi i postoji 5 vrsta binova: [62](https://sourceware.org/git/gitweb.cgi?p=glibc.git;a=blob;f=malloc/malloc.c;h=6e766d11bc85b6480fa5c9f2a76559f8acf9deb5;hb=HEAD#l1407) mali binovi, 63 velika binova, 1 nesortirani bin, 10 brzih binova i 64 tcache binova po niti.
Početna adresa za svaki nesortirani, mali i veliki bin je unutar istog niza. Indeks 0 nije korišćen, 1 je nesortirani bin, binovi 2-64 su mali binovi, a binovi 65-127 su veliki binovi.
### Mali binovi
Mali binovi su brži od velikih binova, ali sporiji od brzih binova.
Svaki bin od 62 će imati **delove iste veličine**: 16, 24, ... (sa maksimalnom veličinom od 504 bajta u 32-bitnom i 1024 u 64-bitnom režimu). Ovo pomaže u brzini pronalaženja binova gde treba alocirati prostor i ubacivanju i uklanjanju unosa sa ovih listi.
### Veliki binovi
Za razliku od malih binova, koji upravljaju delovima fiksnih veličina, svaki **veliki bin upravlja opsegom veličina delova**. Ovo je fleksibilnije, omogućavajući sistemu da se prilagodi **različitim veličinama** bez potrebe za posebnim binom za svaku veličinu.
U alokatoru memorije, veliki binovi počinju tamo gde se završavaju mali binovi. Opsezi za velike binove postaju sve veći, što znači da prvi bin može obuhvatiti delove od 512 do 576 bajtova, dok sledeći obuhvata 576 do 640 bajtova. Ovaj obrazac se nastavlja, pri čemu najveći bin sadrži sve delove iznad 1MB.
Veliki binovi su sporiji za rad u poređenju sa malim binovima jer moraju **sortirati i pretraživati listu delova različitih veličina kako bi pronašli najbolje odgovarajući** za alokaciju. Kada se deo ubaci u veliki bin, mora biti sortiran, a prilikom alokacije memorije, sistem mora pronaći odgovarajući deo. Ovaj dodatni rad ih čini **sporijim**, ali budući da su velike alokacije manje uobičajene od malih, to je prihvatljiva trgovina.
Postoje:
* 32 binova opsega 64B
* 16 binova opsega 512B
* 8 binova opsega 4096B
* 4 binova opsega 32768B
* 2 binova opsega 262144B
* 1 bin za preostale veličine
### Nesortirani bin
Nesortirani bin je **brza keš memorija** koju koristi menadžer hipa kako bi ubrzao alokaciju memorije. Evo kako funkcioniše: Kada program oslobodi memoriju, menadžer hipa je ne stavlja odmah u određeni bin. Umesto toga, prvo pokušava **da je spoji sa bilo kojim susednim slobodnim delovima** kako bi stvorio veći blok slobodne memorije. Zatim, smešta ovaj novi deo u opšti bin nazvan "nesortirani bin".
Kada program **zatraži memoriju**, menadžer hipa **proverava nesortirani bin** da vidi da li postoji deo dovoljne veličine. Ako pronađe jedan, odmah ga koristi. Ako ne pronađe odgovarajući deo, premesti oslobođene delove u njihove odgovarajuće binove, bilo male ili velike, na osnovu njihove veličine.
Dakle, nesortirani bin je način da se ubrza alokacija memorije brzim ponovnim korišćenjem nedavno oslobođene memorije i smanjenjem potrebe za vremenski zahtevnim pretragama i spajanjima.
{% hint style="danger" %}
Imajte na umu da čak i ako su delovi različitih kategorija, ako dostupan deo kolidira sa drugim dostupnim delom (čak i ako su različitih kategorija), biće spojeni.
{% endhint %}
### Brzi binovi
Brzi binovi su dizajnirani da **ubrzaju alokaciju memorije za male delove** čuvanjem nedavno oslobođenih delova u strukturi brzog pristupa. Ovi binovi koriste pristup poslednji unutra, prvi napolje (LIFO), što znači da je **najskorije oslobođeni deo prvi** koji će biti ponovo korišćen kada postoji nova zahtev za alokacijom. Ovo ponašanje je korisno za brzinu, jer je brže ubaciti i ukloniti sa vrha steka (LIFO) u poređenju sa redom (FIFO).
Dodatno, **brzi binovi koriste jednostruko povezane liste**, a ne dvostruko povezane, što dodatno poboljšava brzinu. Budući da se delovi u brzim binovima ne spajaju sa susedima, nema potrebe za složenom strukturom koja omogućava uklanjanje iz sredine. Jednostruko povezana lista je jednostavnija i brža za ove operacije.
U osnovi, ovde se dešava da je zaglavlje (pokazivač na prvi deo koji treba proveriti) uvek usmereno na najskorije oslobođeni deo te veličine. Dakle:
* Kada se alocira novi deo te veličine, zaglavlje pokazuje na slobodan deo za korišćenje. Pošto ovaj slobodan deo pokazuje na sledeći deo za korišćenje, ova adresa je sačuvana u zaglavlju tako da sledeća alokacija zna gde da dobije dostupan deo
* Kada se deo oslobodi, slobodan deo će sačuvati adresu trenutno dostupnog dela i adresa ovog novootvorenog dela će biti smeštena u zaglavlje
{% hint style="danger" %}
Delovi u brzim binovima nisu automatski postavljeni kao dostupni tako da se zadržavaju kao delovi brzih binova neko vreme umesto da mogu da se spoje sa drugim delovima.
{% endhint %}
### Tcache (Tcache po niti)
Iako niti pokušavaju da imaju svoj sopstveni hip (videti [Arenas](bins-and-memory-allocations.md#arenas) i [Pod-hipovi](bins-and-memory-allocations.md#subheaps)), postoji mogućnost da će proces sa puno niti (kao što je veb server) **završiti deljenjem hipa sa drugim nitima**. U ovom slučaju, glavno rešenje je korišćenje **brava**, koje mogu **znatno usporiti niti**.
Stoga, tcache je sličan brzom binu po niti na način da je **jednostruko povezana lista** koja ne spaja delove. Svaka nit ima **64 jednostruko povezana tcache bina**. Svaki bin može imati maksimum [7 delova iste veličine](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l323) u opsegu od [24 do 1032B na 64-bitnim sistemima i od 12 do 516B na 32-bitnim sistemima](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l315).
Kada jedna nit oslobodi deo, **ako nije prevelik** da bi bio alociran u tcache i odgovarajući tcache bin **nije pun** (već 7 delova), **biće alociran tamo**. Ako ne može da ide u tcache, moraće da sačeka da se hip zaključa kako bi mogao da izvrši globalnu operaciju oslobađanja.
Kada se **deo alocira**, ako postoji slobodan deo potrebne veličine u **Tcache-u, koristiće ga**, ako ne, moraće da sačeka da se hip zaključa kako bi pronašao jedan u globalnim binovima ili kreirao novi.\
Postoji i optimizacija, u ovom slučaju, dok ima hip zaključan, nit **će popuniti svoj Tcache hip delovima (7) tražene veličine**, tako da ako zatreba više, moći će da ih pronađe u Tcache-u.
## Tok dodele
{% hint style="success" %}
(Ovo trenutno objašnjenje je sa [https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions](https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions). TODO: Proveri poslednju verziju i ažuriraj je)
{% endhint %}
Dodele se konačno vrše funkcijom: `void * _int_malloc (mstate av, size_t bytes)` i slede ovaj redosled:
1. Ažurira `bytes` da se pobrine za **poravnanja**, itd.
2. Proverava da li je `av` **NULL** ili nije.
3. U slučaju odsustva **upotrebljive arene** (kada je `av` NULL), poziva `sysmalloc` da dobije blok koristeći mmap. Ako je uspešno, poziva `alloc_perturb`. Vraća pokazivač.
4. Zavisno od veličine:
* \[Dodatak originalu] Koristi tcache pre provere sledećeg fastbin-a.
* \[Dodatak originalu] Ako nema tcache-a već se koristi drugi bin (vidi kasniji korak), pokušajte da popunite tcache iz tog bina.
* Ako veličina spada u opseg **fastbin-a**:
1. Dobija indeks u nizu fastbin-a da pristupi odgovarajućem binu prema veličini zahteva.
2. Uklanja prvi blok iz tog bina i čini da `victim` na njega pokazuje.
3. Ako je `victim` NULL, prelazi na sledeći slučaj (smallbin).
4. Ako `victim` nije NULL, proverava veličinu bloka da bi se osiguralo da pripada tom određenom binu. Inače se baca greška ("malloc(): memory corruption (fast)").
5. Poziva `alloc_perturb` i zatim vraća pokazivač.
* Ako veličina spada u opseg **smallbin-a**:
1. Dobija indeks u nizu smallbin-a da pristupi odgovarajućem binu prema veličini zahteva.
2. Ako nema blokova u ovom binu, prelazi na sledeći slučaj. Ovo se proverava poređenjem pokazivača `bin` i `bin->bk`.
3. `victim` postaje jednak `bin->bk` (poslednji blok u binu). Ako je NULL (dešava se tokom `inicijalizacije`), poziva `malloc_consolidate` i preskače ovaj kompletan korak provere u različitim binovima.
4. Inače, kada je `victim` ne NULL, proverava da li su `victim->bk->fd` i `victim` jednaki ili ne. Ako nisu jednaki, baca se greška (`malloc(): smallbin double linked list corrupted`).
5. Postavlja bit PREV\_INSUSE za sledeći blok (u memoriji, ne u dvostrukom povezanom listu) za `victim`.
6. Uklanja ovaj blok iz liste binova.
7. Postavlja odgovarajući bit arene za ovaj blok u zavisnosti od `av`.
8. Poziva `alloc_perturb` i zatim vraća pokazivač.
* Ako veličina ne spada u opseg smallbin-a:
1. Dobija indeks u nizu largebin-a da pristupi odgovarajućem binu prema veličini zahteva.
2. Vidi da li `av` ima brze blokove ili ne. Ovo se radi proverom `FASTCHUNKS_BIT` u `av->flags`. Ako da, poziva `malloc_consolidate` na `av`.
5. Ako još uvek nije vraćen pokazivač, to označava jedan ili više sledećih slučajeva:
1. Veličina spada u opseg 'fastbin-a' ali nema dostupnih brzih blokova.
2. Veličina spada u opseg 'smallbin-a' ali nema dostupnih malih blokova (poziva `malloc_consolidate` tokom inicijalizacije).
3. Veličina spada u opseg 'largebin-a'.
6. Zatim se proveravaju **nesortirani blokovi** i pretraženi blokovi se smeštaju u binove. Ovo je jedino mesto gde se blokovi smeštaju u binove. Iterira se nesortirani bin od 'TAIL'.
1. `victim` pokazuje na trenutni blok koji se razmatra.
2. Proverava da li je veličina bloka `victim` unutar minimalnog (`2*SIZE_SZ`) i maksimalnog (`av->system_mem`) opsega. Inače se baca greška (`malloc(): memory corruption`).
3. Ako (veličina traženog bloka spada u opseg smallbin-a) i (`victim` je poslednji preostali blok) i (to je jedini blok u nesortiranom binu) i (veličina blokova >= tražena): **Podeli blok na 2 bloka**:
* Prvi blok odgovara traženoj veličini i vraća se.
* Preostali blok postaje novi poslednji preostali blok. Ubacuje se nazad u nesortirani bin.
1. Postavlja `chunk_size` i `chunk_prev_size` polja na odgovarajući način za oba bloka.
2. Prvi blok se vraća nakon poziva `alloc_perturb`.
3. Ako je gornji uslov netačan, kontrola dolazi ovde. Uklanja `victim` iz nesortiranog bin. Ako veličina `victim` odgovara tačno traženoj veličini, vrati ovaj blok nakon poziva `alloc_perturb`.
4. Ako veličina `victim` spada u opseg smallbin-a, dodaj blok u odgovarajući smallbin na `HEAD`.
5. Inače ubaci u odgovarajući largebin održavajući sortiran redosled:
6. Prvo proverava poslednji blok (najmanji). Ako je `victim` manji od poslednjeg bloka, ubaci ga na poslednje mesto.
7. Inače, petlja da pronađe blok sa veličinom >= veličini `victim`. Ako je veličina tačno ista, uvek ubaci na drugo mesto.
8. Ponovi ovaj korak maksimalno `MAX_ITERS` (10000) puta ili dok se svi blokovi u nesortiranom binu ne iscrpe.
7. Nakon provere nesortiranih blokova, proveri da li tražena veličina ne spada u opseg smallbin-a, ako da, onda proveri **largebin-ove**.
1. Dobija indeks u nizu largebin-a da pristupi odgovarajućem binu prema veličini zahteva.
2. Ako je veličina najvećeg bloka (prvi blok u binu) veća od tražene veličine:
1. Iterira od 'TAIL' da pronađe blok (`victim`) sa najmanjom veličinom >= tražene veličine.
2. Poziva `unlink` da ukloni blok `victim` iz bina.
3. Izračunava `remainder_size` za blok `victim` (ovo će biti veličina bloka `victim` - tražena veličina).
4. Ako je ovaj `remainder_size` >= `MINSIZE` (minimalna veličina bloka uključujući zaglavlja), podeli blok na dva bloka. Inače, ceo blok `victim` će biti vraćen. Ubaci preostali blok u nesortirani bin (na 'TAIL' kraju). Vrši se provera u nesortiranom binu da li `unsorted_chunks(av)->fd->bk == unsorted_chunks(av)`. Inače se baca greška ("malloc(): corrupted unsorted chunks").
5. Vrati blok `victim` nakon poziva `alloc_perturb`.
8. Do sada smo proverili nesortirani bin i takođe odgovarajući fast, small ili large bin. Napomena da je jedan bin (brzi ili mali) proveren koristeći **tačno** veličinu traženog bloka. Ponavljajte sledeće korake dok se svi binovi ne iscrpe:
1. Indeks u nizu binova se povećava da bi se proverio sledeći bin.
2. Koristi `av->binmap` mapu da preskoči prazne binove.
3. `victim` pokazuje na 'TAIL' trenutnog bina.
4. Korišćenjem binmapa osigurava se da ako je bin preskočen (u prethodnom 2. koraku), definitivno je prazan. Međutim, ne garantuje da će svi prazni binovi biti preskočeni. Proveri da li je `victim` prazan ili ne. Ako je prazan, ponovo preskoči bin i ponovi gore navedeni proces (ili 'nastavi' ovu petlju) dok ne stignemo do nepopunjenog bina.
5. Podeli blok (`victim` pokazuje na poslednji blok nepopunjenog bina) na dva bloka. Ubaci preostali blok u nesortirani bin (na 'TAIL' kraju). Vrši se provera u nesortiranom binu da li `unsorted_chunks(av)->fd->bk == unsorted_chunks(av)`. Inače se baca greška ("malloc(): corrupted unsorted chunks 2").
6. Vrati blok `victim` nakon poziva `alloc_perturb`.
9. Ako još uvek nije pronađen prazan bin, 'top' blok će se koristiti za obradu zahteva:
1. `victim` pokazuje na `av->top`.
2. Ako je veličina 'top' bloka >= 'tražena veličina' + `MINSIZE`, podeli ga na dva bloka. U ovom slučaju, preostali blok postaje novi 'top' blok i drugi blok se vraća korisniku nakon poziva `alloc_perturb`.
3. Vidi da li `av` ima brze blokove ili ne. Ovo se radi proverom `FASTCHUNKS_BIT` u `av->flags`. Ako da, poziva `malloc_consolidate` na `av`. Vrati se na korak 6 (gde proveravamo nesortirani bin).
4. Ako `av` nema brze blokove, poziva `sysmalloc` i vraća dobijeni pokazivač nakon poziva `alloc_perturb`.
## Slobodan protok
{% hint style="success" %}
(Ovo trenutno objašnjenje je sa [https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions](https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions). TODO: Proverite poslednju verziju i ažurirajte je)
{% endhint %}
Konačna funkcija oslobađanja delova memorije je `_int_free (mstate av, mchunkptr p, int have_lock)`:
1. Proverava da li je `p` pre `p + chunksize(p)` u memoriji (kako bi se izbeglo prelamanje). U suprotnom se baca greška (`free(): invalid pointer`).
2. Proverava da li je veličina bloka barem `MINSIZE` ili višekratnik `MALLOC_ALIGNMENT`. U suprotnom se baca greška (`free(): invalid size`).
3. Ako veličina bloka spada u listu fastbin:
1. Proverava da li je veličina sledećeg bloka između minimalne i maksimalne veličine (`av->system_mem`), inače se baca greška (`free(): invalid next size (fast)`).
2. Poziva `free_perturb` na bloku.
3. Postavlja `FASTCHUNKS_BIT` za `av`.
4. Dobija indeks u nizu fastbin prema veličini bloka.
5. Proverava da li vrh bin-a nije blok koji dodajemo. U suprotnom, baca se greška (`double free or corruption (fasttop)`).
6. Proverava da li je veličina bloka u fastbin-u na vrhu ista kao veličina bloka koji dodajemo. U suprotnom, baca se greška (`invalid fastbin entry (free)`).
7. Ubacuje blok na vrh liste fastbin-a i vraća.
4. Ako blok nije mapiran:
1. Proverava da li je blok vrhunski blok ili ne. Ako jeste, baca se greška (`double free or corruption (top)`).
2. Proverava da li je sledeći blok (po memoriji) unutar granica arene. Ako nije, baca se greška (`double free or corruption (out)`).
3. Proverava da li je prethodni bit sledećeg bloka (po memoriji) označen kao u upotrebi ili ne. Ako nije, baca se greška (`double free or corruption (!prev)`).
4. Proverava da li je veličina sledećeg bloka između minimalne i maksimalne veličine (`av->system_mem`). Ako nije, baca se greška (`free(): invalid next size (normal)`).
5. Poziva `free_perturb` na bloku.
6. Ako prethodni blok (po memoriji) nije u upotrebi, poziva `unlink` na prethodnom bloku.
7. Ako sledeći blok (po memoriji) nije vrhunski blok:
1. Ako sledeći blok nije u upotrebi, poziva `unlink` na sledećem bloku.
2. Spaja blok sa prethodnim, sledećim (po memoriji), ako je bilo slobodno i dodaje ga na početak nesortiranog bin-a. Pre ubacivanja, proverava da li `unsorted_chunks(av)->fd->bk == unsorted_chunks(av)` ili ne. Ako nije, baca se greška ("free(): corrupted unsorted chunks").
8. Ako je sledeći blok (po memoriji) bio vrhunski blok, spaja blokove na odgovarajući način u jedan vrhunski blok.
5. Ako je blok bio mapiran, poziva `munmap_chunk`.
## Provere bezbednosti funkcija heap-a
Proverite provere bezbednosti koje vrše često korišćene funkcije u heap-u u:
{% content-ref url="heap-functions-security-checks.md" %}
[heap-functions-security-checks.md](heap-functions-security-checks.md)
{% endcontent-ref %}
## Reference
* [https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/](https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/)
* [https://azeria-labs.com/heap-exploitation-part-2-glibc-heap-free-bins/](https://azeria-labs.com/heap-exploitation-part-2-glibc-heap-free-bins/)
* [https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions](https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions)
<details>
<summary><strong>Naučite hakovanje AWS-a od nule do heroja sa</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
Drugi načini podrške HackTricks-u:
* Ako želite da vidite **vašu kompaniju reklamiranu na HackTricks-u** ili **preuzmete HackTricks u PDF formatu** proverite [**PLANOVE ZA PRETPLATU**](https://github.com/sponsors/carlospolop)!
* Nabavite [**zvanični PEASS & HackTricks swag**](https://peass.creator-spring.com)
* Otkrijte [**The PEASS Family**](https://opensea.io/collection/the-peass-family), našu kolekciju ekskluzivnih [**NFT-ova**](https://opensea.io/collection/the-peass-family)
* **Pridružite se** 💬 [**Discord grupi**](https://discord.gg/hRep4RUj7f) ili **telegram grupi**](https://t.me/peass) ili nas **pratite** na **Twitteru** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Podelite svoje hakovanje trikove slanjem PR-ova na** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repozitorijume.
</details>