Skip to content

MASTG-DEMO-0076: Keyboard Caching Not Prevented for Sensitive Data with r2

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

Sample

The code snippet below creates multiple UI text inputs on the screen.

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
import SwiftUI
import UIKit

struct MastgTest {

  static func mastgTest(completion: @escaping (String) -> Void) {
    DispatchQueue.main.async {
      // Build the alert
      let alert = UIAlertController(
        title: "Enter sensitive text",
        message: "Please fill in all fields.",
        preferredStyle: .alert
      )

      alert.addTextField { tf in
        tf.placeholder = "Name"
        tf.autocorrectionType = .default
        tf.spellCheckingType = .no
        tf.accessibilityIdentifier = "name_field"
      }
      alert.addTextField { tf in
        tf.placeholder = "E-Mail"
        tf.autocorrectionType = .no
        tf.accessibilityIdentifier = "email_field"
      }

      alert.addTextField { tf in
        tf.placeholder = "Password"
        tf.isSecureTextEntry = true
        tf.accessibilityIdentifier = "password_field"
      }

      alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
        let first = alert.textFields?[0].text ?? ""
        let second = alert.textFields?[1].text ?? ""
        let third = alert.textFields?[2].text ?? ""
        completion("Submitted values: '\(first)', '\(second)', \(third)")
      }))

      // Present from the topmost view controller
      if let presenter = topViewController() {
        presenter.present(alert, animated: true, completion: nil)
      } else {
        completion("Failed to present alert (no active view controller).")
      }
    }
  }

  // Finds the currently visible view controller to present from
  private static func topViewController(
    base: UIViewController? = {
      let scenes = UIApplication.shared.connectedScenes
        .compactMap { $0 as? UIWindowScene }
      let keyWindow = scenes
        .flatMap { $0.windows }
        .first { $0.isKeyWindow }
      return keyWindow?.rootViewController
    }()
  ) -> UIViewController? {
    if let nav = base as? UINavigationController {
      return topViewController(base: nav.visibleViewController)
    }
    if let tab = base as? UITabBarController {
      return topViewController(base: tab.selectedViewController)
    }
    if let presented = base?.presentedViewController {
      return topViewController(base: presented)
    }
    return base
  }
}

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.
1
2
#!/bin/bash
r2 -q -i textinputs.r2 -A MASTestApp > output.asm
 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
e asm.bytes=false
e scr.color=false
e asm.var=false

?e Print flags about \"UITextField,UITextView,UISearchBar\"
f~+UITextField,UITextView,UISearchBar

?e Print xrefs to 0x100010180
axt @ 0x100010180

?e Print flags about \"autocorrectionType,setSecureTextEntry,spellCheckingType\"
f~+autocorrectionType,setSecureTextEntry,spellCheckingType

?e

?e Print xrefs to 0x100010110
axt @ 0x100010110

?e Print xrefs to 0x100010120
axt @ 0x100010120

?e Print xrefs to 0x100010128
axt @ 0x100010128

?e

?e Print disassembly around \"name_field\" in the function
pd--10 @ 0x100004550

?e

?e Print disassembly around \"email_field\" in the function
pd--10 @ 0x100004604

?e

?e Print disassembly around \"password_field\" in the function
pd--10 @ 0x1000046b0

Observation

The output reveals:

  • 3 text input fields of type UITextField at addresses 0x1000055d0, 0x10000563c, and 0x1000056c0.
  • 1 call to setSecureTextEntry(...) at 0x1000046b0.
  • 2 calls to setAutocorrectionType(...) at 0x100004550 and 0x100004604.
  • 1 call to setSpellCheckingType(...) at 0x100004564.
output.asm
 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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
Print flags about "+UITextField,UITextView,UISearchBar"
0x100010180 8 reloc.UITextField

Print xrefs to 0x100010180
sym.func.100004704 0x100004760 [ICOD:r--] add x2, x2, reloc.UITextField
sym.func.100004704 0x10000481c [ICOD:r--] add x2, x2, reloc.UITextField
sym.func.100004704 0x1000048e0 [ICOD:r--] add x2, x2, reloc.UITextField
sym.func.10000558c 0x1000055d0 [DATA:r--] ldr x0, reloc.UITextField
sym.func.10000558c 0x10000563c [DATA:r--] ldr x0, reloc.UITextField
sym.func.10000558c 0x1000056c0 [ICOD:r--] add x2, x2, reloc.UITextField

Print flags about "autocorrectionType,setSecureTextEntry,spellCheckingType"
0x10000a125 23 str.setAutocorrectionType:
0x10000a13c 20 str.setSecureTextEntry:
0x10000a150 22 str.setSpellCheckingType:
0x100010110 8 reloc.fixup.setSecureTextEntry:
0x100010120 8 reloc.fixup.setAutocorrectionType:
0x100010128 8 reloc.fixup.setSpellCheckingType:

Print xrefs to 0x100010110
sym.func.10000465c 0x1000046b0 [DATA:r--] ldr x1, reloc.fixup.setSecureTextEntry:
Print xrefs to 0x100010120
sym.func.10000450c 0x100004550 [DATA:r--] ldr x1, reloc.fixup.setAutocorrectionType:
sym.func.1000045bc 0x100004604 [DATA:r--] ldr x1, reloc.fixup.setAutocorrectionType:
Print xrefs to 0x100010128
sym.func.10000450c 0x100004564 [DATA:r--] ldr x1, reloc.fixup.setSpellCheckingType:

Print disassembly around "name_field" in the function
           0x100004528      bl sym.imp.Foundationbool_...ridgeToObjectiveCSo8NSStringCyF_ ; Foundationbool(...ridgeToObjectiveCSo8NSStringCyF)
           0x10000452c      mov x20, x0
           0x100004530      adrp x8, segment.__DATA                   ; 0x100010000
           0x100004534      ldr x1, [x8, 0x108]                       ; [0x100010108:4]=0xa01b ; reloc.fixup.setPlaceholder: ; char *selector
           0x100004538      mov x0, x19                               ; void *instance
           0x10000453c      mov x2, x20
           0x100004540      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
           0x100004544      mov x0, x20                               ; void *instance
           0x100004548      bl sym.imp.objc_release                   ; void objc_release(void *instance)
           0x10000454c      adrp x8, segment.__DATA                   ; 0x100010000
           0x100004550      ldr x1, [x8, 0x120]                       ; [0x100010120:4]=0xa125 ; reloc.fixup.setAutocorrectionType: ; char *selector
           0x100004554      mov x0, x19                               ; void *instance
           0x100004558      mov x2, 0
           0x10000455c      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
           0x100004560      adrp x8, segment.__DATA                   ; 0x100010000
           0x100004564      ldr x1, [x8, 0x128]                       ; [0x100010128:4]=0xa150 ; reloc.fixup.setSpellCheckingType: ; char *selector
           0x100004568      mov x0, x19                               ; void *instance
           0x10000456c      mov w2, 1
           0x100004570      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
           0x100004574      mov x0, 0x616e                            ; 'na'

Print disassembly around "email_field" in the function
           0x1000045dc      bl sym.imp.Foundationbool_...ridgeToObjectiveCSo8NSStringCyF_ ; Foundationbool(...ridgeToObjectiveCSo8NSStringCyF)
           0x1000045e0      mov x20, x0
           0x1000045e4      adrp x8, segment.__DATA                   ; 0x100010000
           0x1000045e8      ldr x1, [x8, 0x108]                       ; [0x100010108:4]=0xa01b ; reloc.fixup.setPlaceholder: ; char *selector
           0x1000045ec      mov x0, x19                               ; void *instance
           0x1000045f0      mov x2, x20
           0x1000045f4      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
           0x1000045f8      mov x0, x20                               ; void *instance
           0x1000045fc      bl sym.imp.objc_release                   ; void objc_release(void *instance)
           0x100004600      adrp x8, segment.__DATA                   ; 0x100010000
           0x100004604      ldr x1, [x8, 0x120]                       ; [0x100010120:4]=0xa125 ; reloc.fixup.setAutocorrectionType: ; char *selector
           0x100004608      mov x0, x19                               ; void *instance
           0x10000460c      mov w2, 1
           0x100004610      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
           0x100004614      mov x0, 0x6d65                            ; 'em'
           0x100004618      movk x0, 0x6961, lsl 16                   ; 'ai'
           0x10000461c      movk x0, 0x5f6c, lsl 32                   ; 'l_'
           0x100004620      movk x0, 0x6966, lsl 48                   ; 'fi'
           0x100004624      mov x1, 0x6c65                            ; 'el'
           0x100004628      movk x1, 0x64, lsl 16                     ; 'd'

Print disassembly around "password_field" in the function
           0x100004688      bl sym.imp.Foundationbool_...ridgeToObjectiveCSo8NSStringCyF_ ; Foundationbool(...ridgeToObjectiveCSo8NSStringCyF)
           0x10000468c      mov x21, x0
           0x100004690      adrp x8, segment.__DATA                   ; 0x100010000
           0x100004694      ldr x1, [x8, 0x108]                       ; [0x100010108:4]=0xa01b ; reloc.fixup.setPlaceholder: ; char *selector
           0x100004698      mov x0, x19                               ; void *instance
           0x10000469c      mov x2, x21
           0x1000046a0      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
           0x1000046a4      mov x0, x21                               ; void *instance
           0x1000046a8      bl sym.imp.objc_release                   ; void objc_release(void *instance)
           0x1000046ac      adrp x8, segment.__DATA                   ; 0x100010000
           0x1000046b0      ldr x1, [x8, 0x110]                       ; [0x100010110:4]=0xa13c ; reloc.fixup.setSecureTextEntry: ; char *selector
           0x1000046b4      mov x0, x19                               ; void *instance
           0x1000046b8      mov w2, 1
           0x1000046bc      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
           0x1000046c0      mov x1, 0x665f                            ; '_f'
           0x1000046c4      movk x1, 0x6569, lsl 16                   ; 'ie'
           0x1000046c8      movk x1, 0x646c, lsl 32                   ; 'ld'
           0x1000046cc      movk x1, 0xee00, lsl 48
           0x1000046d0      orr x0, x20, 0x20
           0x1000046d4      bl sym.imp.Foundationbool_...ridgeToObjectiveCSo8NSStringCyF_ ; Foundationbool(...ridgeToObjectiveCSo8NSStringCyF)

Evaluation

The test fails because the static analysis reveals:

Interpreting the Disassembly:

Although MastgTest.swift is written in Swift, it interacts with UIKit (an Objective-C framework). The compiler translates these interactions into calls to the objc_msgSend function. We analyze the arguments passed to this function using the ARM64 calling convention:

  • x0 register: holds self (the instance of the UITextField).
  • x1 register: holds the selector (the method name).
  • x2 register: holds the argument passed to that method.

1. Name Field (FAIL):

At address 0x100004550, the binary loads the selector setAutocorrectionType:.

Immediately after, at address 0x100004558, the instruction mov x2, 0 sets the argument to 0.

According to UITextInputTraits.h, 0 corresponds to UITextAutocorrectionTypeDefault (the following command requires Xcode and macOS):

grep UITextAutocorrectionType /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UITextInputTraits.h

typedef NS_ENUM(NSInteger, UITextAutocorrectionType) {
    UITextAutocorrectionTypeDefault, // 0
    UITextAutocorrectionTypeNo, // 1
    UITextAutocorrectionTypeYes // 2

Alternatively, you can view the full UITextInputTraits.h header online in public SDK mirrors on GitHub such as GitHub - xybp888/iOS-SDKs.

This confirms that for the input field labeled "Name" (placeholder string constructed earlier in the function), the app explicitly allows the default behavior, enabling the keyboard cache.

Note that there is a call to setSpellCheckingType: at address 0x100004564, and it sets the argument to 1 (i.e., UITextSpellCheckingTypeNo), which correctly disables spell checking. However, since autocorrection is still set to default, this input remains eligible for keyboard caching.

2. Email Field (FAIL):

For the input field labeled "E-Mail", the selector setAutocorrectionType: is loaded at 0x100004604. The argument is set at 0x10000460c via mov w2, 1, corresponding to UITextAutocorrectionTypeNo.

However, no call to setSpellCheckingType(.no) exists for this field. Since spellCheckingType remains at its default value, this input is still eligible for keyboard caching and therefore fails the test.

3. Password Field (PASS):

The selector setSecureTextEntry: is loaded at address 0x1000046b0 and its argument is set to 1 at address 0x1000046b8 using mov w2, 1.

This confirms that isSecureTextEntry is enabled for the field labeled "Password", which disables keyboard caching and correctly protects sensitive input.