Exploatarea de buffer overflow-uri de 1 bit


Acum câteva săptămâni, a fost publicat un articol legat de o provocare implicată de o simplă exploatare a unui buffer overfow în Linux. În soluția publicată cu acel prilej, s-a remarcat modul în care a fost posibilă schimbarea fluxului de execuție a programului vulnerabil, cu cursorul de suprascriere în următoarea instrucțiune, ca urmare a buffer overflow-ului. În acest articol, vom indica cum se poate exploata o altă vulnerabilitate a buffer overflow-ului, unde registrul ebp este mutat pentru a executa un cod arbitrar.

code

Să urmărim codul de mai sus, unde putem identifica cel puțin două erori de programare. În primul rând, bucla de copiere va muta de fiecare dată aceeași cantitate de biți, indiferent de lungimea lanțului care va fi copiat. În al doilea rând și chiar mai important, comparația buclei este greșită: buffer-ul are 256 biți, iar bucla de copiere are 257, mutând cei mai nesemnificativi biți care urmează imediat în memorie (valoarea epb stocată principal).

stack

După cum se poate observa în diagrama de mai sus, adresa de retur este stocată după ebp. Ideea de a exploata această vulnerabilitate implică modificarea ebp pentru a indica o parte din buffer-ul de unde poate fi citită o adresă de retur și, în același timp, indică payload-ul din același buffer.

white code

În procesul de dezasamblare, putem vedea că după permisiunea buclei de copiere (mutarea ebp), două instrucțiuni sunt executate: leave și ret. Instrucțiunea de permisiune restabilizează cadrul seriei main(); permisiunea este echivalentă cu mov %ebp,%esp și pop %ebp, i.e., opuse față de primele două instrucțiuni ale func(). Odată ce EBP a fost alterat, instrucțiunea ret ia valoarea eip din altă locație și nu din cea originală din serie.

În cazul în care vom rula un test al aplicației cu 256 de litere “A” ca argument, ebp este mutat cu bitul nul la sfârșitul lanțului (bit 257), însemnând că adresa de retur este preluată de la buffer-ul plin cu “A”, generând o eroare întrucât adresa 0x41414141 (“A” = 0x41) nu există.

mini code

Dacă punem un punct de întrerupere imediat după valoarea originala ebp din serie, putem observa că este 0xbffff3f8; putem, de asemenea, vedea că este urmat în memorie de adresa originală de retur în main(), 0x08048261. Putem apoi plasa un punct de întrerupere înainte ca instrucțiunea de permisiune să fie executată și vedea că valoarea epb stocată este acum 0xbffff300.

Este important de remarcat faptul că buffer-ul este între 0xbffff2ec și 0xbffff3ec, prin urmare adresa ebp este utilă, deoarece se află în memoria tampon. Odată ce permisiunea a fost executată, putem apoi verifica dacă este direcționat către adresa dorită:

w b code

Pentru a construi shellcode-ul va trebui să:

  • Plasam biții nedoriți de la 0x2ec până la 0x300, i.e., 20 biți de umplutură. Putem plasa NOP (0x90), de exemplu.
  • La 0x300. va viza noul ebp, drept urmare putem completa cu încă 4 biți nedoriți.
  • În 0x304, putem plasa adresa shell-ului în interiorul our buffer-ului nostru, ex. 0xbffff308.
  • În 0x308, shell-ul va porni.
  • Cei 256 biți sunt completați.

wb

După executare, se poate observa că shell-ul este deschis în interiorul GDB.

bw

În instalările care urmează vom vedea tehnici cu o complexitate în creștere.

Sursa foto: ©Jan Ramroth/Flickr

 

MATIAS POROLLI

CORESPONDENT INDEPENDENT

oana May 12, 2016

Lasa un comentariu