(delsi) buffer overflow - prunik

Pavel Kankovsky peak na argo.troja.mff.cuni.cz
Sobota Srpen 5 13:14:40 CEST 2000


On Fri, 4 Aug 2000, Gratz Jaroslav wrote:

> Problem neni v procesoru, ale v prekladaci. x86 nabizi segmentovou ochranu
> pameti a u kazdeho segmentu lze nastavit, jestli ma byt citelny,
> zapisovatelny a spustitelny (nejdou myslim vsechny kombinace, ale to je
> vedlejsi).

Na segmentech to sice nastavit lze, ale na strankach nikoli. Jenze
segmentovany model pameti se dneska uz nepouziva ani ve Windows. Mozna az
vyroste nova generace programatoru, u kterych nebude slovo "segment"
vyvolavat traumaticke vzpominky na MS-DOS a 16-bitova Wokna...
(a az intelsky klan zlikviduje vsechny vyrobce procesoru, co segmentaci
nepodporuji...)

> Jenomze gcc a obecne asi vsechny C prekladace prekladaji nektere
> konstrukce tak, ze v prubehu provadeni programu vygeneruji nejaky kod
> na zasobnik a ten spusti.

Nevim jak "obecne asi vsechny", ale gcc to dela pouze v pripade, ze jsou
pouzity vnorene funkce, coz je jeho vlastni rozsireni jazyka (na zasobnik 
se pak ulozi "trampolina", coz je maly kus kodu umoznujici vnorenou
funkci volat jako by nebyla vnorena a pritom zajistit, aby vedela, kde se 
na zasobniku nachazi zaznam nadrazene funkce). Je pravda, ze vnorene
funkce jsou velice oblibene v glibc (aspon nekterych verzich; osobne
to chapu, protoze spoustu veci lze pak napsat mnohem prirozenejsim
zpusobem), ale glibc je naopak asi jediny kus kodu, kde jsem to videl
opravdu pouzivat.

> Uplne odstraneni buffer overflow exploitu by slo udelat tak, ze by se
> zcela oddelily instrukci, datovy a stack segmenty a pro ucel predavani
> parametru by se stack prekryl datovym segmentem pouze pro cteni.

Neni mi z toho jasne, jak by se delalo, aby se predavane parametry dostaly
na zasobnik, kdyz ten ma byt "pouze pro cteni". Uz vubec nechapu, kam
chcete ukladat automaticke promenne, ktere se take dnes davaji na
zasobnik. Krome toho nevim, jak byste chtel resit situaci, kdy ma program
vice nez jeden zasobnik (coz napr. potrebuji programy pouzivajici interne
nejaky druh paralelismu, napr. multithreaded programy).

Nicmene hlavni problem je ten, ze si asi myslite, ze jediny mozny skodlivy
"buffer overflow" je ten, ktery prepise na zasobniku navratovou adresu (?
nebo i neco jineho). To zdaleka neni pravda (i kdyz vetsina exploitu dela
zrovna tohle). Predstavte si napr. nasledujici kusy kodu (predpokladam
BUNO, ze promenne jsou ulozeny tak, ze preteceni b[] ovlivni promenne
deklarovane pozdeji):

1.
     char b[...]; uid_t u = getuid();
     /* nebezpecna manipulace s b[] */
     if (!u) /* neco, co normalni uzivatel nesmi */

2.
     char b[...]; int (*x)() = &y;
     /* nebezpecna manipulace s b[] */
     (*x)();

3.
     char b[...]; int *x = &y;
     /* nebezpecna manipulace s b[] */
     *x = 1;

Podobne veci navic nejsou omezeny pouze na zasobnik, ale daji se provadet
i se statickymi daty. Ba co vic, daji se dokonce provadet i zajimave
kejkle s dynamicky alokovanymi daty (tj. malloc(); viz napr. "JPEG COM
Marker Processing Vulnerability in Netscape Browsers" od Solar Designera,
v podstate je to variace na tema c. 3).

> Tady je ale problem v tom, ze zasobnik se pouziva i pro vraceni
> vysledku pri navratu z funkce a musel by se tedy pouzit oddeleny
> datovy prostor pro predavani parametru z a do funkci.

Nikoli. Pres zasobnik se (aspon ve vetsine implmentaci jazyka typu C) nic
nevraci: navratove hodnoty jsou predavany v registrech prip. -- specialne
pokud se nevejde do vyhrazenych registru, protoze je to napr. velka
struktura -- se ukladaji na misto urcene nejakym ukazatelem, ktery je
predan jako skryty parametr.

> Tohle vsechno nezpusobuje prakticky zadnou ztratu vykonu, protoze limity
> segmentu hlida procesor...

Samotne hlidani mezi segmentu nic nestoji, ale zmena segmentoveho registru
je docela draha operace (i kdyz je to porad levnejsi nez uplny context
switch s vysypanim TLB). Pouziti neobvykleho seg registru pro urcitou
operaci taky neni zadarmo (jednobajtovy prefix), ale to uz by asi bylo
snesitelne.

> a pri jejich prekroceni generuje tusim vyjimku GPF a program u ktereho
> by se nekdo pokusil o exploit by hned obdrzel SIGKILL.

Spis SIGSEGV. (Mimochodem, SEGV znamena SEGmentation Violation.)

> Result: ochrana proti buffer overflow exploitum by udelat sla, ale musely
> by se dost radikalne predelat prekladace, vsechno znovu prelozit a stare
> binarky by samozrejme nemusely fungovat. Do toho se nikomu nechce.

To sice mozna ano, ale bylo by to jeste radikalnejsi, nez si
predstavujete. A je dost dobre mozne, ze vynalozene usili by se dalo
investovat podstatne efektivneji.

--Pavel Kankovsky aka Peak  [ Boycott Microsoft--http://www.vcnet.com/bms ]
"Resistance is futile. Open your source code and prepare for assimilation."



Další informace o konferenci Linux