mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-17 13:15:16 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Objection iOS Security Assessment Automation
|
||||
|
||||
Automates common Objection commands for iOS app security testing.
|
||||
Runs keychain dump, storage inspection, SSL pinning check, and jailbreak detection analysis.
|
||||
|
||||
Usage:
|
||||
python process.py --bundle-id com.target.app [--device-id UDID] [--output report.json]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class ObjectionAssessor:
|
||||
"""Automates Objection-based iOS security assessment tasks."""
|
||||
|
||||
def __init__(self, bundle_id: str, device_id: str = None):
|
||||
self.bundle_id = bundle_id
|
||||
self.device_id = device_id
|
||||
self.findings = []
|
||||
|
||||
def _run_objection_command(self, command: str, timeout: int = 30) -> str:
|
||||
"""Execute an Objection command and return output."""
|
||||
cmd = ["objection", "--gadget", self.bundle_id, "run", command]
|
||||
if self.device_id:
|
||||
cmd.insert(1, "--serial")
|
||||
cmd.insert(2, self.device_id)
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
return result.stdout + result.stderr
|
||||
except subprocess.TimeoutExpired:
|
||||
return f"TIMEOUT: Command '{command}' exceeded {timeout}s"
|
||||
except FileNotFoundError:
|
||||
return "ERROR: Objection not found. Install with: pip install objection"
|
||||
|
||||
def _run_frida_command(self, script: str, timeout: int = 15) -> str:
|
||||
"""Execute a Frida script snippet."""
|
||||
cmd = ["frida", "-U", "-n", self.bundle_id, "-e", script]
|
||||
if self.device_id:
|
||||
cmd.extend(["-D", self.device_id])
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
return result.stdout
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
return ""
|
||||
|
||||
def check_frida_connectivity(self) -> dict:
|
||||
"""Verify Frida can connect to the device."""
|
||||
cmd = ["frida-ps", "-U"]
|
||||
if self.device_id:
|
||||
cmd.extend(["-D", self.device_id])
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
||||
connected = result.returncode == 0
|
||||
processes = len(result.stdout.strip().split("\n")) - 1 if connected else 0
|
||||
return {
|
||||
"connected": connected,
|
||||
"process_count": processes,
|
||||
"target_running": self.bundle_id in result.stdout,
|
||||
}
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
return {"connected": False, "process_count": 0, "target_running": False}
|
||||
|
||||
def dump_keychain(self) -> dict:
|
||||
"""Dump keychain items accessible to the app."""
|
||||
output = self._run_objection_command("ios keychain dump")
|
||||
items = []
|
||||
current_item = {}
|
||||
|
||||
for line in output.split("\n"):
|
||||
line = line.strip()
|
||||
if "Service" in line and ":" in line:
|
||||
if current_item:
|
||||
items.append(current_item)
|
||||
current_item = {"service": line.split(":", 1)[-1].strip()}
|
||||
elif "Account" in line and ":" in line:
|
||||
current_item["account"] = line.split(":", 1)[-1].strip()
|
||||
elif "Data" in line and ":" in line:
|
||||
data = line.split(":", 1)[-1].strip()
|
||||
current_item["data_preview"] = data[:50] + "..." if len(data) > 50 else data
|
||||
current_item["data_length"] = len(data)
|
||||
|
||||
if current_item:
|
||||
items.append(current_item)
|
||||
|
||||
finding = {
|
||||
"check": "keychain_dump",
|
||||
"category": "MASVS-STORAGE",
|
||||
"owasp_mobile": "M9",
|
||||
"items_found": len(items),
|
||||
"items": items[:20],
|
||||
"severity": "HIGH" if items else "INFO",
|
||||
"description": f"Found {len(items)} keychain items accessible to the application",
|
||||
}
|
||||
self.findings.append(finding)
|
||||
return finding
|
||||
|
||||
def check_nsuserdefaults(self) -> dict:
|
||||
"""Inspect NSUserDefaults for sensitive data."""
|
||||
output = self._run_objection_command("ios nsuserdefaults get")
|
||||
sensitive_patterns = [
|
||||
"password", "token", "secret", "key", "auth",
|
||||
"session", "credential", "api_key", "apikey",
|
||||
]
|
||||
|
||||
sensitive_entries = []
|
||||
for line in output.split("\n"):
|
||||
line_lower = line.lower()
|
||||
for pattern in sensitive_patterns:
|
||||
if pattern in line_lower:
|
||||
sensitive_entries.append(line.strip())
|
||||
break
|
||||
|
||||
finding = {
|
||||
"check": "nsuserdefaults",
|
||||
"category": "MASVS-STORAGE",
|
||||
"owasp_mobile": "M9",
|
||||
"sensitive_entries": len(sensitive_entries),
|
||||
"entries": sensitive_entries[:10],
|
||||
"severity": "HIGH" if sensitive_entries else "PASS",
|
||||
"description": f"Found {len(sensitive_entries)} potentially sensitive NSUserDefaults entries",
|
||||
}
|
||||
self.findings.append(finding)
|
||||
return finding
|
||||
|
||||
def check_ssl_pinning(self) -> dict:
|
||||
"""Assess SSL pinning implementation."""
|
||||
output = self._run_objection_command("ios sslpinning disable")
|
||||
pinning_detected = "pinning" in output.lower() or "hook" in output.lower()
|
||||
|
||||
finding = {
|
||||
"check": "ssl_pinning",
|
||||
"category": "MASVS-NETWORK",
|
||||
"owasp_mobile": "M5",
|
||||
"pinning_detected": pinning_detected,
|
||||
"bypass_output": output[:500],
|
||||
"severity": "MEDIUM" if not pinning_detected else "INFO",
|
||||
"description": "SSL pinning " + ("detected and bypassed" if pinning_detected else "not detected"),
|
||||
}
|
||||
self.findings.append(finding)
|
||||
return finding
|
||||
|
||||
def check_jailbreak_detection(self) -> dict:
|
||||
"""Assess jailbreak detection implementation."""
|
||||
output = self._run_objection_command("ios jailbreak disable")
|
||||
detection_found = "hook" in output.lower() or "bypass" in output.lower()
|
||||
|
||||
finding = {
|
||||
"check": "jailbreak_detection",
|
||||
"category": "MASVS-RESILIENCE",
|
||||
"owasp_mobile": "M7",
|
||||
"detection_implemented": detection_found,
|
||||
"bypass_output": output[:500],
|
||||
"severity": "MEDIUM" if not detection_found else "INFO",
|
||||
"description": "Jailbreak detection " + ("found" if detection_found else "not found or not implemented"),
|
||||
}
|
||||
self.findings.append(finding)
|
||||
return finding
|
||||
|
||||
def search_sensitive_memory(self) -> dict:
|
||||
"""Search app memory for sensitive strings."""
|
||||
patterns = ["password", "Bearer ", "eyJ", "api_key", "secret"]
|
||||
memory_findings = []
|
||||
|
||||
for pattern in patterns:
|
||||
output = self._run_objection_command(f'memory search "{pattern}" --string')
|
||||
matches = output.count("Found")
|
||||
if matches > 0:
|
||||
memory_findings.append({
|
||||
"pattern": pattern,
|
||||
"matches": matches,
|
||||
})
|
||||
|
||||
finding = {
|
||||
"check": "memory_search",
|
||||
"category": "MASVS-STORAGE",
|
||||
"owasp_mobile": "M9",
|
||||
"patterns_with_matches": len(memory_findings),
|
||||
"details": memory_findings,
|
||||
"severity": "HIGH" if memory_findings else "PASS",
|
||||
"description": f"Found sensitive patterns in memory for {len(memory_findings)} search terms",
|
||||
}
|
||||
self.findings.append(finding)
|
||||
return finding
|
||||
|
||||
def get_app_info(self) -> dict:
|
||||
"""Gather basic app information."""
|
||||
output = self._run_objection_command("ios info binary")
|
||||
env_output = self._run_objection_command("env")
|
||||
|
||||
return {
|
||||
"bundle_id": self.bundle_id,
|
||||
"binary_info": output[:1000],
|
||||
"environment": env_output[:1000],
|
||||
}
|
||||
|
||||
def generate_report(self) -> dict:
|
||||
"""Generate consolidated assessment report."""
|
||||
severity_counts = {"HIGH": 0, "MEDIUM": 0, "LOW": 0, "INFO": 0, "PASS": 0}
|
||||
for f in self.findings:
|
||||
sev = f.get("severity", "INFO")
|
||||
severity_counts[sev] = severity_counts.get(sev, 0) + 1
|
||||
|
||||
return {
|
||||
"assessment": {
|
||||
"target": self.bundle_id,
|
||||
"date": datetime.now().isoformat(),
|
||||
"tool": "Objection (Frida-powered)",
|
||||
"type": "iOS Runtime Security Assessment",
|
||||
},
|
||||
"summary": {
|
||||
"total_checks": len(self.findings),
|
||||
"severity_breakdown": severity_counts,
|
||||
"critical_findings": [
|
||||
f for f in self.findings if f.get("severity") in ("HIGH", "CRITICAL")
|
||||
],
|
||||
},
|
||||
"findings": self.findings,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Objection iOS Security Assessment Automation"
|
||||
)
|
||||
parser.add_argument("--bundle-id", required=True, help="iOS app bundle identifier")
|
||||
parser.add_argument("--device-id", help="Device UDID for targeting specific device")
|
||||
parser.add_argument("--output", default="objection_report.json", help="Output report path")
|
||||
parser.add_argument("--checks", nargs="+",
|
||||
default=["keychain", "nsuserdefaults", "ssl", "jailbreak", "memory"],
|
||||
help="Checks to run")
|
||||
args = parser.parse_args()
|
||||
|
||||
assessor = ObjectionAssessor(args.bundle_id, args.device_id)
|
||||
|
||||
# Verify connectivity
|
||||
connectivity = assessor.check_frida_connectivity()
|
||||
if not connectivity["connected"]:
|
||||
print("[-] ERROR: Cannot connect to device via Frida")
|
||||
print(" Ensure Frida server is running on device or IPA is patched")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[+] Connected to device. Target running: {connectivity['target_running']}")
|
||||
|
||||
# Run selected checks
|
||||
check_map = {
|
||||
"keychain": assessor.dump_keychain,
|
||||
"nsuserdefaults": assessor.check_nsuserdefaults,
|
||||
"ssl": assessor.check_ssl_pinning,
|
||||
"jailbreak": assessor.check_jailbreak_detection,
|
||||
"memory": assessor.search_sensitive_memory,
|
||||
}
|
||||
|
||||
for check in args.checks:
|
||||
if check in check_map:
|
||||
print(f"[*] Running check: {check}")
|
||||
result = check_map[check]()
|
||||
print(f" Severity: {result['severity']} - {result['description']}")
|
||||
|
||||
# Generate report
|
||||
report = assessor.generate_report()
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f"\n[+] Report saved: {args.output}")
|
||||
|
||||
# Summary
|
||||
high_count = report["summary"]["severity_breakdown"].get("HIGH", 0)
|
||||
if high_count > 0:
|
||||
print(f"[!] {high_count} HIGH severity findings require attention")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user