Az alábbi cikket Ys. írta a 0×90 blogon, és mivel a cikksorozata kapcsolódik az iPhone-hoz, mi érdekesnek gondoljuk a számotokra is, így ezeket engedelmével egymás után át is emelnénk ide, a Szifonra.
***
Elég sok alkalmazás keresztülment a kezemen az elmúlt pár hónapban (úgy fest, egyre inkább szezonja van mostanában az iOS-es céges appoknak), és a legtöbb tesztriportban ott van egy issue függetlenül attól, hogy az adott appon salesprezentációkat mutogatnak vagy trading algoritmusokat ütögetnek össze.
A tesztelt alkalmazásokban közös, hogy bonyolult lokális kriptóvarázslást alkalmaznak a tárolt adatok védelmére AES-sel meg Blowfish-sel, és hogy a fenyegetési modellben (magyarul threat model) kiemelt figyelmet fordítanak arra, hogy a szenzitív adatokkal megpakolt cégeslogós iPadet ellopják vagy elhagyják. Az egész kriptóvarázslás egyedüli célja az, hogy ilyen esetekben se tudjon a támadó hozzáférni az alkalmazás által tárolt adatokhoz.
Elneveztem magamban a probléma gyökerét: “Önjáró Kriptó” és annyit jelent, hogy a kulcs ott van valahol az eszközön a titkosított adatok mellett. Mutatok néhány tipikus hibát (mind-mind éles alkalmazásokból) és azt, hogy a támadó hogy tudja kihasználni őket.
Egy: “A kulcs a postaládában.”
Az alkalmazás a titkosításhoz használt 256 bites AES-kulcsot az első indításkor az eszköz fizikai véletlenszám-generátorából nyeri. A kulcsot biztonságosan tároljuk az iOS érzékeny adatok tárolására kidolgozott eszközét, a keychaint használva.
Gyakori megközelítés és indokolható is: abból a feltételezésből, hogy az iOS biztonsági kontrolljai megbízhatóak, egy lépésben következik az, hogy használjuk is az érzékeny adatok tárolására használható, AES-enkriptált tárolót.
A problémát az jelenti, hogy az adott eszközt elég egyszerű jailbreakelni, jailbreakelt állapotban pedig nagyon egyszerűen ki lehet dumpolni a keychain teljes tartalmát, ráadásul az egész művelet nem tart tovább négy percnél.
Kettő. “Kossuth utca 22/b, tárulj!”
Az alkalmazás a titkosításhoz használt kulcsot az eszköz egyéni azonosítójából (UDID) és egy, az alkalmazás által tárolt seedből származtatja a PBKDF2 kriptófüggvény felhasználásával. A kulcsot biztonsági okokból nem írjuk ki a tárolóeszközre, hanem minden inicializálásnál újból kiszámoljuk.
Ilyet is láttunk már – az az alapgondolat mögötte, hogy a titkosításhoz használt kulcs legyen az eszközhöz kötve, és ha újratelepítjük az alkalmazást (mondjuk backupból), akkor iTunes-os szinkronizáción keresztül adatokat is tudjunk visszatenni rá: a UDID marad ugye, a seedfájlt pedig a backup visszaállításakor visszamásoljuk.
A probléma ott van, hogy az eszköz UDID-ja nyilvános információ, triviálisan kinyerhető, a seedfájlt pedig a támadó szintén meg tudja szerezni az eszközről – innentől kriptográfiai értelemben nincs titok: az algoritmus ott van az eszközön, a ciphertext és a kulcs úgyszintén.
Három. “Te vagy a Béla?” – “Igen.” – “Akkor gyere.”
Az alkalmazás a felhasználó által beütött jelszót hasheli és ha megegyezik az eltárolt értékkel, akkor authentikáltnak tekinti.
Az, hogy az “eltárolt érték” hol lakik, az most lényegtelen – akár lehet a keychainen is, innentől lásd az első pontot, de lehet bárhol az eszközön, a támadás szempontjából mindegy. Az alkalmazás binárisát felboncolva egyértelműen látszik, hogy hol van az a pont, ahol például egy [NSString isEqualToString:] hívást használva betölti a memóriába a várt hash-t tartalmazó NSString objektumot és elvégzi az összehasonlítást. Ide kell bedugni egy gdb-s breakpointot, átütni egy regisztert és már be is engedett minket az app.
Mi a közös a fenti három szkenárióban? Az, hogy a titok valamilyen módon a titkosított adatok mellett lakik az eszközön, és szemfüles, nagy támadási potenciállal rendelkező személyként először kiismerjük, hogy hogy működik az alkalmazás, majd kihasználjuk a hibát. Ez a titok az első esetben magát a titkosításhoz használt AES kulcsot jelentette, a másodikban a seedfájlt és a UDID-t így együtt, a harmadikban a várt jelszóhash-t.
Mit lehet tenni? Több lehetőség is van, néhányat felvázoltam ide.
- On-line authentikáció. A júzer beüti a jelszavát, az app mondjuk egy távoli OAuth2 protokollos bejelentkezést követően megkapja a lokális kriptóhoz szükséges kulcsot. Hátránya, hogy kell hozzá adatkapcsolat és az, hogy a távoli authentikációs protokoll implementálása rengeteg banánhéjat rejt amúgy is, tehát összességében egy elég komplex problémát (lokális kriptó) egy legalább olyan komplexre cseréltünk (OAuth2 implementáció). Mindenesetre a konkrét problémát megoldja.
- Az authentikációs tokent használjuk a kriptómotor beindítására. Ez már jóval használhatóbb: a felhasználó beüti a jelszavát, mondjuk a UDID-val saltolva beküldjük egy PBKDF2 függvénybe az így eredményül kapott stringet és a kipottyanó AES-kulccsal megkezdjük a tárolt adatok dekriptálását. Ha túl sok hibát kapunk, akkor hibát jelzünk a felhasználónak, mert nem a megfelelő jelszót ütötte be. Előnye ennek a módszernek, hogy hiába lopják el az eszközt, a kulcsfontosságú adatok dekriptálásához szükséges kulcs se titkosított, se hashelt, se semmilyen formában nincs rajta. Hátránya, hogy a jelszó megváltoztatása magával hozza a titkosítási kulcs megváltozását is (azaz újra kell mindent enkriptálni, ezúttal az új jelszóból kapott kulccsal), de ezen a részén pont lehet segíteni – hogy hogyan, azt meghagyom a kedves olvasónak házi feladatként. Mondjuk kommentben lehet beadni.
1 Comment
(A cikkeknél minden OFF-topic hozzászólást törlünk. Erre van a Gyakran Ismételt Kérdések cikk. Kérünk, ott tedd fel a kérdésed!)