Skip to content

MASTG-DEMO-0045: Uses of kSecAccessControlBiometryCurrentSet with r2

Download MASTG-DEMO-0045 IPA Open MASTG-DEMO-0045 Folder Build MASTG-DEMO-0045 IPA

Sample

The following sample uses the kSecAccessControlBiometryAny flag, which is part of the biometric authentication API and can allow unauthorized access. This flag does not ensure that the associated keychain item becomes inaccessible when changes are made to the biometric database (e.g., when a new fingerprint or face is added). Consequently, users who enroll their biometric data after the item is created can unlock it.

MastgTest.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import SwiftUI
import LocalAuthentication
import Security

struct MastgTest {

  static func mastgTest(completion: @escaping (String) -> Void) {
    let account = "com.mastg.sectoken"
    let tokenData = "8767086b9f6f976g-a8df76".data(using: .utf8)!

    // 1. Store the token in the Keychain with ACL flags
    // 1a. Create an accesscontrol object requiring user presence
    guard let accessControl = SecAccessControlCreateWithFlags(
      nil,
      kSecAttrAccessibleWhenUnlocked,
      .biometryAny,
      nil
    ) else {
      completion("❌ Failed to create access control")
      return
    }

    // 1b. Build your additem query
    // Optional: you may provide a customized context to alter the default config, e.g. to set a "reuse duration".
    // See https://developer.apple.com/documentation/localauthentication/accessing-keychain-items-with-face-id-or-touch-id#Optionally-Provide-a-Customized-Context
    // Keychain services automatically makes use of the LocalAuthentication framework, even if you don't provide one.
    //
    //  let context = LAContext()
    //  context.touchIDAuthenticationAllowableReuseDuration = 10
    let storeQuery: [String: Any] = [
      kSecClass as String:          kSecClassGenericPassword,
      kSecAttrAccount as String:    account,
      kSecValueData as String:      tokenData,
      kSecAttrAccessControl as String: accessControl,
      // kSecUseAuthenticationContext as String: context,
    ]

    // Before adding, we delete any existing item
    SecItemDelete(storeQuery as CFDictionary)
    let storeStatus = SecItemAdd(storeQuery as CFDictionary, nil)
    guard storeStatus == errSecSuccess else {
      completion("❌ Failed to store token in Keychain (status \(storeStatus))")
      return
    }

    // 2. Now let's retrieve the token
    // Optional: you may provide a context with a localized reason.
    // See https://developer.apple.com/documentation/localauthentication/accessing-keychain-items-with-face-id-or-touch-id#Provide-a-Prompt-When-Reading-the-Item
    // Keychain services will use LAContext to prompt the user even if you don't provide one.
    //
    // let context = LAContext()
    // context.localizedReason = "Access your token from the keychain"
    let fetchQuery: [String: Any] = [
      kSecClass as String:         kSecClassGenericPassword,
      kSecAttrAccount as String:   account,
      kSecReturnData as String:    true,
      kSecMatchLimit as String:    kSecMatchLimitOne,
      //kSecUseAuthenticationContext as String: context,
    ]

    var result: CFTypeRef?
    let fetchStatus = SecItemCopyMatching(fetchQuery as CFDictionary, &result)

    if fetchStatus == errSecSuccess,
       let data = result as? Data,
       let token = String(data: data, encoding: .utf8) {
      completion("✅ Retrieved token: \(token)")
    } else {
      completion("❌ Authentication failed or token inaccessible (status \(fetchStatus))")
    }
  }
}

Steps

  1. Unzip the app package and locate the main binary file ( Exploring the App Package), which in this case is ./Payload/MASTestApp.app/MASTestApp.
  2. Run run.sh.
biometricAuthenticationEnrollmentChange.r2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
e asm.bytes=false
e scr.color=false
e asm.var=false

?e Print xrefs to \'Run analysis\"
aaa

?e Print xrefs to \'SecAccessControlCreateWithFlags\"
axt @ sym.imp.SecAccessControlCreateWithFlags

?e

?e Print disassembly around \"SecAccessControlCreateWithFlags\" in the function
pdf @  0x100004194 | grep -C 5 "SecAccessControlCreateWithFlags"
run.sh
1
2
#!/bin/bash
r2 -q -i biometricAuthenticationFallback.r2 -A MASTestApp > output.asm

Observation

output.asm
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Print xrefs to 'Run analysis"
Print xrefs to 'SecAccessControlCreateWithFlags"
sym.MASTestApp.MastgTest.createAccessControl.Sec.Ref.d_n 0x100004194 [CALL:--x] bl sym.imp.SecAccessControlCreateWithFlags

Print disassembly around "SecAccessControlCreateWithFlags" in the function
           0x100004180      mov x19, x0
           0x100004184      add x3, sp, 0x10
           0x100004188      mov x0, 0
           0x10000418c      mov x1, x19
           0x100004190      mov w2, 2
           0x100004194      bl sym.imp.SecAccessControlCreateWithFlags
       ┌─< 0x100004198      cbz x0, 0x1000041ac
          0x10000419c      mov x20, x0
          0x1000041a0      mov x0, x19                               ; void *instance
          0x1000041a4      bl sym.imp.objc_release                   ; void objc_release(void *instance)
      ┌──< 0x1000041a8      b 0x100004310

The output reveals the use of SecAccessControlCreateWithFlags(allocator, protection, flags, error) in the app. In this demo, we focus on the flags argument because it specifies the Access Control. flags is the third argument of the function, so it's at x2/w2 register. By looking at the output, we can see that w2 register holds value of 2.

mov w2, 2
bl sym.imp.SecAccessControlCreateWithFlags

The flags is an enum of SecAccessControlCreateFlags. 2 corresponds to kSecAccessControlBiometryAny (see LAPublicDefines.h). This means that the app invokes SecAccessControlCreateWithFlags(..., kSecAccessControlBiometryAny), which means it will accept new biometric added in the system settings.

Evaluation

The test fails because the output shows a reference to SecAccessControlCreateWithFlags with kSecAccessControlBiometryAny, which accepts any additional biometrics added after the Keychain entry was created.

When it is required that the associated keychain item become inaccessible when changes are made to the biometric database (e.g., when a new fingerprint or face is added), the app must use thekSecAccessControlBiometryCurrentSet flag instead.