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/ios/modules/fs/__init__.py b/mvt/ios/modules/fs/__init__.py index 9a54b51..34e1567 100644 --- a/mvt/ios/modules/fs/__init__.py +++ b/mvt/ios/modules/fs/__init__.py @@ -7,10 +7,12 @@ from .cache_files import CacheFiles from .filesystem import Filesystem from .net_netusage import Netusage from .safari_favicon import SafariFavicon +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, IOSVersionHistory, - WebkitIndexedDB, WebkitLocalStorage, WebkitSafariViewService,] +FS_MODULES = [CacheFiles, Filesystem, Netusage, SafariFavicon, ShutdownLog, + IOSVersionHistory, WebkitIndexedDB, WebkitLocalStorage, + WebkitSafariViewService,] 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())