Blog · C.02 · Mobile Security Deep-Dive

Bypassing certificate pinning on iOS in 2026 - Frida, Objection, KeychainItem

Rootless jailbreaks, TrustKit, Network.framework, and custom pinning implementations. A full practitioner walkthrough of every technique we use in iOS mobile pen tests today - including what to do when Objection's sslpinning disable silently fails.

iOS Frida Objection TrustKit MASVS Mobile Pen Test
Tools: Frida · Objection · SSL Kill Switch 3 · Dopamine · Palera1n · Burp Suite · Proxyman · MITM Proxy · KeychainDumper · Bagbak · Ghidra · IDA Pro · r2frida · Clutch Tools: Frida · Objection · SSL Kill Switch 3 · Dopamine · Palera1n · Burp Suite · Proxyman · MITM Proxy · KeychainDumper · Bagbak · Ghidra · IDA Pro · r2frida · Clutch
// TL;DR

Certificate pinning bypass in 2026 is a layered problem. Start with Objection's one-liner - it handles NSURLSession and most third-party libraries. When it fails, identify the pinning implementation (TrustKit, Alamofire, custom SecTrustEvaluate, Network.framework) and write a targeted Frida script. On A15+ chips, rootless jailbreaks (Dopamine, Palera1n) still work. For hardened apps with jailbreak detection, use Frida gadget injection on a re-signed IPA. Always extract KeychainItem secrets post-bypass - they're often more valuable than the traffic.

// 01 What certificate pinning does - and why apps get it wrong

Certificate pinning is a mechanism that ties an app to a specific TLS certificate or public key, preventing a man-in-the-middle attacker (or pen tester) from substituting their own certificate, even if that certificate is signed by a trusted CA. The goal is to ensure traffic can only be intercepted by the intended server - not by a corporate proxy, a rogue CA, or a security researcher with Burp Suite.

For mobile pen testing, this is the gating challenge. Before you can intercept and manipulate API traffic, you need to bypass pinning. In 2021, this was often trivial - Objection's ios sslpinning disable handled the majority of applications. In 2026, the landscape is more varied: some apps pin correctly and deeply; others pin inconsistently across API endpoints; many use third-party SDKs that add their own layer of pinning logic on top of the OS implementation.

4Distinct iOS pinning implementation patterns in active production apps
~60%Of iOS apps Objection handles with the single sslpinning disable command
A15+Chip generation still supported by rootless jailbreaks in 2026
FridaRequired for custom implementations - no substitute for targeted scripting

// 02 Jailbreak landscape in 2026: rootless is the standard

The shift to rootless jailbreaks (introduced with Dopamine and Palera1n in 2022-2023) changed the mobile pen testing workflow significantly. Rootless jailbreaks don't write to the root filesystem - instead they store all modifications in a dedicated /var/jb path. This has two implications: jailbreak detection evasion is harder (the jailbreak leaves fewer filesystem traces), and not all Cydia packages have been ported to rootless.

Recommended 2026 jailbreak setup

A14 and older

Checkra1n / unc0ver

Checkm8 bootrom exploit-based. Persistent, full filesystem access. Most Cydia packages work. Preferred for A9-A14 if available. No longer updated but remains functional for supported chips.

A15 / A16 / A17

Dopamine / Palera1n

Rootless architecture. Requires Sileo or Zebra as package manager. Frida, SSL Kill Switch 3, and most pen testing tools have been ported. iOS 16.x-17.x coverage is good; 18.x is still in progress as of 2026.

No jailbreak needed

Frida gadget (re-signed IPA)

Inject FridaGadget.dylib into the app binary, re-sign with your developer certificate, install via AltStore or Sideloadly. Works on any device including non-jailbroken stock iOS. Best for apps that detect jailbreaks.

Simulator

iOS Simulator + Frida

For apps without device-attestation or biometric requirements, the simulator is often the fastest path. Frida attaches directly - no jailbreak, no re-signing. Not suitable for apps that refuse to run in simulator.

// 03 Start here: Objection's SSL pinning disable

Objection wraps Frida with a CLI interface. For roughly 60% of production iOS apps, the following workflow is all you need:

SHELL# 1. Ensure Frida server is running on the device (via jailbreak or gadget)
frida-ps -Ua                    # list running apps

# 2. Attach Objection to the target app
objection --gadget "com.example.BankingApp" explore

# 3. Inside Objection REPL - disable SSL pinning
ios sslpinning disable

# 4. Verify - route traffic through Burp with your CA installed
# If HTTPS traffic is now visible → bypass successful

# 5. If not working, check what Objection is hooking:
ios sslpinning disable --quiet   # suppress verbose - sometimes fixes hook conflicts

Under the hood, Objection hooks a broad set of iOS SSL/TLS delegation methods: URLSession:didReceiveChallenge:completionHandler:, SecTrustEvaluate, SecTrustEvaluateWithError, and common third-party libraries including Alamofire, AFNetworking, and TrustKit. When it works, it works instantly. When it doesn't, you need to know why.

// 04 When Objection silently fails: identifying the pinning implementation

Objection's pinning disable can fail for several reasons: the app uses a pinning implementation Objection doesn't hook, jailbreak detection is killing Frida before the hook fires, the app is using Network.framework instead of URLSession, or a custom native implementation using Security framework calls directly.

Step 1: Identify the pinning mechanism

Run otool -L or use rabin2 -l on the decrypted IPA binary to check linked libraries:

SHELL# Decrypt the IPA first (if from App Store)
bagbak --output ~/Desktop/decrypted com.example.BankingApp

# Check linked frameworks
otool -L BankingApp.app/BankingApp | grep -i -E "trust|pin|ssl|network|alamofire"

# Common indicators:
# TrustKit.framework     → TrustKit pinning
# Alamofire.framework    → Alamofire + ServerTrustPolicy
# Network.framework      → NWConnection / NWParameters (Network.framework)
# No external libs       → custom SecTrustEvaluate or SecPolicyCreate

# Search for pinning-related strings in the binary
strings BankingApp.app/BankingApp | grep -i -E "pin|publicKey|certHash|spki"

Step 2: Trace with Frida

SHELL# Trace Security framework calls to see what's actually being invoked
frida-trace -U -n "BankingApp" \
  -m "*[* SecTrustEvaluate*]" \
  -m "*[* SecTrustCopyPublicKey*]" \
  -m "*[* SecCertificate*]" \
  -m "*[NSURLSession URLSession:*Challenge*]"

// 05 Frida scripts for common pinning implementations

Universal SecTrustEvaluate hook

This script intercepts the core Security framework function used by nearly all iOS pinning implementations and forces it to return success. It works for apps using the raw Security framework API directly, bypassing any wrapper library.

JAVASCRIPT (FRIDA)if (ObjC.available) {
  // Hook SecTrustEvaluateWithError (iOS 12+)
  const SecTrustEvaluateWithError = Module.findExportByName(
    'Security', 'SecTrustEvaluateWithError'
  );
  if (SecTrustEvaluateWithError) {
    Interceptor.replace(SecTrustEvaluateWithError,
      new NativeCallback(function(trust, error) {
        // Clear any error pointer and return true (trusted)
        if (error.isNull() === false) {
          error.writePointer(NULL);
        }
        return 1; // kSecTrustResultProceed
      }, 'int', ['pointer', 'pointer'])
    );
    console.log('[+] SecTrustEvaluateWithError hooked');
  }

  // Hook legacy SecTrustEvaluate (pre iOS 12)
  const SecTrustEvaluate = Module.findExportByName(
    'Security', 'SecTrustEvaluate'
  );
  if (SecTrustEvaluate) {
    Interceptor.replace(SecTrustEvaluate,
      new NativeCallback(function(trust, result) {
        result.writeU32(4); // kSecTrustResultProceed
        return 0; // errSecSuccess
      }, 'int', ['pointer', 'pointer'])
    );
    console.log('[+] SecTrustEvaluate hooked');
  }
}

TrustKit bypass

TrustKit is a popular open-source pinning library that operates at a higher level - it intercepts NSURLSession challenges and compares the server certificate's SPKI hash against a bundled allowlist. Bypassing it requires hooking TrustKit's validation method, not the underlying Security framework:

JAVASCRIPT (FRIDA)if (ObjC.available) {
  const TSKPinningValidator = ObjC.classes['TSKPinningValidator'];
  if (TSKPinningValidator) {
    // Hook the main validation method
    Interceptor.attach(
      TSKPinningValidator['- evaluateTrust:forHostname:'].implementation,
      {
        onLeave: function(retval) {
          // TSKTrustDecision: 0=shouldBlockConnection, 1=shouldAllowConnection
          retval.replace(ptr('0x1'));
          console.log('[+] TrustKit validation bypassed');
        }
      }
    );
  } else {
    console.log('[-] TrustKit not found in this app');
  }
}

Network.framework (NWConnection) bypass

Apps using Apple's Network.framework directly set pinning in NWParameters via a sec_protocol_options_set_verify_block callback. This is the hardest pattern to bypass because it's set at connection-creation time using a closure, not a delegate method. The approach is to patch the verify block to always call the completion handler with true:

JAVASCRIPT (FRIDA)const sec_protocol_options_set_verify_block = Module.findExportByName(
  'Security',
  'sec_protocol_options_set_verify_block'
);

if (sec_protocol_options_set_verify_block) {
  Interceptor.attach(sec_protocol_options_set_verify_block, {
    onEnter: function(args) {
      // args[1] is the verify_block (a Swift/ObjC closure)
      // args[2] is the dispatch_queue
      // Replace the verify block with one that always succeeds
      const newBlock = new ObjC.Block({
        retType: 'void',
        argTypes: ['pointer', 'pointer', 'pointer'],
        implementation: function(metadata, trust, completionHandler) {
          // Invoke completion with true - always allow
          const handler = new ObjC.Block(completionHandler);
          handler.invoke(1); // bool true
        }
      });
      args[1] = newBlock;
      console.log('[+] NWConnection verify_block replaced');
    }
  });
}

// 06 SSL Kill Switch 3 - the tweak approach

On a fully jailbroken device with Sileo/Zebra, SSL Kill Switch 3 (available from the opa334 repo) installs as a system-wide tweak. Unlike Frida scripts that must be run per-session, SSL Kill Switch 3 hooks at the MobileSubstrate/libhooker layer and persists across app launches. It's the fastest workflow for testing multiple apps on the same device.

SHELL# Add repo and install (Sileo - rootless device)
# Repo: https://repo.opa334.me
# Package: SSLKillSwitch3

# After install, toggle via Settings → SSL Kill Switch 3
# Enable "Disable Certificate Validation" globally

# Confirm via Burp CA already in System Trust Store
# (export Burp CA → AirDrop → install via Settings → VPN & Device Mgmt)

# Limitation: does NOT bypass apps using Network.framework sec_protocol_options
# or apps with jailbreak detection that kill themselves before the tweak fires

// 07 KeychainItem extraction: what to do post-bypass

Once you're proxying traffic, pivot immediately to Keychain extraction. iOS applications store authentication tokens, refresh tokens, API keys, private certificates, and session cookies in the Keychain - often persisted across app reinstalls when the kSecAttrAccessibleAlways or kSecAttrAccessibleAfterFirstUnlock accessibility options are set. These artefacts are frequently more valuable than the intercepted traffic itself.

OBJECTION REPL# Dump all Keychain items for the current app
ios keychain dump

# Filter by type
ios keychain dump --json           # output as JSON
ios keychain dump --show-protected  # attempt to dump protected items

# Example output
{
  "kSecAttrService": "com.example.BankingApp",
  "kSecAttrAccount": "auth_token",
  "kSecValueData": "eyJhbGciOiJSUzI1NiIsInR...", # live JWT
  "kSecAttrAccessible": "kSecAttrAccessibleAfterFirstUnlock"
}
// Finding classification

A Keychain item stored with kSecAttrAccessibleAlways or without a device-unlock requirement is typically a High severity finding - the token persists indefinitely and survives app reinstallation, meaning a physical device compromise or a backup extraction attack can recover valid credentials long after the user believes they've logged out.

// 08 Bypassing jailbreak detection when the app refuses to run

High-value targets - banking apps, healthcare apps, fintech platforms - frequently implement jailbreak detection that kills the app before your hooks can fire. The detection mechanisms typically check for: Cydia/Sileo existence, suspicious file paths (/var/jb, /usr/bin/frida-server), fork() success (only possible on jailbroken devices), dynamic library injection signatures, and process name enumeration.

The most reliable counter-technique in 2026 is Frida gadget injection with jailbreak check patching - no jailbreak, no Frida server process, nothing to detect:

SHELL# Step 1: Obtain decrypted IPA (sideloaded install, or use frida-ios-dump)
# Step 2: Inject FridaGadget.dylib into the main binary
pip install lief
python3 inject_gadget.py BankingApp.ipa FridaGadget.dylib

# Step 3: Re-sign with your Apple Developer certificate
codesign -f -s "iPhone Developer: You" \
  --entitlements entitlements.plist \
  BankingApp.app/BankingApp

# Step 4: Re-package and install via AltStore/Sideloadly
xcrun simctl install booted BankingApp.app

# Step 5: Attach Frida to the gadget
frida -U -n Gadget   # or via Objection
objection --gadget Gadget explore

For applications that use iOS device attestation (App Attest / DeviceCheck), the gadget injection approach still allows you to hook SSL validation - it just won't bypass server-side integrity checks on the backend. In that case, you're testing what you can test and documenting the scope limitation.

// 09 iOS 17 / 18 considerations

Apple's continued hardening of the Secure Enclave and expanded use of pointer authentication codes (PAC) on A17 and M-series chips has made some memory-patching Frida techniques less reliable. The principal changes to be aware of in 2026 engagements:

// 10 What to report when pinning is bypassable

A successful pinning bypass is not itself a vulnerability to report - it's the prerequisite to testing. What you report depends on what you find in the decrypted traffic and the Keychain. However, the pinning implementation quality is worth noting in the report's security posture section. OWASP MASVS-NETWORK controls define three levels:

MASVS ControlRequirementPass criteria in 2026
MASVS-NETWORK-1TLS on all network communicationNo HTTP, TLS 1.2+ minimum
MASVS-NETWORK-2TLS settings aligned with best practicesNo TLS 1.0/1.1, no RC4/3DES, HSTS set
MASVS-RESILIENCE-4App implements certificate pinningSPKI hash pinning, backup pins, not bypassable via sslpinning disable
MASVS-RESILIENCE-1Jailbreak detection presentMultiple layered checks - not a single flag check
MASVS-STORAGE-1Sensitive data not stored in Keychain with wrong accessibilityAuth tokens require user presence (kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)
CY

CyberFortify Research

Mobile Security Practice

Field notes from active iOS and Android mobile application penetration testing engagements, aligned with OWASP MASVS and MSTG. All techniques described are used in authorised security assessments.

Need a mobile application security assessment?

CyberFortify's mobile pen testing engagements cover iOS and Android, aligned with OWASP MASVS and MSTG. We go beyond automated scanning - every assessment includes manual traffic interception, Keychain analysis, and binary reverse engineering.

Schedule scoping call → Mobile pen testing →