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.
***
Teszteltem egy alkalmazást és találtam benne egy hibát, amihez hasonlókat nagyon könnyű iOS-es kriptó implementálásakor elkövetni – a helyenként félreérthető elnevezésű API-hívások kérdését. Megmutatom a konkrét példát is, mert elég tanulságos. Maga az alkalmazás egyébként érzékeny adatok kezelésére és ide-oda tologatására használható, megtömve kriptóval, ami az iPad elveszésekor/ellopásakor is védi az adatok bizalmasságát.
Amikor először elindítjuk az appot, inicializálnunk kell a kriptót, azaz be kell ütnünk egy legalább tíz karakterből álló erős jelszót, ami tartalmaz az [A-Z, a-z, 0-9, speciális karakterek] halmazból legalább hármat. Elég komoly komplexitás jön ebből, legalább 60 bitre számoltam.
Sportszerű nehezítésként nem volt forráskódom az apphoz, ezért a csajom analizálta a binárist. Az olvashatóság érdekében érdemes azokra a részekre fókuszálni, amiket kivastagítottam, a konkrét regiszterműveletek a “mi a fenét csinál ez?” kérdés megválaszolásához nem kellenek.
Amikor az inicializálás megtörténik, az alábbi kód fut le:
CreatePasswordViewController – (void)okButtonPressed:(id)
[egy csomó ellenőrzés, hossz, komplexitás stb.]
MOV R0, (_OBJC_IVAR_$_CreatePasswordViewController.leftPasswordField – 0x2FFC6) ; UITextField *leftPasswordField;
LDR R1, [SP,#0xC0+var_94]
ADD R0, PC ; UITextField *leftPasswordField;
LDR R0, [R0] ; UITextField *leftPasswordField;
LDR R0, [R6,R0]
BLX _objc_msgSend
MOV R7, R7
BLX _objc_retainAutoreleasedReturnValue
MOV R4, R0
MOV R0, (selRef_hash – 0x2FFE0) ; selRef_hash
ADD R0, PC ; selRef_hash
LDR R1, [R0] ; “hash”
MOV R0, R4
BLX _objc_msgSend
MOV R2, R0
MOV R0, (selRef_storePassword_ – 0x2FFF4) ; selRef_storePassword_
ADD R0, PC ; selRef_storePassword_
LDR R1, [R0] ; “storePassword:“
MOV R0, R6
BLX _objc_msgSend
[…]
Lássuk, hogy mi történik: ellenőrizzük, hogy elég hosszú meg bonyolult-e a jelszó, aztán meghívjuk a CreatePasswordViewController ojjektum storePassword függvényét az ilyen módon ellenőrzött jelszóra. A storePassword függvény felboncolva a következő:
; CreatePasswordViewController – (void)storePassword:(unsigned int)
PUSH {R4-R7,LR}
ADD R7, SP, #0xC
SUB SP, SP, #4
MOV R5, R0
MOV R0, (selRef_securedSHA256DigestHashForPIN_ – 0x2FC28) ; selRef_securedSHA256DigestHashForPIN_
MOV R6, (classRef_KeychainWrapper – 0x2FC2A) ; classRef_KeychainWrapper
ADD R0, PC ; selRef_securedSHA256DigestHashForPIN_
ADD R6, PC ; classRef_KeychainWrapper
LDR R1, [R0] ; “securedSHA256DigestHashForPIN:”
LDR R0, [R6] ; _OBJC_CLASS_$_KeychainWrapper
BLX _objc_msgSend
MOV R7, R7
BLX _objc_retainAutoreleasedReturnValue
MOV R4, R0
MOV R0, (selRef_createKeychainValue_forIdentifier_ – 0x2FC46) ; selRef_createKeychainValue_forIdentifier_
MOV R2, R4
ADD R0, PC ; selRef_createKeychainValue_forIdentifier_
LDR R1, [R0] ; “createKeychainValue:forIdentifier:”
LDR R0, [R6] ; _OBJC_CLASS_$_KeychainWrapper
MOV R3, (cfstr_Passsaved – 0x2FC54) ; “passSaved“
ADD R3, PC ; “passSaved”
BLX _objc_msgSend
TST.W R0, #0xFF
[hibakezelés, ellenőrzés stb.]
A storePassword hasheli SHA256-tal a beérkezett inputot és meghívja a [KeyChainWrapper createKeyChainValue:forIdentifier:] függvényét (ezeket nem írom ide, azt csinálják, amit a nevük mond) – azaz végeredményben a beérkezett inputot ledarálja SHA256 hashfüggvénnyel és ráakasztja a keychain passSaved nevű tárolójára.
Idáig elég jó, azt csinálja a kód, amit elgondolunk, hogy csinálnia kell abban az esetben, ha hashelt dolgokat akarunk a keychainre aggatni. Mit rontottak el a fejlesztők?
Nézzük meg még egyszer az első függvényt – ezúttal pirossal jelölöm a trükkös részt.
CreatePasswordViewController – (void)okButtonPressed:(id)
[egy csomó ellenőrzés, hossz, komplexitás stb.]
MOV R0, (_OBJC_IVAR_$_CreatePasswordViewController.leftPasswordField – 0x2FFC6) ; UITextField *leftPasswordField;
LDR R1, [SP,#0xC0+var_94]
ADD R0, PC ; UITextField *leftPasswordField;
LDR R0, [R0] ; UITextField *leftPasswordField;
LDR R0, [R6,R0]
BLX _objc_msgSend
MOV R7, R7
BLX _objc_retainAutoreleasedReturnValue
MOV R4, R0
MOV R0, (selRef_hash – 0x2FFE0) ; selRef_hash
ADD R0, PC ; selRef_hash
LDR R1, [R0] ; “hash”
MOV R0, R4
BLX _objc_msgSend
MOV R2, R0
MOV R0, (selRef_storePassword_ – 0x2FFF4) ; selRef_storePassword_
ADD R0, PC ; selRef_storePassword_
LDR R1, [R0] ; “storePassword:”
MOV R0, R6
BLX _objc_msgSend
[…]
Mi történik? Figyelmesen megnézve a kódot rájövünk, hogy még egy lépést tesz a program a jelszó beolvasása és eltárolása között, ez pedig a “hash” nevű függvény használata. Némi túrás után rájövünk, hogy ez a “hash” ez nem olyan hash, azaz nem kriptográfiai hashfüggvény:
hash
Returns an integer that can be used as a table address in a hash table structure. (required)
A “hashelés” tehát az [NSObject hash] használatát jelenti, ami egy NSUInteger-t ad vissza, ami a teszt iPadünkön 10 jegyű. Ez összevissza ~32 bitnyi entrópia a jelszóhash-re, ami jóval kevesebb, mint az összes lehetséges jelszavak terének ~60 bit entrópiája.
Tényleges forráskód szintjén ez a következő különbséget jelent(het)i (még mindig nincs forrásom hozzá), ha nagyvonalúan eltekintünk attól, hogy az egyik NSStringet ad vissza, a másik meg NSUIntegert.
if(PasswordIsOk)
{
[self storePassword:self.leftPasswordField.text];
}
vs.
if(PasswordIsOk)
{
[self storePassword:[self.leftPasswordField.text hash]];
}
Ha tehát támadóként AES kulcsot akarnék törni és analizáltam a binárist, annyi dolgom van, hogy szép sorban kigenerálom 0-tól MAXUINT-ig az összes NSUInteger értéket, megfuttatom az AES kulcsot generáló, determinisztikus és ismert függvényen és megpróbálom, hogy nyertem-e.
A támadás összes komplexitása töredéke annak a komplexitásnak, amit a jelszavak törése jelentett volna – mindennek az oka pedig egyetlen félreérthető nevű és félreértett API-hívás.
5 Comments
Annyi megjegyzes (amit anno az eredeti oldalon is odairtam a cikkhez): a tulajdonkeppeni feltores meg ennel is egyszerubb lehet, ugyanis – bar nincs dokumentalva, de tudjuk, hogy – a `- hash` metodus egy objektum memoriacimet adja vissza, tehat korulbelul igy lehet implementalva:
– (NSUInteger)hash
{
return (NSUInteger)self;
}
Azaz a hash tulajdonkeppeni erteke nem lehet barmi meg a 0…UINT_MAX intervallumon belul sem, mivel ervenyes memoriacimnek kell lennie – tehat ismerve, hogy az iOS a Darwin/Mach kernelt hasznalja, csak meg kell nezni a dokumentacioban, hogy milyen szamok ervenyes memoriacimek a kernel szerint. es eleg azokat a brute force algoritmusba bevonni, ezzel jopar folosleges probalkozast kikuszobolve es a ‘kodolas’ feltoreset meggyorsitva.
Érdekes cikk, köszönöm!
Amúgy ne értsetek félre, ez egy csípősen szakmai cikk az amúgy könnyed pletykák közé, olyan mint ha a Teszkóban Wellensteyn kabátokat árulnának akciósan 🙂
De tényleg nem bántásból, csak viccelődésből mondom 🙂
@intrex: lehetőség szerint legkevésbé sem írunk könnyed pletykákat, mert az bulvár.
Világos, “pletyka” helyett “hírek” szó értendő 🙂
@intrex: Szerintem nagyon jó,hogy kitesznek/írnak ide szakmai cikkeket.
Én egy kicsit vissza olvastam a szifon cikkeit: nézegettem azokat a cikkeiket amik a 3.1.3 idején és az 1. iPad idején születtek és akkor azért elég gyakran születtek “szakmai” cikkek.(pl.: Témázgassunk; A Deamon-okkal foglalkozó sorozat,a vfdecrypt használatáról szóló cikk, a toolchain-es cikk,push javítással foglalkozó cikkek;magyarítás készítéssel kapcsolatos cikkek…stb.)
Ezeket szerintem a jó cikkek.Az,hogy megjelent egy új Angry Birds meg az ilyen típúsú cikkekről annyit,hogy persze jó tudni ezekről,főleg hogy nem bújom naponta az AppStore-t,de ezeket sok más oldal is megírja.Véleményem szerint a “szakmai” cikkeknek vannak nagy értékei.Kifejezetten jó,hogy kiteszik az iOS App Security sorozat részeit.Nem kell mindenre azt mondani,hogy én nem “értek annál jobban az iPhone-okhoz,hogy megcsináljak egy fotót…blablabla” az ilyen szakmai cikkeknek nagyon nagy értékük van.Hidd csak el.
Miért baj? Kérdezem én.