iOS App Security 17. – Tanmese a félreérthető nevű API-hívásokról

Ez a cikk legalább 1 éve frissült utoljára. A benne szereplő információk a megjelenés idején pontosak voltak, de mára elavultak lehetnek.

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)

– (NSUInteger)hash

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.

Ezek még érdekelhetnek:


  1. 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.

  2. É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 🙂

  3. @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.

Írd le a véleményedet! (Moderációs elveinket ide kattintva olvashatod.)

Hozzászólás írásához be kell jelentkezned!