CVE-2012-0056 – Lerajzoltam

2012 január 25 - admin

A HUP-on már rendkívül szórakoztató eszmecsere alakult ki a legújabb Linux kernel jogosultság-kiterjesztés sebezhetőséggel kapcsolatban, én igyekeztem megállni a szócséplést, amíg legalább nagy vonalakban megértem, hogy miről is van szó :) A probléma részleteivel kapcsolatban remek összefoglaló olvasható Jason A Donenfeld blogján, én is ezt tanulmányoztam, és az alábbi összefoglaló is ez alapján születik.

Top-down megközelítéssel a probléma a 2.6.39-es Linux kernelben került bevezetésre, Linus múlt héten commitolta a javítást. Ez előtt a verzió előtt egy #ifndef kigyilkolta a sebezhető mem_write() függvényt még mielőtt lefordult volna. Ez a függvény a folyamatok memóriaterületének írását teszi lehetővé a /proc/ /mem-en keresztül. A kernel 2.6.39-es változatának kiadásakor úgy gondolták, hogy a mem_write()-ba bevezetett ellenőrzések megakadályozzák a lehetőség rosszindulatú kihasználását – tévedtek (Linus szavaival: “the /proc/ /mem handling really isn’t very robust” :)

A csibészkedést két ellenőrzés hivatott megakadályozni, ezek közül az első azt garantálja, hogy egy folyamat csak a saját memóriaterületét írhatja. Ennek a megkerüléséhet első körben érdemes elolvasnunk az exec() manualját, ami úgy indít, hogy “[Ez a függvénycsalád] lecseréli az aktuális folyamat image-ét egy új folyamatéra”. Vagyis egy SUID root binárist exec()-elve annak a memóriaterületére írhatnék, ami nekem nagyon jó lenne. Erre egy elegáns megoldás a su használata, ami megteszi azt a szívességet, hogy stderr-re egy az egyben kirakja a neki átadott érvénytelen paramétereket. A hibakimenetet egy megfelelő helyre pozicionált, /proc/self/mem-re mutató fájlleíróba irányítva öröm lesz és boldogság. 

Azaz csak lenne, ugyanis van még egy ellenőrzés, ami a folyamathoz rendelt self_exec_id változó értékét ellenőrzi. Ez az érték minden egyes exec()-re egyel nő, az ellenőrző rutin pedig azt vizsgálja, hogy a memória fájlleíróját megnyitó folyamat self_exec_id-ja megegyezik-e az azt írni kívánó folyamatéval. Az előző megoldásban a su folyamat id-ja tehát magasabb lesz a leírót létrehozónál, így bukta van.

Azonban ez a korlátozás megkerülhető a feladatok “párhuzamosításával”:

Először is forkoljuk magunkat, az így kapott gyerek folyamat self_exec_id-ja meg fog egyezni a miénkkel. A gyerek folyamatban hívjuk az exec()-t, amivel létrehozzuk a fájlleírót a szülő folyamat memóriájára – ezt megtehetjük, az open() ugyanis mindig engedélyezett. Eközben a szülő folyamat exec()-eli a shellkódot kiköpő su-t, így a fájlleírót létrehozó folyamat és a su self_exec_id-ja meg fog egyezni! Végül a gyerek folyamat szépen visszadja létrehozott leírót a szülőnek, aki ennek birtokában megcsinálhatja az stderr megfelelő átirányítását, így a su végül önkéntelenül beletúr a saját memóriájába.

Magátólértetődő választás a shellkódot a su exit()-jének helyére írni, hiszen ez gyakorlatilag közvetlenül a hibaüzenet (shellkód) kiírása után meg is hívódik. Probléma akkor van, ha a su-t pozíciófüggetlennek (-PIE) fordították, így a kernel alkalmazza rá az ASLR-t, mi meg nem tudjuk, hova kell írni. A népszerű disztrók többségénél ez azonban úgy tűnik, nincs így, és a szerencsésebb esetekben (SUSE?) is elképzelhető, hogy lehet találni más megfelelő SUID állományt, vagy a nyers erőhöz lehet fordulni. 

Az érintett/javított disztrókról sajnos még nem találtam rendes listát. Remélem nem néztem be semmit, ha mégis, kommenteljetek, javítok! ;)

Megosztom Facebookon!
Megosztom iWiWen!
Megosztom Twitteren!
Megosztom Google Buzzon!
Megosztom Google Readeren!
Megosztom Tumblren!


Szólj hozzá, vagy olvass bővebben a témáról itt: BuheraBlog

Szólj hozzá!

Ha az eredeti cikkhez nem lehet, itt megteheted!