From f864adf97e6db59b8dd0eaaaa11dec6a58ddd98d Mon Sep 17 00:00:00 2001 From: tek Date: Tue, 28 Jun 2022 20:35:52 +0200 Subject: [PATCH] First structure for mvt-ios check-usb --- .github/workflows/python-package.yml | 2 +- mvt/ios/cli.py | 33 +++++++++++++- mvt/ios/cmd_check_usb.py | 45 +++++++++++++++++++ mvt/ios/modules/usb/__init__.py | 8 ++++ mvt/ios/modules/usb/base.py | 34 ++++++++++++++ mvt/ios/modules/usb/processes.py | 39 ++++++++++++++++ setup.cfg | 1 + tests/{ios => ios_backup}/__init__.py | 0 tests/{ios => ios_backup}/test_backup_info.py | 0 tests/{ios => ios_backup}/test_datausage.py | 0 tests/{ios => ios_backup}/test_manifest.py | 0 .../test_safari_browserstate.py | 0 tests/{ios => ios_backup}/test_sms.py | 0 tests/{ios => ios_backup}/test_tcc.py | 0 tests/ios_usb/__init__.py | 0 tests/ios_usb/test_processes.py | 31 +++++++++++++ 16 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 mvt/ios/cmd_check_usb.py create mode 100644 mvt/ios/modules/usb/__init__.py create mode 100644 mvt/ios/modules/usb/base.py create mode 100644 mvt/ios/modules/usb/processes.py rename tests/{ios => ios_backup}/__init__.py (100%) rename tests/{ios => ios_backup}/test_backup_info.py (100%) rename tests/{ios => ios_backup}/test_datausage.py (100%) rename tests/{ios => ios_backup}/test_manifest.py (100%) rename tests/{ios => ios_backup}/test_safari_browserstate.py (100%) rename tests/{ios => ios_backup}/test_sms.py (100%) rename tests/{ios => ios_backup}/test_tcc.py (100%) create mode 100644 tests/ios_usb/__init__.py create mode 100644 tests/ios_usb/test_processes.py diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 767248e..36e25de 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -28,7 +28,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install flake8 pytest safety stix2 + python -m pip install flake8 pytest safety stix2 pytest-mock if [ -f requirements.txt ]; then pip install -r requirements.txt; fi python -m pip install . - name: Lint with flake8 diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 4b5d373..3e159fd 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -13,13 +13,14 @@ from rich.prompt import Prompt from mvt.common.cmd_check_iocs import CmdCheckIOCS from mvt.common.help import (HELP_MSG_FAST, HELP_MSG_IOC, HELP_MSG_LIST_MODULES, HELP_MSG_MODULE, - HELP_MSG_OUTPUT) + HELP_MSG_OUTPUT, HELP_MSG_SERIAL) from mvt.common.indicators import download_indicators_files from mvt.common.logo import logo from mvt.common.options import MutuallyExclusiveOption from .cmd_check_backup import CmdIOSCheckBackup from .cmd_check_fs import CmdIOSCheckFS +from .cmd_check_usb import CmdIOSCheckUSB from .decrypt import DecryptBackup from .modules.backup import BACKUP_MODULES from .modules.fs import FS_MODULES @@ -214,3 +215,33 @@ def check_iocs(ctx, iocs, list_modules, module, folder): @cli.command("download-iocs", help="Download public STIX2 indicators") def download_iocs(): download_indicators_files(log) + +#============================================================================== +# 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 new file mode 100644 index 0000000..51ac7b1 --- /dev/null +++ b/mvt/ios/cmd_check_usb.py @@ -0,0 +1,45 @@ +# 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 logging +import sys +from pymobiledevice3.lockdown import LockdownClient +from pymobiledevice3.exceptions import ConnectionFailedError + +from mvt.common.command import Command + +from .modules.usb import USB_MODULES + +log = logging.getLogger(__name__) + + +class CmdIOSCheckUSB(Command): + + name = "check-usb" + modules = USB_MODULES + + def __init__(self, target_path: str = None, results_path: str = None, + ioc_files: list = [], module_name: str = None, serial: str = None, + fast_mode: bool = False): + super().__init__(target_path=target_path, results_path=results_path, + ioc_files=ioc_files, module_name=module_name, + serial=serial, fast_mode=fast_mode, log=log) + self.lockdown = None + + def init(self): + try: + if self.serial: + 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.") + sys.exit(-1) + except ConnectionFailedError: + log.error("Unable to connect to the device %s", self.serial) + sys.exit(-1) + + def module_init(self, module): + module.lockdown = self.lockdown diff --git a/mvt/ios/modules/usb/__init__.py b/mvt/ios/modules/usb/__init__.py new file mode 100644 index 0000000..67e985f --- /dev/null +++ b/mvt/ios/modules/usb/__init__.py @@ -0,0 +1,8 @@ +# 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/ + +from .processes import Processes + +USB_MODULES = [Processes] diff --git a/mvt/ios/modules/usb/base.py b/mvt/ios/modules/usb/base.py new file mode 100644 index 0000000..0ed0076 --- /dev/null +++ b/mvt/ios/modules/usb/base.py @@ -0,0 +1,34 @@ +# 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 base64 +import getpass +import logging +import os +import random +import string +import sys +import tempfile +import time +from typing import Callable + +from mvt.common.module import InsufficientPrivileges, MVTModule + +log = logging.getLogger(__name__) + + +class IOSUSBExtraction(MVTModule): + """This class provides a base for all iOS USB extraction modules.""" + + def __init__(self, file_path: str = None, target_path: str = None, + results_path: str = None, fast_mode: bool = False, + log: logging.Logger = None, results: list = []) -> None: + super().__init__(file_path=file_path, target_path=target_path, + results_path=results_path, fast_mode=fast_mode, + log=log, results=results) + + self.device = None + self.serial = None + self.lockdown = None diff --git a/mvt/ios/modules/usb/processes.py b/mvt/ios/modules/usb/processes.py new file mode 100644 index 0000000..0c9137a --- /dev/null +++ b/mvt/ios/modules/usb/processes.py @@ -0,0 +1,39 @@ +# 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 logging +from pymobiledevice3.services.os_trace import OsTraceService + +from .base import IOSUSBExtraction + + +class Processes(IOSUSBExtraction): + """This class extracts all processes running on the phone.""" + def __init__(self, file_path: str = None, target_path: str = None, + results_path: str = None, fast_mode: bool = False, + log: logging.Logger = None, results: list = []) -> None: + super().__init__(file_path=file_path, target_path=target_path, + results_path=results_path, fast_mode=fast_mode, + log=log, results=results) + + def check_indicators(self) -> None: + if not self.indicators: + return + + for result in self.results: + ioc = self.indicators.check_process(result["name"]) + if ioc: + result["matched_indicator"] = ioc + self.detected.append(result) + + def run(self) -> None: + processes = OsTraceService(lockdown=self.lockdown).get_pid_list().get('Payload') + for p in processes: + self.results.append({ + "pid": p, + "name": processes[p]["ProcessName"] + }) + + self.log.info("{} running processes identified on the phone".format(len(self.results))) diff --git a/setup.cfg b/setup.cfg index 9f91876..65048ee 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,6 +33,7 @@ install_requires = adb-shell >=0.4.2 libusb1 >=2.0.1 cryptography >=36.0.1 + pymobiledevice3 >= 1.23.9 [options.packages.find] where = ./ diff --git a/tests/ios/__init__.py b/tests/ios_backup/__init__.py similarity index 100% rename from tests/ios/__init__.py rename to tests/ios_backup/__init__.py diff --git a/tests/ios/test_backup_info.py b/tests/ios_backup/test_backup_info.py similarity index 100% rename from tests/ios/test_backup_info.py rename to tests/ios_backup/test_backup_info.py diff --git a/tests/ios/test_datausage.py b/tests/ios_backup/test_datausage.py similarity index 100% rename from tests/ios/test_datausage.py rename to tests/ios_backup/test_datausage.py diff --git a/tests/ios/test_manifest.py b/tests/ios_backup/test_manifest.py similarity index 100% rename from tests/ios/test_manifest.py rename to tests/ios_backup/test_manifest.py diff --git a/tests/ios/test_safari_browserstate.py b/tests/ios_backup/test_safari_browserstate.py similarity index 100% rename from tests/ios/test_safari_browserstate.py rename to tests/ios_backup/test_safari_browserstate.py diff --git a/tests/ios/test_sms.py b/tests/ios_backup/test_sms.py similarity index 100% rename from tests/ios/test_sms.py rename to tests/ios_backup/test_sms.py diff --git a/tests/ios/test_tcc.py b/tests/ios_backup/test_tcc.py similarity index 100% rename from tests/ios/test_tcc.py rename to tests/ios_backup/test_tcc.py diff --git a/tests/ios_usb/__init__.py b/tests/ios_usb/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/ios_usb/test_processes.py b/tests/ios_usb/test_processes.py new file mode 100644 index 0000000..9fc5567 --- /dev/null +++ b/tests/ios_usb/test_processes.py @@ -0,0 +1,31 @@ +# 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 logging + +from mvt.common.module import run_module +from mvt.ios.modules.usb.processes import Processes +from mvt.common.indicators import Indicators + + +class TestUSBProcesses: + def test_run(self, mocker, indicator_file): + # Mock + mocker.patch("pymobiledevice3.services.base_service.BaseService.__init__") + mocker.patch( + "pymobiledevice3.services.os_trace.OsTraceService.get_pid_list", + return_value={"Payload": {"1": {"ProcessName": "storebookkeeperd"}, "1854": {"ProcessName": "cfprefssd"}}} + ) + + # indicators + ind = Indicators(log=logging) + ind.parse_stix2(indicator_file) + ind.ioc_collections[0]["processes"].append("cfprefssd") + + m = Processes(log=logging) + m.indicators = ind + run_module(m) + assert len(m.results) == 2 + assert len(m.detected) == 1