Exploiting Insecure Data Storage in Mobile When to Use Use this skill when: - Assessing whether mobile applications store sensitive data securely on the device filesystem - Testing for credential leakage through SharedPreferences, SQLite databases, or plists - Evaluating keychain/keystore implementation for proper access control attributes - Performing data-at-rest security assessment during mobile penetration tests Do not use this skill on production user devices without authorization -- data extraction techniques require physical access or root/jailbreak privileges. Prerequisites - Rooted A…

)\n\nSENSITIVE_PATTERNS = {\n \"api_key\": re.compile(r'[\"\\']?api[_-]?key[\"\\']?\\s*[:=]\\s*[\"\\']([^\"\\']+)', re.I),\n \"token\": re.compile(r'[\"\\']?(?:access|auth|bearer)[_-]?token[\"\\']?\\s*[:=]\\s*[\"\\']([^\"\\']+)', re.I),\n \"password\": re.compile(r'[\"\\']?password[\"\\']?\\s*[:=]\\s*[\"\\']([^\"\\']+)', re.I),\n \"private_key\": re.compile(r'-----BEGIN (?:RSA )?PRIVATE KEY-----'),\n \"base64_cred\": re.compile(r'[\"\\']?(?:auth|credential)[\"\\']?\\s*[:=]\\s*[\"\\']([A-Za-z0-9+/=]{20,})', re.I),\n}\n\n\ndef scan_shared_prefs(prefs_dir):\n \"\"\"Scan Android SharedPreferences XML files for sensitive data.\"\"\"\n findings = []\n if not os.path.isdir(prefs_dir):\n return findings\n for fname in os.listdir(prefs_dir):\n if not fname.endswith(\".xml\"):\n continue\n fpath = os.path.join(prefs_dir, fname)\n try:\n with open(fpath, \"r\", errors=\"replace\") as f:\n content = f.read()\n for pattern_name, pattern in SENSITIVE_PATTERNS.items():\n matches = pattern.findall(content)\n if matches:\n findings.append({\n \"file\": fpath,\n \"type\": \"shared_prefs\",\n \"pattern\": pattern_name,\n \"match_count\": len(matches),\n \"severity\": \"HIGH\",\n })\n except PermissionError:\n pass\n return findings\n\n\ndef scan_sqlite_databases(db_dir):\n \"\"\"Scan SQLite databases for unencrypted sensitive data.\"\"\"\n findings = []\n if not os.path.isdir(db_dir):\n return findings\n for fname in os.listdir(db_dir):\n if not fname.endswith((\".db\", \".sqlite\", \".sqlite3\")):\n continue\n fpath = os.path.join(db_dir, fname)\n try:\n conn = sqlite3.connect(fpath)\n cursor = conn.cursor()\n cursor.execute(\"SELECT name FROM sqlite_master WHERE type='table'\")\n tables = cursor.fetchall()\n for (table_name,) in tables:\n if not _SAFE_TABLE_RE.match(table_name):\n continue\n cursor.execute(f\"PRAGMA table_info([{table_name}])\")\n columns = cursor.fetchall()\n sensitive_cols = []\n for col in columns:\n col_name = col[1].lower()\n for sf in [\"password\", \"token\", \"secret\", \"key\", \"ssn\", \"credit\"]:\n if sf in col_name:\n sensitive_cols.append(col[1])\n if sensitive_cols:\n cursor.execute(f\"SELECT COUNT(*) FROM [{table_name}]\")\n row_count = cursor.fetchone()[0]\n findings.append({\n \"file\": fpath,\n \"table\": table_name,\n \"sensitive_columns\": sensitive_cols,\n \"row_count\": row_count,\n \"encrypted\": False,\n \"severity\": \"CRITICAL\",\n })\n conn.close()\n except (sqlite3.Error, PermissionError):\n pass\n return findings\n\n\ndef scan_file_storage(files_dir):\n \"\"\"Scan app file storage for sensitive data.\"\"\"\n findings = []\n if not os.path.isdir(files_dir):\n return findings\n for root, _, files in os.walk(files_dir):\n for fname in files:\n fpath = os.path.join(root, fname)\n try:\n with open(fpath, \"r\", errors=\"replace\") as f:\n content = f.read(4096)\n for pattern_name, pattern in SENSITIVE_PATTERNS.items():\n if pattern.search(content):\n findings.append({\n \"file\": fpath,\n \"pattern\": pattern_name,\n \"severity\": \"HIGH\",\n })\n except (PermissionError, UnicodeDecodeError):\n pass\n return findings\n\n\ndef adb_pull_app_data(package_name, output_dir):\n \"\"\"Pull application data via ADB for analysis.\"\"\"\n os.makedirs(output_dir, exist_ok=True)\n paths = [p.format(package=package_name) for p in ANDROID_SENSITIVE_PATHS]\n results = []\n for path in paths:\n try:\n subprocess.check_output(\n [\"adb\", \"pull\", path, output_dir],\n text=True, errors=\"replace\", timeout=15\n )\n results.append({\"path\": path, \"status\": \"pulled\"})\n except subprocess.SubprocessError:\n results.append({\"path\": path, \"status\": \"failed\"})\n return results\n\n\ndef main():\n parser = argparse.ArgumentParser(\n description=\"Detect insecure data storage in mobile apps (authorized testing only)\"\n )\n parser.add_argument(\"--scan-dir\", help=\"Directory containing app data to scan\")\n parser.add_argument(\"--package\", help=\"Android package name for ADB pull\")\n parser.add_argument(\"--pull-dir\", default=os.environ.get(\"MOBILE_AUDIT_DIR\", \"/tmp/mobile_audit\"))\n parser.add_argument(\"--output\", \"-o\", help=\"Output JSON report\")\n args = parser.parse_args()\n\n print(\"[*] Insecure Mobile Data Storage Detection Agent\")\n report = {\"timestamp\": datetime.now(timezone.utc).isoformat(), \"findings\": []}\n\n scan_dir = args.scan_dir or args.pull_dir\n if args.package:\n adb_pull_app_data(args.package, args.pull_dir)\n\n if os.path.isdir(scan_dir):\n report[\"findings\"].extend(scan_shared_prefs(scan_dir))\n report[\"findings\"].extend(scan_sqlite_databases(scan_dir))\n report[\"findings\"].extend(scan_file_storage(scan_dir))\n\n critical = sum(1 for f in report[\"findings\"] if f.get(\"severity\") == \"CRITICAL\")\n report[\"risk_level\"] = \"CRITICAL\" if critical else \"HIGH\" if report[\"findings\"] else \"LOW\"\n print(f\"[*] Findings: {len(report['findings'])} (CRITICAL: {critical})\")\n\n if args.output:\n with open(args.output, \"w\") as f:\n json.dump(report, f, indent=2)\n print(f\"[*] Report saved to {args.output}\")\n else:\n print(json.dumps(report, indent=2))\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":6631,"content_sha256":"bf6bfd64011d7dc7752315a1db6dca3b8ef0c83fecb3e5320a2d9f1557b10509"},{"filename":"scripts/process.py","content":"#!/usr/bin/env python3\n\"\"\"\nMobile Data Storage Security Scanner\n\nAnalyzes extracted mobile app data directories for insecure storage patterns.\nScans SharedPreferences, SQLite databases, plists, and files for sensitive data.\n\nUsage:\n python process.py --data-dir ./extracted_app_data [--platform android|ios] [--output report.json]\n\"\"\"\n\nimport argparse\nimport json\nimport os\nimport re\nimport sqlite3\nimport sys\nimport xml.etree.ElementTree as ET\nfrom datetime import datetime\nfrom pathlib import Path\n\n\nSENSITIVE_PATTERNS = {\n \"password\": re.compile(r\"(?:password|passwd|pwd)\\s*[:=]\\s*['\\\"]?([^\\s'\\\"\u003c]+)\", re.IGNORECASE),\n \"api_key\": re.compile(r\"(?:api[_-]?key|apikey)\\s*[:=]\\s*['\\\"]?([a-zA-Z0-9_-]{16,})\", re.IGNORECASE),\n \"token\": re.compile(r\"(?:auth[_-]?token|access[_-]?token|bearer)\\s*[:=]\\s*['\\\"]?([a-zA-Z0-9_.-]{16,})\", re.IGNORECASE),\n \"secret\": re.compile(r\"(?:secret|private[_-]?key)\\s*[:=]\\s*['\\\"]?([a-zA-Z0-9_/+=]{16,})\", re.IGNORECASE),\n \"jwt\": re.compile(r\"eyJ[a-zA-Z0-9_-]+\\.eyJ[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+\"),\n \"email\": re.compile(r\"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\"),\n \"credit_card\": re.compile(r\"\\b(?:4\\d{3}|5[1-5]\\d{2}|6011|3[47]\\d)\\d{8,12}\\b\"),\n \"ssn\": re.compile(r\"\\b\\d{3}-\\d{2}-\\d{4}\\b\"),\n \"private_key\": re.compile(r\"BEGIN\\s+(?:RSA\\s+)?PRIVATE\\s+KEY\"),\n}\n\n\ndef scan_shared_preferences(prefs_dir: str) -> list:\n \"\"\"Scan Android SharedPreferences XML files for sensitive data.\"\"\"\n findings = []\n prefs_path = Path(prefs_dir)\n\n if not prefs_path.exists():\n return findings\n\n for xml_file in prefs_path.glob(\"*.xml\"):\n try:\n tree = ET.parse(xml_file)\n root = tree.getroot()\n\n for element in root.iter():\n name = element.get(\"name\", \"\")\n value = element.text or element.get(\"value\", \"\")\n\n for pattern_name, pattern in SENSITIVE_PATTERNS.items():\n if pattern.search(name) or pattern.search(str(value)):\n findings.append({\n \"source\": f\"SharedPreferences/{xml_file.name}\",\n \"key\": name,\n \"value_preview\": str(value)[:50] + \"...\" if len(str(value)) > 50 else str(value),\n \"pattern_matched\": pattern_name,\n \"severity\": \"CRITICAL\" if pattern_name in (\"password\", \"private_key\", \"credit_card\") else \"HIGH\",\n })\n except ET.ParseError:\n findings.append({\n \"source\": f\"SharedPreferences/{xml_file.name}\",\n \"key\": \"PARSE_ERROR\",\n \"pattern_matched\": \"error\",\n \"severity\": \"INFO\",\n })\n\n return findings\n\n\ndef scan_sqlite_databases(db_dir: str) -> list:\n \"\"\"Scan SQLite databases for sensitive data.\"\"\"\n findings = []\n db_path = Path(db_dir)\n\n if not db_path.exists():\n return findings\n\n for db_file in list(db_path.glob(\"*.db\")) + list(db_path.glob(\"*.sqlite\")):\n try:\n conn = sqlite3.connect(str(db_file))\n cursor = conn.cursor()\n\n # Check if database is encrypted\n try:\n cursor.execute(\"SELECT count(*) FROM sqlite_master;\")\n findings.append({\n \"source\": f\"Database/{db_file.name}\",\n \"key\": \"encryption_status\",\n \"value_preview\": \"Database is NOT encrypted (SQLCipher not used)\",\n \"pattern_matched\": \"unencrypted_database\",\n \"severity\": \"HIGH\",\n })\n except sqlite3.DatabaseError:\n findings.append({\n \"source\": f\"Database/{db_file.name}\",\n \"key\": \"encryption_status\",\n \"value_preview\": \"Database appears to be encrypted\",\n \"pattern_matched\": \"encrypted_database\",\n \"severity\": \"INFO\",\n })\n continue\n\n # Get all tables\n cursor.execute(\"SELECT name FROM sqlite_master WHERE type='table';\")\n tables = [row[0] for row in cursor.fetchall()]\n\n for table in tables:\n # Get column names\n cursor.execute(f\"PRAGMA table_info('{table}');\")\n columns = [col[1] for col in cursor.fetchall()]\n\n # Check column names for sensitive patterns\n sensitive_cols = []\n for col in columns:\n col_lower = col.lower()\n if any(s in col_lower for s in [\"password\", \"token\", \"secret\", \"key\", \"auth\", \"credential\", \"ssn\", \"credit\"]):\n sensitive_cols.append(col)\n\n if sensitive_cols:\n # Sample data from sensitive columns\n cols_str = \", \".join(sensitive_cols)\n cursor.execute(f\"SELECT {cols_str} FROM '{table}' LIMIT 3;\")\n samples = cursor.fetchall()\n\n findings.append({\n \"source\": f\"Database/{db_file.name}/{table}\",\n \"key\": f\"sensitive_columns: {', '.join(sensitive_cols)}\",\n \"value_preview\": str(samples[:2]) if samples else \"empty\",\n \"pattern_matched\": \"sensitive_database_columns\",\n \"severity\": \"CRITICAL\",\n })\n\n # Full text search in all columns\n try:\n cursor.execute(f\"SELECT * FROM '{table}' LIMIT 100;\")\n rows = cursor.fetchall()\n for row in rows:\n row_text = \" \".join(str(cell) for cell in row if cell)\n for pattern_name, pattern in SENSITIVE_PATTERNS.items():\n if pattern.search(row_text):\n findings.append({\n \"source\": f\"Database/{db_file.name}/{table}\",\n \"key\": f\"row_data_match\",\n \"value_preview\": row_text[:100],\n \"pattern_matched\": pattern_name,\n \"severity\": \"HIGH\",\n })\n break\n except sqlite3.OperationalError:\n pass\n\n conn.close()\n\n except sqlite3.Error as e:\n findings.append({\n \"source\": f\"Database/{db_file.name}\",\n \"key\": \"error\",\n \"value_preview\": str(e),\n \"pattern_matched\": \"error\",\n \"severity\": \"INFO\",\n })\n\n return findings\n\n\ndef scan_plist_files(plist_dir: str) -> list:\n \"\"\"Scan iOS plist files for sensitive data.\"\"\"\n findings = []\n plist_path = Path(plist_dir)\n\n if not plist_path.exists():\n return findings\n\n for plist_file in plist_path.rglob(\"*.plist\"):\n try:\n with open(plist_file, \"r\", errors=\"replace\") as f:\n content = f.read()\n\n for pattern_name, pattern in SENSITIVE_PATTERNS.items():\n matches = pattern.findall(content)\n if matches:\n findings.append({\n \"source\": f\"Plist/{plist_file.name}\",\n \"key\": pattern_name,\n \"value_preview\": str(matches[0])[:50],\n \"pattern_matched\": pattern_name,\n \"severity\": \"HIGH\",\n })\n except (OSError, UnicodeDecodeError):\n pass\n\n return findings\n\n\ndef scan_general_files(data_dir: str) -> list:\n \"\"\"Scan general files for sensitive data and permission issues.\"\"\"\n findings = []\n data_path = Path(data_dir)\n\n for file_path in data_path.rglob(\"*\"):\n if not file_path.is_file():\n continue\n if file_path.suffix in (\".so\", \".dylib\", \".png\", \".jpg\", \".gif\", \".mp3\", \".mp4\"):\n continue\n if file_path.stat().st_size > 5 * 1024 * 1024:\n continue\n\n try:\n with open(file_path, \"r\", errors=\"replace\") as f:\n content = f.read(10000)\n\n for pattern_name, pattern in SENSITIVE_PATTERNS.items():\n matches = pattern.findall(content)\n if matches:\n findings.append({\n \"source\": f\"File/{file_path.relative_to(data_path)}\",\n \"key\": pattern_name,\n \"value_preview\": str(matches[0])[:50],\n \"pattern_matched\": pattern_name,\n \"severity\": \"HIGH\" if pattern_name in (\"password\", \"private_key\") else \"MEDIUM\",\n })\n except (OSError, UnicodeDecodeError):\n pass\n\n return findings\n\n\ndef main():\n parser = argparse.ArgumentParser(description=\"Mobile Data Storage Security Scanner\")\n parser.add_argument(\"--data-dir\", required=True, help=\"Extracted app data directory\")\n parser.add_argument(\"--platform\", choices=[\"android\", \"ios\"], default=\"android\")\n parser.add_argument(\"--output\", default=\"storage_scan.json\", help=\"Output report path\")\n args = parser.parse_args()\n\n data_dir = Path(args.data_dir)\n if not data_dir.exists():\n print(f\"[-] Directory not found: {args.data_dir}\")\n sys.exit(1)\n\n all_findings = []\n\n if args.platform == \"android\":\n print(\"[*] Scanning SharedPreferences...\")\n all_findings.extend(scan_shared_preferences(str(data_dir / \"shared_prefs\")))\n\n print(\"[*] Scanning SQLite databases...\")\n all_findings.extend(scan_sqlite_databases(str(data_dir / \"databases\")))\n else:\n print(\"[*] Scanning plist files...\")\n all_findings.extend(scan_plist_files(str(data_dir / \"Library\" / \"Preferences\")))\n\n print(\"[*] Scanning SQLite databases...\")\n all_findings.extend(scan_sqlite_databases(str(data_dir)))\n\n print(\"[*] Scanning general files...\")\n all_findings.extend(scan_general_files(str(data_dir)))\n\n # Generate report\n severity_counts = {}\n for f in all_findings:\n sev = f[\"severity\"]\n severity_counts[sev] = severity_counts.get(sev, 0) + 1\n\n report = {\n \"scan\": {\n \"data_directory\": str(data_dir),\n \"platform\": args.platform,\n \"date\": datetime.now().isoformat(),\n },\n \"summary\": {\n \"total_findings\": len(all_findings),\n \"by_severity\": severity_counts,\n },\n \"findings\": all_findings,\n }\n\n with open(args.output, \"w\") as f:\n json.dump(report, f, indent=2)\n\n print(f\"\\n[+] Scan complete: {len(all_findings)} findings\")\n print(f\"[+] Report saved: {args.output}\")\n for sev, count in sorted(severity_counts.items()):\n print(f\" {sev}: {count}\")\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":10968,"content_sha256":"4e5a51247b1946777481caa9245e814d689337dc8ee31636ae695fd53cd71af6"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Exploiting Insecure Data Storage in Mobile","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Assessing whether mobile applications store sensitive data securely on the device filesystem","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Testing for credential leakage through SharedPreferences, SQLite databases, or plists","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Evaluating keychain/keystore implementation for proper access control attributes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Performing data-at-rest security assessment during mobile penetration tests","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Do not use","type":"text","marks":[{"type":"strong"}]},{"text":" this skill on production user devices without authorization -- data extraction techniques require physical access or root/jailbreak privileges.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Rooted Android device or emulator with ADB access","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Jailbroken iOS device with SSH access or Objection-patched IPA","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ADB (Android Debug Bridge) for Android filesystem access","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SQLite3 CLI for database inspection","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Frida/Objection for runtime data extraction","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Target application installed and exercised (logged in, data cached)","type":"text"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Legal Notice:","type":"text","marks":[{"type":"strong"}]},{"text":" This skill is for authorized security testing and educational purposes only. Unauthorized use against systems you do not own or have written permission to test is illegal and may violate computer fraud laws.","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Map Application Data Storage Locations","type":"text"}]},{"type":"paragraph","content":[{"text":"Android storage paths:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Internal storage (app-private, requires root)\n/data/data/\u003cpackage_name>/\n├── shared_prefs/ # SharedPreferences XML files\n├── databases/ # SQLite databases\n├── files/ # General files\n├── cache/ # Cached data\n├── lib/ # Native libraries\n└── app_webview/ # WebView data\n\n# External storage (world-readable on older Android)\n/sdcard/Android/data/\u003cpackage_name>/\n\n# Check for world-readable files\nadb shell run-as \u003cpackage_name> ls -la /data/data/\u003cpackage_name>/","type":"text"}]},{"type":"paragraph","content":[{"text":"iOS storage paths:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# App sandbox (accessible via SSH on jailbroken device)\n/var/mobile/Containers/Data/Application/\u003cUUID>/\n├── Documents/ # User data, backed up by default\n├── Library/\n│ ├── Preferences/ # NSUserDefaults plists\n│ ├── Caches/ # Cache data\n│ └── Application Support/\n└── tmp/ # Temporary files","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: Extract and Analyze SharedPreferences (Android)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Pull SharedPreferences files\nadb shell run-as \u003cpackage_name> cat shared_prefs/*.xml\n\n# Or on rooted device\nadb pull /data/data/\u003cpackage_name>/shared_prefs/ ./shared_prefs/\n\n# Search for sensitive data\ngrep -ri \"password\\|token\\|secret\\|key\\|session\\|auth\\|cookie\" shared_prefs/","type":"text"}]},{"type":"paragraph","content":[{"text":"Common insecure storage patterns:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"xml"},"content":[{"text":"\u003c!-- Plaintext credentials -->\n\u003cstring name=\"user_password\">mysecretpass123\u003c/string>\n\u003cstring name=\"auth_token\">eyJhbGciOiJIUzI1NiIs...\u003c/string>\n\u003cstring name=\"api_key\">sk-live-abc123def456\u003c/string>\n\n\u003c!-- Sensitive PII -->\n\u003cstring name=\"user_ssn\">123-45-6789\u003c/string>\n\u003cstring name=\"credit_card\">4111111111111111\u003c/string>","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: Analyze SQLite Databases","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Pull databases\nadb pull /data/data/\u003cpackage_name>/databases/ ./databases/\n\n# Open and inspect\nsqlite3 databases/app.db\n.tables\n.schema users\nSELECT * FROM users;\nSELECT * FROM sessions;\nSELECT * FROM tokens;\n\n# Search all tables for sensitive columns\nsqlite3 databases/app.db \".dump\" | grep -i \"password\\|token\\|secret\\|credit\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Check for unencrypted SQLCipher databases:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# If database opens without password, it's unencrypted\nsqlite3 databases/app.db \"SELECT count(*) FROM sqlite_master;\"\n# Success = unencrypted (vulnerability)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: Inspect iOS Keychain Storage","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Using Objection\nobjection --gadget com.target.app explore\nios keychain dump\n\n# Check protection class attributes\n# kSecAttrAccessibleWhenUnlocked - OK for most data\n# kSecAttrAccessibleAlways - VULNERABLE: accessible even when locked\n# kSecAttrAccessibleAfterFirstUnlock - acceptable for background apps","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: Assess External Storage and Backup Exposure","type":"text"}]},{"type":"paragraph","content":[{"text":"Android:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Check if backup is enabled\naapt dump badging target.apk | grep -i \"allowBackup\"\n# android:allowBackup=\"true\" = vulnerability\n\n# Extract backup data\nadb backup -f backup.ab -apk \u003cpackage_name>\njava -jar abe.jar unpack backup.ab backup.tar\ntar xvf backup.tar\n# Inspect extracted data for sensitive information\n\n# Check external storage\nadb shell ls -la /sdcard/Android/data/\u003cpackage_name>/","type":"text"}]},{"type":"paragraph","content":[{"text":"iOS:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Check backup exclusion\n# Files in Documents/ are backed up by default\n# Check NSURLIsExcludedFromBackupKey attribute\nobjection --gadget com.target.app explore\nios plist cat Info.plist","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 6: Runtime Memory Analysis","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Dump process memory for sensitive data\nobjection --gadget com.target.app explore\nmemory search \"password\" --string\nmemory search \"BEGIN RSA PRIVATE KEY\" --string\nmemory dump all /tmp/memdump/\n\n# Android: Check for sensitive data in logs\nadb logcat -d | grep -i \"password\\|token\\|key\\|secret\"","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Key Concepts","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Term","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Definition","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SharedPreferences","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Android key-value storage in XML format; often misused for storing credentials in plaintext","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Keychain Services","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"iOS secure credential storage backed by Secure Enclave hardware on modern devices","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Android Keystore","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hardware-backed cryptographic key storage on Android; keys cannot be extracted from the device","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SQLCipher","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Transparent encryption extension for SQLite databases; prevents data extraction without password","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Data Protection API","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"iOS file-level encryption tied to device passcode; controlled via protection class attributes","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Tools & Systems","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ADB (Android Debug Bridge)","type":"text","marks":[{"type":"strong"}]},{"text":": Command-line tool for Android device interaction and filesystem access","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Objection","type":"text","marks":[{"type":"strong"}]},{"text":": Frida-powered runtime exploration for keychain dumping and memory inspection","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SQLite3","type":"text","marks":[{"type":"strong"}]},{"text":": Command-line interface for inspecting unencrypted SQLite databases","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Android Backup Extractor (ABE)","type":"text","marks":[{"type":"strong"}]},{"text":": Tool for unpacking ADB backup files to inspect stored data","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"iExplorer","type":"text","marks":[{"type":"strong"}]},{"text":": GUI tool for browsing iOS app sandbox filesystem","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Common Pitfalls","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Encrypted but key in code","type":"text","marks":[{"type":"strong"}]},{"text":": Some apps encrypt databases but store the encryption key in SharedPreferences or hardcoded in the binary. Always check for key storage alongside encryption.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"MODE_WORLD_READABLE deprecation","type":"text","marks":[{"type":"strong"}]},{"text":": This flag was deprecated in API 17, but legacy apps may still use it, making SharedPreferences readable by other apps.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"iOS backup scope","type":"text","marks":[{"type":"strong"}]},{"text":": By default, all files in the Documents directory are included in iTunes/iCloud backups. Verify that sensitive files have the backup exclusion attribute set.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Clipboard exposure","type":"text","marks":[{"type":"strong"}]},{"text":": Data copied to clipboard is accessible to all apps. Check if the app copies sensitive data (passwords, tokens) to the clipboard.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"exploiting-insecure-data-storage-in-mobile","tags":["mobile-security","android","ios","data-storage","owasp-mobile","penetration-testing"],"author":"@skillopedia","domain":"cybersecurity","source":{"stars":13207,"repo_name":"anthropic-cybersecurity-skills","origin_url":"https://github.com/mukul975/anthropic-cybersecurity-skills/blob/HEAD/skills/exploiting-insecure-data-storage-in-mobile/SKILL.md","repo_owner":"mukul975","body_sha256":"44a0c1ce87a34f3a2ccca39469e22ca9a103518a0c3ecb8ba48d6ebdd19b1bdf","cluster_key":"5346877dae46a6c8b99aeb25ce7fd60091365d4d7bd1f9c9d1e40aac701a6126","clean_bundle":{"format":"clean-skill-bundle-v1","source":"mukul975/anthropic-cybersecurity-skills/skills/exploiting-insecure-data-storage-in-mobile/SKILL.md","attachments":[{"id":"fac3831a-6710-59b0-9830-c1a7af322003","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fac3831a-6710-59b0-9830-c1a7af322003/attachment.md","path":"assets/template.md","size":1212,"sha256":"1c7fdf6240259f64a1c3cf4836243bfdaeaa03660b154dfd21a277c2e6a0b9ec","contentType":"text/markdown; charset=utf-8"},{"id":"4dbfdcce-c4df-55d6-aeee-38d6e3e45e25","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4dbfdcce-c4df-55d6-aeee-38d6e3e45e25/attachment.md","path":"references/api-reference.md","size":2632,"sha256":"cad8e5d4debab7f3353dfc2f162f3e54b8334a942a74a60dc0000a799d8c887f","contentType":"text/markdown; charset=utf-8"},{"id":"97d1e1e4-d116-54fe-8aef-24d884820771","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/97d1e1e4-d116-54fe-8aef-24d884820771/attachment.md","path":"references/standards.md","size":2844,"sha256":"bc6fe5eb5bdac662dad0b832c91c55c02c1ac2ff3f2ae1b8860df6567f6e3e0b","contentType":"text/markdown; charset=utf-8"},{"id":"fa533053-d7e8-5656-aaca-0ed1ca90e8d1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fa533053-d7e8-5656-aaca-0ed1ca90e8d1/attachment.md","path":"references/workflows.md","size":3307,"sha256":"4c26466aff8bb61ceeb5f84c681d9d2f6b82ce3aaf1738cced7a282741695334","contentType":"text/markdown; charset=utf-8"},{"id":"aa50a6ad-e43d-57b9-8c55-1073672b7ab5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/aa50a6ad-e43d-57b9-8c55-1073672b7ab5/attachment.py","path":"scripts/agent.py","size":6631,"sha256":"bf6bfd64011d7dc7752315a1db6dca3b8ef0c83fecb3e5320a2d9f1557b10509","contentType":"text/x-python; charset=utf-8"},{"id":"1a279017-c9ad-5c03-b797-b84837aaf1f0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1a279017-c9ad-5c03-b797-b84837aaf1f0/attachment.py","path":"scripts/process.py","size":10968,"sha256":"4e5a51247b1946777481caa9245e814d689337dc8ee31636ae695fd53cd71af6","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"9fbbb9153cd87b79b23eaaacbf60ff5d86f59b017022f5c772bf5d9161a48b4f","attachment_count":6,"text_attachments":6,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/exploiting-insecure-data-storage-in-mobile/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":0},"license":"Apache-2.0","version":"v1","category":"security","nist_csf":["PR.PS-01","PR.AA-05","ID.RA-01","DE.CM-09"],"subdomain":"mobile-security","import_tag":"clean-skills-v1","description":"Identifies and exploits insecure local data storage vulnerabilities in Android and iOS mobile applications including unencrypted databases, world-readable files, insecure SharedPreferences, plaintext credential storage, and improper keychain/keystore usage. Use when performing mobile penetration testing focused on OWASP M9 (Insecure Data Storage) or assessing compliance with MASVS-STORAGE requirements. Activates for requests involving mobile data storage security, local storage exploitation, SharedPreferences analysis, or mobile data leakage assessment.\n","nist_ai_rmf":["MEASURE-2.7","MAP-5.1","MANAGE-2.4","GOVERN-1.1","GOVERN-4.2"],"atlas_techniques":["AML.T0057"]}},"renderedAt":1782980953903}

Exploiting Insecure Data Storage in Mobile When to Use Use this skill when: - Assessing whether mobile applications store sensitive data securely on the device filesystem - Testing for credential leakage through SharedPreferences, SQLite databases, or plists - Evaluating keychain/keystore implementation for proper access control attributes - Performing data-at-rest security assessment during mobile penetration tests Do not use this skill on production user devices without authorization -- data extraction techniques require physical access or root/jailbreak privileges. Prerequisites - Rooted A…