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_msgSendMOV 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