From 904daad93593d87a4114f2a4774b8109c3e3af92 Mon Sep 17 00:00:00 2001 From: pungentsneak Date: Wed, 22 Sep 2021 13:24:17 +0200 Subject: [PATCH 1/2] add ShutdownLog --- docs/ios/records.md | 12 ++++++ mvt/common/indicators.py | 10 ++++- mvt/ios/modules/fs/__init__.py | 3 +- mvt/ios/modules/fs/shutdown.py | 67 ++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 mvt/ios/modules/fs/shutdown.py diff --git a/docs/ios/records.md b/docs/ios/records.md index 3588e45..8d12210 100644 --- a/docs/ios/records.md +++ b/docs/ios/records.md @@ -230,6 +230,18 @@ If indicators are provided through the command-line, they are checked against th --- +### `shutdown_log.json` + +!!! info "Availability" + Backup (if encrypted): :material-close: + Full filesystem dump: :material-check: + +This JSON file is created by mvt-ios' `ShutdownLog` module. The module extracts records from the shutdown log located at *private/var/db/diagnostics/shutdown.log*. When shutting down an iPhone, a SIGTERM will be sent to all processes runnning. The `shutdown.log` file will log any process (with its pid and path) that did not shut down after the SIGTERM was sent. + +If indicators are provided through the command-line, they are checked against the paths. Any matches are stored in *shutdown_log_detected.json*. + +--- + ### `sms.json` !!! info "Availability" diff --git a/mvt/common/indicators.py b/mvt/common/indicators.py index 1310f11..a7d517f 100644 --- a/mvt/common/indicators.py +++ b/mvt/common/indicators.py @@ -163,10 +163,12 @@ class Indicators: return False - def check_process(self, process) -> bool: + def check_process(self, process, contains = False) -> bool: """Check the provided process name against the list of process indicators. + :param contains: Check if the process is contained inside the string. + :type process: bool :param process: Process name to check against process indicators :type process: str :returns: True if process matched an indicator, otherwise False @@ -175,6 +177,12 @@ class Indicators: if not process: return False + if (contains): + for malicious_process in self.ioc_processes: + if malicious_process in process: + self.log.warning("Found a known suspicious process name (\"%s\") in \"%s\"", malicious_process, process) + return True + proc_name = os.path.basename(process) if proc_name in self.ioc_processes: self.log.warning("Found a known suspicious process name \"%s\"", process) diff --git a/mvt/ios/modules/fs/__init__.py b/mvt/ios/modules/fs/__init__.py index 9a54b51..7762f41 100644 --- a/mvt/ios/modules/fs/__init__.py +++ b/mvt/ios/modules/fs/__init__.py @@ -7,10 +7,11 @@ from .cache_files import CacheFiles from .filesystem import Filesystem from .net_netusage import Netusage from .safari_favicon import SafariFavicon +from .shutdown import ShutdownLog from .version_history import IOSVersionHistory from .webkit_indexeddb import WebkitIndexedDB from .webkit_localstorage import WebkitLocalStorage from .webkit_safariviewservice import WebkitSafariViewService -FS_MODULES = [CacheFiles, Filesystem, Netusage, SafariFavicon, IOSVersionHistory, +FS_MODULES = [CacheFiles, Filesystem, Netusage, SafariFavicon, ShutdownLog, IOSVersionHistory, WebkitIndexedDB, WebkitLocalStorage, WebkitSafariViewService,] diff --git a/mvt/ios/modules/fs/shutdown.py b/mvt/ios/modules/fs/shutdown.py new file mode 100644 index 0000000..4d96692 --- /dev/null +++ b/mvt/ios/modules/fs/shutdown.py @@ -0,0 +1,67 @@ +# 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/ + +from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso + +from ..base import IOSExtraction + +SHUTDOWN_LOG_PATH = [ + "private/var/db/diagnostics/shutdown.log", +] + +class ShutdownLog(IOSExtraction): + """This module extracts processes information from the shutdown log 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["isodate"], + "module": self.__class__.__name__, + "event": "shutdown", + "data": f"Client {record['client']} with PID {record['pid']} was running when the device was shut down", + } + + + def check_indicators(self): + if not self.indicators: + return + + for result in self.results: + if self.indicators.check_process(result["client"], True): + self.detected.append(result) + + def run(self): + self._find_ios_database(root_paths=SHUTDOWN_LOG_PATH) + self.log.info("Found shutdown log at path: %s", self.file_path) + + with open(self.file_path, "r") as shutdown_log: + shutdown_log_lines = shutdown_log.readlines() + date = "0000-00-00 00:00:00.000000 (unknown)" + + for line in shutdown_log_lines: + if line.startswith("After "): + continue + + elif line.startswith("\t\tremaining client pid: "): + pid = int(line.split("\t\tremaining client pid: ")[1].split(" ")[0]) + client = line.split("(")[1].split(")")[0] + entry = { + "isodate": date, + "pid": pid, + "client": client + } + if entry not in self.results: + self.results.append(entry) + + elif line.startswith("SIGTERM: ["): + isodate = int(line.split("[")[1].split("]")[0]) + date = convert_timestamp_to_iso(convert_mactime_to_unix(isodate, False)) + + self.results = sorted(self.results, key=lambda entry: entry["isodate"]) From 8fcc79ebfa5a58f98b6ae724aed7da4e6ed36bf4 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 28 Sep 2021 12:42:57 +0200 Subject: [PATCH 2/2] Adapted for better support --- mvt/common/indicators.py | 10 +--- mvt/ios/modules/fs/__init__.py | 7 +-- mvt/ios/modules/fs/shutdown.py | 67 ------------------------- mvt/ios/modules/fs/shutdownlog.py | 82 +++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 79 deletions(-) delete mode 100644 mvt/ios/modules/fs/shutdown.py create mode 100644 mvt/ios/modules/fs/shutdownlog.py diff --git a/mvt/common/indicators.py b/mvt/common/indicators.py index a7d517f..1310f11 100644 --- a/mvt/common/indicators.py +++ b/mvt/common/indicators.py @@ -163,12 +163,10 @@ class Indicators: return False - def check_process(self, process, contains = False) -> bool: + def check_process(self, process) -> bool: """Check the provided process name against the list of process indicators. - :param contains: Check if the process is contained inside the string. - :type process: bool :param process: Process name to check against process indicators :type process: str :returns: True if process matched an indicator, otherwise False @@ -177,12 +175,6 @@ class Indicators: if not process: return False - if (contains): - for malicious_process in self.ioc_processes: - if malicious_process in process: - self.log.warning("Found a known suspicious process name (\"%s\") in \"%s\"", malicious_process, process) - return True - proc_name = os.path.basename(process) if proc_name in self.ioc_processes: self.log.warning("Found a known suspicious process name \"%s\"", process) diff --git a/mvt/ios/modules/fs/__init__.py b/mvt/ios/modules/fs/__init__.py index 7762f41..34e1567 100644 --- a/mvt/ios/modules/fs/__init__.py +++ b/mvt/ios/modules/fs/__init__.py @@ -7,11 +7,12 @@ from .cache_files import CacheFiles from .filesystem import Filesystem from .net_netusage import Netusage from .safari_favicon import SafariFavicon -from .shutdown import ShutdownLog +from .shutdownlog import ShutdownLog from .version_history import IOSVersionHistory from .webkit_indexeddb import WebkitIndexedDB from .webkit_localstorage import WebkitLocalStorage from .webkit_safariviewservice import WebkitSafariViewService -FS_MODULES = [CacheFiles, Filesystem, Netusage, SafariFavicon, ShutdownLog, IOSVersionHistory, - WebkitIndexedDB, WebkitLocalStorage, WebkitSafariViewService,] +FS_MODULES = [CacheFiles, Filesystem, Netusage, SafariFavicon, ShutdownLog, + IOSVersionHistory, WebkitIndexedDB, WebkitLocalStorage, + WebkitSafariViewService,] diff --git a/mvt/ios/modules/fs/shutdown.py b/mvt/ios/modules/fs/shutdown.py deleted file mode 100644 index 4d96692..0000000 --- a/mvt/ios/modules/fs/shutdown.py +++ /dev/null @@ -1,67 +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/ - -from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso - -from ..base import IOSExtraction - -SHUTDOWN_LOG_PATH = [ - "private/var/db/diagnostics/shutdown.log", -] - -class ShutdownLog(IOSExtraction): - """This module extracts processes information from the shutdown log 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["isodate"], - "module": self.__class__.__name__, - "event": "shutdown", - "data": f"Client {record['client']} with PID {record['pid']} was running when the device was shut down", - } - - - def check_indicators(self): - if not self.indicators: - return - - for result in self.results: - if self.indicators.check_process(result["client"], True): - self.detected.append(result) - - def run(self): - self._find_ios_database(root_paths=SHUTDOWN_LOG_PATH) - self.log.info("Found shutdown log at path: %s", self.file_path) - - with open(self.file_path, "r") as shutdown_log: - shutdown_log_lines = shutdown_log.readlines() - date = "0000-00-00 00:00:00.000000 (unknown)" - - for line in shutdown_log_lines: - if line.startswith("After "): - continue - - elif line.startswith("\t\tremaining client pid: "): - pid = int(line.split("\t\tremaining client pid: ")[1].split(" ")[0]) - client = line.split("(")[1].split(")")[0] - entry = { - "isodate": date, - "pid": pid, - "client": client - } - if entry not in self.results: - self.results.append(entry) - - elif line.startswith("SIGTERM: ["): - isodate = int(line.split("[")[1].split("]")[0]) - date = convert_timestamp_to_iso(convert_mactime_to_unix(isodate, False)) - - self.results = sorted(self.results, key=lambda entry: entry["isodate"]) diff --git a/mvt/ios/modules/fs/shutdownlog.py b/mvt/ios/modules/fs/shutdownlog.py new file mode 100644 index 0000000..df08dec --- /dev/null +++ b/mvt/ios/modules/fs/shutdownlog.py @@ -0,0 +1,82 @@ +# 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/ + +from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso + +from ..base import IOSExtraction + +SHUTDOWN_LOG_PATH = [ + "private/var/db/diagnostics/shutdown.log", +] + +class ShutdownLog(IOSExtraction): + """This module extracts processes information from the shutdown log 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["isodate"], + "module": self.__class__.__name__, + "event": "shutdown", + "data": f"Client {record['client']} with PID {record['pid']} was running when the device was shut down", + } + + + def check_indicators(self): + if not self.indicators: + return + + for result in self.results: + for ioc in self.indicators.ioc_processes: + parts = result["client"].split("/") + if ioc in parts: + self.log.warning("Found mention of a known malicious process \"%s\" in shutdown.log", + ioc) + self.detected.append(result) + + def process_shutdownlog(self, content): + current_processes = [] + for line in content.split("\n"): + line = line.strip() + + if line.startswith("remaining client pid:"): + current_processes.append({ + "pid": line[line.find("pid: ")+5:line.find(" (")], + "client": line[line.find("(")+1:line.find(")")], + }) + elif line.startswith("SIGTERM: "): + try: + mac_timestamp = int(line[line.find("[")+1:line.find("]")]) + except ValueError: + try: + start = line.find(" @")+2 + mac_timestamp = int(line[start:start+10]) + except: + mac_timestamp = 0 + + timestamp = convert_mactime_to_unix(mac_timestamp, from_2001=False) + isodate = convert_timestamp_to_iso(timestamp) + + for current_process in current_processes: + self.results.append(dict( + "isodate": isodate, + "pid": current_process["pid"], + "client": current_process["client"], + )) + + current_processes = [] + + self.results = sorted(self.results, key=lambda entry: entry["isodate"]) + + def run(self): + self._find_ios_database(root_paths=SHUTDOWN_LOG_PATH) + self.log.info("Found shutdown log at path: %s", self.file_path) + with open(self.file_path, "r") as handle: + self.process_shutdownlog(handle.read())