npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
When to Use
Use this skill when:
- Mobile app refuses connections through a proxy due to certificate pinning
- Performing authorized security testing requiring HTTPS traffic interception
- Assessing the strength and bypass difficulty of pinning implementations
- Evaluating defense-in-depth of mobile app network security
Do not use to bypass pinning on apps without explicit testing authorization.
Prerequisites
- Burp Suite configured as proxy with listener on all interfaces
- Rooted Android device or jailbroken iOS device
- Frida server running on target device
- Objection installed (
pip install objection) - Target app installed and reproducing the pinning behavior
Workflow
Step 1: Identify Pinning Implementation
Android pinning methods to identify:
1. Network Security Config (res/xml/network_security_config.xml)
<pin-set> with certificate hash pins
2. OkHttp CertificatePinner
CertificatePinner.Builder().add("api.target.com", "sha256/...")
3. Custom TrustManager
X509TrustManager overrides in code
4. Third-party libraries
- TrustKit
- Certificate Transparency checksiOS pinning methods:
1. NSURLSession delegate (URLSession:didReceiveChallenge:)
2. ATS (App Transport Security) with custom trust evaluation
3. TrustKit framework
4. Alamofire ServerTrustPolicy
5. Custom SecTrust evaluationStep 2: Bypass with Objection (Quickest Approach)
# Android
objection --gadget com.target.app explore
android sslpinning disable
# iOS
objection --gadget com.target.app explore
ios sslpinning disableObjection hooks common pinning implementations including OkHttp CertificatePinner, TrustManagerImpl, NSURLSession delegate methods, and SecTrust evaluation.
Step 3: Bypass with Custom Frida Scripts
Android - Universal SSL Pinning Bypass:
// android_ssl_bypass.js
Java.perform(function() {
// Bypass TrustManagerImpl
var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain,
host, clientAuth, ocspData, tlsSctData) {
console.log("[+] Bypassing TrustManagerImpl for: " + host);
return untrustedChain;
};
// Bypass OkHttp3 CertificatePinner
try {
var CertificatePinner = Java.use("okhttp3.CertificatePinner");
CertificatePinner.check.overload("java.lang.String", "java.util.List").implementation =
function(hostname, peerCertificates) {
console.log("[+] Bypassing OkHttp3 pinning for: " + hostname);
return;
};
} catch(e) {}
// Bypass custom X509TrustManager
var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
var TrustManager = Java.registerClass({
name: "com.bypass.TrustManager",
implements: [X509TrustManager],
methods: {
checkClientTrusted: function(chain, authType) {},
checkServerTrusted: function(chain, authType) {},
getAcceptedIssuers: function() { return []; }
}
});
// Bypass SSLContext
var SSLContext = Java.use("javax.net.ssl.SSLContext");
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;",
"[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation =
function(km, tm, sr) {
console.log("[+] Replacing TrustManagers in SSLContext.init");
this.init(km, [TrustManager.$new()], sr);
};
// Bypass NetworkSecurityConfig (Android 7+)
try {
var NetworkSecurityConfig = Java.use(
"android.security.net.config.NetworkSecurityConfig");
NetworkSecurityConfig.isCleartextTrafficPermitted.implementation = function() {
return true;
};
} catch(e) {}
console.log("[*] SSL pinning bypass loaded");
});frida -U -f com.target.app -l android_ssl_bypass.js --no-pauseiOS - Universal SSL Pinning Bypass:
// ios_ssl_bypass.js
if (ObjC.available) {
// Bypass NSURLSession delegate
var resolver = new ApiResolver("objc");
resolver.enumerateMatches(
"-[* URLSession:didReceiveChallenge:completionHandler:]", {
onMatch: function(match) {
Interceptor.attach(match.address, {
onEnter: function(args) {
var completionHandler = new ObjC.Block(args[4]);
var NSURLSessionAuthChallengeUseCredential = 0;
var trust = new ObjC.Object(args[3])
.protectionSpace().serverTrust();
var credential = ObjC.classes.NSURLCredential
.credentialForTrust_(trust);
completionHandler.invoke(NSURLSessionAuthChallengeUseCredential,
credential);
}
});
},
onComplete: function() {}
});
// Bypass SecTrustEvaluate
var SecTrustEvaluateWithError = Module.findExportByName(
"Security", "SecTrustEvaluateWithError");
if (SecTrustEvaluateWithError) {
Interceptor.replace(SecTrustEvaluateWithError, new NativeCallback(
function(trust, error) {
return 1; // Always return true
}, "bool", ["pointer", "pointer"]
));
}
console.log("[*] iOS SSL pinning bypass loaded");
}Step 4: Handle Advanced Pinning
For apps using advanced pinning (TrustKit, custom binary checks):
# Identify the specific pinning library
frida-trace -U -n TargetApp -m "*[*Trust*]" -m "*[*Pin*]" -m "*[*SSL*]" -m "*[*Certificate*]"
# Hook the identified validation function
# Custom Frida script targeting the specific implementationStep 5: Verify Bypass Success
After applying the bypass:
- Configure device proxy to Burp Suite
- Open target app and navigate through authenticated flows
- Verify HTTPS traffic appears in Burp Suite HTTP History
- Check for any remaining pinned connections that are not captured
Key Concepts
| Term | Definition |
|---|---|
| Certificate Pinning | Restricting accepted server certificates to a known set, preventing MITM via rogue CA certificates |
| Public Key Pinning | Pinning the server's public key hash rather than the full certificate, surviving certificate rotation |
| Network Security Config | Android XML configuration for declaring trust anchors, pins, and cleartext policy per-domain |
| TrustKit | Open-source library implementing certificate pinning with reporting for both Android and iOS |
| HPKP Deprecation | HTTP Public Key Pinning header was deprecated in browsers but concept persists in mobile apps |
Tools & Systems
- Objection: Pre-built pinning bypass for common libraries (OkHttp, NSURLSession, TrustKit)
- Frida: Custom JavaScript hooks targeting specific pinning implementations
- apktool: APK decompilation for identifying pinning in Network Security Config
- SSLUnpinning (Xposed): Xposed framework module for system-wide pinning bypass on Android
- ssl-kill-switch2: iOS tweak for disabling SSL pinning system-wide on jailbroken devices
Common Pitfalls
- Certificate transparency: Some apps check CT logs in addition to pinning. May need to bypass CT verification separately.
- Multi-layer pinning: Apps may implement pinning at multiple levels (OkHttp + custom TrustManager). Bypass all layers.
- Binary-level pinning: Some apps validate certificates in native C/C++ code, which requires Interceptor.attach at native function addresses rather than Java/ObjC hooks.
- Dynamic pinning updates: Apps using TrustKit or similar may fetch updated pins from a server. Monitor for pin rotation during testing.
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.5 KB
API Reference — Performing Mobile App Certificate Pinning Bypass
Libraries Used
- subprocess: Execute frida, objection, apktool, adb commands
- pathlib: Read decompiled APK smali files
CLI Interface
python agent.py detect --apk app.apk
python agent.py frida --package com.example.app [--device emulator-5554]
python agent.py objection --package com.example.app
python agent.py proxyCore Functions
detect_pinning_implementation(apk_path) — Static APK analysis
Decompiles APK with apktool. Searches smali for pinning indicators: OkHttp CertificatePinner, X509TrustManager, network_security_config, WebView SSL error handler, Conscrypt TrustManagerImpl, Certificate Transparency.
run_frida_bypass(package_name, device_id) — Dynamic Frida bypass
Injects JavaScript to bypass: TrustManagerImpl.verifyChain, OkHttp CertificatePinner.check, WebViewClient.onReceivedSslError.
run_objection_bypass(package_name) — Objection framework bypass
Runs android sslpinning disable via objection exploration mode.
check_proxy_setup() — Verify interception environment
Checks: Android proxy settings, system CA certificates, user-installed CA certs.
Pinning Strength Classification
| Level | Criteria |
|---|---|
| STRONG | 3+ pinning implementations detected |
| MODERATE | 1-2 implementations |
| NONE | No pinning indicators found |
Dependencies
pip install frida-tools objectionSystem: apktool, adb (Android Debug Bridge)
standards.md0.7 KB
Standards Reference: Certificate Pinning Bypass
OWASP Mobile Top 10 2024
| ID | Risk | Relevance |
|---|---|---|
| M5 | Insecure Communication | Pinning assessment as part of network security |
| M7 | Insufficient Binary Protections | Pinning as binary-level protection |
OWASP MASVS v2.0
| Control | Description |
|---|---|
| MASVS-NETWORK-1 | App uses TLS for all network communication |
| MASVS-NETWORK-2 | App performs certificate pinning for critical connections |
| MASVS-RESILIENCE-1 | App detects and responds to running in instrumented environment |
CWE Mappings
| CWE | Title |
|---|---|
| CWE-295 | Improper Certificate Validation |
| CWE-297 | Improper Validation of Certificate with Host Mismatch |
workflows.md1.5 KB
Workflows: Certificate Pinning Bypass
Workflow 1: Escalating Bypass Approach
[Configure Burp proxy] --> [App connection fails?] --> [Yes: Pinning active]
|
[Try Objection generic bypass]
|
[Traffic visible?]
/ \
[Yes: Done] [No: Advanced]
|
[Identify pinning library]
[frida-trace *Trust* *Pin*]
|
[Write custom Frida script]
[Target specific implementation]
|
[Verify traffic capture]Decision Matrix
| Pinning Method | Bypass Tool | Difficulty |
|---|---|---|
| OkHttp CertificatePinner | Objection | Easy |
| Network Security Config | Manifest modification | Easy |
| Custom TrustManager | Frida hook | Medium |
| TrustKit | Objection or Frida | Medium |
| Native C/C++ validation | Frida Interceptor.attach | Hard |
| Certificate Transparency | Custom Frida script | Hard |
Scripts 2
agent.py6.7 KB
#!/usr/bin/env python3
"""Agent for performing mobile app certificate pinning bypass testing — authorized testing only."""
import json
import argparse
import subprocess
from pathlib import Path
FRIDA_SSL_BYPASS_SCRIPT = """
Java.perform(function() {
// TrustManagerImpl bypass
var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
TrustManagerImpl.verifyChain.implementation = function() {
console.log('[*] Bypassed TrustManagerImpl.verifyChain');
return Java.use('java.util.ArrayList').$new();
};
// OkHttp CertificatePinner bypass
try {
var CertificatePinner = Java.use('okhttp3.CertificatePinner');
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(hostname, peerCerts) {
console.log('[*] Bypassed OkHttp CertificatePinner for: ' + hostname);
};
} catch(e) { console.log('[!] OkHttp not found'); }
// WebViewClient bypass
try {
var WebViewClient = Java.use('android.webkit.WebViewClient');
WebViewClient.onReceivedSslError.implementation = function(view, handler, error) {
console.log('[*] Bypassed WebView SSL error');
handler.proceed();
};
} catch(e) {}
});
"""
def detect_pinning_implementation(apk_path):
"""Detect certificate pinning implementation in an APK."""
cmd = ["apktool", "d", apk_path, "-o", "/tmp/apk_decompile", "-f"]
try:
subprocess.run(cmd, capture_output=True, text=True, timeout=120)
except FileNotFoundError:
return {"error": "apktool not installed"}
pinning_indicators = {
"okhttp_pinner": "CertificatePinner",
"trustmanager": "X509TrustManager",
"network_security_config": "network_security_config",
"ssl_pinning_webview": "onReceivedSslError",
"conscrypt": "TrustManagerImpl",
"certificate_transparency": "CertificateTransparency",
}
findings = {}
smali_dir = Path("/tmp/apk_decompile")
for smali_file in smali_dir.rglob("*.smali"):
content = smali_file.read_text(encoding="utf-8", errors="replace")
for indicator_name, pattern in pinning_indicators.items():
if pattern in content:
findings.setdefault(indicator_name, []).append(str(smali_file.relative_to(smali_dir)))
nsc_file = smali_dir / "res" / "xml" / "network_security_config.xml"
nsc_content = None
if nsc_file.exists():
nsc_content = nsc_file.read_text()
return {
"apk": apk_path,
"pinning_detected": bool(findings),
"implementations": {k: v[:5] for k, v in findings.items()},
"network_security_config": nsc_content[:500] if nsc_content else None,
"pinning_strength": "STRONG" if len(findings) >= 3 else "MODERATE" if findings else "NONE",
}
def run_frida_bypass(package_name, device_id=None):
"""Launch Frida SSL pinning bypass against a running app."""
script_path = Path("/tmp/frida_ssl_bypass.js")
script_path.write_text(FRIDA_SSL_BYPASS_SCRIPT)
cmd = ["frida", "-U", "-l", str(script_path), "-f", package_name, "--no-pause"]
if device_id:
cmd = ["frida", "-D", device_id, "-l", str(script_path), "-f", package_name, "--no-pause"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
return {
"package": package_name,
"script": "ssl_pinning_bypass",
"output": result.stdout[:1000],
"errors": result.stderr[:500] if result.stderr else None,
"success": result.returncode == 0,
}
except FileNotFoundError:
return {"error": "frida not installed — pip install frida-tools"}
except subprocess.TimeoutExpired:
return {"package": package_name, "status": "RUNNING", "note": "Frida is attached — use Ctrl+C to detach"}
def run_objection_bypass(package_name):
"""Use objection to disable SSL pinning on Android/iOS."""
cmd = ["objection", "-g", package_name, "explore", "-s",
"android sslpinning disable"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
return {
"package": package_name,
"tool": "objection",
"output": result.stdout[:1000],
"success": "disabled" in result.stdout.lower(),
}
except FileNotFoundError:
return {"error": "objection not installed — pip install objection"}
except Exception as e:
return {"error": str(e)}
def check_proxy_setup():
"""Verify proxy setup for traffic interception."""
checks = {}
try:
result = subprocess.run(["adb", "shell", "settings", "get", "global", "http_proxy"],
capture_output=True, text=True, timeout=10)
proxy = result.stdout.strip()
checks["android_proxy"] = proxy if proxy and proxy != "null" else "NOT_SET"
except Exception as e:
checks["android_proxy_error"] = str(e)
try:
result = subprocess.run(["adb", "shell", "ls", "/system/etc/security/cacerts/"],
capture_output=True, text=True, timeout=10)
cert_count = len(result.stdout.strip().splitlines())
checks["system_ca_certs"] = cert_count
except Exception as e:
checks["ca_cert_error"] = str(e)
try:
result = subprocess.run(["adb", "shell", "su", "-c", "cat /data/misc/user/0/cacerts-added/ 2>/dev/null | wc -l"],
capture_output=True, text=True, timeout=10)
checks["user_ca_certs"] = result.stdout.strip()
except Exception:
checks["user_ca_certs"] = "unknown"
return {"proxy_setup": checks}
def main():
parser = argparse.ArgumentParser(description="Mobile App Certificate Pinning Bypass Agent (Authorized Only)")
sub = parser.add_subparsers(dest="command")
d = sub.add_parser("detect", help="Detect pinning in APK")
d.add_argument("--apk", required=True)
f = sub.add_parser("frida", help="Frida SSL bypass")
f.add_argument("--package", required=True)
f.add_argument("--device", help="Device ID")
o = sub.add_parser("objection", help="Objection SSL bypass")
o.add_argument("--package", required=True)
sub.add_parser("proxy", help="Check proxy setup")
args = parser.parse_args()
if args.command == "detect":
result = detect_pinning_implementation(args.apk)
elif args.command == "frida":
result = run_frida_bypass(args.package, args.device)
elif args.command == "objection":
result = run_objection_bypass(args.package)
elif args.command == "proxy":
result = check_proxy_setup()
else:
parser.print_help()
return
print(json.dumps(result, indent=2, default=str))
if __name__ == "__main__":
main()
process.py5.3 KB
#!/usr/bin/env python3
"""
Certificate Pinning Bypass Manager
Manages Frida scripts for bypassing SSL pinning on Android and iOS targets.
Provides pre-built bypass scripts and validates successful bypass.
Usage:
python process.py --platform android --package com.target.app [--proxy-host 192.168.1.100]
"""
import argparse
import json
import subprocess
import sys
import time
from datetime import datetime
from pathlib import Path
ANDROID_BYPASS_SCRIPT = """
Java.perform(function() {
console.log("[*] Loading Android SSL pinning bypass...");
// TrustManagerImpl (Android system)
try {
var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
TrustManagerImpl.verifyChain.implementation = function() {
console.log("[+] Bypassed TrustManagerImpl.verifyChain");
return arguments[0];
};
} catch(e) { console.log("[-] TrustManagerImpl not found"); }
// OkHttp3 CertificatePinner
try {
var CertificatePinner = Java.use("okhttp3.CertificatePinner");
CertificatePinner.check.overload("java.lang.String", "java.util.List").implementation =
function(hostname, peerCertificates) {
console.log("[+] Bypassed OkHttp3 pinning for: " + hostname);
};
} catch(e) { console.log("[-] OkHttp3 CertificatePinner not found"); }
// SSLContext init
try {
var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
var Bypass = Java.registerClass({
name: "com.bypass.TrustAll",
implements: [X509TrustManager],
methods: {
checkClientTrusted: function(chain, authType) {},
checkServerTrusted: function(chain, authType) {},
getAcceptedIssuers: function() { return []; }
}
});
var SSLContext = Java.use("javax.net.ssl.SSLContext");
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;",
"[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation =
function(km, tm, sr) {
console.log("[+] Bypassed SSLContext.init");
this.init(km, [Bypass.$new()], sr);
};
} catch(e) { console.log("[-] SSLContext bypass failed"); }
console.log("[*] Bypass script loaded successfully");
});
"""
IOS_BYPASS_SCRIPT = """
if (ObjC.available) {
console.log("[*] Loading iOS SSL pinning bypass...");
// SecTrustEvaluateWithError
var SecTrustEvaluateWithError = Module.findExportByName("Security", "SecTrustEvaluateWithError");
if (SecTrustEvaluateWithError) {
Interceptor.replace(SecTrustEvaluateWithError,
new NativeCallback(function(trust, error) {
console.log("[+] Bypassed SecTrustEvaluateWithError");
return 1;
}, "bool", ["pointer", "pointer"])
);
}
// NSURLSession delegate
var resolver = new ApiResolver("objc");
resolver.enumerateMatches("-[* URLSession:didReceiveChallenge:completionHandler:]", {
onMatch: function(match) {
Interceptor.attach(match.address, {
onEnter: function(args) {
var handler = new ObjC.Block(args[4]);
var trust = new ObjC.Object(args[3]).protectionSpace().serverTrust();
var cred = ObjC.classes.NSURLCredential.credentialForTrust_(trust);
handler.invoke(0, cred);
console.log("[+] Bypassed NSURLSession delegate pinning");
}
});
},
onComplete: function() {}
});
console.log("[*] iOS bypass loaded");
}
"""
def run_bypass(platform: str, package: str, device_id: str = None) -> dict:
"""Deploy and run SSL pinning bypass script."""
script = ANDROID_BYPASS_SCRIPT if platform == "android" else IOS_BYPASS_SCRIPT
script_file = Path(f"/tmp/ssl_bypass_{platform}.js")
script_file.write_text(script)
cmd = ["frida", "-U", "-f", package, "-l", str(script_file), "--no-pause"]
if device_id:
cmd.extend(["-D", device_id])
print(f"[*] Deploying {platform} SSL pinning bypass for {package}...")
print(f"[*] Script saved to {script_file}")
print(f"[*] Command: {' '.join(cmd)}")
return {
"platform": platform,
"package": package,
"script_path": str(script_file),
"command": " ".join(cmd),
"timestamp": datetime.now().isoformat(),
}
def main():
parser = argparse.ArgumentParser(description="SSL Pinning Bypass Manager")
parser.add_argument("--platform", choices=["android", "ios"], required=True)
parser.add_argument("--package", required=True, help="App package/bundle ID")
parser.add_argument("--device-id", help="Device serial/UDID")
parser.add_argument("--output", default="pinning_bypass.json", help="Output report")
parser.add_argument("--execute", action="store_true", help="Execute bypass immediately")
args = parser.parse_args()
result = run_bypass(args.platform, args.package, args.device_id)
with open(args.output, "w") as f:
json.dump(result, f, indent=2)
print(f"[+] Configuration saved: {args.output}")
if args.execute:
print("[*] Starting Frida bypass (Ctrl+C to stop)...")
subprocess.run(result["command"].split())
if __name__ == "__main__":
main()