P0: az NSO zero-click iMessage sebezhetőség részletei

A Google Project Zero 0day sebezhetőségekkel foglalkozó elemező csoport a Citizen Labbal és az Apple Security Engineering and Architecture csoporttal együttműködve részletesen megvizsgálta a FORCEDENTRY néven elhíresült sebezhetőséget, amivel a Pegasus kémprogramot szállították a célpont eszközére. A cikksorozat első részében azt mutatják be, hogy az NSO milyen módon tudott észrevétlenül betörni az áldozat mobiljába és előkészíteni a terepet a Pegasus számára. A P0 munkatársai úgy jellemezték a exploitot, hogy technikailag az egyik legkifinomultabb megoldás, amivel valaha is találkoztak.

A FORCEDENTRY a következőképp működött:

  1. A célpont iMessage fiókjára küldtek egy gif képnek látszó fájlt, ami valójában egy preparált PDF volt.
     
  2. Az iMessage app észlelve a gif kiterjesztést automatikusan meghívta az ImageIO libet egy sandboxban, ami arra szolgált, hogy a kiterjesztéstől függetlenül a fájl tartalma alapján meghatározza a kép formátumát, ezzel azonban több mint 20 képformátum kodekje is támadhatóvá vált.
     
  3. A támadáshoz a JBIG2 formátumot választotta az NSO, amit még az 1990-es évek végén fejlesztettek ki, hogy fekete-fehér szkennelt dokumentumoknál kiemelkedően magas tömörítési arányt érjenek el. A formátum szegmensek (~rajzolási utasítások) sorozatából áll, amit a kitömörítés során egymás után végrehajt a kodek, előállítva a végső képet. A szegmenshez tartozó képadatokat (pl. egy karaktert ábrázoló pixelek tömbjét) pedig a JBIG2Bitmap nevű objektum reprezentálja, amely tartalmazza a buffer szélességét (w) és magasságát (h) bitekben, továbbá hogy egy sorban hány byte-ot tárol a buffer (line), illetve egy pointert (data), ami a pixelek bufferjére mutat.
     
  4. A konkrét sebezhetőséget - természetesen - egy klasszikus integer overflow adta, ami a szegmensek összeválogatásánál jelentkezett. Az overflow hatására egy alulméretezett buffert allokált a kód a heap memóriában, ahova a későbbiekben JBIG2Bitmap objektumokra mutató pointerek kerültek.
     
  5. Megfelelően megválasztva a szegmensek listáját el tudták érni, hogy az alulméretezett bufferbe beírva néhány JBIG2Bitmap pointert, olyan memória-korrupció lépjen fel, hogy az aktív rajzterülethez (oldalhoz) tartozó JBIG2Bitmap objektum h (a buffer magasságára vonatkozó) attribútuma egy ismeretlen, de nagyon nagy szám legyen.
     
  6. Mivel a h értékét felhasználta a rendszer a bounds checkingnél is (~milyen memóriaterületre írhat/olvashat egy objektum), ez a pár byte-os módosítás “unbounding” hatással volt az aktív rajzterületre, tehát a következő szegmens már nem csak az eredeti memóriaterülethez fért hozzá, hanem jóval azon túlnyúló tartományokhoz is.
     
  7. Ezzel párhuzamosan a heap grooming-nak köszönhetően az aktív oldalhoz tartozó JBIG2Bitmap objektum képessé vált az unbounding után a saját attribútumainak írására. Nem kellett hozzá más, mint 4 byte-nyi bitmap képet renderelni megfelelő koordinátákba, amivel precízen beállíthatóvá váltak a w, h, és line értékek, azaz ezen a ponton már tetszőleges offset előállítható volt.
     
  8. A soron következő JBIG2 szekvenciák alkalmas megválasztásával pedig elérhető, hogy csak a kívánt - a formátum által támogatott - logikai operátor (AND, OR, XOR, XNOR) hajtódjon végre (mellékhatások nélkül) a kiválasztott memóriaterületeken. Az unbounding után ez azt jelenti, hogy megfelelő offsetet beállítva a teljes memórián alkalmazhatók a fenti logikai műveletek.
     
  9. Habár a JBIG2 formátum önmagában nem szkriptelhető, a fenti logikai műveletek kombinálásával tetszőleges logikai kapu emulálható. Az NSO mérnökei ezt felhasználva összeállítottak egy több mint 70000 szegmensből álló sorozatot, amivel definiáltak egy komplett 64 bites mini számítógép-architektúrát. Mindezt még a kép kitömörítési folyamata közben.
     
  10. A támadás soron következő részei már egy magasabb szintű nyelven készültek és ezen a virtualizált architektúrán futottak le.

A következő részben a kutatók azt mutatják be miként jutott ki a program a sandbox környezetből.

Hozzászólások

Nem csak ez van a tarsolyban, korábban rákattintós üzeneteket is küldtek. Hogy van-e másik ilyen szintű zero-click megoldásuk Apple eszközökre, azt csak ők tudják, meg a tisztelt vásárlók.

Ha az NSO-nak, mint cégnek vége is lesz, attól valószínűleg nem kell tartani, hogy elveszik a szakmai tudás is. Választanak 3 másik random betűt és máris megvan az új cég,lehet ugyanott folytatni, ugyanazokkal az alkalmazottakkal, régi és új sebezhetőségekkel. :)

A kérdés valamennyire költői akart lenni. Lehet ennek örülni, de elég kicsi az esélye annak, hogy ekkora munkát egyetlen hibára építettek volna, hiszen bármikor előfordulhatott volna, hogy valaki kiszúrja ezt a hibát és javítják.

Az egésznek az univerzalitás a legnagyobb zsenialitása: van egy virtuális backdoor architektúra, amin a kémszoftverük elfut.

Kosz a bemutatas, zsenialis megoldas. El is olvasom az eredeti cikket is. 

Mindenképp ajánlott elolvasni az eredeti cikket, van benne pár ábra is a memória-elrendezésekről, ami segít megérteni a sebezhetőség gyakorlati részét. Mellesleg én sokat tanultam is a cikkből, ameddig eljutottam a folyamat megértésben arra a szintre, hogy ezt a 10 pontos összefoglalót megírhassam hozzá.

Le a kalappal a 9-es és 10-es pontokban megvalósított húzásért.

Kérem szépen, ez a PROGRAMOZÁS!

Noha alapvetően egyetértek, kicsit erősnek érzem, hogy 64-bites "procit" építettek. A feladathoz tippre egy 8-bites is elég lett volna, ha meg a jövőállóság a lényeg, akkor miért nem 128-bites, vagy pláne kvantumszámítógép.

"Using over 70,000 segment commands defining logical bit operations, [NSO’s hackers] define a small computer architecture with features such as registers and a full 64-bit adder and comparator which they use to search memory and perform arithmetic operations. It's not as fast as Javascript, but it's fundamentally computationally equivalent."

Lassú virtual cpu ez és a feladat a teljes RAM elérése. Nem lassították tovább magukat több utasítás időbe fájó komparálással, több utasításos inkrementálással. Inkább 8-szor szélesebb lett az adder és a komparátor.

Zsenialis, de valszeg iranyitott cuccrol volt szo. Ezt magatol nem talakja ki egy ember.

> Az iMessage app észlelve a gif kiterjesztést automatikusan meghívta

lol, 20 evet utaztam az idoben vissza, hajra outlook

ja, valamikor az ezredforduló környékén az exe crackek hőskorában (amikor az "utolsó" licenc check if-eket nop-okra cserélték) vettem 1x egy szoftvert ami az IDA-val visszafejtést nehezítette.

A tool a lefordított exeben fordításkor megjelölt blokkokat implementálta újra egy teljesen random összeállított virtuális CPU random generált utasításkészletével. A virtuális CPU-t lehetett paraméterezni csúszkák tömegével majd legenerálta a VM-et hozzá. Ezt aztán lehetett maximum 8 mélységben egymásba ágyazni, ekkor a belső ugyanígy egyedileg csúszkákkal paraméterezett VM-et már a stacken felette lévő virtualis CPU-val generált VM-et használva implementálta le és így tovább akár 8 szintig. Majd az utolsó szinten lévő VM-en újraimplementálta az exeben megjelölt x86 kódrészt.

A C forráskódban a "kritikus" függvénye(ke)n belül a védendő blokkot (nem a szimpla if-et mert azt semmivel nem lehet megvédeni, az már akkoriban is csak a nagyvállalati "működikjóazúgy" szoftverekre volt jellemző) egy Valami_begin meg Valami_end (már nem emlékszem a nevére) makróval vettem körbe ami asm betéttel marker bájtokat fűzött a kódba így a lefordított exe-ben a tool megtalálta a védendő blokkokat. A tool az exeben a blokkok elejére egy call-t (aztán jmp) tett, az egymásba ágyazott egymással implementált VM-eket az exe végéhez fűzte, ennek az elejére ugrott a call. Akár blokkonként lehetett külön VM stacket generáltatni.

Természetesen minden szoftverkiadáskor érdemes volt a csúszkákon változtatni, így ha a valaki nekiállt IDA Pro-val visszafejteni egy adott exe verziót, akkor a következő exe kiadásnál kezdhette elölről. Aztán jött a CLR meg Web meg a JS és a programozók új generációja...