Wednesday 8 August 2012

Not All Hash Functions Are Created Equal

During another iOS app pentest, I found a mistake which is very easy to commit when doing iOS devving - the problem posed by the API calls that are easy to misunderstand or misinterpret. Function names in such API calls suggest one thing but actual implementations do another.

The application itself is a common one at the client: designed to store loads of sensitive data, stuffed with crypto magic, ready to handle the scenario when the iPad is stolen/lost. No biggie there. When the user starts the application for the first time, he has to initialise the crypto engine with entering a complex, >10 char long password with numbers and special symbols. The entropy of the password space is estimated around 60 bits, which can be considered kind of strong from a brute force perspective.

Unfortunately, the vendor did not give us the source (as the pentest was supposed to model a black-box scenario with a highly skilled attacker), therefore we used IDA to do binary autopsy. When the user initialises the crypto, the following code snippet is executed:

CreatePasswordViewController - (void)okButtonPressed:(id)
[lots of checks for password length, complexity etc.]
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
[...]

Looks kind of okay - we take the text from the leftPasswordField UITextField, and feed the contents to the [CreatePasswordViewController storePassword:] function, which looks like this in a disassembler:

; 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
[error handling etc.]

The operation is simple: we take the input, create a SHA256 hash for the PIN in a smart way (salt and SHA the input 10.000 times in order to thwart brute force attacks), and save the result to the 'passSaved'  keychain item. So far, so good. Looks like things are OK from a crypto perspective, the password is stored after salted secure hashing. A note here: the idea of storing the password in any form suggests usage of the stored value. Most of the time, this involves a comparison of two values: one resulting from chewing the entered password, the other sitting on the keychain. This mode of operation is insecure on mobile devices - I'll show why later. However, this is irrelevant from the implementational glitch I'd like to point out now.

What did the developers get wrong?

The first clue is the header of the CreatePasswordViewController: function. Take a closer look at it.

; CreatePasswordViewController - (void)storePassword:(unsigned int)

Weird... the contents of a UITextField are usually stored in an NSString object, not an unsigned int. The set of acceptable passwords (stored in NSString objects) is much bigger than the set of unsigned integers - a crucial point in cryptographic applications. Going back to our first snippet:

CreatePasswordViewController - (void)okButtonPressed:(id)
[lots of checks for password length, complexity etc.]
MOV             R0, (_OBJC_IVAR_$_CreatePasswordViewController.leftPasswordField - 0x2FFC6) ; UITextField *leftPasswordField;
[...]
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
[...]

Consider the bits highlighted in red. The snippet uses the [NSObject hash] function:

hash
Returns an integer that can be used as a table address in a hash table structure. (required)
- (NSUInteger)hash
Return Value
An integer that can be used as a table address in a hash table structure.

This hash function, despite what it's name suggests is not a cryptographic hash. Using it therefore results in a significant shrink of the key space - from ~60 bits to ~32 bits. Due to this implementational glitch, the overall cost of mounting a brute force attack against the crypto is a fraction of the cost suggested by the complexity of the password space.

No comments:

Post a Comment