From 17cd8717147be21e177a44a91f4e5bf8fca504a5 Mon Sep 17 00:00:00 2001 From: CPol Date: Mon, 17 May 2021 16:11:05 +0000 Subject: [PATCH] GitBook: [master] one page modified --- ios-pentesting/README.md | 187 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/ios-pentesting/README.md b/ios-pentesting/README.md index d32ead26..fbe3be7d 100644 --- a/ios-pentesting/README.md +++ b/ios-pentesting/README.md @@ -643,6 +643,193 @@ In the open source bitcoin wallet app, [Bither](https://github.com/bither/bither This is PIN is stored in the file `net.bither.plist` inside the **pin\_code** **key**. If you clear this key from that plist in the backup and restores the backup, you will be able to access the wallet. +## Local Authentication + +The tester should be aware that **local authentication should always be enforced at a remote endpoint** or based on a cryptographic primitive. Attackers can easily bypass local authentication if no data returns from the authentication process. + +The [**Local Authentication framework**](https://developer.apple.com/documentation/localauthentication) ****provides a set of APIs for developers to extend an authentication dialog to a user. In the context of connecting to a remote service, it is possible \(and recommended\) to leverage the [keychain](https://developer.apple.com/library/content/documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html) for implementing local authentication. + +The **fingerprint ID** sensor is operated by the [SecureEnclave security coprocessor](https://www.blackhat.com/docs/us-16/materials/us-16-Mandt-Demystifying-The-Secure-Enclave-Processor.pdf) and does not expose fingerprint data to any other parts of the system. Next to Touch ID, Apple introduced _Face ID_: which allows authentication based on facial recognition. + +Developers have two options for incorporating Touch ID/Face ID authentication: + +* `LocalAuthentication.framework` is a high-level API that can be used to **authenticate the user via Touch ID**. The app can't access any data associated with the enrolled fingerprint and is notified only whether authentication was successful. +* `Security.framework` is a lower level API to access [keychain services](https://developer.apple.com/documentation/security/keychain_services). This is a secure option if your app needs to **protect some secret data with biometric authentication**, since the access control is managed on a system-level and can not easily be bypassed. `Security.framework` has a C API, but there are several [open source wrappers available](https://www.raywenderlich.com/147308/secure-ios-user-data-keychain-touch-id), making access to the keychain as simple as to NSUserDefaults. + +{% hint style="danger" %} +Please be aware that using either the `LocalAuthentication.framework` or the `Security.framework`, will be a control that can be bypassed by an attacker as it does only return a boolean and no data to proceed with. See [Don't touch me that way, by David Lindner et al](https://www.youtube.com/watch?v=XhXIHVGCFFM) for more details. +{% endhint %} + +### Local Authentication Framework + +Developers can display an **authentication prompt** by utilizing the function `evaluatePolicy` of the `LAContext` class. Two available policies define acceptable forms of authentication: + +* `deviceOwnerAuthentication`\(Swift\) or `LAPolicyDeviceOwnerAuthentication`\(Objective-C\): When available, the user is prompted to perform Touch ID authentication. If Touch ID is not activated, the device passcode is requested instead. If the device passcode is not enabled, policy evaluation fails. +* `deviceOwnerAuthenticationWithBiometrics` \(Swift\) or `LAPolicyDeviceOwnerAuthenticationWithBiometrics`\(Objective-C\): Authentication is restricted to biometrics where the user is prompted for Touch ID. + +The **`evaluatePolicy` function returns a boolean** value indicating whether the user has authenticated successfully. Which means that it can be easily bypassed \(see below\) + +### Local Authentication using Keychain + +The **iOS keychain APIs can \(and should\) be used to implement local authentication**. During this process, the app stores either a secret authentication token or another piece of secret data identifying the user in the keychain. In order to authenticate to a remote service, the user must unlock the keychain using their passphrase or fingerprint to obtain the secret data. + +The keychain allows saving items with the special `SecAccessControl` attribute, which will allow access to the item from the keychain only after the user has passed Touch ID authentication \(or passcode, if such a fallback is allowed by attribute parameters\). + +In the following example we will save the string "test\_strong\_password" to the keychain. The string can be accessed only on the current device while the passcode is set \(`kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` parameter\) and after Touch ID authentication for the currently enrolled fingers only \(`SecAccessControlCreateFlags.biometryCurrentSet` parameter\): + +{% tabs %} +{% tab title="Swift" %} +```swift +// 1. create AccessControl object that will represent authentication settings + +var error: Unmanaged? + +guard let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, + kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, + SecAccessControlCreateFlags.biometryCurrentSet, + &error) else { + // failed to create AccessControl object + + return +} + +// 2. define keychain services query. Pay attention that kSecAttrAccessControl is mutually exclusive with kSecAttrAccessible attribute + +var query: [String: Any] = [:] + +query[kSecClass as String] = kSecClassGenericPassword +query[kSecAttrLabel as String] = "com.me.myapp.password" as CFString +query[kSecAttrAccount as String] = "OWASP Account" as CFString +query[kSecValueData as String] = "test_strong_password".data(using: .utf8)! as CFData +query[kSecAttrAccessControl as String] = accessControl + +// 3. save item + +let status = SecItemAdd(query as CFDictionary, nil) + +if status == noErr { + // successfully saved +} else { + // error while saving +} +``` +{% endtab %} + +{% tab title="Objective-C" %} +```objectivec + // 1. create AccessControl object that will represent authentication settings + CFErrorRef *err = nil; + + SecAccessControlRef sacRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, + kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, + kSecAccessControlUserPresence, + err); + + // 2. define keychain services query. Pay attention that kSecAttrAccessControl is mutually exclusive with kSecAttrAccessible attribute + NSDictionary* query = @{ + (_ _bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrLabel: @"com.me.myapp.password", + (__bridge id)kSecAttrAccount: @"OWASP Account", + (__bridge id)kSecValueData: [@"test_strong_password" dataUsingEncoding:NSUTF8StringEncoding], + (__bridge id)kSecAttrAccessControl: (__bridge_transfer id)sacRef + }; + + // 3. save item + OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, nil); + + if (status == noErr) { + // successfully saved + } else { + // error while saving + } +``` +{% endtab %} +{% endtabs %} + +Now we can request the saved item from the keychain. Keychain services will present the authentication dialog to the user and return data or nil depending on whether a suitable fingerprint was provided or not. + +{% tabs %} +{% tab title="Swift" %} +```swift +// 1. define query +var query = [String: Any]() +query[kSecClass as String] = kSecClassGenericPassword +query[kSecReturnData as String] = kCFBooleanTrue +query[kSecAttrAccount as String] = "My Name" as CFString +query[kSecAttrLabel as String] = "com.me.myapp.password" as CFString +query[kSecUseOperationPrompt as String] = "Please, pass authorisation to enter this area" as CFString + +// 2. get item +var queryResult: AnyObject? +let status = withUnsafeMutablePointer(to: &queryResult) { + SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) +} + +if status == noErr { + let password = String(data: queryResult as! Data, encoding: .utf8)! + // successfully received password +} else { + // authorization not passed +} +``` +{% endtab %} + +{% tab title="Objective-C" %} +```objectivec +// 1. define query +NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecReturnData: @YES, + (__bridge id)kSecAttrAccount: @"My Name1", + (__bridge id)kSecAttrLabel: @"com.me.myapp.password", + (__bridge id)kSecUseOperationPrompt: @"Please, pass authorisation to enter this area" }; + +// 2. get item +CFTypeRef queryResult = NULL; +OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &queryResult); + +if (status == noErr){ + NSData* resultData = ( __bridge_transfer NSData* )queryResult; + NSString* password = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding]; + NSLog(@"%@", password); +} else { + NSLog(@"Something went wrong"); +} +``` +{% endtab %} +{% endtabs %} + +### Detection + +Usage of frameworks in an app can also be detected by analyzing the app binary's list of shared dynamic libraries. This can be done by using `otool`: + +```bash +$ otool -L .app/ +``` + +If `LocalAuthentication.framework` is used in an app, the output will contain both of the following lines \(remember that `LocalAuthentication.framework` uses `Security.framework` under the hood\): + +```bash +/System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication +/System/Library/Frameworks/Security.framework/Security +``` + +If `Security.framework` is used, only the second one will be shown. + +### Local Authentication Framework Bypass + +[Objection Biometrics Bypass](https://github.com/sensepost/objection/wiki/Understanding-the-iOS-Biometrics-Bypass) can be used to bypass LocalAuthentication. Objection **uses Frida to instrument the `evaluatePolicy` function so that it returns `True`** even if authentication was not successfully performed. Use the `ios ui biometrics_bypass` command to bypass the insecure biometric authentication. Objection will register a job, which will replace the `evaluatePolicy` result. It will work in both, Swift and Objective-C implementations. + +```bash +...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # ios ui biometrics_bypass +(agent) Registering job 3mhtws9x47q. Type: ios-biometrics-disable +...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # (agent) [3mhtws9x47q] Localized Reason for auth requirement: Please authenticate yourself +(agent) [3mhtws9x47q] OS authentication response: false +(agent) [3mhtws9x47q] Marking OS response as True instead +(agent) [3mhtws9x47q] Biometrics bypass hook complete +``` + +If vulnerable, the module will automatically bypass the login form. + ### Custom URI Handlers / Deeplinks / Custom Schemes A custom URI handler is used to invoke an application from an URI.