From a30d7b287126242a1f0652c4f9c42ce55f72d293 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 5 Jul 2022 18:12:10 +0200 Subject: [PATCH] Adding support for iOS lockdown management --- mvt/common/utils.py | 12 +++++ mvt/ios/cli.py | 112 ++++++++++++++++++++++++++++----------- mvt/ios/cmd_check_usb.py | 11 ++-- mvt/ios/lockdown.py | 58 ++++++++++++++++++++ 4 files changed, 156 insertions(+), 37 deletions(-) create mode 100644 mvt/ios/lockdown.py diff --git a/mvt/common/utils.py b/mvt/common/utils.py index 19e82f0..a4eb646 100644 --- a/mvt/common/utils.py +++ b/mvt/common/utils.py @@ -5,6 +5,7 @@ import datetime import hashlib +import os import re @@ -119,3 +120,14 @@ def keys_bytes_to_string(obj) -> str: new_obj[key] = value return new_obj + + +def secure_delete(file_path, rounds=10): + file_size = os.path.getsize(file_path) + + with open(file_path, "br+", buffering=-1) as handle: + for i in range(rounds): + handle.seek(0) + handle.write(os.urandom(file_size)) + + os.remove(file_path) diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 65eba46..f125589 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -8,7 +8,8 @@ import os import click from rich.logging import RichHandler -from rich.prompt import Prompt +from rich.prompt import Confirm, Prompt +from simple_term_menu import TerminalMenu from mvt.common.cmd_check_iocs import CmdCheckIOCS from mvt.common.help import (HELP_MSG_FAST, HELP_MSG_IOC, @@ -22,6 +23,7 @@ from .cmd_check_backup import CmdIOSCheckBackup from .cmd_check_fs import CmdIOSCheckFS from .cmd_check_usb import CmdIOSCheckUSB from .decrypt import DecryptBackup +from .lockdown import Lockdown from .modules.backup import BACKUP_MODULES from .modules.fs import FS_MODULES from .modules.mixed import MIXED_MODULES @@ -188,6 +190,83 @@ def check_fs(ctx, iocs, output, fast, list_modules, module, dump_path): len(cmd.timeline_detected)) +#============================================================================== +# Command: check-usb +#============================================================================== +@cli.command("check-usb", help="Extract artifacts from a live iPhone through USB") +@click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL) +@click.option("--iocs", "-i", type=click.Path(exists=True), multiple=True, + default=[], help=HELP_MSG_IOC) +@click.option("--output", "-o", type=click.Path(exists=False), help=HELP_MSG_OUTPUT) +@click.option("--fast", "-f", is_flag=True, help=HELP_MSG_FAST) +@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) +@click.option("--module", "-m", help=HELP_MSG_MODULE) +@click.pass_context +def check_usb(ctx, serial, iocs, output, fast, list_modules, module): + cmd = CmdIOSCheckUSB(results_path=output, ioc_files=iocs, + module_name=module, fast_mode=fast, + serial=serial) + + if list_modules: + cmd.list_modules() + return + + log.info("Checking iPhone through USB, this may take a while") + cmd.run() + + if len(cmd.timeline_detected) > 0: + log.warning("The analysis of the data produced %d detections!", + len(cmd.timeline_detected)) + + +#============================================================================== +# Command: clear-certs +#============================================================================== +@cli.command("clear-certs", help="Clear iOS lockdown certificates") +@click.pass_context +def clear_certs(ctx): + lock = Lockdown() + certs = lock.find_certs() + + if not certs: + log.info("No iOS lockdown certificates found") + return + + choices = [] + for cert in certs: + choices.append(os.path.basename(cert)) + log.info("Found lockdown certificate at %s", cert) + + choices.append("Cancel") + + terminal_menu = TerminalMenu( + choices, + title="Select which certificates to delete:", + multi_select=True, + show_multi_select_hint=True, + ) + terminal_menu.show() + + if "Cancel" in terminal_menu.chosen_menu_entries: + log.info("Cancel, not proceeding") + return + + confirmed = Confirm.ask(f"You have selected {', '.join(terminal_menu.chosen_menu_entries)}. " + "Are you sure you want to proceed deleting them?") + if not confirmed: + log.info("Not proceeding") + return + + for choice in terminal_menu.chosen_menu_entries: + try: + lock.delete_cert(choice) + except PermissionError: + log.error("Not enough permissions to delete certificate at \"%s\": " + "try launching this command with sudo", choice) + else: + log.info("Deleted lockdown certificate \"%s\"", choice) + + #============================================================================== # Command: check-iocs #============================================================================== @@ -216,34 +295,3 @@ def check_iocs(ctx, iocs, list_modules, module, folder): def download_iocs(): ioc_updates = IndicatorsUpdates() ioc_updates.update() - - -#============================================================================== -# Command: check-usb -#============================================================================== -@cli.command("check-usb", help="Extract artifacts from a live iPhone through USB / lockdown") -@click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL) -@click.option("--iocs", "-i", type=click.Path(exists=True), multiple=True, - default=[], help=HELP_MSG_IOC) -@click.option("--output", "-o", type=click.Path(exists=False), help=HELP_MSG_OUTPUT) -@click.option("--fast", "-f", is_flag=True, help=HELP_MSG_FAST) -@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) -@click.option("--module", "-m", help=HELP_MSG_MODULE) -# TODO: serial -# @click.argument("BACKUP_PATH", type=click.Path(exists=True)) -@click.pass_context -def check_usb(ctx, serial, iocs, output, fast, list_modules, module): - cmd = CmdIOSCheckUSB(results_path=output, ioc_files=iocs, - module_name=module, fast_mode=fast, - serial=serial) - - if list_modules: - cmd.list_modules() - return - - log.info("Checking iPhone through USB, this may take a while") - cmd.run() - - if len(cmd.timeline_detected) > 0: - log.warning("The analysis of the data produced %d detections!", - len(cmd.timeline_detected)) diff --git a/mvt/ios/cmd_check_usb.py b/mvt/ios/cmd_check_usb.py index 0a48f1d..0c31d26 100644 --- a/mvt/ios/cmd_check_usb.py +++ b/mvt/ios/cmd_check_usb.py @@ -6,7 +6,8 @@ import logging import sys -from pymobiledevice3.exceptions import ConnectionFailedError +from pymobiledevice3.exceptions import (ConnectionFailedError, + FatalPairingError, NotTrustedError) from pymobiledevice3.lockdown import LockdownClient from mvt.common.command import Command @@ -35,11 +36,11 @@ class CmdIOSCheckUSB(Command): self.lockdown = LockdownClient(udid=self.serial) else: self.lockdown = LockdownClient() - except ConnectionRefusedError: - log.error("Unable to connect to the device over USB. Try to unplug, plug the device and start again.") + except NotTrustedError: + log.error("Trust this computer from the prompt appearing on the iOS device and try again") sys.exit(-1) - except ConnectionFailedError: - log.error("Unable to connect to the device %s", self.serial) + except (ConnectionRefusedError, ConnectionFailedError, FatalPairingError): + log.error("Unable to connect to the device over USB: try to unplug, plug the device and start again") sys.exit(-1) def module_init(self, module): diff --git a/mvt/ios/lockdown.py b/mvt/ios/lockdown.py new file mode 100644 index 0000000..398c872 --- /dev/null +++ b/mvt/ios/lockdown.py @@ -0,0 +1,58 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 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 os +import platform + +from mvt.common.utils import secure_delete + + +class Lockdown: + + def __init__(self, uuids: list = []) -> None: + self.uuids = uuids + self.lockdown_folder = self._get_lockdown_folder() + + @staticmethod + def _get_lockdown_folder(): + system = platform.system() + if system == "Linux": + return "/var/lib/lockdown/" + elif system == "Darwin": + return "/var/db/lockdown/" + elif system == "Windows": + return os.path.join(os.environ.get("ALLUSERSPROFILE", ""), + "Apple", "Lockdown") + + @staticmethod + def _get_pymobiledevice_folder(): + return os.path.expanduser("~/.pymobiledevice3") + + def delete_cert(self, cert_file) -> None: + if not self.lockdown_folder: + return + + cert_path = os.path.join(self.lockdown_folder, cert_file) + if not os.path.exists(cert_path): + return + + secure_delete(cert_path) + + def find_certs(self) -> list: + if not self.lockdown_folder or not os.path.exists(self.lockdown_folder): + return [] + + lockdown_certs = [] + for file_name in os.listdir(self.lockdown_folder): + if not file_name.endswith(".plist"): + continue + + if file_name == "SystemConfiguration.plist": + continue + + file_path = os.path.join(self.lockdown_folder, file_name) + lockdown_certs.append(file_path) + + return sorted(lockdown_certs)