SSL pinning on iOS works similarly to Android but uses different APIs. Most apps use NSURLSession or a third-party library like Alamofire. This guide covers bypassing each implementation.
Quick Bypass with Objection objection -g com.target.app explore ios sslpinning disable
This patches the most common implementations automatically. If traffic still doesn’t appear in Burp, you need a custom bypass.
NSURLSession — Default Pinning var NSURLCredential = ObjC .classes .NSURLCredential ;var SecTrustRef = ObjC .classes .__NSCFType ;var hook = ObjC .classes .NSURLSession ['- URLSession:didReceiveChallenge:completionHandler:' ];if (hook) { Interceptor .attach (hook.implementation , { onEnter : function (args ) { var completion = new ObjC .Block (args[4 ]); var originalImpl = completion.implementation ; completion.implementation = function (disposition, credential ) { var challenge = new ObjC .Object (args[3 ]); var trust = challenge.protectionSpace ().serverTrust (); var newCred = NSURLCredential .credentialForTrust_ (trust); return originalImpl (0 , newCred); }; } }); }
SecTrustEvaluate — Native API Hook This is the lowest-level hook — catches all pinning implementations:
var SecTrustEvaluate = Module .findExportByName ("Security" , "SecTrustEvaluate" );if (SecTrustEvaluate ) { Interceptor .replace (SecTrustEvaluate , new NativeCallback (function (trust, result ) { Memory .writeU32 (result, 1 ); return 0 ; }, 'int' , ['pointer' , 'pointer' ])); console .log ('[+] SecTrustEvaluate hooked' ); }var SecTrustEvaluateWithError = Module .findExportByName ("Security" , "SecTrustEvaluateWithError" );if (SecTrustEvaluateWithError ) { Interceptor .replace (SecTrustEvaluateWithError , new NativeCallback (function (trust, error ) { return 1 ; }, 'bool' , ['pointer' , 'pointer' ])); console .log ('[+] SecTrustEvaluateWithError hooked' ); }
TrustKit Bypass try { var TSKPinningValidator = ObjC .classes .TSKPinningValidator ; var method = TSKPinningValidator ['+ evaluateTrust:forHostname:' ]; Interceptor .attach (method.implementation , { onLeave : function (retval ) { retval.replace (0x0 ); } }); console .log ('[+] TrustKit bypassed' ); } catch (e) { console .log ('[-] TrustKit not found' ); }
Alamofire Bypass try { var block = ObjC .Block ({ retType : 'bool' , argTypes : [], implementation : function ( ) { return 1 ; } }); var cls = ObjC .classes .SessionManager ; cls['- serverTrustPolicy' ].implementation = function ( ) { return null ; }; } catch (e) {}
Universal iOS SSL Bypass Script (function ( ) { ['SecTrustEvaluate' , 'SecTrustEvaluateWithError' , 'SecTrustEvaluateAsync' ].forEach (function (name ) { var fn = Module .findExportByName ('Security' , name); if (fn) { Interceptor .replace (fn, new NativeCallback (function ( ) { var args = Array .prototype .slice .call (arguments ); if (name === 'SecTrustEvaluateWithError' ) return 1 ; if (args[1 ]) Memory .writeU32 (args[1 ], 1 ); return 0 ; }, 'int' , ['pointer' , 'pointer' ])); console .log ('[+] ' + name + ' hooked' ); } });if (ObjC .available ) { var resolve = function (args ) { var challenge = new ObjC .Object (args[3 ]); var space = challenge.protectionSpace (); var trust = space.serverTrust (); var NSURLCredential = ObjC .classes .NSURLCredential ; var cred = NSURLCredential .credentialForTrust_ (trust); var block = new ObjC .Block (args[4 ]); block (0 , cred); return null ; }; try { var m1 = ObjC .classes .NSURLSession ['- URLSession:didReceiveChallenge:completionHandler:' ]; Interceptor .attach (m1.implementation , { onEnter : resolve }); } catch (e) {} try { var m2 = ObjC .classes .NSURLConnection ['- connection:willSendRequestForAuthenticationChallenge:' ]; Interceptor .attach (m2.implementation , { onEnter : function (args ) { var challenge = new ObjC .Object (args[3 ]); var trust = challenge.protectionSpace ().serverTrust (); var cred = ObjC .classes .NSURLCredential .credentialForTrust_ (trust); new ObjC .Object (args[2 ]).useCredential_forAuthenticationChallenge_ (cred, challenge); } }); } catch (e) {} }console .log ('[*] iOS SSL pinning bypass loaded' ); })();
frida -U -f com.target.app -l universal-ios-ssl-bypass.js --no-pause
SSL Kill Switch 2 (Jailbreak Tweak) For jailbroken devices, install SSL Kill Switch 2 from Cydia:
Source: https:// cloud.githubusercontent.com/assets/ 581994 / Package: SSL Kill Switch 2
Enable in Settings → SSL Kill Switch 2 → Disable Certificate Validation.
Rebuilding IPA without Pinning (Static Patch) For apps where Frida is detected:
unzip target.ipa -d extracted/ codesign -f -s "iPhone Developer: Your Name" extracted/Payload/*.app zip -r patched.ipa extracted/ ideviceinstaller -i patched.ipa
Jailbreak Detection Bypass (iOS) Java .perform (function ( ) {}); ObjC .schedule (ObjC .mainQueue , function ( ) { var methods = [ 'isJailbroken' , 'checkJailbreak' , 'isDeviceJailbroken' , 'jailbreakDetection' , 'detectJailbreak' ]; ObjC .enumerateLoadedClasses ({ onMatch : function (name, handle ) { var cls = ObjC .classes [name]; methods.forEach (function (method ) { try { var m = cls['- ' + method]; if (m) { Interceptor .attach (m.implementation , { onLeave : function (retval ) { retval.replace (0 ); } }); } } catch (e) {} }); }, onComplete : function ( ) {} }); var NSFileManager = ObjC .classes .NSFileManager ; NSFileManager ['- fileExistsAtPath:' ].implementation = function (path ) { var jbPaths = ['/Applications/Cydia.app' , '/usr/bin/ssh' , '/etc/apt' , '/bin/bash' , '/private/var/lib/apt' , '/private/var/jb' ]; if (jbPaths.some (p => path.toString ().includes (p))) { return 0 ; } return this ['- fileExistsAtPath:' ](path); }; });
No comments yet. Be the first.