diff --git a/mvt/ios/modules/fs/__init__.py b/mvt/ios/modules/fs/__init__.py index 34e1567..42eb359 100644 --- a/mvt/ios/modules/fs/__init__.py +++ b/mvt/ios/modules/fs/__init__.py @@ -6,6 +6,7 @@ from .cache_files import CacheFiles from .filesystem import Filesystem from .net_netusage import Netusage +from .networking_analytics import NetworkingAnalytics from .safari_favicon import SafariFavicon from .shutdownlog import ShutdownLog from .version_history import IOSVersionHistory @@ -13,6 +14,6 @@ from .webkit_indexeddb import WebkitIndexedDB from .webkit_localstorage import WebkitLocalStorage from .webkit_safariviewservice import WebkitSafariViewService -FS_MODULES = [CacheFiles, Filesystem, Netusage, SafariFavicon, ShutdownLog, +FS_MODULES = [CacheFiles, Filesystem, Netusage, NetworkingAnalytics, SafariFavicon, ShutdownLog, IOSVersionHistory, WebkitIndexedDB, WebkitLocalStorage, WebkitSafariViewService,] diff --git a/mvt/ios/modules/fs/networking_analytics.py b/mvt/ios/modules/fs/networking_analytics.py new file mode 100644 index 0000000..4196cba --- /dev/null +++ b/mvt/ios/modules/fs/networking_analytics.py @@ -0,0 +1,91 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021 The MVT Project Authors. +# Use of this software is governed by the MVT License 1.1 that can be found at +# https://license.mvt.re/1.1/ + +import plistlib +import sqlite3 + +from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso + +from ..base import IOSExtraction + +NETWORKING_ANALYTICS_DB_PATH = [ + "private/var/Keychains/Analytics/networking_analytics.db", +] + +class NetworkingAnalytics(IOSExtraction): + """This module extracts information from the networking_analytics.db file.""" + + def __init__(self, file_path=None, base_folder=None, output_folder=None, + fast_mode=False, log=None, results=[]): + super().__init__(file_path=file_path, base_folder=base_folder, + output_folder=output_folder, fast_mode=fast_mode, + log=log, results=results) + + def serialize(self, record): + return { + "timestamp": record["timestamp"], + "module": self.__class__.__name__, + "event": "network_crash", + "data": f"{record}", + } + + def check_indicators(self): + if not self.indicators: + return + + for result in self.results: + for ioc in self.indicators.ioc_processes: + for key in result.keys(): + if ioc == result[key]: + self.log.warning("Found mention of a known malicious process \"%s\" in networking_analytics.db at %s", + ioc, result["timestamp"]) + self.detected.append(result) + break + + def _extract_networking_analytics_data(self): + conn = sqlite3.connect(self.file_path) + cur = conn.cursor() + cur.execute(""" + SELECT + timestamp, + data + FROM hard_failures + UNION + SELECT + timestamp, + data + FROM soft_failures; + """) + + for row in cur: + if row[0] and row[1]: + timestamp = convert_timestamp_to_iso(convert_mactime_to_unix(row[0], False)) + data = plistlib.loads(row[1]) + data["timestamp"] = timestamp + elif row[0]: + timestamp = convert_timestamp_to_iso(convert_mactime_to_unix(row[0], False)) + data = {} + data["timestamp"] = timestamp + elif row[1]: + timestamp = "" + data = plistlib.loads(row[1]) + data["timestamp"] = timestamp + + self.results.append(data) + + self.results = sorted(self.results, key=lambda entry: entry["timestamp"]) + + cur.close() + conn.close() + + self.log.info("Extracted information on %d network crashes", len(self.results)) + + def run(self): + self._find_ios_database(root_paths=NETWORKING_ANALYTICS_DB_PATH) + if (self.file_path): + self.log.info("Found networking_analytics.db log at path: %s", self.file_path) + self._extract_networking_analytics_data() + else: + self.log.info("networking_analytics.db not found") \ No newline at end of file