Skip to content

MASTG-DEMO-0026: Runtime Use of LAContext.canEvaluatePolicy with Frida

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

Sample

The following sample checks whether the device has a set passcode.

MastgTest.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import SwiftUI
import LocalAuthentication

struct MastgTest {

  static func mastgTest(completion: @escaping (String) -> Void) {
    if devicePasscodeSet(){
      completion("This device is protected with a passcode ✅")
    }
    else{
      completion("This device doesn't have a passcode ⚠️")
    }
  }

  static func devicePasscodeSet() -> Bool {
      // Use LAPolicy.deviceOwnerAuthentication to verify if the device has a passcode.
      // According to docs: "In iOS, policy evaluation fails with the error passcodeNotSet if the device passcode isn’t enabled"
      // Ref: https://developer.apple.com/documentation/localauthentication/lapolicy/deviceownerauthentication
      return LAContext().canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
    }

}

Steps

  1. Install the app on a device ( Installing Apps)
  2. Make sure you have Frida for iOS installed on your machine and the frida-server running on the device
  3. Run run.sh to spawn your app with Frida
  4. Click the Start button
  5. Stop the script by pressing Ctrl+C
1
2
#!/bin/bash
frida -U -f org.owasp.mastestapp.MASTestApp-iOS -l ./script.js -o output.txt
 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
Interceptor.attach(ObjC.classes.LAContext["- canEvaluatePolicy:error:"].implementation, {
  onEnter(args) {

      const LAPolicy = {
          1: ".deviceOwnerAuthenticationWithBiometrics",
          2: ".deviceOwnerAuthentication"
      };

      const policy = args[2].toInt32();
      const policyDescription = LAPolicy[policy] || "Unknown Policy";

      console.log(`\nLAContext.canEvaluatePolicy(${args[2]}) called with ${policyDescription} (${args[2]})\n`);

      // Use an arrow function so that `this` remains the same as in onEnter
      const printBacktrace = (maxLines = 8) => {
          console.log("\nBacktrace:");
          let backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE)
              .map(DebugSymbol.fromAddress);

          for (let i = 0; i < Math.min(maxLines, backtrace.length); i++) {
              console.log(backtrace[i]);
          }
      }
      printBacktrace();
  }
});

Observation

output.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
LAContext.canEvaluatePolicy(0x2) called with .deviceOwnerAuthentication (0x2)


Backtrace:
0x100f58110 MASTestApp.debug.dylib!static MastgTest.devicePasscodeSet()
0x100f5802c MASTestApp.debug.dylib!static MastgTest.mastgTest(completion:)
0x100f599d4 MASTestApp.debug.dylib!$s10MASTestApp11ContentViewV4bodyQrvg7SwiftUI05TupleD0VyAE0D0PAEE7paddingyQrAE4EdgeO3SetV_12CoreGraphics7CGFloatVSgtFQOyAE6HStackVyAGyAE4TextV_AE6SpacerVAiEEAJyQrAN_ARtFQOyAiEE12cornerRadius_11antialiasedQrAQ_SbtFQOyAiEE10background_9alignmentQrqd___AE9AlignmentVtAeHRd__lFQOyAE6ButtonVyAiEE4fontyQrAE4FontVSgFQOyAiEEAJyQrAN_ARtFQOyAiEEAJyQrAN_ARtFQOyAV_Qo__Qo__Qo_G_AE14LinearGradientVQo__Qo__Qo_tGG_Qo__AiEEAJyQrAN_ARtFQOyAiEEAY_AZQrAQ_SbtFQOyAiEEA__A0_Qrqd___A2_tAeHRd__lFQOyAiEE5frame8minWidth10idealWidth8maxWidth9minHeight11idealHeight9maxHeightA0_QrAR_A5RA2_tFQOyAE06ScrollD0VyAiEEAJyQrAN_ARtFQOyAiEEA21_A22_A23_A24_A25_A26_A27_A0_QrAR_A5RA2_tFQOyAV_Qo__Qo_G_Qo__AE5ColorVQo__Qo__Qo_tGyXEfU_A18_yXEfU_yyScMYccfU_
0x19afc6e30 SwiftUI!partial apply for implicit closure #2 in implicit closure #1 in WrappedButtonStyle.Body.body.getter
0x19b2ddf28 SwiftUI!closure #1 in PressableGestureCallbacks.dispatch(phase:state:)
0x19b047b98 SwiftUI!thunk for @escaping @callee_guaranteed () -> ()
0x19b047bc0 SwiftUI!thunk for @escaping @callee_guaranteed () -> (@out ())
0x19b047b98 SwiftUI!thunk for @escaping @callee_guaranteed () -> ()

The output reveals the use of LAcontext.canEvaluatePolicy(0x2) in the app. Policy 0x2 is .deviceOwnerAuthentication.

Evaluation

The test passes because the output shows the runtime use of LAcontext.canEvaluatePolicy(.deviceOwnerAuthentication) which verifies whether the device has passcode set.