diff --git a/docs/ios/records.md b/docs/ios/records.md index 3cdfa8f..17be192 100644 --- a/docs/ios/records.md +++ b/docs/ios/records.md @@ -142,6 +142,16 @@ If indicators are provided through the command-line, they are checked against th --- +### `global_preferences.json` + +!!! info "Availability" + Backup: :material-check: + Full filesystem dump: :material-check: + +This JSON file is created by mvt-ios' `GlobalPreferences` module. The module extracts records from a Plist file located at */private/var/mobile/Library/Preferences/.GlobalPreferences.plist*, which contains a system preferences including if Lockdown Mode is enabled. + +--- + ### `id_status_cache.json` !!! info "Availability" diff --git a/mvt/ios/modules/mixed/__init__.py b/mvt/ios/modules/mixed/__init__.py index 5bc2dea..de6f808 100644 --- a/mvt/ios/modules/mixed/__init__.py +++ b/mvt/ios/modules/mixed/__init__.py @@ -11,6 +11,7 @@ from .chrome_history import ChromeHistory from .contacts import Contacts from .firefox_favicon import FirefoxFavicon from .firefox_history import FirefoxHistory +from .global_preferences import GlobalPreferences from .idstatuscache import IDStatusCache from .interactionc import InteractionC from .locationd import LocationdClients @@ -49,4 +50,5 @@ MIXED_MODULES = [ Shortcuts, Applications, Calendar, + GlobalPreferences, ] diff --git a/mvt/ios/modules/mixed/global_preferences.py b/mvt/ios/modules/mixed/global_preferences.py new file mode 100644 index 0000000..02c7445 --- /dev/null +++ b/mvt/ios/modules/mixed/global_preferences.py @@ -0,0 +1,63 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2023 Claudio Guarnieri. +# Use of this software is governed by the MVT License 1.1 that can be found at +# https://license.mvt.re/1.1/ + +import logging +import plistlib +from typing import Optional + +from ..base import IOSExtraction + +GLOBAL_PREFERENCES_BACKUP_IDS = ["0dc926a1810f7aee4e8f38793ed788701f93bf9d"] +GLOBAL_PREFERENCES_ROOT_PATHS = [ + "private/var/mobile/Library/Preferences/.GlobalPreferences.plist", +] + + +class GlobalPreferences(IOSExtraction): + """This module extracts Global Preferences to check if Lockdown mode is enabled.""" + + def __init__( + self, + file_path: Optional[str] = None, + target_path: Optional[str] = None, + results_path: Optional[str] = None, + module_options: Optional[dict] = None, + log: logging.Logger = logging.getLogger(__name__), + results: Optional[list] = None, + ) -> None: + super().__init__( + file_path=file_path, + target_path=target_path, + results_path=results_path, + module_options=module_options, + log=log, + results=results, + ) + + def check_indicators(self) -> None: + for entry in self.results: + if entry["entry"] == "LDMGlobalEnabled": + if entry["value"]: + self.log.info("Lockdown mode enabled") + else: + self.log.info("Lockdown mode disabled") + + def process_file(self, file_path: str) -> None: + with open(file_path, "rb") as handle: + data = plistlib.load(handle) + + for entry in data: + self.results.append({"entry": entry, "value": data[entry]}) + + def run(self) -> None: + self._find_ios_database( + backup_ids=GLOBAL_PREFERENCES_BACKUP_IDS, + root_paths=GLOBAL_PREFERENCES_ROOT_PATHS, + ) + self.log.info("Found Global Preference database at path: %s", self.file_path) + + self.process_file(self.file_path) + + self.log.info("Extracted a total of %d Global Preferences", len(self.results)) diff --git a/tests/artifacts/ios_backup/0d/0dc926a1810f7aee4e8f38793ed788701f93bf9d b/tests/artifacts/ios_backup/0d/0dc926a1810f7aee4e8f38793ed788701f93bf9d new file mode 100644 index 0000000..387e796 Binary files /dev/null and b/tests/artifacts/ios_backup/0d/0dc926a1810f7aee4e8f38793ed788701f93bf9d differ diff --git a/tests/ios_backup/test_global_preferences.py b/tests/ios_backup/test_global_preferences.py new file mode 100644 index 0000000..877bca6 --- /dev/null +++ b/tests/ios_backup/test_global_preferences.py @@ -0,0 +1,20 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2023 Claudio Guarnieri. +# 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.module import run_module +from mvt.ios.modules.mixed.global_preferences import GlobalPreferences + +from ..utils import get_ios_backup_folder + + +class TestGlobalPreferencesModule: + def test_global_preferences(self): + m = GlobalPreferences(target_path=get_ios_backup_folder()) + run_module(m) + assert len(m.results) == 16 + assert len(m.timeline) == 0 + assert len(m.detected) == 0 + assert m.results[0]["entry"] == "WebKitShowLinkPreviews" + assert m.results[0]["value"] is False diff --git a/tests/ios_backup/test_tcc.py b/tests/ios_backup/test_tcc.py index 5b683b8..e0c7a4b 100644 --- a/tests/ios_backup/test_tcc.py +++ b/tests/ios_backup/test_tcc.py @@ -12,7 +12,7 @@ from mvt.ios.modules.mixed.tcc import TCC from ..utils import get_ios_backup_folder -class TestTCCtModule: +class TestTCCModule: def test_tcc(self): m = TCC(target_path=get_ios_backup_folder()) run_module(m) diff --git a/tests/ios_fs/test_filesystem.py b/tests/ios_fs/test_filesystem.py index 869aed2..2060102 100644 --- a/tests/ios_fs/test_filesystem.py +++ b/tests/ios_fs/test_filesystem.py @@ -24,8 +24,8 @@ class TestFilesystem: def test_filesystem(self, cleanup_tmp_artifacts): m = Filesystem(target_path=get_ios_backup_folder()) run_module(m) - assert len(m.results) == 14 - assert len(m.timeline) == 14 + assert len(m.results) == 15 + assert len(m.timeline) == 15 assert len(m.detected) == 0 def test_detection(self, indicator_file, cleanup_tmp_artifacts): @@ -38,6 +38,6 @@ class TestFilesystem: ) m.indicators = ind run_module(m) - assert len(m.results) == 14 - assert len(m.timeline) == 14 + assert len(m.results) == 15 + assert len(m.timeline) == 15 assert len(m.detected) == 1