From 064b9fbeb9a9f6cba6aa42c2dfec634cf9fb64f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donncha=20=C3=93=20Cearbhaill?= Date: Sat, 15 Feb 2025 22:47:42 +0100 Subject: [PATCH] Remove check-adb command and update docs --- docs/android/adb.md | 48 ++++-------- docs/android/methodology.md | 46 +++++++++-- docs/docker.md | 19 +---- src/mvt/android/cli.py | 128 ++----------------------------- src/mvt/android/cmd_check_adb.py | 44 ----------- src/mvt/common/help.py | 18 ++--- src/mvt/common/version.py | 2 +- src/mvt/common/virustotal.py | 52 ------------- 8 files changed, 71 insertions(+), 286 deletions(-) delete mode 100644 src/mvt/android/cmd_check_adb.py delete mode 100644 src/mvt/common/virustotal.py diff --git a/docs/android/adb.md b/docs/android/adb.md index d5c0660..8e4d070 100644 --- a/docs/android/adb.md +++ b/docs/android/adb.md @@ -1,42 +1,26 @@ -# Check over ADB - -In order to check an Android device over the [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb) you will first need to install [Android SDK Platform Tools](https://developer.android.com/studio/releases/platform-tools). If you have installed [Android Studio](https://developer.android.com/studio/) you should already have access to `adb` and other utilities. - -While many Linux distributions already package Android Platform Tools (for example `android-platform-tools-base` on Debian), it is preferable to install the most recent version from the official website. Packaged versions might be outdated and incompatible with most recent Android handsets. - -Next you will need to enable debugging on the Android device you are testing. [Please follow the official instructions on how to do so.](https://developer.android.com/studio/command-line/adb) - -## Connecting over USB - -The easiest way to check the device is over a USB transport. You will need to have USB debugging enabled and the device plugged into your computer. If everything is configured appropriately you should see your device when launching the command `adb devices`. - -Now you can try launching MVT with: - -```bash -mvt-android check-adb --output /path/to/results -``` - -If you have previously started an adb daemon MVT will alert you and require you to kill it with `adb kill-server` and relaunch the command. +# Deprecation of ADB command in MVT !!! warning - MVT relies on the Python library [adb-shell](https://pypi.org/project/adb-shell/) to connect to an Android device, which relies on libusb for the USB transport. Because of known driver issues, Windows users [are recommended](https://github.com/JeffLIrion/adb_shell/issues/118) to install appropriate drivers using [Zadig](https://zadig.akeo.ie/). Alternatively, an easier option might be to use the TCP transport and connect over Wi-Fi as describe next. -## Connecting over Wi-FI + The `mvt-android check-adb` command has been deprecated and removed from MVT. -When connecting to the device over USB is not possible or not working properly, an alternative option is to connect over the network. In order to do so, first launch an adb daemon at a fixed port number: +The ability to analyze Android devices over ADB (`mvt-android check-adb`) has been removed from MVT due to several technical and forensic limitations. -```bash -adb tcpip 5555 -``` +## Reasons for Deprecation -Then you can specify the IP address of the phone with the adb port number to MVT like so: +1. **Inconsistent Data Collection Across Devices** + Android devices vary significantly in their system architecture, security policies, and available diagnostic logs. This inconsistency makes it difficult to ensure that MVT can reliably collect necessary forensic data across all devices. -```bash -mvt-android check-adb --serial 192.168.1.20:5555 --output /path/to/results -``` +2. **Incomplete Forensic Data Acquisition** + The `check-adb` command did not retrieve a full forensic snapshot of all available data on the device. For example, critical logs such as the **full bugreport** were not systematically collected, leading to potential gaps in forensic analysis. This can be a serious problem in scenarios where the analyst only had one time access to the Android device. -Where `192.168.1.20` is the correct IP address of your device. +4. **Code Duplication and Difficulty Ensuring Consistent Behavior Across Sources** + Similar forensic data such as "dumpsys" logs were being loaded and parsed by MVT's ADB, AndroidQF and Bugreport commands. Multiple modules were needed to handle each source format which created duplication leading to inconsistent + behavior and difficulties in maintaining the code base. -## MVT modules requiring root privileges +5. **Alignment with iOS Workflow** + MVT’s forensic workflow for iOS relies on pre-extracted artifacts, such as iTunes backups or filesystem dumps, rather than preforming commands or interactions directly on a live device. Removing the ADB functionality ensures a more consistent methodology across both Android and iOS mobile forensic. -Of the currently available `mvt-android check-adb` modules a handful require root privileges to function correctly. This is because certain files, such as browser history and SMS messages databases are not accessible with user privileges through adb. These modules are to be considered OPTIONALLY available in case the device was already jailbroken. **Do NOT jailbreak your own device unless you are sure of what you are doing!** Jailbreaking your phone exposes it to considerable security risks! +## Alternative: Using AndroidQF for Forensic Data Collection + +To replace the deprecated ADB-based approach, forensic analysts should use [AndroidQF](https://github.com/mvt-project/androidqf) for comprehensive data collection, followed by MVT for forensic analysis. The workflow is outlined in the MVT [Android methodology](./methodology.md) diff --git a/docs/android/methodology.md b/docs/android/methodology.md index e8062c6..958f447 100644 --- a/docs/android/methodology.md +++ b/docs/android/methodology.md @@ -1,23 +1,53 @@ # Methodology for Android forensic -Unfortunately Android devices provide much less observability than their iOS cousins. Android stores very little diagnostic information useful to triage potential compromises, and because of this `mvt-android` capabilities are limited as well. +Unfortunately Android devices provide fewer complete forensically useful datasources than their iOS cousins. Unlike iOS, the Android backup feature only provides a limited about of relevant data. + +Android diagnostic logs such as *bugreport files* can be inconsistent in format and structure across different Android versions and device vendors. The limited diagnostic information available makes it difficult to triage potential compromises, and because of this `mvt-android` capabilities are limited as well. However, not all is lost. -## Check installed Apps +## Check Android devices with AndroidQF and MVT -Because malware attacks over Android typically take the form of malicious or backdoored apps, the very first thing you might want to do is to extract and verify all installed Android packages and triage quickly if there are any which stand out as malicious or which might be atypical. +The [AndroidQF](https://github.com/mvt-project/androidqf) tool can be used to collect a wide range of forensic artifacts from an Android device including an Android backup, a bugreport file, and a range of system logs. MVT natively supports analyzing the generated AndroidQF output for signs of device compromise. -While it is out of the scope of this documentation to dwell into details on how to analyze Android apps, MVT does allow to easily and automatically extract information about installed apps, download copies of them, and quickly look them up on services such as [VirusTotal](https://www.virustotal.com). +### Why Use AndroidQF? -!!! info "Using VirusTotal" - Please note that in order to use VirusTotal lookups you are required to provide your own API key through the `MVT_VT_API_KEY` environment variable. You should also note that VirusTotal enforces strict API usage. Be mindful that MVT might consume your hourly search quota. +- **Complete and raw data extraction** + AndroidQF collects full forensic artifacts using an on-device forensic collection agent, ensuring that no crucial data is overlooked. The data collection does not depended on the shell environment or utilities available on the device. + +- **Consistent and standardized output** + By collecting a predefined and complete set of forensic files, AndroidQF ensures consistency in data acquisition across different Android devices. + +- **Future-proof analysis** + Since the full forensic artifacts are preserved, analysts can extract new evidence or apply updated analysis techniques without requiring access to the original device. + +- **Cross-platform tool without dependencies** + AndroidQF is a standalone Go binary which can be used to remotely collect data from an Android device without the device owner needing to install MVT or a Python environment. + +### Workflow for Android Forensic Analysis with AndroidQF + +With AndroidQF the analysis process is split into a separate data collection and data analysis stages. + +1. **Extract Data Using AndroidQF** + Deploy the AndroidQF forensic collector to acquire all relevant forensic artifacts from the Android device. + +2. **Analyze Extracted Data with MVT** + Use the `mvt-android check-androidqf` command to perform forensic analysis on the extracted artifacts. + +By separating artifact collection from forensic analysis, this approach ensures a more reliable and scalable methodology for Android forensic investigations. + +For more information, refer to the [AndroidQF project documentation](https://github.com/mvt-project/androidqf). ## Check the device over Android Debug Bridge -Some additional diagnostic information can be extracted from the phone using the [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb). `mvt-android` allows to automatically extract information including [dumpsys](https://developer.android.com/studio/command-line/dumpsys) results, details on installed packages (without download), running processes, presence of root binaries and packages, and more. +The ability to analyze Android devices over ADB (`mvt-android check-adb`) has been removed from MVT. +See the [Android ADB documentation](./adb.md) for more information. ## Check an Android Backup (SMS messages) -Although Android backups are becoming deprecated, it is still possible to generate one. Unfortunately, because apps these days typically favor backup over the cloud, the amount of data available is limited. Currently, `mvt-android check-backup` only supports checking SMS messages containing links. +Although Android backups are becoming deprecated, it is still possible to generate one. Unfortunately, because apps these days typically favor backup over the cloud, the amount of data available is limited. + +The `mvt-android check-androidqf` command will automatically check an Android backup and SMS messages if an SMS backup is included in the AndroidQF extraction. + +The `mvt-android check-backup` command can also be used directly with an Android backup file. diff --git a/docs/docker.md b/docs/docker.md index be8631c..d8f4ff8 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -31,21 +31,4 @@ Test if the image was created successfully: docker run -it mvt ``` -If a prompt is spawned successfully, you can close it with `exit`. - - -## Docker usage with Android devices - -If you wish to use MVT to test an Android device you will need to enable the container's access to the host's USB devices. You can do so by enabling the `--privileged` flag and mounting the USB bus device as a volume: - -```bash -docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb mvt -``` - -**Please note:** the `--privileged` parameter is generally regarded as a security risk. If you want to learn more about this check out [this explainer on container escapes](https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/) as it gives access to the whole system. - -Recent versions of Docker provide a `--device` parameter allowing to specify a precise USB device without enabling `--privileged`: - -```bash -docker run -it --device=/dev/ mvt -``` +If a prompt is spawned successfully, you can close it with `exit`. \ No newline at end of file diff --git a/src/mvt/android/cli.py b/src/mvt/android/cli.py index 8e9086f..5d23411 100644 --- a/src/mvt/android/cli.py +++ b/src/mvt/android/cli.py @@ -11,20 +11,14 @@ from mvt.common.cmd_check_iocs import CmdCheckIOCS from mvt.common.help import ( HELP_MSG_VERSION, HELP_MSG_OUTPUT, - HELP_MSG_SERIAL, - HELP_MSG_DOWNLOAD_APKS, - HELP_MSG_DOWNLOAD_ALL_APKS, - HELP_MSG_VIRUS_TOTAL, - HELP_MSG_APK_OUTPUT, - HELP_MSG_APKS_FROM_FILE, HELP_MSG_VERBOSE, - HELP_MSG_CHECK_ADB, HELP_MSG_IOC, - HELP_MSG_FAST, HELP_MSG_LIST_MODULES, HELP_MSG_MODULE, HELP_MSG_NONINTERACTIVE, HELP_MSG_ANDROID_BACKUP_PASSWORD, + HELP_MSG_CHECK_ADB_REMOVED, + HELP_MSG_CHECK_ADB_REMOVED_DESCRIPTION, HELP_MSG_CHECK_BUGREPORT, HELP_MSG_CHECK_ANDROID_BACKUP, HELP_MSG_CHECK_ANDROIDQF, @@ -36,13 +30,10 @@ from mvt.common.logo import logo from mvt.common.updates import IndicatorsUpdates from mvt.common.utils import init_logging, set_verbose_logging -from .cmd_check_adb import CmdAndroidCheckADB + from .cmd_check_androidqf import CmdAndroidCheckAndroidQF from .cmd_check_backup import CmdAndroidCheckBackup from .cmd_check_bugreport import CmdAndroidCheckBugreport -from .cmd_download_apks import DownloadAPKs -from .modules.adb import ADB_MODULES -from .modules.adb.packages import Packages from .modules.backup import BACKUP_MODULES from .modules.backup.helpers import cli_load_android_backup_password from .modules.bugreport import BUGREPORT_MODULES @@ -70,117 +61,14 @@ def version(): # ============================================================================== -# Command: download-apks +# Command: check-adb (removed) # ============================================================================== @cli.command( - "download-apks", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_DOWNLOAD_APKS + "check-adb", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_CHECK_ADB_REMOVED ) -@click.option("--serial", "-s", type=str, help=HELP_MSG_SERIAL) -@click.option("--all-apks", "-a", is_flag=True, help=HELP_MSG_DOWNLOAD_ALL_APKS) -@click.option("--virustotal", "-V", is_flag=True, help=HELP_MSG_VIRUS_TOTAL) -@click.option("--output", "-o", type=click.Path(exists=False), help=HELP_MSG_APK_OUTPUT) -@click.option( - "--from-file", "-f", type=click.Path(exists=True), help=HELP_MSG_APKS_FROM_FILE -) -@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE) @click.pass_context -def download_apks(ctx, all_apks, virustotal, output, from_file, serial, verbose): - set_verbose_logging(verbose) - try: - if from_file: - download = DownloadAPKs.from_json(from_file) - else: - # TODO: Do we actually want to be able to run without storing any - # file? - if not output: - log.critical("You need to specify an output folder with --output!") - ctx.exit(1) - - download = DownloadAPKs(results_path=output, all_apks=all_apks) - if serial: - download.serial = serial - download.run() - - packages_to_lookup = [] - if all_apks: - packages_to_lookup = download.packages - else: - for package in download.packages: - if not package.get("system", False): - packages_to_lookup.append(package) - - if len(packages_to_lookup) == 0: - return - - if virustotal: - m = Packages() - m.check_virustotal(packages_to_lookup) - except KeyboardInterrupt: - print("") - ctx.exit(1) - - -# ============================================================================== -# Command: check-adb -# ============================================================================== -@cli.command("check-adb", context_settings=CONTEXT_SETTINGS, help=HELP_MSG_CHECK_ADB) -@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.option("--non-interactive", "-n", is_flag=True, help=HELP_MSG_NONINTERACTIVE) -@click.option("--backup-password", "-p", help=HELP_MSG_ANDROID_BACKUP_PASSWORD) -@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE) -@click.pass_context -def check_adb( - ctx, - serial, - iocs, - output, - fast, - list_modules, - module, - non_interactive, - backup_password, - verbose, -): - set_verbose_logging(verbose) - module_options = { - "fast_mode": fast, - "interactive": not non_interactive, - "backup_password": cli_load_android_backup_password(log, backup_password), - } - - cmd = CmdAndroidCheckADB( - results_path=output, - ioc_files=iocs, - module_name=module, - serial=serial, - module_options=module_options, - ) - - if list_modules: - cmd.list_modules() - return - - log.info("Checking Android device over debug bridge") - - cmd.run() - - if cmd.detected_count > 0: - log.warning( - "The analysis of the Android device produced %d detections!", - cmd.detected_count, - ) +def check_adb(ctx): + log.error(HELP_MSG_CHECK_ADB_REMOVED_DESCRIPTION) # ============================================================================== @@ -373,7 +261,7 @@ def check_androidqf( @click.pass_context def check_iocs(ctx, iocs, list_modules, module, folder): cmd = CmdCheckIOCS(target_path=folder, ioc_files=iocs, module_name=module) - cmd.modules = BACKUP_MODULES + ADB_MODULES + BUGREPORT_MODULES + cmd.modules = BACKUP_MODULES + BUGREPORT_MODULES if list_modules: cmd.list_modules() diff --git a/src/mvt/android/cmd_check_adb.py b/src/mvt/android/cmd_check_adb.py deleted file mode 100644 index c1444f4..0000000 --- a/src/mvt/android/cmd_check_adb.py +++ /dev/null @@ -1,44 +0,0 @@ -# Mobile Verification Toolkit (MVT) -# Copyright (c) 2021-2023 The MVT Authors. -# 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 typing import Optional - -from mvt.common.command import Command -from mvt.common.indicators import Indicators - -from .modules.adb import ADB_MODULES - -log = logging.getLogger(__name__) - - -class CmdAndroidCheckADB(Command): - def __init__( - self, - target_path: Optional[str] = None, - results_path: Optional[str] = None, - ioc_files: Optional[list] = None, - iocs: Optional[Indicators] = None, - module_name: Optional[str] = None, - serial: Optional[str] = None, - module_options: Optional[dict] = None, - hashes: Optional[bool] = False, - sub_command: Optional[bool] = False, - ) -> None: - super().__init__( - target_path=target_path, - results_path=results_path, - ioc_files=ioc_files, - iocs=iocs, - module_name=module_name, - serial=serial, - module_options=module_options, - hashes=hashes, - sub_command=sub_command, - log=log, - ) - - self.name = "check-adb" - self.modules = ADB_MODULES diff --git a/src/mvt/common/help.py b/src/mvt/common/help.py index 0cca7ab..3046e36 100644 --- a/src/mvt/common/help.py +++ b/src/mvt/common/help.py @@ -33,19 +33,15 @@ HELP_MSG_CHECK_IOS_BACKUP = "Extract artifacts from an iTunes backup" HELP_MSG_CHECK_FS = "Extract artifacts from a full filesystem dump" # Android Specific -HELP_MSG_SERIAL = "Specify a device serial number or HOST:PORT connection string" -HELP_MSG_DOWNLOAD_APKS = "Download all or only non-system installed APKs" HELP_MSG_ANDROID_BACKUP_PASSWORD = "The backup password to use for an Android backup" -HELP_MSG_DOWNLOAD_ALL_APKS = ( - "Extract all packages installed on the phone, including system packages" +HELP_MSG_CHECK_ADB_REMOVED = "REMOVED: Check an Android device over ADB" +HELP_MSG_CHECK_ADB_REMOVED_DESCRIPTION = ( + "The 'mvt-android check-adb' command has been removed from MVT. " + "Use AndroidQF to collect full forensic artifacts from an Android device. \n\n" + "The 'mvt-android check-androidqf' command in MVT can be used to fully analyze " + "forensic data collected with AndroidQF. Minimal checks can also be performed " + "on an Android bugreport using the 'mvt-android check-bugreport' command." ) -HELP_MSG_VIRUS_TOTAL = "Check packages on VirusTotal" -HELP_MSG_APK_OUTPUT = "Specify a path to a folder where you want to store the APKs" -HELP_MSG_APKS_FROM_FILE = ( - "Instead of acquiring APKs from a phone, load an existing packages.json file for " - "lookups (mainly for debug purposes)" -) -HELP_MSG_CHECK_ADB = "Check an Android device over ADB" HELP_MSG_CHECK_BUGREPORT = "Check an Android Bug Report" HELP_MSG_CHECK_ANDROID_BACKUP = "Check an Android Backup" HELP_MSG_CHECK_ANDROIDQF = "Check data collected with AndroidQF" diff --git a/src/mvt/common/version.py b/src/mvt/common/version.py index dd19c1e..b31392c 100644 --- a/src/mvt/common/version.py +++ b/src/mvt/common/version.py @@ -3,4 +3,4 @@ # Use of this software is governed by the MVT License 1.1 that can be found at # https://license.mvt.re/1.1/ -MVT_VERSION = "2.6.0" +MVT_VERSION = "3.0.0" diff --git a/src/mvt/common/virustotal.py b/src/mvt/common/virustotal.py deleted file mode 100644 index e0749ea..0000000 --- a/src/mvt/common/virustotal.py +++ /dev/null @@ -1,52 +0,0 @@ -# Mobile Verification Toolkit (MVT) -# Copyright (c) 2021-2023 The MVT Authors. -# 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 os - -import requests - -log = logging.getLogger(__name__) - -MVT_VT_API_KEY = "MVT_VT_API_KEY" - - -class VTNoKey(Exception): - pass - - -class VTQuotaExceeded(Exception): - pass - - -def virustotal_lookup(file_hash: str): - if MVT_VT_API_KEY not in os.environ: - raise VTNoKey( - "No VirusTotal API key provided: to use VirusTotal " - "lookups please provide your API key with " - "`export MVT_VT_API_KEY=`" - ) - - headers = { - "User-Agent": "VirusTotal", - "Content-Type": "application/json", - "x-apikey": os.environ[MVT_VT_API_KEY], - } - res = requests.get( - f"https://www.virustotal.com/api/v3/files/{file_hash}", headers=headers - ) - - if res.status_code == 200: - report = res.json() - return report["data"] - - if res.status_code == 404: - log.info("Could not find results for file with hash %s", file_hash) - elif res.status_code == 429: - raise VTQuotaExceeded("You have exceeded the quota for your VirusTotal API key") - else: - raise Exception(f"Unexpected response from VirusTotal: {res.status_code}") - - return None