( turdus | 2012. 06. 22., p – 23:12 )

@turul16: "Alj le egy picit es gondolkozzal."
Megtettem, találtam is megoldást, és mellesleg rájöttem, mi lett félreértve.

A sebezhetőséget végigolvasva hirtelen nem egy, hanem több módszer is eszembe jutott, hogy a cpu hardware-s védelmi mehanizmusával hogy lehet játszi könnyedséggel megakadályozni ezt a támadást. Plusz, ismertem az Intel mérnök pofonegyszerű szoftveres megoldását. Mindennek a tetejébe túl extrém helyen keletkezik a GPF, sokkal egyszerűbb és kényelmesebb exploitok is vannak. Ezért gondoltam, hogy ez baromság.
Első olvasatra pedig valóban nem volt egészen világos, melyik stack kivel is van, ezért kérdeztem.

Az, hogy az olyan kerneleket, amik a rendelkezésre álló védelmi lehetőségek közül egyetlen egyet sem vesznek igénybe, nem ellenőrzik a hívót, illetve stackben tárolják a visszatérési címet (syscall védelmi mechanizmusának egyik fő eleme, hogy ne stacken adódjon át a cím), javítani kell, számomra triviális volt. Éppen ezért még legrosszabb rémálmomban sem gondoltam, hogy van olyan, aki úgy gondolja, hogy én azt gondolom, hogy nem kell javítani.

Az már csak a sors puszta fintora, hogy az Intel mérnök által javasolt pofonegyszerű és megkerülhetetlen szoftveres védelem pont az ASLR miatt nem kerülhet be a kernelbe, pedig minden feltétel adott. *Sigh*

Na szóval, akkor térjünk a lényegre.

Mivel kezdettől fogva biztos voltam benne, hogy adatinjektálással nem sokra megyünk, a kód injektálás témakörét jártam körül.
A kód nem folyamatos, és nem az injektált adat elején kezdődik, így meg kell oldani, hogy a vezérlés valahova a közepébe kerüljön. Elővettem az ABI spec-t, és meg is találtam, amit kerestem a 49. oldal 3.30-as ábráján. Egy switchbe el lehet rejteni az injektált byte-ok elejét, és egy kalap alatt arról is lehet gondoskodni, hogy a kód közepébe ugorjon a vezérlés. A kód pedig egyből a címtábla után következik, azaz egyben írhatók.
A gond ott van, hogy nem minden switch felel meg. Mivel az injektált adatok eleje nem befolyásolható, ezért kellő számú case ágra van szükség (legalább annyira, mint ahány érték a verembe kerül a legelső kontrollált regiszterig). Továbbá, fontos, hogy az injektálás és az első szándékos hívás között az adott switch még véletlenül se hívódjon meg olyan case ágra, amit nem mi felügyelünk, mert az szinte biztos crash (akár a címtáblába barmoltunk bele, akár a mutatott kódba. De akár még gond nélkül le is futhat, ha pont nem bántottuk egyiket se).

Ilyen speckó switch a GP handlerben nincs, ezért mindenképp késleltetett aktiválás jöhet csak szóba, azaz nem számíthatunk a meghaló task text szegmensére. Kizárólag a kernelterületre frissen injektált kódra építhetünk csak. Ez azonban nem lehet nagyobb, mint a kontrollált regiszternyi terület (~50 byte körül lehet, mert a kód nem egybefüggő, és a near jmp-k blokkonként 2 byteot elvesznek). Ebben kell megoldani azt, hogy további text szegmenst töltessünk a kernelmemóriába, ráadásul úgy, hogy saját adatterületünk egyáltalán nincs, csak azt használhatjuk, amit épp a memóriában a környéken találunk.

Tehát megvan az elmélet, de továbbra sem valószínű, hogy egyhamar talál valaki megfelelő switch-et egy kernelben (ha ugyan talál), és továbbra is kérdéses, hogy ilyen kevés darabolt helyre belefér-e az összes szükséges kód (csak a text szegmens célcímének betöltése egy regiszterbe a teljes mennyiség kb. 1/5-e, ráadásul legalább két szomszédos kontrollált regiszternyi terület kell hozzá).