From 34c997f923dd97e7a825ce58953ed71aea4f98a7 Mon Sep 17 00:00:00 2001 From: panelmix Date: Tue, 2 Nov 2021 13:29:12 +0100 Subject: [PATCH] Replace NetworkingAnalytics with Analytics --- docs/ios/records.md | 18 +++- mvt/ios/modules/fs/__init__.py | 4 +- mvt/ios/modules/fs/analytics.py | 119 +++++++++++++++++++++ mvt/ios/modules/fs/networking_analytics.py | 91 ---------------- 4 files changed, 136 insertions(+), 96 deletions(-) create mode 100644 mvt/ios/modules/fs/analytics.py delete mode 100644 mvt/ios/modules/fs/networking_analytics.py diff --git a/docs/ios/records.md b/docs/ios/records.md index 8d12210..6a8444a 100644 --- a/docs/ios/records.md +++ b/docs/ios/records.md @@ -4,6 +4,18 @@ In this page you can find a (reasonably) up-to-date breakdown of the files creat ## Records extracted by `check-fs` or `check-backup` +### `analytics.json` + +!!! info "Availability" + Backup (if encrypted): :material-close: + Full filesystem dump: :material-check: + +This JSON file is created by mvt-ios' `Analytics` module. The module extracts records from the plists inside the SQLite databases located at *private/var/Keychains/Analytics/\*.db*, which contain various analytics information regarding networking, certificate-pinning, TLS, etc. failures. + +If indicators are provided through the command-line, processes and domains are checked against all fields of the plist. Any matches are stored in *analytics_detected.json*. + +--- + ### `backup_info.json` !!! info "Availabiliy" @@ -56,7 +68,7 @@ If indicators are provided through the command-line, they are checked against bo This JSON file is created by mvt-ios' `ChromeHistory` module. The module extracts records from a SQLite database located at */private/var/mobile/Containers/Data/Application/\*/Library/Application Support/Google/Chrome/Default/History*, which contains a history of URL visits. -If indicators a provided through the command-line, they are checked against the visited URL. Any matches are stored in *chrome_history_detected.json*. +If indicators are provided through the command-line, they are checked against the visited URL. Any matches are stored in *chrome_history_detected.json*. --- @@ -100,7 +112,7 @@ If indicators are provided through the command-line, they are checked against bo This JSON file is created by mvt-ios' `FirefoxHistory` module. The module extracts records from a SQLite database located at */private/var/mobile/profile.profile/browser.db*, which contains a history of URL visits. -If indicators a provided through the command-line, they are checked against the visited URL. Any matches are stored in *firefox_history_detected.json*. +If indicators are provided through the command-line, they are checked against the visited URL. Any matches are stored in *firefox_history_detected.json*. --- @@ -202,7 +214,7 @@ This JSON file is created by mvt-ios' `ProfileEvents` module. The module extract This JSON file is created by mvt-ios' `SafariBrowserState` module. The module extracts records from the SQLite databases located at */private/var/mobile/Library/Safari/BrowserState.db* or */private/var/mobile/Containers/Data/Application/\*/Library/Safari/BrowserState.db*, which contain records of opened tabs. -If indicators a provided through the command-line, they are checked against the visited URL. Any matches are stored in *safari_browser_state_detected.json*. +If indicators are provided through the command-line, they are checked against the visited URL. Any matches are stored in *safari_browser_state_detected.json*. --- diff --git a/mvt/ios/modules/fs/__init__.py b/mvt/ios/modules/fs/__init__.py index 42eb359..af0b39f 100644 --- a/mvt/ios/modules/fs/__init__.py +++ b/mvt/ios/modules/fs/__init__.py @@ -6,7 +6,7 @@ from .cache_files import CacheFiles from .filesystem import Filesystem from .net_netusage import Netusage -from .networking_analytics import NetworkingAnalytics +from .analytics import Analytics from .safari_favicon import SafariFavicon from .shutdownlog import ShutdownLog from .version_history import IOSVersionHistory @@ -14,6 +14,6 @@ from .webkit_indexeddb import WebkitIndexedDB from .webkit_localstorage import WebkitLocalStorage from .webkit_safariviewservice import WebkitSafariViewService -FS_MODULES = [CacheFiles, Filesystem, Netusage, NetworkingAnalytics, SafariFavicon, ShutdownLog, +FS_MODULES = [CacheFiles, Filesystem, Netusage, Analytics, SafariFavicon, ShutdownLog, IOSVersionHistory, WebkitIndexedDB, WebkitLocalStorage, WebkitSafariViewService,] diff --git a/mvt/ios/modules/fs/analytics.py b/mvt/ios/modules/fs/analytics.py new file mode 100644 index 0000000..b69282d --- /dev/null +++ b/mvt/ios/modules/fs/analytics.py @@ -0,0 +1,119 @@ +# 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 + +ANALYTICS_DB_PATH = [ + "private/var/Keychains/Analytics/*.db", +] + +class Analytics(IOSExtraction): + """This module extracts information from the private/var/Keychains/Analytics/*.db files.""" + + 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": record["artifact"], + "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 malicious process \"%s\" in %s file at %s", + ioc, result["artifact"], result["timestamp"]) + self.detected.append(result) + break + for ioc in self.indicators.ioc_domains: + for key in result.keys(): + if ioc in str(result[key]): + self.log.warning("Found mention of a malicious domain \"%s\" in %s file at %s", + ioc, result["artifact"], result["timestamp"]) + self.detected.append(result) + break + + def _extract_analytics_data(self): + artifact = self.file_path.split("/")[-1] + + conn = sqlite3.connect(self.file_path) + cur = conn.cursor() + + try: + cur.execute(""" + SELECT + timestamp, + data + FROM hard_failures + UNION + SELECT + timestamp, + data + FROM soft_failures + UNION + SELECT + timestamp, + data + FROM all_events; + """) + except sqlite3.OperationalError: + 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 + data["artifact"] = artifact + + 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 analytics data from %s", len(self.results), artifact) + + def run(self): + for file_path in self._get_fs_files_from_patterns(ANALYTICS_DB_PATH): + self.file_path = file_path + self.log.info("Found Analytics database file at path: %s", file_path) + self._extract_analytics_data() diff --git a/mvt/ios/modules/fs/networking_analytics.py b/mvt/ios/modules/fs/networking_analytics.py deleted file mode 100644 index 0b8c176..0000000 --- a/mvt/ios/modules/fs/networking_analytics.py +++ /dev/null @@ -1,91 +0,0 @@ -# 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")