#!/usr/bin/env python3 """iOS app security analysis agent using Objection/Frida concepts. Performs runtime security assessment of iOS apps including SSL pinning bypass, keychain dumping, filesystem inspection, and jailbreak detection bypass. """ import subprocess import json import sys def run_objection(command, app_id=None, timeout=30): """Execute an Objection command against a target app.""" cmd = ["objection"] if app_id: cmd.extend(["-g", app_id]) cmd.extend(["explore", "-c", command]) try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) return result.stdout, result.returncode except FileNotFoundError: return "objection not installed (pip install objection)", 1 except subprocess.TimeoutExpired: return "Command timed out", 1 def run_frida(script_code, app_id, timeout=30): """Execute a Frida script against target app.""" cmd = ["frida", "-U", "-n", app_id, "-e", script_code] try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) return result.stdout, result.returncode except FileNotFoundError: return "frida not installed (pip install frida-tools)", 1 except subprocess.TimeoutExpired: return "Command timed out", 1 def dump_keychain(app_id): """Dump keychain items accessible by the application.""" return run_objection("ios keychain dump", app_id) def dump_cookies(app_id): """Dump HTTP cookies stored by the application.""" return run_objection("ios cookies get", app_id) def list_classes(app_id, filter_str=None): """List Objective-C classes loaded in the app.""" cmd = "ios hooking list classes" if filter_str: cmd += f" --include {filter_str}" return run_objection(cmd, app_id) def check_ssl_pinning(app_id): """Check and bypass SSL certificate pinning.""" return run_objection("ios sslpinning disable", app_id) def check_jailbreak_detection(app_id): """Check for and bypass jailbreak detection.""" return run_objection("ios jailbreak disable", app_id) def inspect_filesystem(app_id, path="/"): """Inspect the application's filesystem sandbox.""" return run_objection(f"ls {path}", app_id) def dump_plist(app_id): """Dump application plist configuration files.""" return run_objection("ios plist cat Info.plist", app_id) def check_pasteboard(app_id): """Check pasteboard/clipboard for sensitive data.""" return run_objection("ios pasteboard monitor", app_id) def search_binary_strings(app_id, pattern): """Search for strings in the app binary.""" return run_objection(f"memory search '{pattern}'", app_id) OWASP_MOBILE_CHECKS = { "M1_Improper_Platform_Usage": { "checks": ["ios keychain dump", "ios plist cat Info.plist"], "description": "Check for misuse of platform security features", }, "M2_Insecure_Data_Storage": { "checks": ["ios keychain dump", "ios cookies get", "ios nsuserdefaults get"], "description": "Check for sensitive data in insecure storage", }, "M3_Insecure_Communication": { "checks": ["ios sslpinning disable"], "description": "Test SSL/TLS implementation and certificate pinning", }, "M4_Insecure_Authentication": { "checks": ["ios hooking list classes --include Auth", "ios hooking list classes --include Login"], "description": "Analyze authentication mechanisms", }, "M5_Insufficient_Cryptography": { "checks": ["ios hooking list classes --include Crypto", "ios hooking list classes --include AES"], "description": "Review cryptographic implementations", }, "M8_Code_Tampering": { "checks": ["ios jailbreak disable"], "description": "Test runtime integrity and jailbreak detection", }, "M9_Reverse_Engineering": { "checks": ["ios hooking list classes"], "description": "Assess reverse engineering protections", }, } def run_owasp_assessment(app_id): """Run OWASP Mobile Top 10 security checks.""" results = {} for category, config in OWASP_MOBILE_CHECKS.items(): category_results = {"description": config["description"], "findings": []} for check in config["checks"]: output, rc = run_objection(check, app_id) category_results["findings"].append({ "command": check, "status": "success" if rc == 0 else "failed", "output_preview": output[:200] if output else "", }) results[category] = category_results return results FRIDA_SCRIPTS = { "ssl_pinning_bypass": """ ObjC.choose(ObjC.classes.NSURLSessionConfiguration, { onMatch: function(instance) { instance['- setTLSMinimumSupportedProtocol:'](0); }, onComplete: function() {} }); """, "jailbreak_bypass": """ var paths = ['/Applications/Cydia.app', '/usr/sbin/sshd', '/etc/apt']; Interceptor.attach(ObjC.classes.NSFileManager['- fileExistsAtPath:'].implementation, { onEnter: function(args) { this.path = ObjC.Object(args[2]).toString(); }, onLeave: function(retval) { if (paths.some(p => this.path.includes(p))) retval.replace(0); } }); """, "keychain_dump": """ var kSecClass = ObjC.classes.__NSDictionary.dictionaryWithObject_forKey_( ObjC.classes.__NSCFConstantString.alloc().initWithUTF8String_('genp'), ObjC.classes.__NSCFConstantString.alloc().initWithUTF8String_('class') ); console.log('Keychain query prepared'); """, } def generate_report(app_id, assessment_results): """Generate iOS security assessment report.""" findings_count = sum( len(cat["findings"]) for cat in assessment_results.values() ) return { "app_identifier": app_id, "assessment_framework": "OWASP Mobile Top 10", "categories_tested": len(assessment_results), "total_checks": findings_count, "results": assessment_results, } if __name__ == "__main__": print("=" * 60) print("iOS App Security Analysis Agent (Objection/Frida)") print("Runtime analysis, SSL bypass, keychain dump, OWASP checks") print("=" * 60) app_id = sys.argv[1] if len(sys.argv) > 1 else None if not app_id: print("\n[DEMO] Usage: python agent.py ") print(" e.g. python agent.py com.example.app") print("\nAvailable checks:") for category, config in OWASP_MOBILE_CHECKS.items(): print(f" {category}: {config['description']}") print("\nFrida scripts available:") for name in FRIDA_SCRIPTS: print(f" {name}") sys.exit(0) print(f"\n[*] Target: {app_id}") print("[*] Running OWASP Mobile Top 10 assessment...") results = run_owasp_assessment(app_id) report = generate_report(app_id, results) for category, data in results.items(): status_counts = {"success": 0, "failed": 0} for f in data["findings"]: status_counts[f["status"]] += 1 print(f"\n [{category}] {data['description']}") print(f" Checks: {status_counts['success']} passed, {status_counts['failed']} failed") print(f"\n{json.dumps(report, indent=2, default=str)}")