From 732db070f2d13709688b7d2982c4492c0956d13e Mon Sep 17 00:00:00 2001 From: febrezo Date: Tue, 20 Jul 2021 03:09:53 +0200 Subject: [PATCH 01/99] Add implicit creation of output folders --- .gitignore | 2 ++ mvt/android/cli.py | 30 ++++++++++++++++++++++++------ mvt/ios/cli.py | 17 +++++++++++++++-- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 73b7def..985254e 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,5 @@ dmypy.json .pyre/ *.pyc +# Temporal files +*~ diff --git a/mvt/android/cli.py b/mvt/android/cli.py index eccf9e1..11e9070 100644 --- a/mvt/android/cli.py +++ b/mvt/android/cli.py @@ -3,6 +3,7 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import errno import os import sys import click @@ -45,7 +46,7 @@ def cli(): @click.option("--virustotal", "-v", is_flag=True, help="Check packages on VirusTotal") @click.option("--koodous", "-k", is_flag=True, help="Check packages on Koodous") @click.option("--all-checks", "-A", is_flag=True, help="Run all available checks") -@click.option("--output", "-o", type=click.Path(exists=True), +@click.option("--output", "-o", type=click.Path(exists=False), help="Specify a path to a folder where you want to store JSON results") @click.option("--from-file", "-f", type=click.Path(exists=True), help="Instead of acquiring from phone, load an existing packages.json file for lookups (mainly for debug purposes)") @@ -54,9 +55,12 @@ def download_apks(all_apks, virustotal, koodous, all_checks, output, from_file): if from_file: download = DownloadAPKs.from_json(from_file) else: - if not output: - log.critical("You need to specify an output folder (with --output, -o) when extracting APKs from a device") - sys.exit(-1) + try: + os.makedirs(output) + except OSError as e: + if e.errno != errno.EEXIST: + log.critical("You need to specify a writable output folder (with --output, -o) when extracting APKs from a device") + sys.exit(-1) download = DownloadAPKs(output_folder=output, all_apks=all_apks) download.run() @@ -81,7 +85,7 @@ def download_apks(all_apks, virustotal, koodous, all_checks, output, from_file): #============================================================================== @cli.command("check-adb", help="Check an Android device over adb") @click.option("--iocs", "-i", type=click.Path(exists=True), help="Path to indicators file") -@click.option("--output", "-o", type=click.Path(exists=True), +@click.option("--output", "-o", type=click.Path(exists=False), help="Specify a path to a folder where you want to store JSON results") @click.option("--list-modules", "-l", is_flag=True, help="Print list of available modules and exit") @click.option("--module", "-m", help="Name of a single module you would like to run instead of all") @@ -117,6 +121,13 @@ def check_adb(iocs, output, list_modules, module): timeline_detected.extend(m.timeline_detected) if output: + try: + os.makedirs(output) + except OSError as e: + if e.errno != errno.EEXIST: + log.critical("You need to specify a writable output folder (with --output, -o) to log the details observed from ADB") + sys.exit(-1) + if len(timeline) > 0: save_timeline(timeline, os.path.join(output, "timeline.csv")) if len(timeline_detected) > 0: @@ -127,11 +138,18 @@ def check_adb(iocs, output, list_modules, module): #============================================================================== @cli.command("check-backup", help="Check an Android Backup") @click.option("--iocs", "-i", type=click.Path(exists=True), help="Path to indicators file") -@click.option("--output", "-o", type=click.Path(exists=True), help=OUTPUT_HELP_MESSAGE) +@click.option("--output", "-o", type=click.Path(exists=False), help=OUTPUT_HELP_MESSAGE) @click.argument("BACKUP_PATH", type=click.Path(exists=True)) def check_backup(iocs, output, backup_path): log.info("Checking ADB backup located at: %s", backup_path) + try: + os.makedirs(output) + except OSError as e: + if e.errno != errno.EEXIST: + log.critical("You need to specify a writable output folder (with --output, -o) when analysing the ADB backup") + sys.exit(-1) + if iocs: # Pre-load indicators for performance reasons. log.info("Loading indicators from provided file at %s", iocs) diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 67b2864..ef786d2 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -3,6 +3,7 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import errno import os import sys import click @@ -65,7 +66,7 @@ def decrypt_backup(destination, password, key_file, backup_path): #============================================================================== @cli.command("check-backup", help="Extract artifacts from an iTunes backup") @click.option("--iocs", "-i", type=click.Path(exists=True), help="Path to indicators file") -@click.option("--output", "-o", type=click.Path(exists=True), help=OUTPUT_HELP_MESSAGE) +@click.option("--output", "-o", type=click.Path(exists=False), help=OUTPUT_HELP_MESSAGE) @click.option("--fast", "-f", is_flag=True, help="Avoid running time/resource consuming features") @click.option("--list-modules", "-l", is_flag=True, help="Print list of available modules and exit") @click.option("--module", "-m", help="Name of a single module you would like to run instead of all") @@ -104,6 +105,12 @@ def check_backup(iocs, output, fast, backup_path, list_modules, module): timeline_detected.extend(m.timeline_detected) if output: + try: + os.makedirs(output) + except OSError as e: + if e.errno != errno.EEXIST: + log.critical("You need to specify a writable output folder (with --output, -o) when analysing the backup") + sys.exit(-1) if len(timeline) > 0: save_timeline(timeline, os.path.join(output, "timeline.csv")) if len(timeline_detected) > 0: @@ -115,7 +122,7 @@ def check_backup(iocs, output, fast, backup_path, list_modules, module): #============================================================================== @cli.command("check-fs", help="Extract artifacts from a full filesystem dump") @click.option("--iocs", "-i", type=click.Path(exists=True), help="Path to indicators file") -@click.option("--output", "-o", type=click.Path(exists=True), help=OUTPUT_HELP_MESSAGE) +@click.option("--output", "-o", type=click.Path(exists=False), help=OUTPUT_HELP_MESSAGE) @click.option("--fast", "-f", is_flag=True, help="Avoid running time/resource consuming features") @click.option("--list-modules", "-l", is_flag=True, help="Print list of available modules and exit") @click.option("--module", "-m", help="Name of a single module you would like to run instead of all") @@ -155,6 +162,12 @@ def check_fs(iocs, output, fast, dump_path, list_modules, module): timeline_detected.extend(m.timeline_detected) if output: + try: + os.makedirs(output) + except OSError as e: + if e.errno != errno.EEXIST: + log.critical("You need to specify a writable output folder (with --output, -o) when analysing the file system") + sys.exit(-1) if len(timeline) > 0: save_timeline(timeline, os.path.join(output, "timeline.csv")) if len(timeline_detected) > 0: From 8c855b645d9e71269f5b666272c17e5569dae43f Mon Sep 17 00:00:00 2001 From: febrezo Date: Tue, 20 Jul 2021 12:10:37 +0200 Subject: [PATCH 02/99] Add Dockerfile with Android dependencies solved --- Dockerfile | 29 +++++++++++++++++++++++++++++ README.md | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9547810 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +FROM ubuntu:20.04 + +# Fixing major OS dependencies +# ---------------------------- +RUN apt update \ + && apt install -y python3 python3-pip libusb-1.0-0 \ + && apt install -y wget \ + && apt install -y adb \ + && DEBIAN_FRONTEND=noninteractive apt-get -y install default-jre-headless \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Installing MVT +# -------------- +RUN pip3 install mvt + +# Installing ABE +# -------------- +RUN mkdir /opt/abe +RUN wget https://github.com/nelenkov/android-backup-extractor/releases/download/20210709062403-4c55371/abe.jar -O /opt/abe/abe.jar +# Create shortcut for abe +RUN echo "java -jar /opt/abe/abe.jar" > /usr/local/bin/abe && chmod +x /usr/local/bin/abe + +# Setup investigations environment +# -------------------------------- +RUN mkdir /home/cases +WORKDIR /home/cases + +CMD /bin/bash diff --git a/README.md b/README.md index a9eb292..71754ae 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,46 @@ Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and a [Please check out the documentation](https://mvt-docs.readthedocs.io/en/latest/).. -## Installation +### Installation using the Docker image + +Using Docker is the easiest way of having all the dependencies fixed with a couple of commands. +Install Docker using the [official instructions](https://docs.docker.com/get-docker/) provided in the Website page. + +Afterwards, you can build the Docker image once inside: + +``` +bash +git clone https://github.com/mvt-project/mvt.git +cd mvt +docker build -t mvt . +``` + +Once the image is built, can now be tested using, what will prompt a bash terminal: + +```bash +docker run -it mvt +``` + +If this is correct, close the container (`exit`) and it is time to connect the Android device to analyse to the USB port using the development mode as explained in the official docs [here](https://developer.android.com/studio/debug/dev-options). +To have visibility of the USB, the container WILL need to have access to the USB which is not activated in Docker by default. +This can be done using the `--privileged` parameter when launching Docker as follows and mounting the USB as a volume. + +```bash +docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb mvt +``` + +Note that using the `--pivileged` parameter is insecure for a number of reasons explained in detail [here](https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/) as it gives access to the whole system. +As a brief explanation, the `-v :` syntax maps the host path to the dockerized path to allow the connection. +Modern versions of Docker have a `--device` option where you can specify the exact USB to mount without the `--privileged` option: + +```bash +docker run -it --device=/dev/ mvt +``` + +The Docker image contains the dependencies fixed to perform a forensic analysis on an Android device using MVT, including ADB (reachable using `adb` as expected) and ABE (installed under `/opt/abe` and reachable using `abe` from the command line) which is ready to be launched using the installed version of Java. +Thus, the forensic analyst can proceed as expected to grab the evidences needed and performs the required tests. + +## Manual Installation First you need to install dependencies, on Linux `sudo apt install python3 python3-pip libusb-1.0-0` or on MacOS `brew install python3 libusb`. @@ -35,6 +74,7 @@ MVT provides two commands `mvt-ios` and `mvt-android` with the following subcomm Check out [the documentation to see how to use them.](https://mvt-docs.readthedocs.io/en/latest/). + ## License The purpose of MVT is to facilitate the ***consensual forensic analysis*** of devices of those who might be targets of sophisticated mobile spyware attacks, especially members of civil society and marginalized communities. We do not want MVT to enable privacy violations of non-consenting individuals. Therefore, the goal of this license is to prohibit the use of MVT (and any other software licensed the same) for the purpose of *adversarial forensics*. From f3afc871cd203d3efea0cf2f9274f176b7213290 Mon Sep 17 00:00:00 2001 From: febrezo Date: Tue, 20 Jul 2021 13:45:55 +0200 Subject: [PATCH 03/99] Create alias for abe instead of custom command --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9547810..60805e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,8 +18,8 @@ RUN pip3 install mvt # -------------- RUN mkdir /opt/abe RUN wget https://github.com/nelenkov/android-backup-extractor/releases/download/20210709062403-4c55371/abe.jar -O /opt/abe/abe.jar -# Create shortcut for abe -RUN echo "java -jar /opt/abe/abe.jar" > /usr/local/bin/abe && chmod +x /usr/local/bin/abe +# Create alias for abe +RUN echo 'alias abe="java -jar /opt/abe/abe.jar"' >> ~/.bashrc # Setup investigations environment # -------------------------------- From cb6bde5b8c256eca24ed3357996e64f05a51f6dd Mon Sep 17 00:00:00 2001 From: Adam Lawson Date: Tue, 20 Jul 2021 12:43:54 +0100 Subject: [PATCH 04/99] Fix download of APKs that require root privileges Some system APKs are stored in directories that require root privileges, such as /system/product. --- mvt/android/download_apks.py | 2 +- mvt/android/modules/adb/base.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/mvt/android/download_apks.py b/mvt/android/download_apks.py index 5dd4cbc..b670411 100644 --- a/mvt/android/download_apks.py +++ b/mvt/android/download_apks.py @@ -135,7 +135,7 @@ class DownloadAPKs(AndroidExtraction): try: with PullProgress(unit='B', unit_divisor=1024, unit_scale=True, miniters=1) as pp: - self._adb_download(remote_path, local_path, + self._adb_download(remote_path, local_path, package_name, progress_callback=pp.update_to) except Exception as e: log.exception("Failed to pull package file from %s: %s", diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index 31aa931..3c9e9b1 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -111,7 +111,7 @@ class AndroidExtraction(MVTModule): """ return self._adb_command(f"su -c {command}") - def _adb_download(self, remote_path, local_path, progress_callback=None): + def _adb_download(self, remote_path, local_path, package_name, progress_callback=None): """Download a file form the device. :param remote_path: Path to download from the device :param local_path: Path to where to locally store the copy of the file @@ -119,8 +119,35 @@ class AndroidExtraction(MVTModule): """ try: self.device.pull(remote_path, local_path, progress_callback) + except AdbCommandFailureException: + self._adb_download_root(remote_path, local_path, package_name, progress_callback) except AdbCommandFailureException as e: raise Exception(f"Unable to download file {remote_path}: {e}") + + def _adb_download_root(self, remote_path, local_path, package_name, progress_callback=None): + try: + # Check if we have root, if not raise an Exception. + self._adb_root_or_die() + + # We create a temporary local file. + new_remote_path = f"/sdcard/Download/{package_name}" + + # We copy the file from the data folder to /sdcard/. + cp = self._adb_command_as_root(f"cp {remote_path} {new_remote_path}") + if cp.startswith("cp: ") and "No such file or directory" in cp: + raise Exception(f"Unable to process file {remote_path}: File not found") + elif cp.startswith("cp: ") and "Permission denied" in cp: + raise Exception(f"Unable to process file {remote_path}: Permission denied") + + # We download from /sdcard/ to the local temporary file. + self._adb_download(new_remote_path, local_path, package_name) + + # Delete the copy on /sdcard/. + self._adb_command(f"rm -f {new_remote_path}") + + except AdbCommandFailureException as e: + raise Exception(f"Unable to download file {remote_path}: {e}") + def _adb_process_file(self, remote_path, process_routine): """Download a local copy of a file which is only accessible as root. From 9317586851434973f1d5d4bb41b70b83cfe535ad Mon Sep 17 00:00:00 2001 From: Adam Lawson Date: Tue, 20 Jul 2021 12:52:14 +0100 Subject: [PATCH 05/99] Better check for if device has root "which su" will return the path of the su binary, or it will return nothing. The python boolean of a string with something in it (such as the path of the su binary), will be True. An empty string (where there is no su binary) will be False. --- mvt/android/modules/adb/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index 31aa931..3deacf7 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -96,7 +96,7 @@ class AndroidExtraction(MVTModule): """Check if we have a `su` binary on the Android device. :returns: Boolean indicating whether a `su` binary is present or not """ - return bool(self._adb_command("[ ! -f /sbin/su ] || echo 1")) + return bool(self._adb_command("which su")) def _adb_root_or_die(self): """Check if we have a `su` binary, otherwise raise an Exception. From 1adf3f430bfe3ad3f191a4bff48c618a864fa02c Mon Sep 17 00:00:00 2001 From: febrezo Date: Tue, 20 Jul 2021 14:02:29 +0200 Subject: [PATCH 06/99] Add welcome message when the terminal is launched --- Dockerfile | 2 ++ README.md | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 60805e6..b49239f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,5 +25,7 @@ RUN echo 'alias abe="java -jar /opt/abe/abe.jar"' >> ~/.bashrc # -------------------------------- RUN mkdir /home/cases WORKDIR /home/cases +RUN echo 'echo "Mobile Verification Toolkit @ Docker\n------------------------------------\n\nYou can find information about how to use this image for Android (https://github.com/mvt-project/mvt/tree/master/docs/android) and iOS (https://github.com/mvt-project/mvt/tree/master/docs/ios) in the official docs of the project.\n"' >> ~/.bashrc +RUN echo 'echo "Note that to perform the debug via USB you might need to give the Docker image access to the USB using \"docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb mvt\" or, preferably, the \"--device=\" parameter.\n"' >> ~/.bashrc CMD /bin/bash diff --git a/README.md b/README.md index 71754ae..cf3145a 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,7 @@ Install Docker using the [official instructions](https://docs.docker.com/get-doc Afterwards, you can build the Docker image once inside: -``` -bash +```bash git clone https://github.com/mvt-project/mvt.git cd mvt docker build -t mvt . From b19db5543b750353e7fe4bcb69562623171db41f Mon Sep 17 00:00:00 2001 From: tek Date: Wed, 21 Jul 2021 13:59:54 +0200 Subject: [PATCH 07/99] Update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ac1e3a8..2a802a0 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and a It has been developed and released by the [Amnesty International Security Lab](https://www.amnesty.org/en/tech/) in July 2021 in the context of the [Pegasus project](https://forbiddenstories.org/about-the-pegasus-project/) along with [a technical forensic methodology and forensic evidences](https://www.amnesty.org/en/latest/research/2021/07/forensic-methodology-report-how-to-catch-nso-groups-pegasus/). +*Warning*: this tool has been released as a forensic tool for a technical audience. Using it requires some technical skills such as understanding basics of forensic analysis and using command line tools. + [Please check out the documentation.](https://mvt.readthedocs.io/en/latest/) ## Installation From 684aed8d1177d449af12aed7131dd9eb424c5385 Mon Sep 17 00:00:00 2001 From: febrezo Date: Thu, 22 Jul 2021 17:43:32 +0200 Subject: [PATCH 08/99] Add compilation of libimobiledevice for iOS compatibility Added considering the feedback reported in the #16 discussion. --- Dockerfile | 42 ++++++++++++++++++++++++++++++++++++++---- README.md | 2 +- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index b49239f..d7278a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,49 @@ FROM ubuntu:20.04 +# Ref. https://github.com/mvt-project/mvt + # Fixing major OS dependencies # ---------------------------- -RUN apt update \ - && apt install -y python3 python3-pip libusb-1.0-0 \ +RUN apt update \ + && apt install -y python3 python3-pip libusb-1.0-0-dev \ && apt install -y wget \ && apt install -y adb \ - && DEBIAN_FRONTEND=noninteractive apt-get -y install default-jre-headless \ - && apt-get clean \ + && DEBIAN_FRONTEND=noninteractive apt-get -y install default-jre-headless + +# Install build tools for libimobiledevice +# ---------------------------------------- +RUN apt install -y build-essential \ + checkinstall \ + git \ + autoconf \ + automake \ + libtool-bin \ + libplist-dev \ + libusbmuxd-dev \ + libssl-dev \ + pkg-config + +# Clean up +# -------- +RUN apt-get clean \ && rm -rf /var/lib/apt/lists/* + +# Build libimobiledevice +# ---------------------- +RUN git clone https://github.com/libimobiledevice/libplist +RUN git clone https://github.com/libimobiledevice/libusbmuxd +RUN git clone https://github.com/libimobiledevice/libimobiledevice +RUN git clone https://github.com/libimobiledevice/usbmuxd + +RUN cd libplist && ./autogen.sh && make && make install && ldconfig + +RUN cd libusbmuxd && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh && make && make install && ldconfig + +RUN cd libimobiledevice && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh --enable-debug && make && make install && ldconfig + +RUN cd usbmuxd && PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --runstatedir=/run && make && make install + # Installing MVT # -------------- RUN pip3 install mvt diff --git a/README.md b/README.md index cf3145a..a93aa0f 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ This can be done using the `--privileged` parameter when launching Docker as fol docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb mvt ``` -Note that using the `--pivileged` parameter is insecure for a number of reasons explained in detail [here](https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/) as it gives access to the whole system. +Note that using the `--privileged` parameter is insecure for a number of reasons explained in detail [here](https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/) as it gives access to the whole system. As a brief explanation, the `-v :` syntax maps the host path to the dockerized path to allow the connection. Modern versions of Docker have a `--device` option where you can specify the exact USB to mount without the `--privileged` option: From e69449a2f000bbc615a502a179a3d02ee67a4b58 Mon Sep 17 00:00:00 2001 From: tek Date: Thu, 22 Jul 2021 23:21:31 +0200 Subject: [PATCH 09/99] Fixes typos --- LICENSE | 6 ++-- docs/ios/filesystem/dump.md | 2 +- docs/ios/methodology.md | 2 +- docs/ios/records.md | 50 +++++++++++++++++----------------- mvt/android/download_apks.py | 4 +-- mvt/common/module.py | 2 +- mvt/ios/modules/fs/whatsapp.py | 2 +- 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/LICENSE b/LICENSE index 54123c1..8bbc392 100644 --- a/LICENSE +++ b/LICENSE @@ -35,7 +35,7 @@ MVT License 1.0 means any form of the work other than Source Code Form. 1.7. "Larger Work" - means a work that combines Covered Software with other material, in + means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" @@ -89,13 +89,13 @@ MVT License 1.0 a Larger Work. 1.16. "Device Owner" (or "Device Owners") - means an individal or a legal entity with legal ownership of an + means an individual or a legal entity with legal ownership of an electronic device which is being analysed through the use of Covered Software or a Larger Work, or from which Data was extracted for subsequent analysis. 1.17. "Data Owner" (or "Data Owners") - means an individial or group of individuals who made use of the + means an individual or group of individuals who made use of the electronic device from which Data that is extracted and/or analyzed originated. "Data Owner" might or might not differ from "Device Owner". diff --git a/docs/ios/filesystem/dump.md b/docs/ios/filesystem/dump.md index bdaf5a9..7aa537f 100644 --- a/docs/ios/filesystem/dump.md +++ b/docs/ios/filesystem/dump.md @@ -1,6 +1,6 @@ # Dumping the filesystem -While iTunes backup provide a lot of very useful databases and diagnistic data, in some cases you might want to jailbreak the device and perform a full filesystem dump. In that case, you should take a look at [checkra1n](https://checkra.in/), which provides an easy way to obtain root on most recent iPhone models. +While iTunes backup provide a lot of very useful databases and diagnostic data, in some cases you might want to jailbreak the device and perform a full filesystem dump. In that case, you should take a look at [checkra1n](https://checkra.in/), which provides an easy way to obtain root on most recent iPhone models. !!! warning Before you checkra1n any device, make sure you take a full backup, and that you are prepared to do a full factory reset before restoring it. Even after using checkra1n's "Restore System", some traces of the jailbreak are still left on the device and [apps with anti-jailbreaks will be able to detect them](https://github.com/checkra1n/BugTracker/issues/279) and stop functioning. diff --git a/docs/ios/methodology.md b/docs/ios/methodology.md index bbf15d8..637f07e 100644 --- a/docs/ios/methodology.md +++ b/docs/ios/methodology.md @@ -6,7 +6,7 @@ Before jumping into acquiring and analyzing data from an iOS device, you should You will need to decide whether to attempt to jailbreak the device and obtain a full filesystem dump, or not. -While access the full filesystem allows to extact data that would otherwise be unavailable, it might not always be possible to jailbreak a certain iPhone model or version of iOS. In addition, depending on the type of jailbreak available, doing so might compromise some important records, pollute others, or potentially cause unintended malfunctioning of the device later in case it is used again. +While access the full filesystem allows to extract data that would otherwise be unavailable, it might not always be possible to jailbreak a certain iPhone model or version of iOS. In addition, depending on the type of jailbreak available, doing so might compromise some important records, pollute others, or potentially cause unintended malfunctioning of the device later in case it is used again. If you are not expected to return the phone, you might want to consider to attempt a jailbreak after having exhausted all other options, including a backup. diff --git a/docs/ios/records.md b/docs/ios/records.md index f5dad16..742e566 100644 --- a/docs/ios/records.md +++ b/docs/ios/records.md @@ -7,7 +7,7 @@ In this page you can find a (reasonably) up-to-date breakdown of the files creat ### `cache_files.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `CacheFiles` module. The module extracts records from all SQLite database files stored on disk with the name *Cache.db*. These databases typically contain data from iOS' [internal URL caching](https://developer.apple.com/documentation/foundation/nsurlcache). Through this module you might be able to recover records of HTTP requests and responses performed my applications as well as system services, that would otherwise be unavailable. For example, you might see HTTP requests part of an exploitation chain performed by an iOS service attempting to download a first stage malicious payload. @@ -19,7 +19,7 @@ If indicators are provided through the command-line, they are checked against th ### `calls.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `Calls` module. The module extracts records from a SQLite database located at */private/var/mobile/Library/CallHistoryDB/CallHistory.storedata*, which contains records of incoming and outgoing calls, including from messaging apps such as WhatsApp or Skype. @@ -29,7 +29,7 @@ This JSON file is created by mvt-ios' `Calls` module. The module extracts record ### `chrome_favicon.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `ChromeFavicon` module. The module extracts records from a SQLite database located at */private/var/mobile/Containers/Data/Application/\*/Library/Application Support/Google/Chrome/Default/Favicons*, which contains a mapping of favicons' URLs and the visited URLs which loaded them. @@ -41,7 +41,7 @@ If indicators are provided through the command-line, they are checked against bo ### `chrome_history.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `ChromeHistory` module. The module extracts records from a SQLite database located at */private/var/mobile/Containers/Data/Application/\*/Library/Application Support/Google/Chrome/Default/History*, which contains a history of URL visits. @@ -53,7 +53,7 @@ If indicators a provided through the command-line, they are checked against the ### `contacts.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `Contacts` module. The module extracts records from a SQLite database located at */private/var/mobile/Library/AddressBook/AddressBook.sqlitedb*, which contains records from the phone's address book. While this database obviously would not contain any malicious indicators per se, you might want to use it to compare records from other apps (such as iMessage, SMS, etc.) to filter those originating from unknown origins. @@ -63,7 +63,7 @@ This JSON file is created by mvt-ios' `Contacts` module. The module extracts rec ### `firefox_favicon.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `FirefoxFavicon` module. The module extracts records from a SQLite database located at */private/var/mobile/profile.profile/browser.db*, which contains a mapping of favicons' URLs and the visited URLs which loaded them. @@ -75,7 +75,7 @@ If indicators are provided through the command-line, they are checked against bo ### `firefox_history.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `FirefoxHistory` module. The module extracts records from a SQLite database located at */private/var/mobile/profile.profile/browser.db*, which contains a history of URL visits. @@ -87,7 +87,7 @@ If indicators a provided through the command-line, they are checked against the ### `id_status_cache.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `IDStatusCache` module. The module extracts records from a plist file located at */private/var/mobile/Library/Preferences/com.apple.identityservices.idstatuscache.plist*, which contains a cache of Apple user ID authentication. This chance will indicate when apps like Facetime and iMessage first established contacts with other registered Apple IDs. This is significant because it might contain traces of malicious accounts involved in exploitation of those apps. @@ -97,7 +97,7 @@ This JSON file is created by mvt-ios' `IDStatusCache` module. The module extract ### `interaction_c.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `InteractionC` module. The module extracts records from a SQLite database located at */private/var/mobile/Library/CoreDuet/People/interactionC.db*, which contains details about user interactions with installed apps. @@ -107,7 +107,7 @@ This JSON file is created by mvt-ios' `InteractionC` module. The module extracts ### `locationd_clients.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `LocationdClients` module. The module extracts records from a plist file located at */private/var/mobile/Library/Caches/locationd/clients.plist*, which contains a cache of apps which requested access to location services. @@ -117,7 +117,7 @@ This JSON file is created by mvt-ios' `LocationdClients` module. The module extr ### `manifest.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-close: This JSON file is created by mvt-ios' `Manifest` module. The module extracts records from the SQLite database *Manifest.db* contained in iTunes backups, and which indexes the locally backed-up files to the original paths on the iOS device. @@ -129,7 +129,7 @@ If indicators are provided through the command-line, they are checked against th ### `datausage.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `Datausage` module. The module extracts records from a SQLite database located */private/var/wireless/Library/Databases/DataUsage.sqlite*, which contains a history of data usage by processes running on the system. Besides the network statistics, these records are particularly important because they might show traces of malicious process executions and the relevant timeframe. In particular, processes which do not have a valid bundle ID might require particular attention. @@ -141,7 +141,7 @@ If indicators are provided through the command-line, they are checked against th ### `netusage.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `Netusage` module. The module extracts records from a SQLite database located */private/var/networkd/netusage.sqlite*, which contains a history of data usage by processes running on the system. Besides the network statistics, these records are particularly important because they might show traces of malicious process executions and the relevant timeframe. In particular, processes which do not have a valid bundle ID might require particular attention. @@ -153,7 +153,7 @@ If indicators are provided through the command-line, they are checked against th ### `safari_browser_state.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `SafariBrowserState` module. The module extracts records from the SQLite databases located at */private/var/mobile/Library/Safari/BrowserState.db* or */private/var/mobile/Containers/Data/Application/\*/Library/Safari/BrowserState.db*, which contain records of opened tabs. @@ -165,7 +165,7 @@ If indicators a provided through the command-line, they are checked against the ### `safari_favicon.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `SafariFavicon` module. The module extracts records from the SQLite databases located at */private/var/mobile/Library/Image Cache/Favicons/Favicons.db* or */private/var/mobile/Containers/Data/Application/\*/Library/Image Cache/Favicons/Favicons.db*, which contain mappings of favicons' URLs and the visited URLs which loaded them. @@ -177,7 +177,7 @@ If indicators are provided through the command-line, they are checked against bo ### `safari_history.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `SafariHistory` module. The module extracts records from the SQLite databases located at */private/var/mobile/Library/Safari/History.db* or */private/var/mobile/Containers/Data/Application/\*/Library/Safari/History.db*, which contain a history of URL visits. @@ -189,7 +189,7 @@ If indicators are provided through the command-line, they are checked against th ### `sms.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `SMS` module. The module extracts a list of SMS messages containing HTTP links from the SQLite database located at */private/var/mobile/Library/SMS/sms.db*. @@ -201,7 +201,7 @@ If indicators are provided through the command-line, they are checked against th ### `sms_attachments.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `SMSAttachments` module. The module extracts details about attachments sent via SMS or iMessage from the same database used by the `SMS` module. These records might be useful to indicate unique patterns that might be indicative of exploitation attempts leveraging potential vulnerabilities in file format parsers or other forms of file handling by the Messages app. @@ -211,7 +211,7 @@ This JSON file is created by mvt-ios' `SMSAttachments` module. The module extrac ### `version_history.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `IOSVersionHistory` module. The module extracts records of iOS software updates from analytics plist files located at */private/var/db/analyticsd/Analytics-Journal-\*.ips*. @@ -221,7 +221,7 @@ This JSON file is created by mvt-ios' `IOSVersionHistory` module. The module ext ### `webkit_indexeddb.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `WebkitIndexedDB` module. The module extracts a list of file and folder names located at the following path */private/var/mobile/Containers/Data/Application/\*/Library/WebKit/WebsiteData/IndexedDB*, which contains IndexedDB files created by any app installed on the device. @@ -233,10 +233,10 @@ If indicators are provided through the command-line, they are checked against th ### `webkit_local_storage.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: -This JSON file is created by mvt-ios' `WebkitLocalStorage` module. The module extracts a lsit of file and folder names located at the following path */private/var/mobile/Containers/Data/Application/\*/Library/WebKit/WebsiteData/LocalStorage/*, which contains local storage files created by any app installed on the device. +This JSON file is created by mvt-ios' `WebkitLocalStorage` module. The module extracts a list of file and folder names located at the following path */private/var/mobile/Containers/Data/Application/\*/Library/WebKit/WebsiteData/LocalStorage/*, which contains local storage files created by any app installed on the device. If indicators are provided through the command-line, they are checked against the extracted names. Any matches are stored in *webkit_local_storage_detected.json*. @@ -245,7 +245,7 @@ If indicators are provided through the command-line, they are checked against th ### `webkit_safari_view_service.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `WebkitSafariViewService` module. The module extracts a list of file and folder names located at the following path */private/var/mobile/Containers/Data/Application/\*/SystemData/com.apple.SafariViewService/Library/WebKit/WebsiteData/*, which contains files cached by SafariVewService. @@ -257,7 +257,7 @@ If indicators are provided through the command-line, they are checked against th ### `webkit_session_resource_log.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `WebkitSessionResourceLog` module. The module extracts records from plist files with the name *full_browsing_session_resourceLog.plist*, which contain records of resources loaded by different domains visited. @@ -269,7 +269,7 @@ If indicators are provided through the command-line, they are checked against th ### `whatsapp.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `WhatsApp` module. The module extracts a list of WhatsApp messages containing HTTP links from the SQLite database located at *private/var/mobile/Containers/Shared/AppGroup/\*/ChatStorage.sqlite*. diff --git a/mvt/android/download_apks.py b/mvt/android/download_apks.py index 5dd4cbc..6213ef9 100644 --- a/mvt/android/download_apks.py +++ b/mvt/android/download_apks.py @@ -16,7 +16,7 @@ log = logging.getLogger(__name__) # TODO: Would be better to replace tqdm with rich.progress to reduce # the number of dependencies. Need to investigate whether -# it's possible to have a simialr callback system. +# it's possible to have a similar callback system. class PullProgress(tqdm): """PullProgress is a tqdm update system for APK downloads.""" @@ -42,7 +42,7 @@ class DownloadAPKs(AndroidExtraction): """Initialize module. :param output_folder: Path to the folder where data should be stored :param all_apks: Boolean indicating whether to download all packages - or filter known-goods + or filter known-goods :param packages: Provided list of packages, typically for JSON checks """ super().__init__(file_path=None, base_folder=None, diff --git a/mvt/common/module.py b/mvt/common/module.py index 7b6acac..3dd2ae8 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -67,7 +67,7 @@ class MVTModule(object): self.indicators = Indicators(file_path, self.log) def check_indicators(self): - """Check the results of this module againt a provided list of + """Check the results of this module against a provided list of indicators.""" raise NotImplementedError diff --git a/mvt/ios/modules/fs/whatsapp.py b/mvt/ios/modules/fs/whatsapp.py index 34db73a..fb962bb 100644 --- a/mvt/ios/modules/fs/whatsapp.py +++ b/mvt/ios/modules/fs/whatsapp.py @@ -73,7 +73,7 @@ class Whatsapp(IOSExtraction): # Extract links from the WhatsApp message. message_links = check_for_links(new_message["ZTEXT"]) - # If we find mesages, or if there's an empty message we add it to the list. + # If we find messages, or if there's an empty message we add it to the list. if new_message["ZTEXT"] and (message_links or new_message["ZTEXT"].strip() == ""): self.results.append(new_message) From fa49203c9bebc752f9c3ef11319408e525bc51cc Mon Sep 17 00:00:00 2001 From: Bryan Scheetz Date: Thu, 22 Jul 2021 22:49:26 -0400 Subject: [PATCH 10/99] Typo adversial -> adversarial --- docs/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction.md b/docs/introduction.md index 06d66ea..740efaf 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -14,4 +14,4 @@ Mobile Verification Toolkit (MVT) is a collection of utilities designed to facil While MVT is capable of extracting and processing various types of very personal records typically found on a mobile phone (such as calls history, SMS and WhatsApp messages, etc.), this is intended to help identify potential attack vectors such as malicious SMS messages leading to exploitation. -MVT's purpose is not to facilitate adversial forensics of non-consenting individuals' devices. The use of MVT and derivative products to extract and/or analyse data originating from devices used by individuals not consenting to the procedure is explicitly prohibited in the [license](license.md). +MVT's purpose is not to facilitate adversarial forensics of non-consenting individuals' devices. The use of MVT and derivative products to extract and/or analyse data originating from devices used by individuals not consenting to the procedure is explicitly prohibited in the [license](license.md). From ece88744ed6ab37297e7ce7e5e3645e4f8faf18d Mon Sep 17 00:00:00 2001 From: Pavel Kirkovsky Date: Thu, 22 Jul 2021 23:52:39 -0700 Subject: [PATCH 11/99] KeyUtils class for working with decryption keys --- mvt/ios/keyutils.py | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 mvt/ios/keyutils.py diff --git a/mvt/ios/keyutils.py b/mvt/ios/keyutils.py new file mode 100644 index 0000000..361df45 --- /dev/null +++ b/mvt/ios/keyutils.py @@ -0,0 +1,52 @@ +import os +import logging +from iOSbackup import iOSbackup + +log = logging.getLogger(__name__) + +class KeyUtils: + """This class provides functions to extract a backup key from a password. + """ + + def __init__(self, password, backup_path): + """Generates a key file for an iOS backup. + :param password: Backup encryption password + :param key_file: Path to the file where to store the generated key file + """ + self.password = password + self.backup_path = backup_path + self._backup = None + + def get_key(self): + try: + self._backup = iOSbackup(udid=os.path.basename(self.backup_path), + cleartextpassword=self.password, + backuproot=os.path.dirname(self.backup_path)) + except Exception as e: + log.exception(e) + log.critical("Failed to decrypt backup. Did you provide the correct password?") + return + else: + self.decryption_key = self._backup.getDecryptionKey() + log.info("Extracted decryption key.") + + def print_key(self): + self.get_key() + log.info("Decryption key for backup at path %s is:\n %s", + self.backup_path, self.decryption_key) + + def write_key(self, key_file): + self.get_key() + + try: + with open(key_file, 'w') as writer: + writer.write(self.decryption_key) + except Exception as e: + log.exception(e) + log.critical("Failed to write key file.") + return + else: + log.info("Wrote decryption key for backup at path %s to file %s", + self.backup_path, key_file) + log.warn("The file %s is equivalent to a plaintext password. Keep this file safe!", + key_file) From 9fbcce4340755d0e7dfbde98eeb9803edfa1062d Mon Sep 17 00:00:00 2001 From: Pavel Kirkovsky Date: Thu, 22 Jul 2021 23:52:52 -0700 Subject: [PATCH 12/99] Add `extract-key` command --- mvt/ios/cli.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 47a45cb..bd939c6 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -15,6 +15,7 @@ from mvt.common.options import MutuallyExclusiveOption from mvt.common.indicators import Indicators from .decrypt import DecryptBackup +from .keyutils import KeyUtils from .modules.fs import BACKUP_MODULES, FS_MODULES # Setup logging using Rich. @@ -60,6 +61,26 @@ def decrypt_backup(destination, password, key_file, backup_path): raise click.ClickException("Missing required option. Specify either " "--password or --key-file.") +#============================================================================== +# Command: extract-key +#============================================================================== +@cli.command("extract-key", help="Extract decryption key from an iTunes backup") +@click.option("--password", "-p", + help="Password to use to decrypt the backup", + prompt="Backup password", + hide_input=True, prompt_required=False, required=True) +@click.option("--key-file", "-k", + help="Key file to be written (if unset, will print to STDOUT)", + required=False, + type=click.Path(exists=False, file_okay=True, dir_okay=False, writable=True)) +@click.argument("BACKUP_PATH", type=click.Path(exists=True)) +def extract_key(password, backup_path, key_file): + key_utils = KeyUtils(password, backup_path) + if key_file: + key_utils.write_key(key_file) + else: + key_utils.print_key() + #============================================================================== # Command: check-backup From af4826070a6e7bb3df6e62f53d61a81da7150224 Mon Sep 17 00:00:00 2001 From: Pavel Kirkovsky Date: Thu, 22 Jul 2021 23:55:08 -0700 Subject: [PATCH 13/99] Update README with `extract-key` command --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2a802a0..cfa7221 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ MVT provides two commands `mvt-ios` and `mvt-android` with the following subcomm * `check-fs`: Extract artifacts from a full filesystem dump * `check-iocs`: Compare stored JSON results to provided indicators * `decrypt-backup`: Decrypt an encrypted iTunes backup + * `extract-key`: Extract decryption key from an iTunes backup * `mvt-android`: * `check-backup`: Check an Android Backup * `download-apks`: Download all or non-safelisted installed APKs From 8048ed8c3a3dcddbdfc03d96f1b9b79db95f74b7 Mon Sep 17 00:00:00 2001 From: Pavel Kirkovsky Date: Fri, 23 Jul 2021 02:08:15 -0700 Subject: [PATCH 14/99] Require click >= 8.0.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c705fad..430da0a 100755 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ with open(readme_path, encoding="utf-8") as handle: requires = ( # Base dependencies: - "click", + "click >= 8.0.1", "rich", "tld", "tqdm", From 30d03482560f1d3fb121dac45fea828ccf814ab1 Mon Sep 17 00:00:00 2001 From: Pavel Kirkovsky Date: Fri, 23 Jul 2021 03:46:48 -0700 Subject: [PATCH 15/99] Added `extract-key` info to main docs --- docs/ios/backup/check.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/ios/backup/check.md b/docs/ios/backup/check.md index 3a1fd70..551205c 100644 --- a/docs/ios/backup/check.md +++ b/docs/ios/backup/check.md @@ -2,6 +2,32 @@ The backup might take some time. It is best to make sure the phone remains unlocked during the backup process. Afterwards, a new folder will be created under the path you specified using the UDID of the iPhone you backed up. +## Extracting and saving the decryption key (optional) + +If you do not wish to enter a password every time when decrypting a backup, MVT can accept a key file instead. This key can be used with the `decrypt-backup` command. + +To generate a key file, you will need your device backup and the backup password: + + $ mvt-ios extract-key --help + Usage: mvt-ios extract-key [OPTIONS] BACKUP_PATH + + Extract decryption key from an iTunes backup + + Options: + -p, --password TEXT Password to use to decrypt the backup [required] + -k, --key-file FILE Key file to be written (if unset, will print to STDOUT) + --help Show this message and exit. + +You can specify the password on the command line, or omit the `-p` option to have MVT prompt for a password. The `-k` option specifies where to save the file containing the decryption key. If `-k` is omitted, MVT will display the decryption key without saving. + +_Note_: This decryption key is sensitive data! Keep the file safe. + +To extract the key and have MVT prompt for a password: + +```bash +mvt-ios extract-key -k /path/to/save/key /path/to/backup +``` + ## Decrypting a backup In case you have an encrypted backup, you will need to decrypt it first. This can be done with `mvt-ios` as well: @@ -25,7 +51,7 @@ In case you have an encrypted backup, you will need to decrypt it first. This ca --help Show this message and exit. -You can specify either a password via command-line or pass a key file, and you need to specify a destination path where the decrypted backup will be stored. Following is an example usage of `decrypt-backup`: +You can specify either a password via command-line or pass a key file, and you need to specify a destination path where the decrypted backup will be stored. If `-p` is omitted, MVT will ask for a password. Following is an example usage of `decrypt-backup`: ```bash mvt-ios decrypt-backup -p password -d /path/to/decrypted /path/to/backup From ad3faa186bd54d7f487d3b262900213c9e72b2fa Mon Sep 17 00:00:00 2001 From: goshawk22 Date: Fri, 23 Jul 2021 15:35:56 +0100 Subject: [PATCH 16/99] Use command -v instead of which to check for root `command` is built in, unlike `which`, and is more reliable. https://github.com/mvt-project/mvt/pull/19#issuecomment-885650430 https://stackoverflow.com/questions/592620/how-can-i-check-if-a-program-exists-from-a-bash-script/677212#677212 --- mvt/android/modules/adb/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index 3deacf7..8b173f5 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -96,7 +96,7 @@ class AndroidExtraction(MVTModule): """Check if we have a `su` binary on the Android device. :returns: Boolean indicating whether a `su` binary is present or not """ - return bool(self._adb_command("which su")) + return bool(self._adb_command("command -v su")) def _adb_root_or_die(self): """Check if we have a `su` binary, otherwise raise an Exception. From 8e099e5985a9a2b5222e397d60675de08acf6f4d Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 23 Jul 2021 18:04:41 +0200 Subject: [PATCH 17/99] Checking for valid indicators before continuing (closes: #35) --- mvt/ios/modules/fs/webkit_session_resource_log.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mvt/ios/modules/fs/webkit_session_resource_log.py b/mvt/ios/modules/fs/webkit_session_resource_log.py index 299306d..b8d36ff 100644 --- a/mvt/ios/modules/fs/webkit_session_resource_log.py +++ b/mvt/ios/modules/fs/webkit_session_resource_log.py @@ -70,6 +70,9 @@ class WebkitSessionResourceLog(IOSExtraction): return domains def check_indicators(self): + if not self.indicators: + return + for key, entries in self.results.items(): for entry in entries: source_domains = self._extract_domains(entry["redirect_source"]) From ba84b3c18d0a973d62fecdfb9c00ebfcba652a22 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 23 Jul 2021 18:05:51 +0200 Subject: [PATCH 18/99] Fixed variable name (closes: #72) --- mvt/ios/modules/fs/locationd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mvt/ios/modules/fs/locationd.py b/mvt/ios/modules/fs/locationd.py index fe39f17..16d706e 100644 --- a/mvt/ios/modules/fs/locationd.py +++ b/mvt/ios/modules/fs/locationd.py @@ -43,10 +43,10 @@ class LocationdClients(IOSExtraction): for ts in self.timestamps: if ts in record.keys(): records.append({ - "timestamp": entry[ts], + "timestamp": record[ts], "module": self.__class__.__name__, "event": ts, - "data": f"{ts} from {entry['package']}" + "data": f"{ts} from {record['package']}" }) return records From 03523a40c07516796fa5f7f442fad30e35969794 Mon Sep 17 00:00:00 2001 From: Trigus42 Date: Sat, 24 Jul 2021 12:09:59 +0200 Subject: [PATCH 19/99] Fix _adb_process_file & Improve _adb_download_root - The _adb_download function doesn't need a package_name argument. This broke _adb_process_file and unnecessarily clutters function calls. Also, the function may be used to download other files or folders too. Generating a random filename seems like the best solution to me since it is less likely to get a duplicate filename and thus to replace an existing file. - The path /sdcard/Download doesn't necessarily exist. Using /sdcard seems more reliable. --- mvt/android/download_apks.py | 2 +- mvt/android/modules/adb/base.py | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/mvt/android/download_apks.py b/mvt/android/download_apks.py index b670411..5dd4cbc 100644 --- a/mvt/android/download_apks.py +++ b/mvt/android/download_apks.py @@ -135,7 +135,7 @@ class DownloadAPKs(AndroidExtraction): try: with PullProgress(unit='B', unit_divisor=1024, unit_scale=True, miniters=1) as pp: - self._adb_download(remote_path, local_path, package_name, + self._adb_download(remote_path, local_path, progress_callback=pp.update_to) except Exception as e: log.exception("Failed to pull package file from %s: %s", diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index 3c9e9b1..67139e1 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -4,6 +4,8 @@ # https://github.com/mvt-project/mvt/blob/main/LICENSE import os +import random +import string import sys import time import logging @@ -111,7 +113,7 @@ class AndroidExtraction(MVTModule): """ return self._adb_command(f"su -c {command}") - def _adb_download(self, remote_path, local_path, package_name, progress_callback=None): + def _adb_download(self, remote_path, local_path, progress_callback=None, retry_root=True): """Download a file form the device. :param remote_path: Path to download from the device :param local_path: Path to where to locally store the copy of the file @@ -119,18 +121,22 @@ class AndroidExtraction(MVTModule): """ try: self.device.pull(remote_path, local_path, progress_callback) - except AdbCommandFailureException: - self._adb_download_root(remote_path, local_path, package_name, progress_callback) except AdbCommandFailureException as e: - raise Exception(f"Unable to download file {remote_path}: {e}") + if retry_root: + self._adb_download_root(remote_path, local_path, progress_callback) + else: + raise Exception(f"Unable to download file {remote_path}: {e}") - def _adb_download_root(self, remote_path, local_path, package_name, progress_callback=None): + def _adb_download_root(self, remote_path, local_path, progress_callback=None): try: # Check if we have root, if not raise an Exception. self._adb_root_or_die() + # We generate a random temporary filename. + tmp_filename = "tmp_" + ''.join(random.choices(string.ascii_uppercase + string.ascii_lowercase + string.digits, k=10)) + # We create a temporary local file. - new_remote_path = f"/sdcard/Download/{package_name}" + new_remote_path = f"/sdcard/{tmp_filename}" # We copy the file from the data folder to /sdcard/. cp = self._adb_command_as_root(f"cp {remote_path} {new_remote_path}") @@ -140,14 +146,14 @@ class AndroidExtraction(MVTModule): raise Exception(f"Unable to process file {remote_path}: Permission denied") # We download from /sdcard/ to the local temporary file. - self._adb_download(new_remote_path, local_path, package_name) + # If it doesn't work now, don't try again (retry_root=False) + self._adb_download(new_remote_path, local_path, retry_root=False) # Delete the copy on /sdcard/. - self._adb_command(f"rm -f {new_remote_path}") - + self._adb_command(f"rm -rf {new_remote_path}") + except AdbCommandFailureException as e: raise Exception(f"Unable to download file {remote_path}: {e}") - def _adb_process_file(self, remote_path, process_routine): """Download a local copy of a file which is only accessible as root. From d6f49e76d6f6c25ca0fee68df107d64bdb57dc07 Mon Sep 17 00:00:00 2001 From: Nex Date: Sat, 24 Jul 2021 13:23:45 +0200 Subject: [PATCH 20/99] Included Docker details in the documentation --- README.md | 47 +++++------------------------------------------ docs/docker.md | 35 +++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 41 insertions(+), 42 deletions(-) create mode 100644 docs/docker.md diff --git a/README.md b/README.md index 1b556bc..d96cc3d 100644 --- a/README.md +++ b/README.md @@ -14,54 +14,17 @@ It has been developed and released by the [Amnesty International Security Lab](h [Please check out the documentation.](https://mvt.readthedocs.io/en/latest/) -### Installation using the Docker image -Using Docker is the easiest way of having all the dependencies fixed with a couple of commands. -Install Docker using the [official instructions](https://docs.docker.com/get-docker/) provided in the Website page. +## Installation -Afterwards, you can build the Docker image once inside: +MVT can be installed from sources or conveniently using: -```bash -git clone https://github.com/mvt-project/mvt.git -cd mvt -docker build -t mvt . -``` +`pip3 install mvt`. -Once the image is built, can now be tested using, what will prompt a bash terminal: +You will need some dependencies, so please check the [documentation](https://mvt.readthedocs.io/en/latest/install.html). -```bash -docker run -it mvt -``` +Alternatively, you can decide to run MVT and all relevant tools through a [Docker container](https://mvt.readthedocs.io/en/latest/docker.html) -If this is correct, close the container (`exit`) and it is time to connect the Android device to analyse to the USB port using the development mode as explained in the official docs [here](https://developer.android.com/studio/debug/dev-options). -To have visibility of the USB, the container WILL need to have access to the USB which is not activated in Docker by default. -This can be done using the `--privileged` parameter when launching Docker as follows and mounting the USB as a volume. - -```bash -docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb mvt -``` - -Note that using the `--privileged` parameter is insecure for a number of reasons explained in detail [here](https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/) as it gives access to the whole system. -As a brief explanation, the `-v :` syntax maps the host path to the dockerized path to allow the connection. -Modern versions of Docker have a `--device` option where you can specify the exact USB to mount without the `--privileged` option: - -```bash -docker run -it --device=/dev/ mvt -``` - -The Docker image contains the dependencies fixed to perform a forensic analysis on an Android device using MVT, including ADB (reachable using `adb` as expected) and ABE (installed under `/opt/abe` and reachable using `abe` from the command line) which is ready to be launched using the installed version of Java. -Thus, the forensic analyst can proceed as expected to grab the evidences needed and performs the required tests. - -## Manual Installation - -First you need to install dependencies, on Linux `sudo apt install python3 python3-pip libusb-1.0-0` or on MacOS `brew install python3 libusb`. - -Then you can install mvt from pypi with `pip3 install mvt`, or directly from sources: -```bash -git clone https://github.com/mvt-project/mvt.git -cd mvt -pip3 install . -``` ## Usage diff --git a/docs/docker.md b/docs/docker.md new file mode 100644 index 0000000..2596fa1 --- /dev/null +++ b/docs/docker.md @@ -0,0 +1,35 @@ +## Using Docker + +Using Docker simplifies having all the required dependencies and tools (including most recent versions of [libimobiledevice](https://libimobiledevice.org)) readily installed. + +Install Docker following the [official documentation](https://docs.docker.com/get-docker/). + +Once installed, you can clone MVT's repository and build its Docker image: + +```bash +git clone https://github.com/mvt-project/mvt.git +cd mvt +docker build -t mvt . +``` + +Test if the image was created successfully: + +```bash +docker run -it mvt +``` + +If a prompt is spawned successfully, you can close it with `exit`. + +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 +``` diff --git a/mkdocs.yml b/mkdocs.yml index 448b1f6..1837158 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,6 +28,7 @@ nav: - Welcome: "index.md" - Introduction: "introduction.md" - Installation: "install.md" + - Using Docker: "docker.md" - MVT for iOS: - iOS Forensic Methodology: "ios/methodology.md" - Install libimobiledevice: "ios/install.md" From 6d0ff11540751c19a8afe2c1a256905c2a5ecb75 Mon Sep 17 00:00:00 2001 From: Nex Date: Sat, 24 Jul 2021 14:27:16 +0200 Subject: [PATCH 21/99] Restored empty spaces for new line --- docs/ios/records.md | 48 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/ios/records.md b/docs/ios/records.md index 742e566..1e6f113 100644 --- a/docs/ios/records.md +++ b/docs/ios/records.md @@ -7,7 +7,7 @@ In this page you can find a (reasonably) up-to-date breakdown of the files creat ### `cache_files.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `CacheFiles` module. The module extracts records from all SQLite database files stored on disk with the name *Cache.db*. These databases typically contain data from iOS' [internal URL caching](https://developer.apple.com/documentation/foundation/nsurlcache). Through this module you might be able to recover records of HTTP requests and responses performed my applications as well as system services, that would otherwise be unavailable. For example, you might see HTTP requests part of an exploitation chain performed by an iOS service attempting to download a first stage malicious payload. @@ -19,7 +19,7 @@ If indicators are provided through the command-line, they are checked against th ### `calls.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `Calls` module. The module extracts records from a SQLite database located at */private/var/mobile/Library/CallHistoryDB/CallHistory.storedata*, which contains records of incoming and outgoing calls, including from messaging apps such as WhatsApp or Skype. @@ -29,7 +29,7 @@ This JSON file is created by mvt-ios' `Calls` module. The module extracts record ### `chrome_favicon.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `ChromeFavicon` module. The module extracts records from a SQLite database located at */private/var/mobile/Containers/Data/Application/\*/Library/Application Support/Google/Chrome/Default/Favicons*, which contains a mapping of favicons' URLs and the visited URLs which loaded them. @@ -41,7 +41,7 @@ If indicators are provided through the command-line, they are checked against bo ### `chrome_history.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `ChromeHistory` module. The module extracts records from a SQLite database located at */private/var/mobile/Containers/Data/Application/\*/Library/Application Support/Google/Chrome/Default/History*, which contains a history of URL visits. @@ -53,7 +53,7 @@ If indicators a provided through the command-line, they are checked against the ### `contacts.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `Contacts` module. The module extracts records from a SQLite database located at */private/var/mobile/Library/AddressBook/AddressBook.sqlitedb*, which contains records from the phone's address book. While this database obviously would not contain any malicious indicators per se, you might want to use it to compare records from other apps (such as iMessage, SMS, etc.) to filter those originating from unknown origins. @@ -63,7 +63,7 @@ This JSON file is created by mvt-ios' `Contacts` module. The module extracts rec ### `firefox_favicon.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `FirefoxFavicon` module. The module extracts records from a SQLite database located at */private/var/mobile/profile.profile/browser.db*, which contains a mapping of favicons' URLs and the visited URLs which loaded them. @@ -75,7 +75,7 @@ If indicators are provided through the command-line, they are checked against bo ### `firefox_history.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `FirefoxHistory` module. The module extracts records from a SQLite database located at */private/var/mobile/profile.profile/browser.db*, which contains a history of URL visits. @@ -87,7 +87,7 @@ If indicators a provided through the command-line, they are checked against the ### `id_status_cache.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `IDStatusCache` module. The module extracts records from a plist file located at */private/var/mobile/Library/Preferences/com.apple.identityservices.idstatuscache.plist*, which contains a cache of Apple user ID authentication. This chance will indicate when apps like Facetime and iMessage first established contacts with other registered Apple IDs. This is significant because it might contain traces of malicious accounts involved in exploitation of those apps. @@ -97,7 +97,7 @@ This JSON file is created by mvt-ios' `IDStatusCache` module. The module extract ### `interaction_c.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `InteractionC` module. The module extracts records from a SQLite database located at */private/var/mobile/Library/CoreDuet/People/interactionC.db*, which contains details about user interactions with installed apps. @@ -107,7 +107,7 @@ This JSON file is created by mvt-ios' `InteractionC` module. The module extracts ### `locationd_clients.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `LocationdClients` module. The module extracts records from a plist file located at */private/var/mobile/Library/Caches/locationd/clients.plist*, which contains a cache of apps which requested access to location services. @@ -117,7 +117,7 @@ This JSON file is created by mvt-ios' `LocationdClients` module. The module extr ### `manifest.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-close: This JSON file is created by mvt-ios' `Manifest` module. The module extracts records from the SQLite database *Manifest.db* contained in iTunes backups, and which indexes the locally backed-up files to the original paths on the iOS device. @@ -129,7 +129,7 @@ If indicators are provided through the command-line, they are checked against th ### `datausage.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `Datausage` module. The module extracts records from a SQLite database located */private/var/wireless/Library/Databases/DataUsage.sqlite*, which contains a history of data usage by processes running on the system. Besides the network statistics, these records are particularly important because they might show traces of malicious process executions and the relevant timeframe. In particular, processes which do not have a valid bundle ID might require particular attention. @@ -141,7 +141,7 @@ If indicators are provided through the command-line, they are checked against th ### `netusage.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `Netusage` module. The module extracts records from a SQLite database located */private/var/networkd/netusage.sqlite*, which contains a history of data usage by processes running on the system. Besides the network statistics, these records are particularly important because they might show traces of malicious process executions and the relevant timeframe. In particular, processes which do not have a valid bundle ID might require particular attention. @@ -153,7 +153,7 @@ If indicators are provided through the command-line, they are checked against th ### `safari_browser_state.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `SafariBrowserState` module. The module extracts records from the SQLite databases located at */private/var/mobile/Library/Safari/BrowserState.db* or */private/var/mobile/Containers/Data/Application/\*/Library/Safari/BrowserState.db*, which contain records of opened tabs. @@ -165,7 +165,7 @@ If indicators a provided through the command-line, they are checked against the ### `safari_favicon.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `SafariFavicon` module. The module extracts records from the SQLite databases located at */private/var/mobile/Library/Image Cache/Favicons/Favicons.db* or */private/var/mobile/Containers/Data/Application/\*/Library/Image Cache/Favicons/Favicons.db*, which contain mappings of favicons' URLs and the visited URLs which loaded them. @@ -177,7 +177,7 @@ If indicators are provided through the command-line, they are checked against bo ### `safari_history.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `SafariHistory` module. The module extracts records from the SQLite databases located at */private/var/mobile/Library/Safari/History.db* or */private/var/mobile/Containers/Data/Application/\*/Library/Safari/History.db*, which contain a history of URL visits. @@ -189,7 +189,7 @@ If indicators are provided through the command-line, they are checked against th ### `sms.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `SMS` module. The module extracts a list of SMS messages containing HTTP links from the SQLite database located at */private/var/mobile/Library/SMS/sms.db*. @@ -201,7 +201,7 @@ If indicators are provided through the command-line, they are checked against th ### `sms_attachments.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `SMSAttachments` module. The module extracts details about attachments sent via SMS or iMessage from the same database used by the `SMS` module. These records might be useful to indicate unique patterns that might be indicative of exploitation attempts leveraging potential vulnerabilities in file format parsers or other forms of file handling by the Messages app. @@ -211,7 +211,7 @@ This JSON file is created by mvt-ios' `SMSAttachments` module. The module extrac ### `version_history.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `IOSVersionHistory` module. The module extracts records of iOS software updates from analytics plist files located at */private/var/db/analyticsd/Analytics-Journal-\*.ips*. @@ -221,7 +221,7 @@ This JSON file is created by mvt-ios' `IOSVersionHistory` module. The module ext ### `webkit_indexeddb.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `WebkitIndexedDB` module. The module extracts a list of file and folder names located at the following path */private/var/mobile/Containers/Data/Application/\*/Library/WebKit/WebsiteData/IndexedDB*, which contains IndexedDB files created by any app installed on the device. @@ -233,7 +233,7 @@ If indicators are provided through the command-line, they are checked against th ### `webkit_local_storage.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `WebkitLocalStorage` module. The module extracts a list of file and folder names located at the following path */private/var/mobile/Containers/Data/Application/\*/Library/WebKit/WebsiteData/LocalStorage/*, which contains local storage files created by any app installed on the device. @@ -245,7 +245,7 @@ If indicators are provided through the command-line, they are checked against th ### `webkit_safari_view_service.json` !!! info "Availability" - Backup: :material-close: + Backup: :material-close: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `WebkitSafariViewService` module. The module extracts a list of file and folder names located at the following path */private/var/mobile/Containers/Data/Application/\*/SystemData/com.apple.SafariViewService/Library/WebKit/WebsiteData/*, which contains files cached by SafariVewService. @@ -257,7 +257,7 @@ If indicators are provided through the command-line, they are checked against th ### `webkit_session_resource_log.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `WebkitSessionResourceLog` module. The module extracts records from plist files with the name *full_browsing_session_resourceLog.plist*, which contain records of resources loaded by different domains visited. @@ -269,7 +269,7 @@ If indicators are provided through the command-line, they are checked against th ### `whatsapp.json` !!! info "Availability" - Backup: :material-check: + Backup: :material-check: Full filesystem dump: :material-check: This JSON file is created by mvt-ios' `WhatsApp` module. The module extracts a list of WhatsApp messages containing HTTP links from the SQLite database located at *private/var/mobile/Containers/Shared/AppGroup/\*/ChatStorage.sqlite*. From 9d9b77e02e6b742ce4e47939e4dab5619214dc9f Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 25 Jul 2021 10:46:10 +0200 Subject: [PATCH 22/99] Changing error message to info, to avoid confusion --- mvt/common/module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mvt/common/module.py b/mvt/common/module.py index 3dd2ae8..ce02cf2 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -137,8 +137,8 @@ def run_module(module): module.log.exception("The run() procedure of module %s was not implemented yet!", module.__class__.__name__) except FileNotFoundError as e: - module.log.error("There might be no data to extract by module %s: %s", - module.__class__.__name__, e) + module.log.info("There might be no data to extract by module %s: %s", + module.__class__.__name__, e) except Exception as e: module.log.exception("Error in running extraction from module %s: %s", module.__class__.__name__, e) From b7d65e6123de58ae9ab80eb44b3b8eb61207bf7e Mon Sep 17 00:00:00 2001 From: U039b Date: Sun, 25 Jul 2021 11:03:28 +0200 Subject: [PATCH 23/99] Fix #108 --- mvt/ios/modules/fs/net_base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mvt/ios/modules/fs/net_base.py b/mvt/ios/modules/fs/net_base.py index b5cd550..01c385c 100644 --- a/mvt/ios/modules/fs/net_base.py +++ b/mvt/ios/modules/fs/net_base.py @@ -167,6 +167,10 @@ class NetBase(IOSExtraction): results_by_proc = {proc["proc_id"]: proc for proc in self.results if proc["proc_id"]} all_proc_id = sorted(results_by_proc.keys()) + # Fix issue #108 + if not all_proc_id: + return + missing_procs, last_proc_id = {}, None for proc_id in range(min(all_proc_id), max(all_proc_id)): if proc_id not in all_proc_id: From 7fffef77cebd210a58a01a1fb7193af5f21ef567 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 25 Jul 2021 11:47:05 +0200 Subject: [PATCH 24/99] Automatically recover malformed sqlite3 databases (closes: #25 #37) --- docs/install.md | 4 ++-- mvt/common/module.py | 11 ++++++++- mvt/ios/modules/fs/base.py | 48 +++++++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/docs/install.md b/docs/install.md index 5e24576..bed90ee 100644 --- a/docs/install.md +++ b/docs/install.md @@ -7,7 +7,7 @@ Before proceeding, please note that mvt requires Python 3.6+ to run. While it sh First install some basic dependencies that will be necessary to build all required tools: ```bash -sudo apt install python3 python3-pip libusb-1.0-0 +sudo apt install python3 python3-pip libusb-1.0-0 sqlite3 ``` *libusb-1.0-0* is not required if you intend to only use `mvt-ios` and not `mvt-android`. @@ -19,7 +19,7 @@ Running MVT on Mac requires Xcode and [homebrew](https://brew.sh) to be installe In order to install dependencies use: ```bash -brew install python3 libusb +brew install python3 libusb sqlite3 ``` *libusb* is not required if you intend to only use `mvt-ios` and not `mvt-android`. diff --git a/mvt/common/module.py b/mvt/common/module.py index ce02cf2..7d9a56d 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -13,6 +13,12 @@ import simplejson as json from .indicators import Indicators +class DatabaseNotFoundError(Exception): + pass + +class DatabaseCorruptedError(Exception): + pass + class MVTModule(object): """This class provides a base for all extraction modules.""" @@ -136,9 +142,12 @@ def run_module(module): except NotImplementedError: module.log.exception("The run() procedure of module %s was not implemented yet!", module.__class__.__name__) - except FileNotFoundError as e: + except DatabaseNotFoundError as e: module.log.info("There might be no data to extract by module %s: %s", module.__class__.__name__, e) + except DatabaseCorruptedError as e: + module.log.error("The %s module database seems to be corrupted and recovery failed: %s", + module.__class__.__name__, e) except Exception as e: module.log.exception("Error in running extraction from module %s: %s", module.__class__.__name__, e) diff --git a/mvt/ios/modules/fs/base.py b/mvt/ios/modules/fs/base.py index 7a5437d..e90dbfc 100644 --- a/mvt/ios/modules/fs/base.py +++ b/mvt/ios/modules/fs/base.py @@ -3,10 +3,15 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import io import os import glob +import shutil +import sqlite3 +import subprocess from mvt.common.module import MVTModule +from mvt.common.module import DatabaseNotFoundError, DatabaseCorruptedError class IOSExtraction(MVTModule): """This class provides a base for all iOS filesystem/backup extraction modules.""" @@ -15,6 +20,31 @@ class IOSExtraction(MVTModule): is_fs_dump = False is_sysdiagnose = False + def _recover_database(self, file_path): + """Tries to recover a malformed database by running a .clone command. + :param file_path: Path to the malformed database file. + """ + # TODO: Find a better solution. + + self.log.info("Database at path %s is malformed. Trying to recover...", file_path) + + if not os.path.exists(file_path): + return + + if not shutil.which("sqlite3"): + raise DatabaseCorruptedError("Unable to recover without sqlite3 binary. Please install sqlite3!") + + bak_path = f"{file_path}.bak" + shutil.move(file_path, bak_path) + + cmd = f"sqlite3 {bak_path} '.clone {file_path}'" + ret = subprocess.call(cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + if ret != 0: + raise DatabaseCorruptedError("Recovery of database failed") + + self.log.info("Database at path %s recovered successfully!", file_path) + def _find_ios_database(self, backup_ids=None, root_paths=[]): """Try to locate the module's database file from either an iTunes backup or a full filesystem dump. @@ -52,4 +82,20 @@ class IOSExtraction(MVTModule): if file_path: self.file_path = file_path else: - raise FileNotFoundError("Unable to find the module's database file") + raise DatabaseNotFoundError("Unable to find the module's database file") + + # Check if the database is corrupted. + conn = sqlite3.connect(self.file_path) + cur = conn.cursor() + + try: + recover = False + cur.execute("SELECT name FROM sqlite_master WHERE type='table';") + except sqlite3.DatabaseError as e: + if "database disk image is malformed" in str(e): + recover = True + finally: + conn.close() + + if recover: + self._recover_database(self.file_path) From 3fd8d1524f0b4e28f49199d0e783749646ff67c7 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 25 Jul 2021 12:01:23 +0200 Subject: [PATCH 25/99] Updated LICENSE --- LICENSE | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/LICENSE b/LICENSE index 8bbc392..94f1ae4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -MVT License 1.0 +MVT License 1.1 =============== 1. Definitions @@ -95,10 +95,10 @@ MVT License 1.0 for subsequent analysis. 1.17. "Data Owner" (or "Data Owners") - means an individual or group of individuals who made use of the - electronic device from which Data that is extracted and/or analyzed - originated. "Data Owner" might or might not differ from "Device - Owner". + means an individual or group of individuals who made legitimate use + of the electronic device from which Data that is extracted and/or + analyzed originated. "Data Owner" might or might not differ from + "Device Owner". 2. License Grants and Conditions -------------------------------- @@ -381,8 +381,8 @@ Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the MVT License, - v. 1.0. If a copy of the MVT License was not distributed with this - file, You can obtain one at TODO. + v. 1.1. If a copy of the MVT License was not distributed with this + file, You can obtain one at https://license.mvt.re/1.1/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE @@ -395,7 +395,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the MVT License, v. 1.0. + defined by the MVT License, v. 1.1. This license is an adaption of Mozilla Public License, v. 2.0. From a61d4e17eb07120191baa4d17bd39a128e4b4f44 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 25 Jul 2021 12:03:45 +0200 Subject: [PATCH 26/99] Snapshotting dependencies --- setup.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index 430da0a..f62e6c9 100755 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ import os from setuptools import setup, find_packages __package_name__ = "mvt" -__version__ = "1.0.11" +__version__ = "1.1" __description__ = "Mobile Verification Toolkit" this_directory = os.path.abspath(os.path.dirname(__file__)) @@ -17,18 +17,18 @@ with open(readme_path, encoding="utf-8") as handle: requires = ( # Base dependencies: - "click >= 8.0.1", - "rich", - "tld", - "tqdm", - "requests", - "simplejson", + "click>=8.0.1", + "rich>=10.6.0", + "tld>=0.12.6", + "tqdm>=4.61.2", + "requests>=2.26.0", + "simplejson>=3.17.3", # iOS dependencies: - "biplist", - "iOSbackup", + "biplist>=1.0.3", + "iOSbackup>=0.9.912", # Android dependencies: - "adb-shell", - "libusb1", + "adb-shell>=0.4.0", + "libusb1>=1.9.3", ) def get_package_data(package): From 8f558db60bcd2fdca6930d796dc9c85389b86d67 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 25 Jul 2021 12:07:22 +0200 Subject: [PATCH 27/99] Fixed version number --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f62e6c9..1e19e24 100755 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ import os from setuptools import setup, find_packages __package_name__ = "mvt" -__version__ = "1.1" +__version__ = "1.0.12" __description__ = "Mobile Verification Toolkit" this_directory = os.path.abspath(os.path.dirname(__file__)) From 3dcc24acd5345c1da3cab46bd559ec30b7616fd5 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 25 Jul 2021 12:19:45 +0200 Subject: [PATCH 28/99] Added build 18G69 --- mvt/ios/versions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mvt/ios/versions.py b/mvt/ios/versions.py index 6ca0b50..e688e54 100644 --- a/mvt/ios/versions.py +++ b/mvt/ios/versions.py @@ -102,6 +102,7 @@ IPHONE_IOS_VERSIONS = [ {"build": "10A405", "version": "6.0"}, {"build": "11B601", "version": "7.0.5"}, {"build": "18F72", "version": "14.6"}, + {"build": "18G69", "version": "14.7"}, {"build": "18E199", "version": "14.5"}, {"build": "18E212", "version": "14.5.1"}, {"build": "18D52", "version": "14.4"}, From 3d9574682cfd88cb33f6a8888f7cad11cc31f60a Mon Sep 17 00:00:00 2001 From: emilien Date: Sun, 25 Jul 2021 14:13:10 +0200 Subject: [PATCH 29/99] Fix WhatsApp thumb image --- mvt/android/modules/adb/whatsapp.py | 3 +++ mvt/common/module.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mvt/android/modules/adb/whatsapp.py b/mvt/android/modules/adb/whatsapp.py index 6d369d6..f8eacbd 100644 --- a/mvt/android/modules/adb/whatsapp.py +++ b/mvt/android/modules/adb/whatsapp.py @@ -6,6 +6,7 @@ import os import sqlite3 import logging +import base64 from .base import AndroidExtraction from mvt.common.utils import convert_timestamp_to_iso, check_for_links @@ -69,6 +70,8 @@ class Whatsapp(AndroidExtraction): # If we find links in the messages or if they are empty we add them to the list. if check_for_links(message["data"]) or message["data"].strip() == "": + if (message.get('thumb_image') is not None): + message['thumb_image'] = base64.b64encode(message['thumb_image']) messages.append(message) cur.close() diff --git a/mvt/common/module.py b/mvt/common/module.py index 7d9a56d..ebb8a27 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -89,7 +89,10 @@ class MVTModule(object): results_file_name = f"{name}.json" results_json_path = os.path.join(self.output_folder, results_file_name) with open(results_json_path, "w") as handle: - json.dump(self.results, handle, indent=4) + try: + json.dump(self.results, handle, indent=4) + except Exception as e: + self.log.error(e) if self.detected: detected_file_name = f"{name}_detected.json" From e5003b64902ce5e54a79711a7287aeff915b3fe2 Mon Sep 17 00:00:00 2001 From: emilien Date: Sun, 25 Jul 2021 15:06:22 +0200 Subject: [PATCH 30/99] Handle SMS bases in mmssms.db instead of bugle_db --- mvt/android/modules/adb/base.py | 13 ++++++++ mvt/android/modules/adb/sms.py | 54 ++++++++++++++++++++++++--------- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index aa0c0a7..8b09864 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -112,6 +112,19 @@ class AndroidExtraction(MVTModule): :returns: Output of command """ return self._adb_command(f"su -c {command}") + + def _adb_check_file_exists(self, file): + """Verify that a file exists. + :param file: Path of the file + :returns: Boolean indicating whether the file exists or not + """ + + # Connect to the device over adb. + self._adb_connect() + # Check if we have root, if not raise an Exception. + self._adb_root_or_die() + + return bool(self._adb_command_as_root(f"[ ! -f {file} ] || echo 1")) def _adb_download(self, remote_path, local_path, progress_callback=None, retry_root=True): """Download a file form the device. diff --git a/mvt/android/modules/adb/sms.py b/mvt/android/modules/adb/sms.py index dcaccac..62b8007 100644 --- a/mvt/android/modules/adb/sms.py +++ b/mvt/android/modules/adb/sms.py @@ -13,6 +13,29 @@ from mvt.common.utils import convert_timestamp_to_iso, check_for_links log = logging.getLogger(__name__) SMS_PATH = "data/data/com.google.android.apps.messaging/databases/bugle_db" +SMS_PATH_2 = "data/data/com.android.providers.telephony/databases/mmssms.db" +sql_command_1 = """ +SELECT + ppl.normalized_destination AS number, + p.timestamp AS timestamp, +CASE WHEN m.sender_id IN +(SELECT _id FROM participants WHERE contact_id=-1) +THEN 2 ELSE 1 END incoming, p.text AS text +FROM messages m, conversations c, parts p, + participants ppl, conversation_participants cp +WHERE (m.conversation_id = c._id) + AND (m._id = p.message_id) + AND (cp.conversation_id = c._id) + AND (cp.participant_id = ppl._id); +""" +sql_command_2 = """ +SELECT + address AS number, + date_sent AS timestamp, + type as incoming, + body AS text +FROM sms; +""" class SMS(AndroidExtraction): """This module extracts all SMS messages containing links.""" @@ -47,23 +70,16 @@ class SMS(AndroidExtraction): def _parse_db(self, db_path): """Parse an Android bugle_db SMS database file. :param db_path: Path to the Android SMS database file to process + :param sql_command: SQL command to execute """ conn = sqlite3.connect(db_path) cur = conn.cursor() - cur.execute(""" - SELECT - ppl.normalized_destination AS number, - p.timestamp AS timestamp, - CASE WHEN m.sender_id IN - (SELECT _id FROM participants WHERE contact_id=-1) - THEN 2 ELSE 1 END incoming, p.text AS text - FROM messages m, conversations c, parts p, - participants ppl, conversation_participants cp - WHERE (m.conversation_id = c._id) - AND (m._id = p.message_id) - AND (cp.conversation_id = c._id) - AND (cp.participant_id = ppl._id); - """) + + if (self.SMS_DB_TYPE == 1): + cur.execute(sql_command_1) + elif (self.SMS_DB_TYPE == 2): + cur.execute(sql_command_2) + names = [description[0] for description in cur.description] for item in cur: @@ -85,7 +101,15 @@ class SMS(AndroidExtraction): log.info("Extracted a total of %d SMS messages containing links", len(self.results)) def run(self): + # Checking the SMS database path try: - self._adb_process_file(os.path.join("/", SMS_PATH), self._parse_db) + if (self._adb_check_file_exists(os.path.join("/", SMS_PATH))): + self.SMS_DB_TYPE = 1 + self._adb_process_file(os.path.join("/", SMS_PATH), self._parse_db) + elif (self._adb_check_file_exists(os.path.join("/", SMS_PATH_2))): + self.SMS_DB_TYPE = 2 + self._adb_process_file(os.path.join("/", SMS_PATH_2), self._parse_db) + else: + self.log.error("No SMS database found") except Exception as e: self.log.error(e) From 47df94fa12abc107b78108e5cd54f2f29e984ed0 Mon Sep 17 00:00:00 2001 From: emilien Date: Sun, 25 Jul 2021 15:13:23 +0200 Subject: [PATCH 31/99] fix typo --- mvt/android/modules/adb/sms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mvt/android/modules/adb/sms.py b/mvt/android/modules/adb/sms.py index 62b8007..21676ba 100644 --- a/mvt/android/modules/adb/sms.py +++ b/mvt/android/modules/adb/sms.py @@ -70,7 +70,6 @@ class SMS(AndroidExtraction): def _parse_db(self, db_path): """Parse an Android bugle_db SMS database file. :param db_path: Path to the Android SMS database file to process - :param sql_command: SQL command to execute """ conn = sqlite3.connect(db_path) cur = conn.cursor() From 13ce55f4ac3cedd16f1d81b56bc400501452a030 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 25 Jul 2021 15:51:24 +0200 Subject: [PATCH 32/99] Added some context to error message --- mvt/common/module.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mvt/common/module.py b/mvt/common/module.py index ebb8a27..3e5a19c 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -92,7 +92,8 @@ class MVTModule(object): try: json.dump(self.results, handle, indent=4) except Exception as e: - self.log.error(e) + self.log.error("Unable to store results of module %s to file %s: %s", + self.__class__.__name__, results_file_name, e) if self.detected: detected_file_name = f"{name}_detected.json" From 6b436f2057688791e884b72a03a51b34fcc2579b Mon Sep 17 00:00:00 2001 From: Adam Stiefel <80831741+adamstiefel@users.noreply.github.com> Date: Sun, 25 Jul 2021 17:16:26 -0400 Subject: [PATCH 33/99] fix: readme grammar Changed "evidences" to "evidence". Changed "understanding basics" to "understanding the basics". Changed "command line" to "command-line" --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d96cc3d..b785100 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and automate the process of gathering forensic traces helpful to identify a potential compromise of Android and iOS devices. -It has been developed and released by the [Amnesty International Security Lab](https://www.amnesty.org/en/tech/) in July 2021 in the context of the [Pegasus project](https://forbiddenstories.org/about-the-pegasus-project/) along with [a technical forensic methodology and forensic evidences](https://www.amnesty.org/en/latest/research/2021/07/forensic-methodology-report-how-to-catch-nso-groups-pegasus/). +It has been developed and released by the [Amnesty International Security Lab](https://www.amnesty.org/en/tech/) in July 2021 in the context of the [Pegasus project](https://forbiddenstories.org/about-the-pegasus-project/) along with [a technical forensic methodology and forensic evidence](https://www.amnesty.org/en/latest/research/2021/07/forensic-methodology-report-how-to-catch-nso-groups-pegasus/). -*Warning*: this tool has been released as a forensic tool for a technical audience. Using it requires some technical skills such as understanding basics of forensic analysis and using command line tools. +*Warning*: this tool has been released as a forensic tool for a technical audience. Using it requires some technical skills such as understanding the basics of forensic analysis and using command-line tools. [Please check out the documentation.](https://mvt.readthedocs.io/en/latest/) From 95ab2696716c99b7f96572724549066d434529fd Mon Sep 17 00:00:00 2001 From: Nex Date: Mon, 26 Jul 2021 19:33:12 +0200 Subject: [PATCH 34/99] Fixed some formatting --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b785100..8a7f4b1 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,13 @@ It has been developed and released by the [Amnesty International Security Lab](h MVT can be installed from sources or conveniently using: -`pip3 install mvt`. +``` +pip3 install mvt +``` You will need some dependencies, so please check the [documentation](https://mvt.readthedocs.io/en/latest/install.html). -Alternatively, you can decide to run MVT and all relevant tools through a [Docker container](https://mvt.readthedocs.io/en/latest/docker.html) +Alternatively, you can decide to run MVT and all relevant tools through a [Docker container](https://mvt.readthedocs.io/en/latest/docker.html). ## Usage From e821421ca7e5d3863b4bbd5588d188caa2d8404a Mon Sep 17 00:00:00 2001 From: Nex Date: Mon, 26 Jul 2021 21:35:35 +0200 Subject: [PATCH 35/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a7f4b1..862daeb 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and a It has been developed and released by the [Amnesty International Security Lab](https://www.amnesty.org/en/tech/) in July 2021 in the context of the [Pegasus project](https://forbiddenstories.org/about-the-pegasus-project/) along with [a technical forensic methodology and forensic evidence](https://www.amnesty.org/en/latest/research/2021/07/forensic-methodology-report-how-to-catch-nso-groups-pegasus/). -*Warning*: this tool has been released as a forensic tool for a technical audience. Using it requires some technical skills such as understanding the basics of forensic analysis and using command-line tools. +*Warning*: this tool has been released as a forensic tool for technologists and investigators. Using it requires understanding the basics of forensic analysis and using command-line tools. [Please check out the documentation.](https://mvt.readthedocs.io/en/latest/) From 362bce7c76a34cbe24a7e8283376036c1955c986 Mon Sep 17 00:00:00 2001 From: Nex Date: Mon, 26 Jul 2021 21:38:36 +0200 Subject: [PATCH 36/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 862daeb..db5e419 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and a It has been developed and released by the [Amnesty International Security Lab](https://www.amnesty.org/en/tech/) in July 2021 in the context of the [Pegasus project](https://forbiddenstories.org/about-the-pegasus-project/) along with [a technical forensic methodology and forensic evidence](https://www.amnesty.org/en/latest/research/2021/07/forensic-methodology-report-how-to-catch-nso-groups-pegasus/). -*Warning*: this tool has been released as a forensic tool for technologists and investigators. Using it requires understanding the basics of forensic analysis and using command-line tools. +*Warning*: MVT is a forensic research tool intended for technologists and investigators. Using it requires understanding the basics of forensic analysis and using command-line tools. [Please check out the documentation.](https://mvt.readthedocs.io/en/latest/) From 8b253b5e7c741c35a98274d73420876270135c00 Mon Sep 17 00:00:00 2001 From: Nex Date: Mon, 26 Jul 2021 21:39:49 +0200 Subject: [PATCH 37/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db5e419..252a653 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and a It has been developed and released by the [Amnesty International Security Lab](https://www.amnesty.org/en/tech/) in July 2021 in the context of the [Pegasus project](https://forbiddenstories.org/about-the-pegasus-project/) along with [a technical forensic methodology and forensic evidence](https://www.amnesty.org/en/latest/research/2021/07/forensic-methodology-report-how-to-catch-nso-groups-pegasus/). -*Warning*: MVT is a forensic research tool intended for technologists and investigators. Using it requires understanding the basics of forensic analysis and using command-line tools. +*Warning*: MVT is a forensic research tool intended for technologists and investigators. Using it requires understanding the basics of forensic analysis and using command-line tools. This is not intended for end-user self-assessment. If you are concerned with the security of your device please seek expert assistance. [Please check out the documentation.](https://mvt.readthedocs.io/en/latest/) From 32aeaaf91cd24a9bb2fe961541c7697784915937 Mon Sep 17 00:00:00 2001 From: Nex Date: Mon, 26 Jul 2021 21:48:55 +0200 Subject: [PATCH 38/99] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 252a653..c4da85c 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ # Mobile Verification Toolkit [![](https://img.shields.io/pypi/v/mvt)](https://pypi.org/project/mvt/) +[![](https://img.shields.io/badge/docs-blue.svg)](https://mvt.readthedocs.io) Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and automate the process of gathering forensic traces helpful to identify a potential compromise of Android and iOS devices. @@ -12,9 +13,6 @@ It has been developed and released by the [Amnesty International Security Lab](h *Warning*: MVT is a forensic research tool intended for technologists and investigators. Using it requires understanding the basics of forensic analysis and using command-line tools. This is not intended for end-user self-assessment. If you are concerned with the security of your device please seek expert assistance. -[Please check out the documentation.](https://mvt.readthedocs.io/en/latest/) - - ## Installation MVT can be installed from sources or conveniently using: From 9e33ece3e900d68184a96b059da54cd2df86db72 Mon Sep 17 00:00:00 2001 From: tek Date: Tue, 27 Jul 2021 01:23:22 +0200 Subject: [PATCH 39/99] Fixes issue with Manifest format --- mvt/common/module.py | 18 ++++++++++-------- mvt/ios/modules/fs/manifest.py | 13 +++++++++---- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/mvt/common/module.py b/mvt/common/module.py index 3e5a19c..f4dffcd 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -109,17 +109,19 @@ class MVTModule(object): """ for result in self.results: record = self.serialize(result) - if type(record) == list: - self.timeline.extend(record) - else: - self.timeline.append(record) + if record: + if type(record) == list: + self.timeline.extend(record) + else: + self.timeline.append(record) for detected in self.detected: record = self.serialize(detected) - if type(record) == list: - self.timeline_detected.extend(record) - else: - self.timeline_detected.append(record) + if record: + if type(record) == list: + self.timeline_detected.extend(record) + else: + self.timeline_detected.append(record) # De-duplicate timeline entries self.timeline = self.timeline_deduplicate(self.timeline) diff --git a/mvt/ios/modules/fs/manifest.py b/mvt/ios/modules/fs/manifest.py index d5c1c74..64484ec 100644 --- a/mvt/ios/modules/fs/manifest.py +++ b/mvt/ios/modules/fs/manifest.py @@ -40,6 +40,8 @@ class Manifest(IOSExtraction): def serialize(self, record): records = [] + if "modified" not in record or "statusChanged" not in record: + return for ts in set([record["created"], record["modified"], record["statusChanged"]]): macb = "" macb += "M" if ts == record["modified"] else "-" @@ -63,12 +65,15 @@ class Manifest(IOSExtraction): for result in self.results: if not "relativePath" in result: continue - - if os.path.basename(result["relativePath"]) == "com.apple.CrashReporter.plist" and result["domain"] == "RootDomain": - self.log.warning("Found a potentially suspicious \"com.apple.CrashReporter.plist\" file created in RootDomain") - self.detected.append(result) + if not result["relativePath"]: continue + if result["domain"]: + if os.path.basename(result["relativePath"]) == "com.apple.CrashReporter.plist" and result["domain"] == "RootDomain": + self.log.warning("Found a potentially suspicious \"com.apple.CrashReporter.plist\" file created in RootDomain") + self.detected.append(result) + continue + if self.indicators.check_file(result["relativePath"]): self.log.warning("Found a known malicious file at path: %s", result["relativePath"]) self.detected.append(result) From 9931edccc452f8624149dd61360f69f4da525f8f Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 27 Jul 2021 12:06:47 +0200 Subject: [PATCH 40/99] GitHub Action to lint Python code Output: --- .github/workflows/lint_python.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/lint_python.yml diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml new file mode 100644 index 0000000..f70fb93 --- /dev/null +++ b/.github/workflows/lint_python.yml @@ -0,0 +1,21 @@ +name: lint_python +on: [pull_request, push] +jobs: + lint_python: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - run: pip install bandit black codespell flake8 isort mypy pytest pyupgrade safety + - run: bandit --recursive --skip B101 . || true # B101 is assert statements + - run: black --check . || true + - run: codespell || true # --ignore-words-list="" --skip="" + - run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + - run: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --show-source --statistics + - run: isort --check-only --profile black . || true + - run: pip install -r requirements.txt || true + - run: mypy --install-types --non-interactive . || true + - run: pytest . || true + - run: pytest --doctest-modules . || true + - run: shopt -s globstar && pyupgrade --py36-plus **/*.py || true + - run: safety check From 3f2058441a193b87c50b763ccf00b2eae86746f6 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 27 Jul 2021 12:09:52 +0200 Subject: [PATCH 41/99] bandit --recursive --skip B108,B112,B404,B602 . --- .github/workflows/lint_python.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index f70fb93..03a58d4 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -7,8 +7,8 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - run: pip install bandit black codespell flake8 isort mypy pytest pyupgrade safety - - run: bandit --recursive --skip B101 . || true # B101 is assert statements - - run: black --check . || true + - run: bandit --recursive --skip B108,B112,B404,B602 . + - run: black --check . - run: codespell || true # --ignore-words-list="" --skip="" - run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - run: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --show-source --statistics From b500ee94293b618fc170946f66061a7b7616239e Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 27 Jul 2021 12:11:31 +0200 Subject: [PATCH 42/99] codespell --- .github/workflows/lint_python.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index 03a58d4..4f742d0 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -8,8 +8,8 @@ jobs: - uses: actions/setup-python@v2 - run: pip install bandit black codespell flake8 isort mypy pytest pyupgrade safety - run: bandit --recursive --skip B108,B112,B404,B602 . - - run: black --check . - - run: codespell || true # --ignore-words-list="" --skip="" + - run: black --check . || true + - run: codespell - run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - run: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --show-source --statistics - run: isort --check-only --profile black . || true From 3bb0d5020ca80749d2326358314c8f99c4061981 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 27 Jul 2021 21:27:43 +0200 Subject: [PATCH 43/99] Fixed variable name --- mvt/ios/modules/fs/locationd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mvt/ios/modules/fs/locationd.py b/mvt/ios/modules/fs/locationd.py index 16d706e..48d1136 100644 --- a/mvt/ios/modules/fs/locationd.py +++ b/mvt/ios/modules/fs/locationd.py @@ -62,7 +62,7 @@ class LocationdClients(IOSExtraction): result["package"] = app for ts in self.timestamps: if ts in result.keys(): - result[ts] = convert_timestamp_to_iso(convert_mactime_to_unix(result[date])) + result[ts] = convert_timestamp_to_iso(convert_mactime_to_unix(result["date"])) self.results.append(result) From b1ae7776219b2ad7a60e2ee8a74024748bfd3f3e Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 27 Jul 2021 21:29:14 +0200 Subject: [PATCH 44/99] Fixed variable name --- mvt/ios/modules/fs/locationd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mvt/ios/modules/fs/locationd.py b/mvt/ios/modules/fs/locationd.py index 48d1136..799bb36 100644 --- a/mvt/ios/modules/fs/locationd.py +++ b/mvt/ios/modules/fs/locationd.py @@ -62,7 +62,7 @@ class LocationdClients(IOSExtraction): result["package"] = app for ts in self.timestamps: if ts in result.keys(): - result[ts] = convert_timestamp_to_iso(convert_mactime_to_unix(result["date"])) + result[ts] = convert_timestamp_to_iso(convert_mactime_to_unix(result[ts])) self.results.append(result) From 2baac1f52c21080f79e6b0397c45f090d8dc7c3b Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 27 Jul 2021 21:37:06 +0200 Subject: [PATCH 45/99] Create python-publish.yml --- .github/workflows/python-publish.yml | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/python-publish.yml diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 0000000..3bfabfc --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,36 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Upload Python Package + +on: + release: + types: [published] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} From caa5d8ee8c0ba25a4122ad074511476d1a70cfd6 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 27 Jul 2021 21:37:26 +0200 Subject: [PATCH 46/99] Rename lint_python.yml to lint-python.yml --- .github/workflows/{lint_python.yml => lint-python.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{lint_python.yml => lint-python.yml} (100%) diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint-python.yml similarity index 100% rename from .github/workflows/lint_python.yml rename to .github/workflows/lint-python.yml From fe8c013b0fe850d68c5ca3d52694fa2223ac1250 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 27 Jul 2021 21:40:15 +0200 Subject: [PATCH 47/99] Bumped version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1e19e24..2768cea 100755 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ import os from setuptools import setup, find_packages __package_name__ = "mvt" -__version__ = "1.0.12" +__version__ = "1.0.13" __description__ = "Mobile Verification Toolkit" this_directory = os.path.abspath(os.path.dirname(__file__)) From ad81d5c450c6701e0be07e895272d9a74ae3daf6 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 27 Jul 2021 21:42:21 +0200 Subject: [PATCH 48/99] Delete python-publish.yml --- .github/workflows/python-publish.yml | 36 ---------------------------- 1 file changed, 36 deletions(-) delete mode 100644 .github/workflows/python-publish.yml diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml deleted file mode 100644 index 3bfabfc..0000000 --- a/.github/workflows/python-publish.yml +++ /dev/null @@ -1,36 +0,0 @@ -# This workflow will upload a Python Package using Twine when a release is created -# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries - -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: Upload Python Package - -on: - release: - types: [published] - -jobs: - deploy: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - - name: Build package - run: python -m build - - name: Publish package - uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} From 45beb6eeda65187c7e1a02005b0bb5840a1e1bc5 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 27 Jul 2021 21:43:25 +0200 Subject: [PATCH 49/99] Update lint-python.yml --- .github/workflows/lint-python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml index 4f742d0..f697799 100644 --- a/.github/workflows/lint-python.yml +++ b/.github/workflows/lint-python.yml @@ -1,5 +1,5 @@ name: lint_python -on: [pull_request, push] +on: [pull_request] jobs: lint_python: runs-on: ubuntu-latest From 7947d413b53fa7c7dc71ce2cd6e5463590fb5cfb Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 27 Jul 2021 21:44:31 +0200 Subject: [PATCH 50/99] Update lint-python.yml --- .github/workflows/lint-python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml index f697799..d5863ed 100644 --- a/.github/workflows/lint-python.yml +++ b/.github/workflows/lint-python.yml @@ -1,4 +1,4 @@ -name: lint_python +name: Python Lint on: [pull_request] jobs: lint_python: From 4fb6e204d1ae9e07ab2c7fdb143cff07b8c0db83 Mon Sep 17 00:00:00 2001 From: Nex Date: Wed, 28 Jul 2021 08:33:33 +0200 Subject: [PATCH 51/99] Ordered iOS versions --- mvt/ios/versions.py | 334 ++++++++++++++++++++++---------------------- 1 file changed, 167 insertions(+), 167 deletions(-) diff --git a/mvt/ios/versions.py b/mvt/ios/versions.py index e688e54..1828421 100644 --- a/mvt/ios/versions.py +++ b/mvt/ios/versions.py @@ -4,186 +4,186 @@ # https://github.com/mvt-project/mvt/blob/main/LICENSE IPHONE_IOS_VERSIONS = [ - {"build": "7E18", "version": "3.1.3"}, - {"build": "7D11", "version": "3.1.2"}, - {"build": "7C144", "version": "3.1"}, - {"build": "7A400", "version": "3.0.1"}, - {"build": "7A341", "version": "3.0"}, - {"build": "5H11", "version": "2.2.1"}, - {"build": "5G77", "version": "2.2"}, - {"build": "5F136", "version": "2.1"}, - {"build": "5C1", "version": "2.0.2"}, - {"build": "5B108", "version": "2.0.1"}, - {"build": "5A347", "version": "2.0"}, - {"build": "4A102", "version": "1.1.4"}, - {"build": "4A93", "version": "1.1.3"}, - {"build": "3B48b", "version": "1.1.2"}, - {"build": "3A109a", "version": "1.1.1"}, - {"build": "1C28", "version": "1.0.2"}, - {"build": "1C25", "version": "1.0.1"}, {"build": "1A543a", "version": "1.0"}, - {"build": "8C148", "version": "4.2"}, - {"build": "8B117", "version": "4.1"}, - {"build": "8A306", "version": "4.0.1"}, + {"build": "1C25", "version": "1.0.1"}, + {"build": "1C28", "version": "1.0.2"}, + {"build": "3A109a", "version": "1.1.1"}, + {"build": "3B48b", "version": "1.1.2"}, + {"build": "4A93", "version": "1.1.3"}, + {"build": "4A102", "version": "1.1.4"}, + {"build": "5A347", "version": "2.0"}, + {"build": "5B108", "version": "2.0.1"}, + {"build": "5C1", "version": "2.0.2"}, + {"build": "5F136", "version": "2.1"}, + {"build": "5G77", "version": "2.2"}, + {"build": "5H11", "version": "2.2.1"}, + {"build": "7A341", "version": "3.0"}, + {"build": "7A400", "version": "3.0.1"}, + {"build": "7C144", "version": "3.1"}, + {"build": "7D11", "version": "3.1.2"}, + {"build": "7E18", "version": "3.1.3"}, {"build": "8A293", "version": "4.0"}, - {"build": "10B500", "version": "6.1.6"}, - {"build": "10B329", "version": "6.1.3"}, - {"build": "10B146", "version": "6.1.2"}, - {"build": "10B141", "version": "6.1"}, - {"build": "10A523", "version": "6.0.1"}, - {"build": "10A403", "version": "6.0"}, - {"build": "9B206", "version": "5.1.1"}, - {"build": "9B176", "version": "5.1"}, - {"build": "9A405", "version": "5.0.1"}, - {"build": "9A334", "version": "5.0"}, - {"build": "8L1", "version": "4.3.5"}, - {"build": "8K2", "version": "4.3.4"}, - {"build": "8J2", "version": "4.3.3"}, - {"build": "8F190", "version": "4.3"}, + {"build": "8A306", "version": "4.0.1"}, + {"build": "8B117", "version": "4.1"}, + {"build": "8C148", "version": "4.2"}, {"build": "8C148a", "version": "4.2.1"}, - {"build": "11D257", "version": "7.1.2"}, - {"build": "11D201", "version": "7.1.1"}, - {"build": "11D169", "version": "7.1"}, - {"build": "11B651", "version": "7.0.6"}, - {"build": "11B554a", "version": "7.0.4"}, - {"build": "11B511", "version": "7.0.3"}, - {"build": "10B144", "version": "6.1"}, - {"build": "9B208", "version": "5.1.1"}, {"build": "8C148", "version": "4.2.1"}, - {"build": "11D167", "version": "7.1"}, {"build": "8E600", "version": "4.2.10"}, - {"build": "8E501", "version": "4.2.9"}, {"build": "8E401", "version": "4.2.8"}, - {"build": "13G37", "version": "9.3.6"}, - {"build": "13G36", "version": "9.3.5"}, - {"build": "13G35", "version": "9.3.4"}, - {"build": "13G34", "version": "9.3.3"}, - {"build": "13F69", "version": "9.3.2"}, - {"build": "13E238", "version": "9.3.1"}, + {"build": "8E501", "version": "4.2.9"}, + {"build": "8F190", "version": "4.3"}, + {"build": "8J2", "version": "4.3.3"}, + {"build": "8K2", "version": "4.3.4"}, + {"build": "8L1", "version": "4.3.5"}, + {"build": "9A334", "version": "5.0"}, + {"build": "9A405", "version": "5.0.1"}, + {"build": "9A406", "version": "5.0.1"}, + {"build": "9B176", "version": "5.1"}, + {"build": "9B179", "version": "5.1"}, + {"build": "9B206", "version": "5.1.1"}, + {"build": "9B208", "version": "5.1.1"}, + {"build": "10A403", "version": "6.0"}, + {"build": "10A405", "version": "6.0"}, + {"build": "10A523", "version": "6.0.1"}, + {"build": "10A525", "version": "6.0.1"}, + {"build": "10A551", "version": "6.0.2"}, + {"build": "10B141", "version": "6.1"}, + {"build": "10B144", "version": "6.1"}, + {"build": "10B142", "version": "6.1"}, + {"build": "10B143", "version": "6.1"}, + {"build": "10B145", "version": "6.1.1"}, + {"build": "10B146", "version": "6.1.2"}, + {"build": "10B329", "version": "6.1.3"}, + {"build": "10B350", "version": "6.1.4"}, + {"build": "10B500", "version": "6.1.6"}, + {"build": "11B511", "version": "7.0.3"}, + {"build": "11B554a", "version": "7.0.4"}, + {"build": "11B601", "version": "7.0.5"}, + {"build": "11B651", "version": "7.0.6"}, + {"build": "11D169", "version": "7.1"}, + {"build": "11D167", "version": "7.1"}, + {"build": "11D201", "version": "7.1.1"}, + {"build": "11D257", "version": "7.1.2"}, + {"build": "12A365", "version": "8.0"}, + {"build": "12A366", "version": "8.0"}, + {"build": "12A402", "version": "8.0.1"}, + {"build": "12A405", "version": "8.0.2"}, + {"build": "12B411", "version": "8.1"}, + {"build": "12B435", "version": "8.1.1"}, + {"build": "12B436", "version": "8.1.1"}, + {"build": "12B440", "version": "8.1.2"}, + {"build": "12B466", "version": "8.1.3"}, + {"build": "12D508", "version": "8.2"}, + {"build": "12F70", "version": "8.3"}, + {"build": "12H143", "version": "8.4"}, + {"build": "12H321", "version": "8.4.1"}, + {"build": "13A344", "version": "9.0"}, + {"build": "13A342", "version": "9.0"}, + {"build": "13A343", "version": "9.0"}, + {"build": "13A404", "version": "9.0.1"}, + {"build": "13A405", "version": "9.0.1"}, + {"build": "13A452", "version": "9.0.2"}, + {"build": "13B143", "version": "9.1"}, + {"build": "13C75", "version": "9.2"}, + {"build": "13D15", "version": "9.2.1"}, + {"build": "13D20", "version": "9.2.1"}, {"build": "13E237", "version": "9.3"}, {"build": "13E233", "version": "9.3"}, - {"build": "13D15", "version": "9.2.1"}, - {"build": "13C75", "version": "9.2"}, - {"build": "13B143", "version": "9.1"}, - {"build": "13A452", "version": "9.0.2"}, - {"build": "13A404", "version": "9.0.1"}, - {"build": "13A344", "version": "9.0"}, - {"build": "12H321", "version": "8.4.1"}, - {"build": "12H143", "version": "8.4"}, - {"build": "12F70", "version": "8.3"}, - {"build": "12D508", "version": "8.2"}, - {"build": "12B466", "version": "8.1.3"}, - {"build": "12B440", "version": "8.1.2"}, - {"build": "12B435", "version": "8.1.1"}, - {"build": "12B411", "version": "8.1"}, - {"build": "12A405", "version": "8.0.2"}, - {"build": "12A402", "version": "8.0.1"}, - {"build": "12A365", "version": "8.0"}, - {"build": "10B145", "version": "6.1.1"}, - {"build": "10B142", "version": "6.1"}, - {"build": "9B179", "version": "5.1"}, - {"build": "9A406", "version": "5.0.1"}, - {"build": "14G61", "version": "10.3.4"}, - {"build": "14G60", "version": "10.3.3"}, - {"build": "14F89", "version": "10.3.2"}, - {"build": "14E304", "version": "10.3.1"}, - {"build": "14E277", "version": "10.3"}, - {"build": "14D27", "version": "10.2.1"}, - {"build": "14C92", "version": "10.2"}, + {"build": "13E234", "version": "9.3"}, + {"build": "13E238", "version": "9.3.1"}, + {"build": "13F69", "version": "9.3.2"}, + {"build": "13G34", "version": "9.3.3"}, + {"build": "13G35", "version": "9.3.4"}, + {"build": "13G36", "version": "9.3.5"}, + {"build": "13G37", "version": "9.3.6"}, + {"build": "14A403", "version": "10.0.1"}, + {"build": "14A456", "version": "10.0.2"}, + {"build": "14A551", "version": "10.0.3"}, + {"build": "14B72", "version": "10.1"}, + {"build": "14B72c", "version": "10.1"}, {"build": "14B150", "version": "10.1.1"}, {"build": "14B100", "version": "10.1.1"}, - {"build": "14B72", "version": "10.1"}, - {"build": "14A456", "version": "10.0.2"}, - {"build": "14A403", "version": "10.0.1"}, - {"build": "10B350", "version": "6.1.4"}, - {"build": "10B143", "version": "6.1"}, - {"build": "10A551", "version": "6.0.2"}, - {"build": "10A525", "version": "6.0.1"}, - {"build": "10A405", "version": "6.0"}, - {"build": "11B601", "version": "7.0.5"}, - {"build": "18F72", "version": "14.6"}, - {"build": "18G69", "version": "14.7"}, - {"build": "18E199", "version": "14.5"}, - {"build": "18E212", "version": "14.5.1"}, + {"build": "14C92", "version": "10.2"}, + {"build": "14D27", "version": "10.2.1"}, + {"build": "14E277", "version": "10.3"}, + {"build": "14E304", "version": "10.3.1"}, + {"build": "14F89", "version": "10.3.2"}, + {"build": "14G60", "version": "10.3.3"}, + {"build": "14G61", "version": "10.3.4"}, + {"build": "15A372", "version": "11.0"}, + {"build": "15A402", "version": "11.0.1"}, + {"build": "15A421", "version": "11.0.2"}, + {"build": "15A432", "version": "11.0.3"}, + {"build": "15B93", "version": "11.1"}, + {"build": "15B150", "version": "11.1.1"}, + {"build": "15B202", "version": "11.1.2"}, + {"build": "15C114", "version": "11.2"}, + {"build": "15C153", "version": "11.2.1"}, + {"build": "15C202", "version": "11.2.2"}, + {"build": "15D60", "version": "11.2.5"}, + {"build": "15D100", "version": "11.2.6"}, + {"build": "15E216", "version": "11.3"}, + {"build": "15E302", "version": "11.3.1"}, + {"build": "15F79", "version": "11.4"}, + {"build": "15G77", "version": "11.4.1"}, + {"build": "16A366", "version": "12.0"}, + {"build": "16A404", "version": "12.0.1"}, + {"build": "16A405", "version": "12.0.1"}, + {"build": "16B92", "version": "12.1"}, + {"build": "16B94", "version": "12.1"}, + {"build": "16B93", "version": "12.1"}, + {"build": "16C50", "version": "12.1.1"}, + {"build": "16C104", "version": "12.1.2"}, + {"build": "16C101", "version": "12.1.2"}, + {"build": "16D39", "version": "12.1.3"}, + {"build": "16D40", "version": "12.1.3"}, + {"build": "16D57", "version": "12.1.4"}, + {"build": "16E227", "version": "12.2"}, + {"build": "16F156", "version": "12.3"}, + {"build": "16F203", "version": "12.3.1"}, + {"build": "16F250", "version": "12.3.2"}, + {"build": "16G77", "version": "12.4"}, + {"build": "16G102", "version": "12.4.1"}, + {"build": "16G114", "version": "12.4.2"}, + {"build": "16G130", "version": "12.4.3"}, + {"build": "16G161", "version": "12.4.5"}, + {"build": "16G183", "version": "12.4.6"}, + {"build": "16G192", "version": "12.4.7"}, + {"build": "16G201", "version": "12.4.8"}, + {"build": "16H5", "version": "12.4.9"}, + {"build": "16H20", "version": "12.5"}, + {"build": "16H22", "version": "12.5.1"}, + {"build": "17A577", "version": "13.0"}, + {"build": "17A844", "version": "13.1"}, + {"build": "17A854", "version": "13.1.1"}, + {"build": "17A860", "version": "13.1.2"}, + {"build": "17A861", "version": "13.1.2"}, + {"build": "17A878", "version": "13.1.3"}, + {"build": "17B84", "version": "13.2"}, + {"build": "17B102", "version": "13.2.2"}, + {"build": "17B111", "version": "13.2.3"}, + {"build": "17C54", "version": "13.3"}, + {"build": "17D50", "version": "13.3.1"}, + {"build": "17E255", "version": "13.4"}, + {"build": "17E262", "version": "13.4.1"}, + {"build": "17E8258", "version": "13.4.1"}, + {"build": "17F75", "version": "13.5"}, + {"build": "17F80", "version": "13.5.1"}, + {"build": "17G68", "version": "13.6"}, + {"build": "17G80", "version": "13.6.1"}, + {"build": "17H35", "version": "13.7"}, + {"build": "18A373", "version": "14.0"}, + {"build": "18A393", "version": "14.0.1"}, + {"build": "18A8395", "version": "14.1"}, + {"build": "18B92", "version": "14.2"}, + {"build": "18C66", "version": "14.3"}, {"build": "18D52", "version": "14.4"}, {"build": "18D61", "version": "14.4.1"}, {"build": "18D70", "version": "14.4.2"}, - {"build": "18C66", "version": "14.3"}, - {"build": "18B92", "version": "14.2"}, - {"build": "18A8395", "version": "14.1"}, - {"build": "18A393", "version": "14.0.1"}, - {"build": "18A373", "version": "14.0"}, - {"build": "17H35", "version": "13.7"}, - {"build": "17G80", "version": "13.6.1"}, - {"build": "17G68", "version": "13.6"}, - {"build": "17F80", "version": "13.5.1"}, - {"build": "17F75", "version": "13.5"}, - {"build": "17E262", "version": "13.4.1"}, - {"build": "17E255", "version": "13.4"}, - {"build": "17C54", "version": "13.3"}, - {"build": "17D50", "version": "13.3.1"}, - {"build": "17B111", "version": "13.2.3"}, - {"build": "17B102", "version": "13.2.2"}, - {"build": "17B84", "version": "13.2"}, - {"build": "17A878", "version": "13.1.3"}, - {"build": "17A860", "version": "13.1.2"}, - {"build": "17A854", "version": "13.1.1"}, - {"build": "17A844", "version": "13.1"}, - {"build": "17A577", "version": "13.0"}, - {"build": "16H22", "version": "12.5.1"}, - {"build": "16H20", "version": "12.5"}, - {"build": "16H5", "version": "12.4.9"}, - {"build": "16G201", "version": "12.4.8"}, - {"build": "16G192", "version": "12.4.7"}, - {"build": "16G183", "version": "12.4.6"}, - {"build": "16G161", "version": "12.4.5"}, - {"build": "16G130", "version": "12.4.3"}, - {"build": "16G114", "version": "12.4.2"}, - {"build": "16G102", "version": "12.4.1"}, - {"build": "16G77", "version": "12.4"}, - {"build": "16F203", "version": "12.3.1"}, - {"build": "16F156", "version": "12.3"}, - {"build": "16E227", "version": "12.2"}, - {"build": "16D57", "version": "12.1.4"}, - {"build": "16D39", "version": "12.1.3"}, - {"build": "16C104", "version": "12.1.2"}, - {"build": "16C101", "version": "12.1.2"}, - {"build": "16C50", "version": "12.1.1"}, - {"build": "16B92", "version": "12.1"}, - {"build": "16A404", "version": "12.0.1"}, - {"build": "16A366", "version": "12.0"}, - {"build": "15G77", "version": "11.4.1"}, - {"build": "15F79", "version": "11.4"}, - {"build": "15E302", "version": "11.3.1"}, - {"build": "15E216", "version": "11.3"}, - {"build": "15D100", "version": "11.2.6"}, - {"build": "15D60", "version": "11.2.5"}, - {"build": "15C202", "version": "11.2.2"}, - {"build": "15C153", "version": "11.2.1"}, - {"build": "15C114", "version": "11.2"}, - {"build": "15B202", "version": "11.1.2"}, - {"build": "15B150", "version": "11.1.1"}, - {"build": "15B93", "version": "11.1"}, - {"build": "15A432", "version": "11.0.3"}, - {"build": "15A421", "version": "11.0.2"}, - {"build": "15A402", "version": "11.0.1"}, - {"build": "15A372", "version": "11.0"}, - {"build": "13D20", "version": "9.2.1"}, - {"build": "12B436", "version": "8.1.1"}, - {"build": "12A366", "version": "8.0"}, - {"build": "13E234", "version": "9.3"}, - {"build": "13A405", "version": "9.0.1"}, - {"build": "13A342", "version": "9.0"}, - {"build": "13A343", "version": "9.0"}, - {"build": "14B72c", "version": "10.1"}, - {"build": "14A551", "version": "10.0.3"}, - {"build": "16F250", "version": "12.3.2"}, - {"build": "17A861", "version": "13.1.2"}, - {"build": "16D40", "version": "12.1.3"}, - {"build": "16A405", "version": "12.0.1"}, - {"build": "16B94", "version": "12.1"}, - {"build": "16B93", "version": "12.1"}, - {"build": "17E8258", "version": "13.4.1"} + {"build": "18E199", "version": "14.5"}, + {"build": "18E212", "version": "14.5.1"}, + {"build": "18F72", "version": "14.6"}, + {"build": "18G69", "version": "14.7"}, ] def find_version_by_build(build): From 5e03c28dbdd566aa51c7507367d5d7691a7fd2af Mon Sep 17 00:00:00 2001 From: macmade Date: Thu, 29 Jul 2021 02:33:32 +0200 Subject: [PATCH 52/99] Chrome History - Cheking extracted URLs against indicators. --- mvt/ios/modules/fs/chrome_history.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mvt/ios/modules/fs/chrome_history.py b/mvt/ios/modules/fs/chrome_history.py index 8d8a64e..557b196 100644 --- a/mvt/ios/modules/fs/chrome_history.py +++ b/mvt/ios/modules/fs/chrome_history.py @@ -35,6 +35,14 @@ class ChromeHistory(IOSExtraction): "data": f"{record['id']} - {record['url']} (visit ID: {record['visit_id']}, redirect source: {record['redirect_source']})" } + def check_indicators(self): + if not self.indicators: + return + + for result in self.results: + if self.indicators.check_domain(result["url"]): + self.detected.append(result) + def run(self): self._find_ios_database(backup_ids=CHROME_HISTORY_BACKUP_IDS, root_paths=CHROME_HISTORY_ROOT_PATHS) self.log.info("Found Chrome history database at path: %s", self.file_path) From 60a2dbb8601a70d1a5d9e93011b1b799274732a6 Mon Sep 17 00:00:00 2001 From: Nex Date: Thu, 29 Jul 2021 13:46:58 +0200 Subject: [PATCH 53/99] Added module to parse WebKit ResourceLoadStatistics observations.db (ref: #133) --- mvt/common/module.py | 2 +- mvt/ios/modules/fs/__init__.py | 8 +- mvt/ios/modules/fs/base.py | 32 +++--- .../fs/webkit_resource_load_statistics.py | 98 +++++++++++++++++++ 4 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 mvt/ios/modules/fs/webkit_resource_load_statistics.py diff --git a/mvt/common/module.py b/mvt/common/module.py index f4dffcd..cf391e3 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -123,7 +123,7 @@ class MVTModule(object): else: self.timeline_detected.append(record) - # De-duplicate timeline entries + # De-duplicate timeline entries. self.timeline = self.timeline_deduplicate(self.timeline) self.timeline_detected = self.timeline_deduplicate(self.timeline_detected) diff --git a/mvt/ios/modules/fs/__init__.py b/mvt/ios/modules/fs/__init__.py index dad5124..1c72e74 100644 --- a/mvt/ios/modules/fs/__init__.py +++ b/mvt/ios/modules/fs/__init__.py @@ -14,6 +14,7 @@ from .webkit_indexeddb import WebkitIndexedDB from .webkit_localstorage import WebkitLocalStorage from .webkit_safariviewservice import WebkitSafariViewService from .webkit_session_resource_log import WebkitSessionResourceLog +from .webkit_resource_load_statistics import WebkitResourceLoadStatistics from .chrome_history import ChromeHistory from .chrome_favicon import ChromeFavicon from .firefox_history import FirefoxHistory @@ -31,12 +32,13 @@ from .filesystem import Filesystem BACKUP_MODULES = [SafariBrowserState, SafariHistory, Datausage, SMS, SMSAttachments, ChromeHistory, ChromeFavicon, WebkitSessionResourceLog, - Calls, IDStatusCache, LocationdClients, InteractionC, - FirefoxHistory, FirefoxFavicon, Contacts, Manifest, Whatsapp] + WebkitResourceLoadStatistics, Calls, IDStatusCache, LocationdClients, + InteractionC, FirefoxHistory, FirefoxFavicon, Contacts, Manifest, Whatsapp] FS_MODULES = [IOSVersionHistory, SafariHistory, SafariFavicon, SafariBrowserState, WebkitIndexedDB, WebkitLocalStorage, WebkitSafariViewService, - WebkitSessionResourceLog, Datausage, Netusage, ChromeHistory, + WebkitResourceLoadStatistics, WebkitSessionResourceLog, + Datausage, Netusage, ChromeHistory, ChromeFavicon, Calls, IDStatusCache, SMS, SMSAttachments, LocationdClients, InteractionC, FirefoxHistory, FirefoxFavicon, Contacts, CacheFiles, Whatsapp, Filesystem] diff --git a/mvt/ios/modules/fs/base.py b/mvt/ios/modules/fs/base.py index e90dbfc..c0dcee3 100644 --- a/mvt/ios/modules/fs/base.py +++ b/mvt/ios/modules/fs/base.py @@ -20,6 +20,22 @@ class IOSExtraction(MVTModule): is_fs_dump = False is_sysdiagnose = False + def _is_database_malformed(self, file_path): + # Check if the database is malformed. + conn = sqlite3.connect(file_path) + cur = conn.cursor() + + try: + recover = False + cur.execute("SELECT name FROM sqlite_master WHERE type='table';") + except sqlite3.DatabaseError as e: + if "database disk image is malformed" in str(e): + recover = True + finally: + conn.close() + + return recover + def _recover_database(self, file_path): """Tries to recover a malformed database by running a .clone command. :param file_path: Path to the malformed database file. @@ -49,6 +65,7 @@ class IOSExtraction(MVTModule): """Try to locate the module's database file from either an iTunes backup or a full filesystem dump. :param backup_id: iTunes backup database file's ID (or hash). + :param root_paths: Glob patterns for files to seek in filesystem dump. """ file_path = None # First we check if the was an explicit file path specified. @@ -84,18 +101,5 @@ class IOSExtraction(MVTModule): else: raise DatabaseNotFoundError("Unable to find the module's database file") - # Check if the database is corrupted. - conn = sqlite3.connect(self.file_path) - cur = conn.cursor() - - try: - recover = False - cur.execute("SELECT name FROM sqlite_master WHERE type='table';") - except sqlite3.DatabaseError as e: - if "database disk image is malformed" in str(e): - recover = True - finally: - conn.close() - - if recover: + if self._is_database_malformed(self.file_path): self._recover_database(self.file_path) diff --git a/mvt/ios/modules/fs/webkit_resource_load_statistics.py b/mvt/ios/modules/fs/webkit_resource_load_statistics.py new file mode 100644 index 0000000..2c15c65 --- /dev/null +++ b/mvt/ios/modules/fs/webkit_resource_load_statistics.py @@ -0,0 +1,98 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021 MVT Project Developers. +# See the file 'LICENSE' for usage and copying permissions, or find a copy at +# https://github.com/mvt-project/mvt/blob/main/LICENSE + +import os +import sqlite3 +import datetime + +from .base import IOSExtraction + +from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso + +WEBKIT_RESOURCELOADSTATICS_BACKUP_RELPATH = "Library/WebKit/WebsiteData/ResourceLoadStatistics/observations.db" +WEBKIT_RESOURCELOADSTATICS_ROOT_PATHS = [ + "private/var/mobile/Containers/Data/Application/*/Library/WebKit/WebsiteData/ResourceLoadStatistics/observations.db", + "private/var/mobile/Containers/Data/Application/*/SystemData/com.apple.SafariViewService/Library/WebKit/WebsiteData/observations.db", +] + +class WebkitResourceLoadStatistics(IOSExtraction): + """This module extracts records from WebKit ResourceLoadStatistics observations.db. + """ + # TODO: Add serialize(). + + 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 check_indicators(self): + if not self.indicators: + return + + self.detected = {} + for key, items in self.results.items(): + for item in items: + if self.indicators.check_domain(item["registrable_domain"]): + if key not in self.detected: + self.detected[key] = [item,] + else: + self.detected[key].append(item) + + def _process_observations_db(self, db_path, key): + self.log.info("Found WebKit ResourceLoadStatistics observations.db file at path %s", db_path) + + if self._is_database_malformed(db_path): + self._recover_database(db_path) + + conn = sqlite3.connect(db_path) + cur = conn.cursor() + + try: + cur.execute("SELECT * from ObservedDomains;") + except sqlite3.OperationalError: + return + + if not key in self.results: + self.results[key] = [] + + for row in cur: + self.results[key].append(dict( + domain_id=row[0], + registrable_domain=row[1], + last_seen=row[2], + had_user_interaction=bool(row[3]), + # TODO: Fix isodate. + last_seen_isodate=convert_timestamp_to_iso(datetime.datetime.utcfromtimestamp(int(row[2]))), + )) + + if len(self.results[key]) > 0: + self.log.info("Extracted a total of %d records from %s", len(self.results[key]), db_path) + + def run(self): + self.results = {} + + if self.is_backup: + manifest_db_path = os.path.join(self.base_folder, "Manifest.db") + if not os.path.exists(manifest_db_path): + self.log.info("Unable to search for WebKit observations.db files in backup because of missing Manifest.db") + return + + try: + conn = sqlite3.connect(manifest_db_path) + cur = conn.cursor() + cur.execute("SELECT fileID, domain FROM Files WHERE relativePath = ?;", (WEBKIT_RESOURCELOADSTATICS_BACKUP_RELPATH,)) + except Exception as e: + self.log.error("Unable to search for WebKit observations.db files in backup because of failed query to Manifest.db: %s", e) + + for row in cur: + file_id = row[0] + domain = row[1] + db_path = os.path.join(self.base_folder, file_id[0:2], file_id) + if os.path.exists(db_path): + self._process_observations_db(db_path=db_path, key=f"{domain}/{WEBKIT_RESOURCELOADSTATICS_BACKUP_RELPATH}") + elif self.is_fs_dump: + for db_path in self._find_paths(WEBKIT_RESOURCELOADSTATICS_ROOT_PATHS): + self._process_observations_db(db_path=db_path, key=os.path.relpath(db_path, self.base_folder)) From 490fb123025fc86bdb6586fed82e3ecc2cd29629 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 11:08:32 +0200 Subject: [PATCH 54/99] Refactored creation of output folders --- mvt/android/cli.py | 36 ++++++++++++++++++------------------ mvt/ios/cli.py | 26 ++++++++++++++------------ 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/mvt/android/cli.py b/mvt/android/cli.py index 11e9070..ceae580 100644 --- a/mvt/android/cli.py +++ b/mvt/android/cli.py @@ -47,7 +47,7 @@ def cli(): @click.option("--koodous", "-k", is_flag=True, help="Check packages on Koodous") @click.option("--all-checks", "-A", is_flag=True, help="Run all available checks") @click.option("--output", "-o", type=click.Path(exists=False), - help="Specify a path to a folder where you want to store JSON results") + help="Specify a path to a folder where you want to store the APKs") @click.option("--from-file", "-f", type=click.Path(exists=True), help="Instead of acquiring from phone, load an existing packages.json file for lookups (mainly for debug purposes)") def download_apks(all_apks, virustotal, koodous, all_checks, output, from_file): @@ -55,11 +55,11 @@ def download_apks(all_apks, virustotal, koodous, all_checks, output, from_file): if from_file: download = DownloadAPKs.from_json(from_file) else: - try: - os.makedirs(output) - except OSError as e: - if e.errno != errno.EEXIST: - log.critical("You need to specify a writable output folder (with --output, -o) when extracting APKs from a device") + if output and not os.path.exists(output): + try: + os.makedirs(output) + except Exception as e: + log.critical("Unable to create output folder %s: %s", output, e) sys.exit(-1) download = DownloadAPKs(output_folder=output, all_apks=all_apks) @@ -99,6 +99,13 @@ def check_adb(iocs, output, list_modules, module): log.info("Checking Android through adb bridge") + if output and not os.path.exists(output): + try: + os.makedirs(output) + except Exception as e: + log.critical("Unable to create output folder %s: %s", output, e) + sys.exit(-1) + if iocs: # Pre-load indicators for performance reasons. log.info("Loading indicators from provided file at %s", iocs) @@ -121,13 +128,6 @@ def check_adb(iocs, output, list_modules, module): timeline_detected.extend(m.timeline_detected) if output: - try: - os.makedirs(output) - except OSError as e: - if e.errno != errno.EEXIST: - log.critical("You need to specify a writable output folder (with --output, -o) to log the details observed from ADB") - sys.exit(-1) - if len(timeline) > 0: save_timeline(timeline, os.path.join(output, "timeline.csv")) if len(timeline_detected) > 0: @@ -143,11 +143,11 @@ def check_adb(iocs, output, list_modules, module): def check_backup(iocs, output, backup_path): log.info("Checking ADB backup located at: %s", backup_path) - try: - os.makedirs(output) - except OSError as e: - if e.errno != errno.EEXIST: - log.critical("You need to specify a writable output folder (with --output, -o) when analysing the ADB backup") + if output and not os.path.exists(output): + try: + os.makedirs(output) + except Exception as e: + log.critical("Unable to create output folder %s: %s", output, e) sys.exit(-1) if iocs: diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index bc5d022..3e265bf 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -82,6 +82,13 @@ def check_backup(iocs, output, fast, backup_path, list_modules, module): log.info("Checking iTunes backup located at: %s", backup_path) + if output and not os.path.exists(output): + try: + os.makedirs(output) + except Exception as e: + log.critical("Unable to create output folder %s: %s", output, e) + sys.exit(-1) + if iocs: # Pre-load indicators for performance reasons. log.info("Loading indicators from provided file at: %s", iocs) @@ -106,12 +113,6 @@ def check_backup(iocs, output, fast, backup_path, list_modules, module): timeline_detected.extend(m.timeline_detected) if output: - try: - os.makedirs(output) - except OSError as e: - if e.errno != errno.EEXIST: - log.critical("You need to specify a writable output folder (with --output, -o) when analysing the backup") - sys.exit(-1) if len(timeline) > 0: save_timeline(timeline, os.path.join(output, "timeline.csv")) if len(timeline_detected) > 0: @@ -138,6 +139,13 @@ def check_fs(iocs, output, fast, dump_path, list_modules, module): log.info("Checking filesystem dump located at: %s", dump_path) + if output and not os.path.exists(output): + try: + os.makedirs(output) + except Exception as e: + log.critical("Unable to create output folder %s: %s", output, e) + sys.exit(-1) + if iocs: # Pre-load indicators for performance reasons. log.info("Loading indicators from provided file at: %s", iocs) @@ -163,12 +171,6 @@ def check_fs(iocs, output, fast, dump_path, list_modules, module): timeline_detected.extend(m.timeline_detected) if output: - try: - os.makedirs(output) - except OSError as e: - if e.errno != errno.EEXIST: - log.critical("You need to specify a writable output folder (with --output, -o) when analysing the file system") - sys.exit(-1) if len(timeline) > 0: save_timeline(timeline, os.path.join(output, "timeline.csv")) if len(timeline_detected) > 0: From 18ed58cbf9757df3adca859fe40d3a9fd4237839 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 11:19:15 +0200 Subject: [PATCH 55/99] Removed unused dependency --- mvt/android/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mvt/android/cli.py b/mvt/android/cli.py index ceae580..e7aaa2d 100644 --- a/mvt/android/cli.py +++ b/mvt/android/cli.py @@ -3,7 +3,6 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import errno import os import sys import click From 7f361fb60051f7db71fdc6b4c46ee0b57e50996c Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 11:19:20 +0200 Subject: [PATCH 56/99] Create python-package.yml --- .github/workflows/python-package.yml | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/python-package.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..efccd59 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,40 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python package + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.7, 3.8, 3.9] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest From d490344142b30df57ff6bef76f4384cb99031608 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 11:19:51 +0200 Subject: [PATCH 57/99] Removed lint --- .github/workflows/lint-python.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/lint-python.yml diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml deleted file mode 100644 index d5863ed..0000000 --- a/.github/workflows/lint-python.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Python Lint -on: [pull_request] -jobs: - lint_python: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - run: pip install bandit black codespell flake8 isort mypy pytest pyupgrade safety - - run: bandit --recursive --skip B108,B112,B404,B602 . - - run: black --check . || true - - run: codespell - - run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - - run: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --show-source --statistics - - run: isort --check-only --profile black . || true - - run: pip install -r requirements.txt || true - - run: mypy --install-types --non-interactive . || true - - run: pytest . || true - - run: pytest --doctest-modules . || true - - run: shopt -s globstar && pyupgrade --py36-plus **/*.py || true - - run: safety check From 22d090569cfbe11baa7278c198a9d3a3871646cd Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 11:20:59 +0200 Subject: [PATCH 58/99] Disabled pytest until unit tests are available --- .github/workflows/python-package.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index efccd59..9a0fa1d 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -35,6 +35,6 @@ jobs: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with pytest - run: | - pytest + # - name: Test with pytest + # run: | + # pytest From abcbefe3594ff2f9609c978f81c1ca0434b87d7b Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 11:39:43 +0200 Subject: [PATCH 59/99] Added safety checks to workflow --- .github/workflows/python-package.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 9a0fa1d..05b33b1 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -27,7 +27,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install flake8 pytest + python -m pip install flake8 pytest safety if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint with flake8 run: | @@ -35,6 +35,9 @@ jobs: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + -name: Safety checks + run: safety check + # - name: Test with pytest # run: | # pytest From c966eea7e658a061f59c91ed00c00cf887525938 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 11:40:09 +0200 Subject: [PATCH 60/99] Sorted imports --- dev/mvt-android | 1 + dev/mvt-ios | 1 + mvt/android/cli.py | 10 +++-- mvt/android/download_apks.py | 4 +- mvt/android/lookups/koodous.py | 8 ++-- mvt/android/lookups/virustotal.py | 9 +++-- mvt/android/modules/adb/__init__.py | 4 +- mvt/android/modules/adb/base.py | 9 +++-- mvt/android/modules/adb/chrome_history.py | 5 ++- .../modules/adb/dumpsys_batterystats.py | 2 +- mvt/android/modules/adb/dumpsys_packages.py | 2 +- mvt/android/modules/adb/dumpsys_procstats.py | 2 +- mvt/android/modules/adb/packages.py | 3 +- mvt/android/modules/adb/rootbinaries.py | 3 +- mvt/android/modules/adb/sms.py | 5 ++- mvt/android/modules/adb/whatsapp.py | 7 ++-- mvt/android/modules/backup/sms.py | 6 +-- mvt/common/indicators.py | 3 +- mvt/common/module.py | 8 ++-- mvt/common/options.py | 3 +- mvt/common/utils.py | 5 ++- mvt/ios/cli.py | 7 ++-- mvt/ios/decrypt.py | 5 ++- mvt/ios/modules/fs/__init__.py | 38 +++++++++---------- mvt/ios/modules/fs/base.py | 7 ++-- mvt/ios/modules/fs/cache_files.py | 1 + mvt/ios/modules/fs/calls.py | 1 + mvt/ios/modules/fs/chrome_favicon.py | 3 +- mvt/ios/modules/fs/chrome_history.py | 3 +- mvt/ios/modules/fs/filesystem.py | 3 +- mvt/ios/modules/fs/firefox_favicon.py | 2 +- mvt/ios/modules/fs/firefox_history.py | 2 +- mvt/ios/modules/fs/idstatuscache.py | 7 ++-- mvt/ios/modules/fs/locationd.py | 3 +- mvt/ios/modules/fs/manifest.py | 6 ++- mvt/ios/modules/fs/net_base.py | 3 +- mvt/ios/modules/fs/safari_browserstate.py | 7 ++-- mvt/ios/modules/fs/sms.py | 4 +- mvt/ios/modules/fs/sms_attachments.py | 4 +- mvt/ios/modules/fs/version_history.py | 2 +- mvt/ios/modules/fs/webkit_base.py | 5 ++- .../fs/webkit_resource_load_statistics.py | 6 +-- .../modules/fs/webkit_session_resource_log.py | 3 +- mvt/ios/modules/fs/whatsapp.py | 5 ++- setup.py | 3 +- 45 files changed, 133 insertions(+), 97 deletions(-) diff --git a/dev/mvt-android b/dev/mvt-android index 4ac6a2f..bd1ef58 100755 --- a/dev/mvt-android +++ b/dev/mvt-android @@ -10,4 +10,5 @@ import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from mvt import android + android.cli() diff --git a/dev/mvt-ios b/dev/mvt-ios index ab89c1e..70a016e 100755 --- a/dev/mvt-ios +++ b/dev/mvt-ios @@ -10,4 +10,5 @@ import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from mvt import ios + ios.cli() diff --git a/mvt/android/cli.py b/mvt/android/cli.py index e7aaa2d..50c3762 100644 --- a/mvt/android/cli.py +++ b/mvt/android/cli.py @@ -3,15 +3,17 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os -import sys -import click import argparse import logging +import os +import sys + +import click from rich.logging import RichHandler -from mvt.common.module import run_module, save_timeline from mvt.common.indicators import Indicators +from mvt.common.module import run_module, save_timeline + from .download_apks import DownloadAPKs from .lookups.koodous import koodous_lookup from .lookups.virustotal import virustotal_lookup diff --git a/mvt/android/download_apks.py b/mvt/android/download_apks.py index 6213ef9..e2855f6 100644 --- a/mvt/android/download_apks.py +++ b/mvt/android/download_apks.py @@ -3,13 +3,15 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import json import logging +import os + import pkg_resources from tqdm import tqdm from mvt.common.utils import get_sha256_from_file_path + from .modules.adb.base import AndroidExtraction log = logging.getLogger(__name__) diff --git a/mvt/android/lookups/koodous.py b/mvt/android/lookups/koodous.py index 78f5c0f..7fb94e9 100644 --- a/mvt/android/lookups/koodous.py +++ b/mvt/android/lookups/koodous.py @@ -3,13 +3,13 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import requests import logging -from rich.text import Text -from rich.table import Table -from rich.progress import track +import requests from rich.console import Console +from rich.progress import track +from rich.table import Table +from rich.text import Text log = logging.getLogger(__name__) diff --git a/mvt/android/lookups/virustotal.py b/mvt/android/lookups/virustotal.py index eaac727..d1f4440 100644 --- a/mvt/android/lookups/virustotal.py +++ b/mvt/android/lookups/virustotal.py @@ -3,12 +3,13 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import requests import logging -from rich.text import Text -from rich.table import Table -from rich.progress import track + +import requests from rich.console import Console +from rich.progress import track +from rich.table import Table +from rich.text import Text log = logging.getLogger(__name__) diff --git a/mvt/android/modules/adb/__init__.py b/mvt/android/modules/adb/__init__.py index ddca6b4..175d81e 100644 --- a/mvt/android/modules/adb/__init__.py +++ b/mvt/android/modules/adb/__init__.py @@ -7,11 +7,11 @@ from .chrome_history import ChromeHistory from .dumpsys_batterystats import DumpsysBatterystats from .dumpsys_packages import DumpsysPackages from .dumpsys_procstats import DumpsysProcstats +from .packages import Packages from .processes import Processes +from .rootbinaries import RootBinaries from .sms import SMS from .whatsapp import Whatsapp -from .packages import Packages -from .rootbinaries import RootBinaries ADB_MODULES = [ChromeHistory, SMS, Whatsapp, Processes, DumpsysBatterystats, DumpsysProcstats, diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index aa0c0a7..92853a9 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -3,18 +3,19 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import logging import os import random import string import sys -import time -import logging import tempfile +import time + from adb_shell.adb_device import AdbDeviceUsb from adb_shell.auth.keygen import keygen, write_public_keyfile from adb_shell.auth.sign_pythonrsa import PythonRSASigner -from adb_shell.exceptions import DeviceAuthError, AdbCommandFailureException -from usb1 import USBErrorBusy, USBErrorAccess +from adb_shell.exceptions import AdbCommandFailureException, DeviceAuthError +from usb1 import USBErrorAccess, USBErrorBusy from mvt.common.module import MVTModule diff --git a/mvt/android/modules/adb/chrome_history.py b/mvt/android/modules/adb/chrome_history.py index 6b223d2..211130d 100644 --- a/mvt/android/modules/adb/chrome_history.py +++ b/mvt/android/modules/adb/chrome_history.py @@ -3,11 +3,12 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import logging import os import sqlite3 -import logging -from mvt.common.utils import convert_chrometime_to_unix, convert_timestamp_to_iso +from mvt.common.utils import (convert_chrometime_to_unix, + convert_timestamp_to_iso) from .base import AndroidExtraction diff --git a/mvt/android/modules/adb/dumpsys_batterystats.py b/mvt/android/modules/adb/dumpsys_batterystats.py index ad90027..d272af8 100644 --- a/mvt/android/modules/adb/dumpsys_batterystats.py +++ b/mvt/android/modules/adb/dumpsys_batterystats.py @@ -3,8 +3,8 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import logging +import os from .base import AndroidExtraction diff --git a/mvt/android/modules/adb/dumpsys_packages.py b/mvt/android/modules/adb/dumpsys_packages.py index 45fbf68..7324e44 100644 --- a/mvt/android/modules/adb/dumpsys_packages.py +++ b/mvt/android/modules/adb/dumpsys_packages.py @@ -3,8 +3,8 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import logging +import os from .base import AndroidExtraction diff --git a/mvt/android/modules/adb/dumpsys_procstats.py b/mvt/android/modules/adb/dumpsys_procstats.py index 416f01f..051762c 100644 --- a/mvt/android/modules/adb/dumpsys_procstats.py +++ b/mvt/android/modules/adb/dumpsys_procstats.py @@ -3,8 +3,8 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import logging +import os from .base import AndroidExtraction diff --git a/mvt/android/modules/adb/packages.py b/mvt/android/modules/adb/packages.py index dcdc375..a28b2b7 100644 --- a/mvt/android/modules/adb/packages.py +++ b/mvt/android/modules/adb/packages.py @@ -3,8 +3,9 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import logging +import os + import pkg_resources from .base import AndroidExtraction diff --git a/mvt/android/modules/adb/rootbinaries.py b/mvt/android/modules/adb/rootbinaries.py index b591603..babce47 100644 --- a/mvt/android/modules/adb/rootbinaries.py +++ b/mvt/android/modules/adb/rootbinaries.py @@ -3,8 +3,9 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import logging +import os + import pkg_resources from .base import AndroidExtraction diff --git a/mvt/android/modules/adb/sms.py b/mvt/android/modules/adb/sms.py index dcaccac..c5d491e 100644 --- a/mvt/android/modules/adb/sms.py +++ b/mvt/android/modules/adb/sms.py @@ -3,12 +3,13 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import logging import os import sqlite3 -import logging + +from mvt.common.utils import check_for_links, convert_timestamp_to_iso from .base import AndroidExtraction -from mvt.common.utils import convert_timestamp_to_iso, check_for_links log = logging.getLogger(__name__) diff --git a/mvt/android/modules/adb/whatsapp.py b/mvt/android/modules/adb/whatsapp.py index f8eacbd..3345072 100644 --- a/mvt/android/modules/adb/whatsapp.py +++ b/mvt/android/modules/adb/whatsapp.py @@ -3,13 +3,14 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import base64 +import logging import os import sqlite3 -import logging -import base64 + +from mvt.common.utils import check_for_links, convert_timestamp_to_iso from .base import AndroidExtraction -from mvt.common.utils import convert_timestamp_to_iso, check_for_links log = logging.getLogger(__name__) diff --git a/mvt/android/modules/backup/sms.py b/mvt/android/modules/backup/sms.py index 86cc54b..5426498 100644 --- a/mvt/android/modules/backup/sms.py +++ b/mvt/android/modules/backup/sms.py @@ -3,13 +3,13 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import json +import os import zlib from mvt.common.module import MVTModule -from mvt.common.utils import check_for_links -from mvt.common.utils import convert_timestamp_to_iso +from mvt.common.utils import check_for_links, convert_timestamp_to_iso + class SMS(MVTModule): diff --git a/mvt/common/indicators.py b/mvt/common/indicators.py index 9532295..8ca747d 100644 --- a/mvt/common/indicators.py +++ b/mvt/common/indicators.py @@ -3,11 +3,12 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import json +import os from .url import URL + class Indicators: """This class is used to parse indicators from a STIX2 file and provide functions to compare extracted artifacts to the indicators. diff --git a/mvt/common/module.py b/mvt/common/module.py index cf391e3..2975812 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -3,16 +3,18 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import io -import os -import re import csv import glob +import io import logging +import os +import re + import simplejson as json from .indicators import Indicators + class DatabaseNotFoundError(Exception): pass diff --git a/mvt/common/options.py b/mvt/common/options.py index bc0842b..794c704 100644 --- a/mvt/common/options.py +++ b/mvt/common/options.py @@ -5,7 +5,8 @@ # From: https://gist.github.com/stanchan/bce1c2d030c76fe9223b5ff6ad0f03db -from click import command, option, Option, UsageError +from click import Option, UsageError, command, option + class MutuallyExclusiveOption(Option): """This class extends click to support mutually exclusive options. diff --git a/mvt/common/utils.py b/mvt/common/utils.py index 6805704..b3061b9 100644 --- a/mvt/common/utils.py +++ b/mvt/common/utils.py @@ -3,10 +3,11 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os -import re import datetime import hashlib +import os +import re + def convert_mactime_to_unix(timestamp, from_2001=True): """Converts Mac Standard Time to a Unix timestamp. diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 3e265bf..22a2981 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -4,16 +4,17 @@ # https://github.com/mvt-project/mvt/blob/main/LICENSE import errno +import logging import os import sys -import click import tarfile -import logging + +import click from rich.logging import RichHandler +from mvt.common.indicators import Indicators from mvt.common.module import run_module, save_timeline from mvt.common.options import MutuallyExclusiveOption -from mvt.common.indicators import Indicators from .decrypt import DecryptBackup from .modules.fs import BACKUP_MODULES, FS_MODULES diff --git a/mvt/ios/decrypt.py b/mvt/ios/decrypt.py index 7b1acb0..385a3a9 100644 --- a/mvt/ios/decrypt.py +++ b/mvt/ios/decrypt.py @@ -3,11 +3,12 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import binascii +import logging import os import shutil import sqlite3 -import logging -import binascii + from iOSbackup import iOSbackup log = logging.getLogger(__name__) diff --git a/mvt/ios/modules/fs/__init__.py b/mvt/ios/modules/fs/__init__.py index 1c72e74..94620f3 100644 --- a/mvt/ios/modules/fs/__init__.py +++ b/mvt/ios/modules/fs/__init__.py @@ -3,32 +3,32 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -from .manifest import Manifest -from .contacts import Contacts -from .net_netusage import Netusage -from .net_datausage import Datausage -from .safari_history import SafariHistory -from .safari_favicon import SafariFavicon -from .safari_browserstate import SafariBrowserState -from .webkit_indexeddb import WebkitIndexedDB -from .webkit_localstorage import WebkitLocalStorage -from .webkit_safariviewservice import WebkitSafariViewService -from .webkit_session_resource_log import WebkitSessionResourceLog -from .webkit_resource_load_statistics import WebkitResourceLoadStatistics -from .chrome_history import ChromeHistory +from .cache_files import CacheFiles +from .calls import Calls from .chrome_favicon import ChromeFavicon -from .firefox_history import FirefoxHistory +from .chrome_history import ChromeHistory +from .contacts import Contacts +from .filesystem import Filesystem from .firefox_favicon import FirefoxFavicon -from .version_history import IOSVersionHistory +from .firefox_history import FirefoxHistory from .idstatuscache import IDStatusCache -from .locationd import LocationdClients from .interactionc import InteractionC +from .locationd import LocationdClients +from .manifest import Manifest +from .net_datausage import Datausage +from .net_netusage import Netusage +from .safari_browserstate import SafariBrowserState +from .safari_favicon import SafariFavicon +from .safari_history import SafariHistory from .sms import SMS from .sms_attachments import SMSAttachments -from .calls import Calls +from .version_history import IOSVersionHistory +from .webkit_indexeddb import WebkitIndexedDB +from .webkit_localstorage import WebkitLocalStorage +from .webkit_resource_load_statistics import WebkitResourceLoadStatistics +from .webkit_safariviewservice import WebkitSafariViewService +from .webkit_session_resource_log import WebkitSessionResourceLog from .whatsapp import Whatsapp -from .cache_files import CacheFiles -from .filesystem import Filesystem BACKUP_MODULES = [SafariBrowserState, SafariHistory, Datausage, SMS, SMSAttachments, ChromeHistory, ChromeFavicon, WebkitSessionResourceLog, diff --git a/mvt/ios/modules/fs/base.py b/mvt/ios/modules/fs/base.py index c0dcee3..a93a770 100644 --- a/mvt/ios/modules/fs/base.py +++ b/mvt/ios/modules/fs/base.py @@ -3,15 +3,16 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import glob import io import os -import glob import shutil import sqlite3 import subprocess -from mvt.common.module import MVTModule -from mvt.common.module import DatabaseNotFoundError, DatabaseCorruptedError +from mvt.common.module import (DatabaseCorruptedError, DatabaseNotFoundError, + MVTModule) + class IOSExtraction(MVTModule): """This class provides a base for all iOS filesystem/backup extraction modules.""" diff --git a/mvt/ios/modules/fs/cache_files.py b/mvt/ios/modules/fs/cache_files.py index ef35931..eff3421 100644 --- a/mvt/ios/modules/fs/cache_files.py +++ b/mvt/ios/modules/fs/cache_files.py @@ -8,6 +8,7 @@ import sqlite3 from .base import IOSExtraction + class CacheFiles(IOSExtraction): def __init__(self, file_path=None, base_folder=None, output_folder=None, diff --git a/mvt/ios/modules/fs/calls.py b/mvt/ios/modules/fs/calls.py index 2ed3de5..974fcb2 100644 --- a/mvt/ios/modules/fs/calls.py +++ b/mvt/ios/modules/fs/calls.py @@ -6,6 +6,7 @@ import sqlite3 from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso + from .base import IOSExtraction CALLS_BACKUP_IDS = [ diff --git a/mvt/ios/modules/fs/chrome_favicon.py b/mvt/ios/modules/fs/chrome_favicon.py index 666e7c8..dc50b8a 100644 --- a/mvt/ios/modules/fs/chrome_favicon.py +++ b/mvt/ios/modules/fs/chrome_favicon.py @@ -5,7 +5,8 @@ import sqlite3 -from mvt.common.utils import convert_chrometime_to_unix, convert_timestamp_to_iso +from mvt.common.utils import (convert_chrometime_to_unix, + convert_timestamp_to_iso) from .base import IOSExtraction diff --git a/mvt/ios/modules/fs/chrome_history.py b/mvt/ios/modules/fs/chrome_history.py index 557b196..39a878a 100644 --- a/mvt/ios/modules/fs/chrome_history.py +++ b/mvt/ios/modules/fs/chrome_history.py @@ -5,7 +5,8 @@ import sqlite3 -from mvt.common.utils import convert_chrometime_to_unix, convert_timestamp_to_iso +from mvt.common.utils import (convert_chrometime_to_unix, + convert_timestamp_to_iso) from .base import IOSExtraction diff --git a/mvt/ios/modules/fs/filesystem.py b/mvt/ios/modules/fs/filesystem.py index fc170f6..6198516 100644 --- a/mvt/ios/modules/fs/filesystem.py +++ b/mvt/ios/modules/fs/filesystem.py @@ -3,13 +3,14 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import datetime +import os from mvt.common.utils import convert_timestamp_to_iso from .base import IOSExtraction + class Filesystem(IOSExtraction): """This module extracts creation and modification date of files from a full file-system dump.""" diff --git a/mvt/ios/modules/fs/firefox_favicon.py b/mvt/ios/modules/fs/firefox_favicon.py index ad0c09d..225c7a6 100644 --- a/mvt/ios/modules/fs/firefox_favicon.py +++ b/mvt/ios/modules/fs/firefox_favicon.py @@ -4,8 +4,8 @@ # https://github.com/mvt-project/mvt/blob/main/LICENSE import sqlite3 - from datetime import datetime + from mvt.common.url import URL from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso diff --git a/mvt/ios/modules/fs/firefox_history.py b/mvt/ios/modules/fs/firefox_history.py index 8d9bff9..d6dcfcd 100644 --- a/mvt/ios/modules/fs/firefox_history.py +++ b/mvt/ios/modules/fs/firefox_history.py @@ -4,8 +4,8 @@ # https://github.com/mvt-project/mvt/blob/main/LICENSE import sqlite3 - from datetime import datetime + from mvt.common.url import URL from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso diff --git a/mvt/ios/modules/fs/idstatuscache.py b/mvt/ios/modules/fs/idstatuscache.py index b447e58..443b623 100644 --- a/mvt/ios/modules/fs/idstatuscache.py +++ b/mvt/ios/modules/fs/idstatuscache.py @@ -3,10 +3,11 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os -import glob -import biplist import collections +import glob +import os + +import biplist from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso diff --git a/mvt/ios/modules/fs/locationd.py b/mvt/ios/modules/fs/locationd.py index 799bb36..62fcd77 100644 --- a/mvt/ios/modules/fs/locationd.py +++ b/mvt/ios/modules/fs/locationd.py @@ -3,8 +3,9 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import glob +import os + import biplist from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso diff --git a/mvt/ios/modules/fs/manifest.py b/mvt/ios/modules/fs/manifest.py index 64484ec..67b38a7 100644 --- a/mvt/ios/modules/fs/manifest.py +++ b/mvt/ios/modules/fs/manifest.py @@ -3,16 +3,18 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import datetime import io import os -import biplist import sqlite3 -import datetime + +import biplist from mvt.common.utils import convert_timestamp_to_iso from .base import IOSExtraction + class Manifest(IOSExtraction): """This module extracts information from a backup Manifest.db file.""" diff --git a/mvt/ios/modules/fs/net_base.py b/mvt/ios/modules/fs/net_base.py index 01c385c..c9b1dd6 100644 --- a/mvt/ios/modules/fs/net_base.py +++ b/mvt/ios/modules/fs/net_base.py @@ -3,14 +3,15 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import sqlite3 import operator +import sqlite3 from pathlib import Path from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso from .base import IOSExtraction + class NetBase(IOSExtraction): """This class provides a base for DataUsage and NetUsage extraction modules.""" diff --git a/mvt/ios/modules/fs/safari_browserstate.py b/mvt/ios/modules/fs/safari_browserstate.py index 98d6bcb..8e79ce7 100644 --- a/mvt/ios/modules/fs/safari_browserstate.py +++ b/mvt/ios/modules/fs/safari_browserstate.py @@ -4,11 +4,12 @@ # https://github.com/mvt-project/mvt/blob/main/LICENSE import io -import biplist import sqlite3 -from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso -from mvt.common.utils import keys_bytes_to_string +import biplist + +from mvt.common.utils import (convert_mactime_to_unix, + convert_timestamp_to_iso, keys_bytes_to_string) from .base import IOSExtraction diff --git a/mvt/ios/modules/fs/sms.py b/mvt/ios/modules/fs/sms.py index 1208074..b94dbed 100644 --- a/mvt/ios/modules/fs/sms.py +++ b/mvt/ios/modules/fs/sms.py @@ -6,8 +6,8 @@ import sqlite3 from base64 import b64encode -from mvt.common.utils import check_for_links -from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso +from mvt.common.utils import (check_for_links, convert_mactime_to_unix, + convert_timestamp_to_iso) from .base import IOSExtraction diff --git a/mvt/ios/modules/fs/sms_attachments.py b/mvt/ios/modules/fs/sms_attachments.py index 57cb8c1..89f0cf3 100644 --- a/mvt/ios/modules/fs/sms_attachments.py +++ b/mvt/ios/modules/fs/sms_attachments.py @@ -6,8 +6,8 @@ import sqlite3 from base64 import b64encode -from mvt.common.utils import check_for_links -from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso +from mvt.common.utils import (check_for_links, convert_mactime_to_unix, + convert_timestamp_to_iso) from .base import IOSExtraction diff --git a/mvt/ios/modules/fs/version_history.py b/mvt/ios/modules/fs/version_history.py index 70dc054..a9bf2cd 100644 --- a/mvt/ios/modules/fs/version_history.py +++ b/mvt/ios/modules/fs/version_history.py @@ -3,8 +3,8 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import json import datetime +import json from mvt.common.utils import convert_timestamp_to_iso diff --git a/mvt/ios/modules/fs/webkit_base.py b/mvt/ios/modules/fs/webkit_base.py index 2adb717..ff1c8ef 100644 --- a/mvt/ios/modules/fs/webkit_base.py +++ b/mvt/ios/modules/fs/webkit_base.py @@ -3,12 +3,13 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import datetime +import os + +from mvt.common.utils import convert_timestamp_to_iso from .base import IOSExtraction -from mvt.common.utils import convert_timestamp_to_iso class WebkitBase(IOSExtraction): """This class is a base for other WebKit-related modules.""" diff --git a/mvt/ios/modules/fs/webkit_resource_load_statistics.py b/mvt/ios/modules/fs/webkit_resource_load_statistics.py index 2c15c65..4ac91ec 100644 --- a/mvt/ios/modules/fs/webkit_resource_load_statistics.py +++ b/mvt/ios/modules/fs/webkit_resource_load_statistics.py @@ -3,14 +3,14 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +import datetime import os import sqlite3 -import datetime - -from .base import IOSExtraction from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso +from .base import IOSExtraction + WEBKIT_RESOURCELOADSTATICS_BACKUP_RELPATH = "Library/WebKit/WebsiteData/ResourceLoadStatistics/observations.db" WEBKIT_RESOURCELOADSTATICS_ROOT_PATHS = [ "private/var/mobile/Containers/Data/Application/*/Library/WebKit/WebsiteData/ResourceLoadStatistics/observations.db", diff --git a/mvt/ios/modules/fs/webkit_session_resource_log.py b/mvt/ios/modules/fs/webkit_session_resource_log.py index b8d36ff..5f5f7b3 100644 --- a/mvt/ios/modules/fs/webkit_session_resource_log.py +++ b/mvt/ios/modules/fs/webkit_session_resource_log.py @@ -3,8 +3,9 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import os import glob +import os + import biplist from mvt.common.utils import convert_timestamp_to_iso diff --git a/mvt/ios/modules/fs/whatsapp.py b/mvt/ios/modules/fs/whatsapp.py index fb962bb..afdd9e7 100644 --- a/mvt/ios/modules/fs/whatsapp.py +++ b/mvt/ios/modules/fs/whatsapp.py @@ -3,10 +3,11 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import sqlite3 import logging +import sqlite3 -from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso, check_for_links +from mvt.common.utils import (check_for_links, convert_mactime_to_unix, + convert_timestamp_to_iso) from .base import IOSExtraction diff --git a/setup.py b/setup.py index 2768cea..edbf8cc 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,8 @@ # https://github.com/mvt-project/mvt/blob/main/LICENSE import os -from setuptools import setup, find_packages + +from setuptools import find_packages, setup __package_name__ = "mvt" __version__ = "1.0.13" From eda1976518e3b35795ac5c7f44a613bc1dbd7589 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 11:43:52 +0200 Subject: [PATCH 61/99] Added missing space in workflow file --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 05b33b1..06d6413 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -35,7 +35,7 @@ jobs: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - -name: Safety checks + - name: Safety checks run: safety check # - name: Test with pytest From 9a6c4d251e4776d82fe36dcf15b850eb4f5a0106 Mon Sep 17 00:00:00 2001 From: opsec-infosec <74446520+opsec-infosec@users.noreply.github.com> Date: Fri, 30 Jul 2021 16:13:06 +0400 Subject: [PATCH 62/99] Update Dockerfile Add sqlite3 to Dockerfile for extraction of SMS messages --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index d7278a6..bb467a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ RUN apt install -y build-essential \ libplist-dev \ libusbmuxd-dev \ libssl-dev \ + sqlite3 \ pkg-config # Clean up From 9451da4514a96b87e8cb75060ed7780dcb98edf1 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 17:56:05 +0200 Subject: [PATCH 63/99] Removed duplicate title --- docs/docker.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index 2596fa1..bb2d1bf 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -1,5 +1,3 @@ -## Using Docker - Using Docker simplifies having all the required dependencies and tools (including most recent versions of [libimobiledevice](https://libimobiledevice.org)) readily installed. Install Docker following the [official documentation](https://docs.docker.com/get-docker/). From 99e80fd942fda019290c0bd45c3f2277681e11ea Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 17:59:17 +0200 Subject: [PATCH 64/99] Updated documentation links --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c4da85c..300bc41 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # Mobile Verification Toolkit [![](https://img.shields.io/pypi/v/mvt)](https://pypi.org/project/mvt/) -[![](https://img.shields.io/badge/docs-blue.svg)](https://mvt.readthedocs.io) +[![](https://img.shields.io/badge/docs-blue.svg)](https://docs.mvt.re) Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and automate the process of gathering forensic traces helpful to identify a potential compromise of Android and iOS devices. @@ -21,9 +21,9 @@ MVT can be installed from sources or conveniently using: pip3 install mvt ``` -You will need some dependencies, so please check the [documentation](https://mvt.readthedocs.io/en/latest/install.html). +You will need some dependencies, so please check the [documentation](https://docs.mvt.re/en/latest/install.html). -Alternatively, you can decide to run MVT and all relevant tools through a [Docker container](https://mvt.readthedocs.io/en/latest/docker.html). +Alternatively, you can decide to run MVT and all relevant tools through a [Docker container](https://docs.mvt.re/en/latest/docker.html). ## Usage @@ -39,7 +39,7 @@ MVT provides two commands `mvt-ios` and `mvt-android` with the following subcomm * `check-backup`: Check an Android Backup * `download-apks`: Download all or non-safelisted installed APKs -Check out [the documentation to see how to use them](https://mvt.readthedocs.io/en/latest/). +Check out [the documentation to see how to use them](https://docs.mvt.re/). ## License From 632409c81d2207e451a680a14d26101b5118bd20 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 18:08:52 +0200 Subject: [PATCH 65/99] Using consistent constant names --- mvt/android/modules/adb/base.py | 2 +- mvt/android/modules/adb/sms.py | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index cccce7e..2ddb95d 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -124,7 +124,7 @@ class AndroidExtraction(MVTModule): self._adb_connect() # Check if we have root, if not raise an Exception. self._adb_root_or_die() - + return bool(self._adb_command_as_root(f"[ ! -f {file} ] || echo 1")) def _adb_download(self, remote_path, local_path, progress_callback=None, retry_root=True): diff --git a/mvt/android/modules/adb/sms.py b/mvt/android/modules/adb/sms.py index f00108c..eab9031 100644 --- a/mvt/android/modules/adb/sms.py +++ b/mvt/android/modules/adb/sms.py @@ -13,9 +13,8 @@ from .base import AndroidExtraction log = logging.getLogger(__name__) -SMS_PATH = "data/data/com.google.android.apps.messaging/databases/bugle_db" -SMS_PATH_2 = "data/data/com.android.providers.telephony/databases/mmssms.db" -sql_command_1 = """ +SMS_BUGLE_PATH = "data/data/com.google.android.apps.messaging/databases/bugle_db" +SMS_BUGLE_QUERY = """ SELECT ppl.normalized_destination AS number, p.timestamp AS timestamp, @@ -29,7 +28,9 @@ WHERE (m.conversation_id = c._id) AND (cp.conversation_id = c._id) AND (cp.participant_id = ppl._id); """ -sql_command_2 = """ + +SMS_MMSSMS_PATH = "data/data/com.android.providers.telephony/databases/mmssms.db" +SMS_MMSMS_QUERY = """ SELECT address AS number, date_sent AS timestamp, @@ -76,9 +77,9 @@ class SMS(AndroidExtraction): cur = conn.cursor() if (self.SMS_DB_TYPE == 1): - cur.execute(sql_command_1) + cur.execute(SMS_BUGLE_QUERY) elif (self.SMS_DB_TYPE == 2): - cur.execute(sql_command_2) + cur.execute(SMS_MMSMS_QUERY) names = [description[0] for description in cur.description] @@ -103,12 +104,12 @@ class SMS(AndroidExtraction): def run(self): # Checking the SMS database path try: - if (self._adb_check_file_exists(os.path.join("/", SMS_PATH))): + if (self._adb_check_file_exists(os.path.join("/", SMS_BUGLE_PATH))): self.SMS_DB_TYPE = 1 - self._adb_process_file(os.path.join("/", SMS_PATH), self._parse_db) - elif (self._adb_check_file_exists(os.path.join("/", SMS_PATH_2))): + self._adb_process_file(os.path.join("/", SMS_BUGLE_PATH), self._parse_db) + elif (self._adb_check_file_exists(os.path.join("/", SMS_MMSSMS_PATH))): self.SMS_DB_TYPE = 2 - self._adb_process_file(os.path.join("/", SMS_PATH_2), self._parse_db) + self._adb_process_file(os.path.join("/", SMS_MMSSMS_PATH), self._parse_db) else: self.log.error("No SMS database found") except Exception as e: From b264ae946d39a4b6584da50b8f3bd2f5086c2503 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 18:46:45 +0200 Subject: [PATCH 66/99] Refactored to include functionality in existing DecryptBackup class --- mvt/ios/cli.py | 16 ++++++++------ mvt/ios/decrypt.py | 44 +++++++++++++++++++++++++++----------- mvt/ios/keyutils.py | 52 --------------------------------------------- 3 files changed, 42 insertions(+), 70 deletions(-) delete mode 100644 mvt/ios/keyutils.py diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 49d3552..78b1793 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -17,7 +17,6 @@ from mvt.common.module import run_module, save_timeline from mvt.common.options import MutuallyExclusiveOption from .decrypt import DecryptBackup -from .keyutils import KeyUtils from .modules.fs import BACKUP_MODULES, FS_MODULES # Setup logging using Rich. @@ -55,6 +54,7 @@ def cli(): @click.argument("BACKUP_PATH", type=click.Path(exists=True)) def decrypt_backup(destination, password, key_file, backup_path): backup = DecryptBackup(backup_path, destination) + if password: backup.decrypt_with_password(password) elif key_file: @@ -63,13 +63,16 @@ def decrypt_backup(destination, password, key_file, backup_path): raise click.ClickException("Missing required option. Specify either " "--password or --key-file.") + backup.process_backup() + + #============================================================================== # Command: extract-key #============================================================================== @cli.command("extract-key", help="Extract decryption key from an iTunes backup") @click.option("--password", "-p", help="Password to use to decrypt the backup", - prompt="Backup password", + prompt="Enter backup password", hide_input=True, prompt_required=False, required=True) @click.option("--key-file", "-k", help="Key file to be written (if unset, will print to STDOUT)", @@ -77,11 +80,12 @@ def decrypt_backup(destination, password, key_file, backup_path): type=click.Path(exists=False, file_okay=True, dir_okay=False, writable=True)) @click.argument("BACKUP_PATH", type=click.Path(exists=True)) def extract_key(password, backup_path, key_file): - key_utils = KeyUtils(password, backup_path) + backup = DecryptBackup(backup_path) + backup.decrypt_with_password(password) + backup.get_key() + if key_file: - key_utils.write_key(key_file) - else: - key_utils.print_key() + backup.write_key(key_file) #============================================================================== diff --git a/mvt/ios/decrypt.py b/mvt/ios/decrypt.py index 385a3a9..db93bc8 100644 --- a/mvt/ios/decrypt.py +++ b/mvt/ios/decrypt.py @@ -18,7 +18,7 @@ class DecryptBackup: using either a password or a key file. """ - def __init__(self, backup_path, dest_path): + def __init__(self, backup_path, dest_path=None): """Decrypts an encrypted iOS backup. :param backup_path: Path to the encrypted backup folder :param dest_path: Path to the folder where to store the decrypted backup @@ -26,8 +26,12 @@ class DecryptBackup: self.backup_path = backup_path self.dest_path = dest_path self._backup = None + self._decryption_key = None + + def process_backup(self): + if not os.path.exists(self.dest_path): + os.makedirs(self.dest_path) - def _process_backup(self): manifest_path = os.path.join(self.dest_path, "Manifest.db") # We extract a decrypted Manifest.db. self._backup.getManifestDB() @@ -69,9 +73,6 @@ class DecryptBackup: """ log.info("Decrypting iOS backup at path %s with password", self.backup_path) - if not os.path.exists(self.dest_path): - os.makedirs(self.dest_path) - try: self._backup = iOSbackup(udid=os.path.basename(self.backup_path), cleartextpassword=password, @@ -79,9 +80,6 @@ class DecryptBackup: except Exception as e: log.exception(e) log.critical("Failed to decrypt backup. Did you provide the correct password?") - return - else: - self._process_backup() def decrypt_with_key_file(self, key_file): """Decrypts an encrypted iOS backup using a key file. @@ -90,9 +88,6 @@ class DecryptBackup: log.info("Decrypting iOS backup at path %s with key file %s", self.backup_path, key_file) - if not os.path.exists(self.dest_path): - os.makedirs(self.dest_path) - with open(key_file, "rb") as handle: key_bytes = handle.read() @@ -109,6 +104,31 @@ class DecryptBackup: except Exception as e: log.exception(e) log.critical("Failed to decrypt backup. Did you provide the correct key file?") + + def get_key(self): + """Retrieve and prints the encryption key. + """ + if not self._backup: + return + + self._decryption_key = self._backup.getDecryptionKey() + log.info("Derived decryption key for backup at path %s is: \"%s\"", + self.backup_path, self._decryption_key) + + def write_key(self, key_path): + """Save extracted key to file. + :param key_path: Path to the file where to write the derived decryption key. + """ + if not self._decryption_key: + return + + try: + with open(key_path, 'w') as handle: + handle.write(self._decryption_key) + except Exception as e: + log.exception(e) + log.critical("Failed to write key to file: %s", key_path) return else: - self._process_backup() + log.info("Wrote decryption key to file: %s. This file is equivalent to a plaintext password. Keep it safe!", + key_path) diff --git a/mvt/ios/keyutils.py b/mvt/ios/keyutils.py deleted file mode 100644 index 361df45..0000000 --- a/mvt/ios/keyutils.py +++ /dev/null @@ -1,52 +0,0 @@ -import os -import logging -from iOSbackup import iOSbackup - -log = logging.getLogger(__name__) - -class KeyUtils: - """This class provides functions to extract a backup key from a password. - """ - - def __init__(self, password, backup_path): - """Generates a key file for an iOS backup. - :param password: Backup encryption password - :param key_file: Path to the file where to store the generated key file - """ - self.password = password - self.backup_path = backup_path - self._backup = None - - def get_key(self): - try: - self._backup = iOSbackup(udid=os.path.basename(self.backup_path), - cleartextpassword=self.password, - backuproot=os.path.dirname(self.backup_path)) - except Exception as e: - log.exception(e) - log.critical("Failed to decrypt backup. Did you provide the correct password?") - return - else: - self.decryption_key = self._backup.getDecryptionKey() - log.info("Extracted decryption key.") - - def print_key(self): - self.get_key() - log.info("Decryption key for backup at path %s is:\n %s", - self.backup_path, self.decryption_key) - - def write_key(self, key_file): - self.get_key() - - try: - with open(key_file, 'w') as writer: - writer.write(self.decryption_key) - except Exception as e: - log.exception(e) - log.critical("Failed to write key file.") - return - else: - log.info("Wrote decryption key for backup at path %s to file %s", - self.backup_path, key_file) - log.warn("The file %s is equivalent to a plaintext password. Keep this file safe!", - key_file) From d7f29a4e886c5000626cf2443382eccb8b04bcf8 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 30 Jul 2021 21:26:48 +0200 Subject: [PATCH 67/99] Updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e385ffe..eeb6d45 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # Mobile Verification Toolkit [![](https://img.shields.io/pypi/v/mvt)](https://pypi.org/project/mvt/) -[![](https://img.shields.io/badge/docs-blue.svg)](https://docs.mvt.re) +[![Documentation Status](https://readthedocs.org/projects/mvt/badge/?version=latest)](https://docs.mvt.re/en/latest/?badge=latest) Mobile Verification Toolkit (MVT) is a collection of utilities to simplify and automate the process of gathering forensic traces helpful to identify a potential compromise of Android and iOS devices. From 53adc053382d3d6943172723c1ce48504b1e1b06 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 30 Jul 2021 02:02:59 -0400 Subject: [PATCH 68/99] mvt-ios decrypt-backup: Enable pulling password from the environment. Specifying the password on the command line with `--password XXX` leaves the password itself visible to any process on the machine which can scan the process table. On some systems (including common GNU/Linux distributions) this visibility is possible by default. This change should make it possible to offer the password without putting it into the process table; rather, the user puts the password in the environment, and specifies the name of the environment variable, like so: ``` $ export MVT_IOS_BACKUP_PASSWORD=WronglySconeRoundnessUnruffled $ mvt-ios decrypt-backup -d /path/to/dest /path/to/data/XXXXXXXX-YYYYYYYYYYYYYYY/ $ unset MVT_IOS_BACKUP_PASSWORD ``` or you can do so using a prefixed env var, as described in the updated check.md documentation. --- docs/ios/backup/check.md | 11 ++++++----- mvt/ios/cli.py | 24 +++++++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/docs/ios/backup/check.md b/docs/ios/backup/check.md index 551205c..b51e074 100644 --- a/docs/ios/backup/check.md +++ b/docs/ios/backup/check.md @@ -41,9 +41,10 @@ In case you have an encrypted backup, you will need to decrypt it first. This ca -d, --destination TEXT Path to the folder where to store the decrypted backup [required] - -p, --password TEXT Password to use to decrypt the backup NOTE: This - argument is mutually exclusive with arguments: - [key_file]. + -p, --password TEXT Password to use to decrypt the backup (or, set + MVT_IOS_BACKUP_PASSWORD environment variable) + NOTE: This argument is mutually exclusive with + arguments: [key_file]. -k, --key-file PATH File containing raw encryption key to use to decrypt the backup NOTE: This argument is mutually exclusive @@ -51,10 +52,10 @@ In case you have an encrypted backup, you will need to decrypt it first. This ca --help Show this message and exit. -You can specify either a password via command-line or pass a key file, and you need to specify a destination path where the decrypted backup will be stored. If `-p` is omitted, MVT will ask for a password. Following is an example usage of `decrypt-backup`: +You can specify the password in the environment variable `MVT_IOS_BACKUP_PASSWORD`, or via command-line argument, or you can pass a key file. You need to specify a destination path where the decrypted backup will be stored. If a password cannot be found and no key file is specified, MVT will ask for a password. Following is an example usage of `decrypt-backup` sending the password via an environment variable: ```bash -mvt-ios decrypt-backup -p password -d /path/to/decrypted /path/to/backup +MVT_IOS_BACKUP_PASSWORD="mypassword" mvt-ios decrypt-backup -d /path/to/decrypted /path/to/backup ``` ## Run `mvt-ios` on a Backup diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 78b1793..ceabd94 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -4,6 +4,7 @@ # https://github.com/mvt-project/mvt/blob/main/LICENSE import errno +import getpass import logging import os import sys @@ -28,6 +29,8 @@ log = logging.getLogger(__name__) # Help messages of repeating options. OUTPUT_HELP_MESSAGE = "Specify a path to a folder where you want to store JSON results" +# set this environment variable to a password if needed +PASSWD_ENV = 'MVT_IOS_BACKUP_PASSWORD' #============================================================================== # Main @@ -44,8 +47,7 @@ def cli(): @click.option("--destination", "-d", required=True, help="Path to the folder where to store the decrypted backup") @click.option("--password", "-p", cls=MutuallyExclusiveOption, - help="Password to use to decrypt the backup", - prompt="Enter backup password", hide_input=True, prompt_required=False, + help=f"Password to use to decrypt the backup (or, set {PASSWD_ENV} environment variable)", mutually_exclusive=["key_file"]) @click.option("--key-file", "-k", cls=MutuallyExclusiveOption, type=click.Path(exists=True), @@ -55,13 +57,21 @@ def cli(): def decrypt_backup(destination, password, key_file, backup_path): backup = DecryptBackup(backup_path, destination) - if password: - backup.decrypt_with_password(password) - elif key_file: + if key_file: + if PASSWD_ENV in os.environ: + log.warning(f"Ignoring {PASSWD_ENV} environment variable, using --key-file '{key_file}' instead") backup.decrypt_with_key_file(key_file) + elif password: + log.warning("Your password may be visible in the process table because it was supplied on the command line!") + if PASSWD_ENV in os.environ: + log.warning(f"Ignoring {PASSWD_ENV} environment variable, using --password argument instead") + backup.decrypt_with_password(password) + elif PASSWD_ENV in os.environ: + log.info(f"Using password from {PASSWD_ENV} environment variable") + backup.decrypt_with_password(os.environ[PASSWD_ENV]) else: - raise click.ClickException("Missing required option. Specify either " - "--password or --key-file.") + sekrit = getpass.getpass(prompt='Enter iOS backup password: ') + backup.decrypt_with_password(sekrit) backup.process_backup() From 270e002f1b1de9d17d4477c85fbb6c46c437934b Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 30 Jul 2021 23:08:57 -0400 Subject: [PATCH 69/99] mvt-ios extract-key: enable pulling password from the environment This enables automated use of extract-key without requiring a password to be placed in the command line, where it might leak. --- mvt/ios/cli.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index ceabd94..c8897c2 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -81,9 +81,7 @@ def decrypt_backup(destination, password, key_file, backup_path): #============================================================================== @cli.command("extract-key", help="Extract decryption key from an iTunes backup") @click.option("--password", "-p", - help="Password to use to decrypt the backup", - prompt="Enter backup password", - hide_input=True, prompt_required=False, required=True) + help=f"Password to use to decrypt the backup (or, set {PASSWD_ENV} environment variable)") @click.option("--key-file", "-k", help="Key file to be written (if unset, will print to STDOUT)", required=False, @@ -91,6 +89,17 @@ def decrypt_backup(destination, password, key_file, backup_path): @click.argument("BACKUP_PATH", type=click.Path(exists=True)) def extract_key(password, backup_path, key_file): backup = DecryptBackup(backup_path) + + if password: + log.warning("Your password may be visible in the process table because it was supplied on the command line!") + if PASSWD_ENV in os.environ: + log.warning(f"Ignoring {PASSWD_ENV} environment variable, using --password argument instead") + elif PASSWD_ENV in os.environ: + log.info(f"Using password from {PASSWD_ENV} environment variable") + password = os.environ[PASSWD_ENV] + else: + password = getpass.getpass(prompt='Enter iOS backup password: ') + backup.decrypt_with_password(password) backup.get_key() From 7b1b31f7be3d357cbb4f401b3e0944f8aaf7d136 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 30 Jul 2021 23:44:44 -0400 Subject: [PATCH 70/99] Update libimobiledevice docs about backup password reset In this stage, the user is likely to want to run `idevicebackup2` in interactive mode, so clearly specify the `-i` flag in the right place (just dropping `-i` at the end of the command does not work as expected -- i think `idevicebackup2 backup encryption on -i` tries to set the password to `-i`). More importantly, note that resetting the password by resetting all the settings runs a risk of removing some of the forensic information. Etienne identified a file that he thought was wiped as a result of this in the call this morning, but I don't remember which file it was. Maybe `id_status_cache.json` ? If you have more concrete info, please add it here too! --- docs/ios/backup/libimobiledevice.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/ios/backup/libimobiledevice.md b/docs/ios/backup/libimobiledevice.md index 892717c..9b40903 100644 --- a/docs/ios/backup/libimobiledevice.md +++ b/docs/ios/backup/libimobiledevice.md @@ -3,10 +3,12 @@ If you have correctly [installed libimobiledevice](../install.md) you can easily generate an iTunes backup using the `idevicebackup2` tool included in the suite. First, you might want to ensure that backup encryption is enabled (**note: encrypted backup contain more data than unencrypted backups**): ```bash -idevicebackup2 backup encryption on +idevicebackup2 -i backup encryption on ``` -Note that if a backup password was previously set on this device, you might need to use the same or change it. You can try changing password using `idevicebackup2 backup changepw` or resetting the password by resetting only the settings through the iPhone's Settings app. +Note that if a backup password was previously set on this device, you might need to use the same or change it. You can try changing password using `idevicebackup2 -i backup changepw`, or by turning off encryption (`idevicebackup2 -i backup encryption off`) and turning it back on again. + +If that fails, as a last resort you can try resetting the password by [resetting all the settings through the iPhone's Settings app](https://support.apple.com/en-us/HT205220), via `Settings » General » Reset » Reset All Settings`. Note that resetting the settings through the iPhone's Settings app may wipe some of the files that contain useful forensic traces, so try to reset the device's password using `idevicebackup2` as described above first. Once ready, you can proceed performing the backup: From 59206fc450931d8d464dcf8f1a49e442ea5413c9 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 31 Jul 2021 00:46:36 -0400 Subject: [PATCH 71/99] Describe how to use and find IOCs This offers generic documentation, to show how MVT can be used with arbitrary STIX-formatted IOCs, while still pointing users at some known-to-be-useful sample files. --- docs/iocs.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docs/iocs.md diff --git a/docs/iocs.md b/docs/iocs.md new file mode 100644 index 0000000..78f0295 --- /dev/null +++ b/docs/iocs.md @@ -0,0 +1,32 @@ +# Indicators of Compromise (IOCs) + +MVT uses [Structured Threat Information Expression (STIX)](https://oasis-open.github.io/cti-documentation/stix/intro.html) files to identify potential traces of compromise. + +These indicators of compromise are contained in a file with a particular structure of [JSON](https://en.wikipedia.org/wiki/JSON) with the `.stix2` or `.json` extensions. + +For example, after extracting iOS forensics data from an iPhone using `mvt-ios check-backup` or `mvt-ios check-fs`, you might run: + +```bash +mvt-ios check-iocs --iocs ~/iocs/wintermute.stix2 /path/to/iphone/output/ +``` + +Or, with data from an android backup: + +```bash +mvt-android check-backup --iocs ~/iocs/wintermute.stix2 /path/to/android/backup/ +``` + +If you're looking for indicators of compromise for a specific piece of malware or adversary, please ask investigators or anti-malware researchers who have the relevant expertise for a STIX file. + +## Known repositories of STIX IOCs + +We currently know of the following STIX-formatted IOCs: + +- [Cyber Threat Intelligence Technical Committee's sample STIX 2.1 Threat reports](https://oasis-open.github.io/cti-documentation/stix/examples#stix-21-threat-reports): the "JSON representation" column offers sample STIX-formatted IOCs for: + - [APT1](https://en.wikipedia.org/wiki/APT1) ([STIX](https://oasis-open.github.io/cti-documentation/examples/example_json/apt1.json)), + - [Poison Ivy](https://www.cyber.nj.gov/threat-center/threat-profiles/trojan-variants/poison-ivy/) ([STIX](https://oasis-open.github.io/cti-documentation/examples/example_json/poisonivy.json)), and + - [IMDDOS](https://www.coresecurity.com/publication/imddos-botnet-discovery-and-analysis)([STIX](https://gist.github.com/rjsmitre/79775df68b0d1c7c0985b4fe7f115586/raw/d5d2a3e7b4ae52ff7153a8b7b5b57dd066611803/imddos.json)) +- The [Amnesty International investigations repository](https://github.com/AmnestyTech/investigations) contains STIX-formatted IOCs for: + - [Pegasus](https://en.wikipedia.org/wiki/Pegasus_(spyware)) + +Please [open an issue](https://github.com/mvt-project/mvt/issues/) to suggest new sources of STIX-formatted IOCs. From 5b5b065bc49e82d6c99bb23c7fcb123ddc6e97e6 Mon Sep 17 00:00:00 2001 From: Nex Date: Sat, 31 Jul 2021 10:05:41 +0200 Subject: [PATCH 72/99] Updated doc page on IOCs --- docs/iocs.md | 24 ++++++++++++------------ mkdocs.yml | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/iocs.md b/docs/iocs.md index 78f0295..5a903ce 100644 --- a/docs/iocs.md +++ b/docs/iocs.md @@ -4,29 +4,29 @@ MVT uses [Structured Threat Information Expression (STIX)](https://oasis-open.gi These indicators of compromise are contained in a file with a particular structure of [JSON](https://en.wikipedia.org/wiki/JSON) with the `.stix2` or `.json` extensions. -For example, after extracting iOS forensics data from an iPhone using `mvt-ios check-backup` or `mvt-ios check-fs`, you might run: +You can indicate a path to a STIX2 indicators file when checking iPhone backups or filesystem dumps. For example: ```bash -mvt-ios check-iocs --iocs ~/iocs/wintermute.stix2 /path/to/iphone/output/ +mvt-ios check-backup --iocs ~/ios/malware.stix2 --output /path/to/iphone/output /path/to/backup ``` -Or, with data from an android backup: +Or, with data from an Android backup: ```bash -mvt-android check-backup --iocs ~/iocs/wintermute.stix2 /path/to/android/backup/ +mvt-android check-backup --iocs ~/iocs/malware.stix2 /path/to/android/backup/ +``` + +After extracting forensics data from a device, you are also able to compare it with any STIX2 file you indicate: + +```bash +mvt-ios check-iocs --iocs ~/iocs/malware.stix2 /path/to/iphone/output/ ``` If you're looking for indicators of compromise for a specific piece of malware or adversary, please ask investigators or anti-malware researchers who have the relevant expertise for a STIX file. -## Known repositories of STIX IOCs +## Known repositories of STIX2 IOCs -We currently know of the following STIX-formatted IOCs: - -- [Cyber Threat Intelligence Technical Committee's sample STIX 2.1 Threat reports](https://oasis-open.github.io/cti-documentation/stix/examples#stix-21-threat-reports): the "JSON representation" column offers sample STIX-formatted IOCs for: - - [APT1](https://en.wikipedia.org/wiki/APT1) ([STIX](https://oasis-open.github.io/cti-documentation/examples/example_json/apt1.json)), - - [Poison Ivy](https://www.cyber.nj.gov/threat-center/threat-profiles/trojan-variants/poison-ivy/) ([STIX](https://oasis-open.github.io/cti-documentation/examples/example_json/poisonivy.json)), and - - [IMDDOS](https://www.coresecurity.com/publication/imddos-botnet-discovery-and-analysis)([STIX](https://gist.github.com/rjsmitre/79775df68b0d1c7c0985b4fe7f115586/raw/d5d2a3e7b4ae52ff7153a8b7b5b57dd066611803/imddos.json)) - The [Amnesty International investigations repository](https://github.com/AmnestyTech/investigations) contains STIX-formatted IOCs for: - - [Pegasus](https://en.wikipedia.org/wiki/Pegasus_(spyware)) + - [Pegasus](https://en.wikipedia.org/wiki/Pegasus_(spyware)) ([STIX2](https://raw.githubusercontent.com/AmnestyTech/investigations/master/2021-07-18_nso/pegasus.stix2)) Please [open an issue](https://github.com/mvt-project/mvt/issues/) to suggest new sources of STIX-formatted IOCs. diff --git a/mkdocs.yml b/mkdocs.yml index 1837158..282b0ef 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -44,4 +44,5 @@ nav: - Android Forensic Methodology: "android/methodology.md" - Check APKs: "android/download_apks.md" - Check an Android Backup: "android/backup.md" + - Indicators of Compromise: "iocs.md" - License: "license.md" From ad9ab1aeba7b5b6ec1c7552486efd49895d5ffb2 Mon Sep 17 00:00:00 2001 From: Nex Date: Sat, 31 Jul 2021 10:13:18 +0200 Subject: [PATCH 73/99] Switched to using rich Prompt --- mvt/ios/cli.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index c8897c2..454fa2e 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -4,7 +4,6 @@ # https://github.com/mvt-project/mvt/blob/main/LICENSE import errno -import getpass import logging import os import sys @@ -12,6 +11,7 @@ import tarfile import click from rich.logging import RichHandler +from rich.prompt import Prompt from mvt.common.indicators import Indicators from mvt.common.module import run_module, save_timeline @@ -29,7 +29,7 @@ log = logging.getLogger(__name__) # Help messages of repeating options. OUTPUT_HELP_MESSAGE = "Specify a path to a folder where you want to store JSON results" -# set this environment variable to a password if needed +# Set this environment variable to a password if needed. PASSWD_ENV = 'MVT_IOS_BACKUP_PASSWORD' #============================================================================== @@ -59,18 +59,21 @@ def decrypt_backup(destination, password, key_file, backup_path): if key_file: if PASSWD_ENV in os.environ: - log.warning(f"Ignoring {PASSWD_ENV} environment variable, using --key-file '{key_file}' instead") + log.info(f"Ignoring {PASSWD_ENV} environment variable, using --key-file '{key_file}' instead") + backup.decrypt_with_key_file(key_file) elif password: - log.warning("Your password may be visible in the process table because it was supplied on the command line!") + log.info("Your password may be visible in the process table because it was supplied on the command line!") + if PASSWD_ENV in os.environ: - log.warning(f"Ignoring {PASSWD_ENV} environment variable, using --password argument instead") + log.info(f"Ignoring {PASSWD_ENV} environment variable, using --password argument instead") + backup.decrypt_with_password(password) elif PASSWD_ENV in os.environ: log.info(f"Using password from {PASSWD_ENV} environment variable") backup.decrypt_with_password(os.environ[PASSWD_ENV]) else: - sekrit = getpass.getpass(prompt='Enter iOS backup password: ') + sekrit = Prompt.ask("Enter backup password") backup.decrypt_with_password(sekrit) backup.process_backup() @@ -91,14 +94,15 @@ def extract_key(password, backup_path, key_file): backup = DecryptBackup(backup_path) if password: - log.warning("Your password may be visible in the process table because it was supplied on the command line!") + log.info("Your password may be visible in the process table because it was supplied on the command line!") + if PASSWD_ENV in os.environ: - log.warning(f"Ignoring {PASSWD_ENV} environment variable, using --password argument instead") + log.info(f"Ignoring {PASSWD_ENV} environment variable, using --password argument instead") elif PASSWD_ENV in os.environ: log.info(f"Using password from {PASSWD_ENV} environment variable") password = os.environ[PASSWD_ENV] else: - password = getpass.getpass(prompt='Enter iOS backup password: ') + password = Prompt.ask("Enter backup password") backup.decrypt_with_password(password) backup.get_key() From 38b7aa60320a8f20e22d5357f267433b80046427 Mon Sep 17 00:00:00 2001 From: Nex Date: Sat, 31 Jul 2021 10:19:38 +0200 Subject: [PATCH 74/99] Updated doc on backup --- docs/ios/backup/libimobiledevice.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/ios/backup/libimobiledevice.md b/docs/ios/backup/libimobiledevice.md index 9b40903..5d05c8a 100644 --- a/docs/ios/backup/libimobiledevice.md +++ b/docs/ios/backup/libimobiledevice.md @@ -8,7 +8,9 @@ idevicebackup2 -i backup encryption on Note that if a backup password was previously set on this device, you might need to use the same or change it. You can try changing password using `idevicebackup2 -i backup changepw`, or by turning off encryption (`idevicebackup2 -i backup encryption off`) and turning it back on again. -If that fails, as a last resort you can try resetting the password by [resetting all the settings through the iPhone's Settings app](https://support.apple.com/en-us/HT205220), via `Settings » General » Reset » Reset All Settings`. Note that resetting the settings through the iPhone's Settings app may wipe some of the files that contain useful forensic traces, so try to reset the device's password using `idevicebackup2` as described above first. +If you are not able to recover or change the password, you should try to disable encryption and obtain an unencrypted backup. + +If all else fails, as a *last resort* you can try resetting the password by [resetting all the settings through the iPhone's Settings app](https://support.apple.com/en-us/HT205220), via `Settings » General » Reset » Reset All Settings`. Note that resetting the settings through the iPhone's Settings app will wipe some of the files that contain useful forensic traces, so try the options explained above first. Once ready, you can proceed performing the backup: From 87ffd9e0030d5eb3e9e6bc5ec4aa0db09f59f219 Mon Sep 17 00:00:00 2001 From: Nex Date: Sat, 31 Jul 2021 10:23:38 +0200 Subject: [PATCH 75/99] Bumped version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index edbf8cc..f634b55 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ import os from setuptools import find_packages, setup __package_name__ = "mvt" -__version__ = "1.0.13" +__version__ = "1.0.14" __description__ = "Mobile Verification Toolkit" this_directory = os.path.abspath(os.path.dirname(__file__)) From bc3634bf309f4903e410cbbde37fc4c9fb948574 Mon Sep 17 00:00:00 2001 From: Nex Date: Sat, 31 Jul 2021 10:27:44 +0200 Subject: [PATCH 76/99] Specifying it is a password prompt --- mvt/ios/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 454fa2e..166044e 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -73,7 +73,7 @@ def decrypt_backup(destination, password, key_file, backup_path): log.info(f"Using password from {PASSWD_ENV} environment variable") backup.decrypt_with_password(os.environ[PASSWD_ENV]) else: - sekrit = Prompt.ask("Enter backup password") + sekrit = Prompt.ask("Enter backup password", password=True) backup.decrypt_with_password(sekrit) backup.process_backup() @@ -102,7 +102,7 @@ def extract_key(password, backup_path, key_file): log.info(f"Using password from {PASSWD_ENV} environment variable") password = os.environ[PASSWD_ENV] else: - password = Prompt.ask("Enter backup password") + password = Prompt.ask("Enter backup password", password=True) backup.decrypt_with_password(password) backup.get_key() From 1650aea24854b0f5019f7569c3fc584341e867e6 Mon Sep 17 00:00:00 2001 From: Nex Date: Sat, 31 Jul 2021 19:48:19 +0200 Subject: [PATCH 77/99] pip3 for clarity --- docs/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.md b/docs/install.md index bed90ee..3bd8642 100644 --- a/docs/install.md +++ b/docs/install.md @@ -35,7 +35,7 @@ export PATH=$PATH:~/.local/bin Then you can install MVT directly from [pypi](https://pypi.org/project/mvt/) ```bash -pip install mvt +pip3 install mvt ``` Or from the source code: From eaef75d93124344630ef477144eaada2c178533a Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 1 Aug 2021 13:59:30 +0200 Subject: [PATCH 78/99] Added iPhone models definitions --- mvt/ios/versions.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/mvt/ios/versions.py b/mvt/ios/versions.py index 1828421..6b2e6be 100644 --- a/mvt/ios/versions.py +++ b/mvt/ios/versions.py @@ -3,6 +3,46 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE +IPHONE_MODELS = [ + {"description": "iPhone 4S", "identifier": "iPhone4,1"}, + {"description": "iPhone 5", "identifier": "iPhone5,1"}, + {"description": "iPhone 5", "identifier": "iPhone5,2"}, + {"description": "iPhone 5c", "identifier": "iPhone5,3"}, + {"description": "iPhone 5c", "identifier": "iPhone5,4"}, + {"description": "iPhone 5s", "identifier": "iPhone6,1"}, + {"description": "iPhone 5s", "identifier": "iPhone6,2"}, + {"description": "iPhone 6 Plus", "identifier": "iPhone7,1"}, + {"description": "iPhone 6", "identifier": "iPhone7,2"}, + {"description": "iPhone 6s", "identifier": "iPhone8,1"}, + {"description": "iPhone 6s", "identifier": "iPhone8,1"}, + {"description": "iPhone 6s Plus", "identifier": "iPhone8,2"}, + {"description": "iPhone 6s Plus", "identifier": "iPhone8,2"}, + {"description": "iPhone SE (1st generation)", "identifier": "iPhone8,4"}, + {"description": "iPhone SE (1st generation)", "identifier": "iPhone8,4"}, + {"description": "iPhone 7", "identifier": "iPhone9,1"}, + {"description": "iPhone 7 Plus", "identifier": "iPhone9,2"}, + {"description": "iPhone 7", "identifier": "iPhone9,3"}, + {"description": "iPhone 7 Plus", "identifier": "iPhone9,4"}, + {"description": "iPhone 8", "identifier": "iPhone10,1"}, + {"description": "iPhone 8 Plus", "identifier": "iPhone10,2"}, + {"description": "iPhone X", "identifier": "iPhone10,3"}, + {"description": "iPhone 8", "identifier": "iPhone10,4"}, + {"description": "iPhone 8 Plus", "identifier": "iPhone10,5"}, + {"description": "iPhone X", "identifier": "iPhone10,6"}, + {"description": "iPhone XS", "identifier": "iPhone11,2"}, + {"description": "iPhone XS Max", "identifier": "iPhone11,4"}, + {"description": "iPhone XS Max", "identifier": "iPhone11,6"}, + {"description": "iPhone XR", "identifier": "iPhone11,8"}, + {"description": "iPhone 11", "identifier": "iPhone12,1"}, + {"description": "iPhone 11 Pro", "identifier": "iPhone12,3"}, + {"description": "iPhone 11 Pro Max", "identifier": "iPhone12,5"}, + {"description": "iPhone SE (2nd generation)", "identifier": "iPhone12,8"}, + {"description": "iPhone 12 mini", "identifier": "iPhone13,1"}, + {"description": "iPhone 12", "identifier": "iPhone13,2"}, + {"description": "iPhone 12 Pro", "identifier": "iPhone13,3"}, + {"description": "iPhone 12 Pro Max", "identifier": "iPhone13,4"}, +] + IPHONE_IOS_VERSIONS = [ {"build": "1A543a", "version": "1.0"}, {"build": "1C25", "version": "1.0.1"}, @@ -186,6 +226,11 @@ IPHONE_IOS_VERSIONS = [ {"build": "18G69", "version": "14.7"}, ] +def get_device_desc_from_id(identifier, devices_list=IPHONE_MODELS): + for model in IPHONE_MODELS: + if identifier == model["identifier"]: + return model["description"] + def find_version_by_build(build): build = build.upper() for version in IPHONE_IOS_VERSIONS: From a979b82ec68c46cb4cec78c6b2b1c9c15305a482 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 1 Aug 2021 13:59:59 +0200 Subject: [PATCH 79/99] Bumped version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f634b55..adc0eb0 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ import os from setuptools import find_packages, setup __package_name__ = "mvt" -__version__ = "1.0.14" +__version__ = "1.0.15" __description__ = "Mobile Verification Toolkit" this_directory = os.path.abspath(os.path.dirname(__file__)) From 6f83bf5ae1bc8964ee2e56b02423c17b115f6ba7 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 1 Aug 2021 14:05:21 +0200 Subject: [PATCH 80/99] Removed duplicates --- mvt/ios/versions.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mvt/ios/versions.py b/mvt/ios/versions.py index 6b2e6be..1fab0ee 100644 --- a/mvt/ios/versions.py +++ b/mvt/ios/versions.py @@ -14,10 +14,7 @@ IPHONE_MODELS = [ {"description": "iPhone 6 Plus", "identifier": "iPhone7,1"}, {"description": "iPhone 6", "identifier": "iPhone7,2"}, {"description": "iPhone 6s", "identifier": "iPhone8,1"}, - {"description": "iPhone 6s", "identifier": "iPhone8,1"}, {"description": "iPhone 6s Plus", "identifier": "iPhone8,2"}, - {"description": "iPhone 6s Plus", "identifier": "iPhone8,2"}, - {"description": "iPhone SE (1st generation)", "identifier": "iPhone8,4"}, {"description": "iPhone SE (1st generation)", "identifier": "iPhone8,4"}, {"description": "iPhone 7", "identifier": "iPhone9,1"}, {"description": "iPhone 7 Plus", "identifier": "iPhone9,2"}, From b2e210e91ced1d61be9e22a7deb07b53f2f97ed6 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 1 Aug 2021 14:16:28 +0200 Subject: [PATCH 81/99] Removed unused import --- mvt/ios/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 166044e..4e61341 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -3,7 +3,6 @@ # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE -import errno import logging import os import sys From b2afce5c79b9305028e1ff7bb226a0ef3d9affc5 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 30 Jul 2021 23:59:12 -0400 Subject: [PATCH 82/99] Avoid breakage with paths with unusual names If file_path has any whitespace or shell metacharacters in it, then the invocation of subprocess.call would be likely to break (or even accidentally execute code, depending on how perverse the pathnames are). It's generally a good plan to avoid shell=True for subprocess.call where you can lay out the arguments deliberately in python. This one looks relatively straightforward (but note, i have not tested it, sorry!) Note that if a name has a `"` character in it, we still fail, out of safety reasons. in particular, we want to avoid command injection into the sqlite binary with particularly malicious names that look something like the following: ``` foo.db"; .shell touch should-not-exist; .nullvalue " ``` --- mvt/ios/modules/fs/base.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mvt/ios/modules/fs/base.py b/mvt/ios/modules/fs/base.py index a93a770..4c2d145 100644 --- a/mvt/ios/modules/fs/base.py +++ b/mvt/ios/modules/fs/base.py @@ -50,13 +50,14 @@ class IOSExtraction(MVTModule): if not shutil.which("sqlite3"): raise DatabaseCorruptedError("Unable to recover without sqlite3 binary. Please install sqlite3!") + if '"' in file_path: + raise DatabaseCorruptedError(f"Database at path '{file_path}' is corrupted. unable to recover because it has a quotation mark (\") in its name.") bak_path = f"{file_path}.bak" shutil.move(file_path, bak_path) - cmd = f"sqlite3 {bak_path} '.clone {file_path}'" - ret = subprocess.call(cmd, shell=True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + ret = subprocess.call(['sqlite3', bak_path, f'.clone "{file_path}"'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) if ret != 0: raise DatabaseCorruptedError("Recovery of database failed") From be2468004613fe3a40dcf6c8acebd8c866d635d3 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 1 Aug 2021 19:50:04 +0200 Subject: [PATCH 83/99] Enforcing double quotes --- mvt/ios/cli.py | 2 +- mvt/ios/modules/fs/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index 4e61341..a332596 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -29,7 +29,7 @@ log = logging.getLogger(__name__) OUTPUT_HELP_MESSAGE = "Specify a path to a folder where you want to store JSON results" # Set this environment variable to a password if needed. -PASSWD_ENV = 'MVT_IOS_BACKUP_PASSWORD' +PASSWD_ENV = "MVT_IOS_BACKUP_PASSWORD" #============================================================================== # Main diff --git a/mvt/ios/modules/fs/base.py b/mvt/ios/modules/fs/base.py index 4c2d145..dbc3751 100644 --- a/mvt/ios/modules/fs/base.py +++ b/mvt/ios/modules/fs/base.py @@ -56,7 +56,7 @@ class IOSExtraction(MVTModule): bak_path = f"{file_path}.bak" shutil.move(file_path, bak_path) - ret = subprocess.call(['sqlite3', bak_path, f'.clone "{file_path}"'], + ret = subprocess.call(["sqlite3", bak_path, f".clone '{file_path}'"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if ret != 0: raise DatabaseCorruptedError("Recovery of database failed") From f3c0948283ef12698b6eea8a7cd6797049c824bd Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 1 Aug 2021 19:50:25 +0200 Subject: [PATCH 84/99] Fixing exception name in Manifest module --- mvt/ios/modules/fs/manifest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mvt/ios/modules/fs/manifest.py b/mvt/ios/modules/fs/manifest.py index 67b38a7..bc00180 100644 --- a/mvt/ios/modules/fs/manifest.py +++ b/mvt/ios/modules/fs/manifest.py @@ -11,6 +11,7 @@ import sqlite3 import biplist from mvt.common.utils import convert_timestamp_to_iso +from mvt.common.module import DatabaseNotFoundError from .base import IOSExtraction @@ -91,7 +92,7 @@ class Manifest(IOSExtraction): def run(self): manifest_db_path = os.path.join(self.base_folder, "Manifest.db") if not os.path.isfile(manifest_db_path): - raise FileNotFoundError("Impossible to find the module's database file") + raise DatabaseNotFoundError("Impossible to find the module's database file") self.log.info("Found Manifest.db database at path: %s", manifest_db_path) From bc48dc2cf50cbbff69b74daf715f4927a5460925 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 1 Aug 2021 19:53:20 +0200 Subject: [PATCH 85/99] Fixed import order --- mvt/ios/modules/fs/manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mvt/ios/modules/fs/manifest.py b/mvt/ios/modules/fs/manifest.py index bc00180..7347522 100644 --- a/mvt/ios/modules/fs/manifest.py +++ b/mvt/ios/modules/fs/manifest.py @@ -10,8 +10,8 @@ import sqlite3 import biplist -from mvt.common.utils import convert_timestamp_to_iso from mvt.common.module import DatabaseNotFoundError +from mvt.common.utils import convert_timestamp_to_iso from .base import IOSExtraction From f011fd19e8baa37be2b9df280a14054cb91aec22 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 1 Aug 2021 21:11:08 +0200 Subject: [PATCH 86/99] More explicit copyright and licensing notes --- AUTHORS | 7 +++++++ dev/mvt-android | 6 +++--- dev/mvt-ios | 6 +++--- mvt/__init__.py | 6 +++--- mvt/android/__init__.py | 6 +++--- mvt/android/cli.py | 6 +++--- mvt/android/download_apks.py | 6 +++--- mvt/android/lookups/__init__.py | 6 +++--- mvt/android/lookups/koodous.py | 6 +++--- mvt/android/lookups/virustotal.py | 6 +++--- mvt/android/modules/__init__.py | 6 +++--- mvt/android/modules/adb/__init__.py | 6 +++--- mvt/android/modules/adb/base.py | 6 +++--- mvt/android/modules/adb/chrome_history.py | 6 +++--- mvt/android/modules/adb/dumpsys_batterystats.py | 6 +++--- mvt/android/modules/adb/dumpsys_packages.py | 6 +++--- mvt/android/modules/adb/dumpsys_procstats.py | 6 +++--- mvt/android/modules/adb/packages.py | 6 +++--- mvt/android/modules/adb/processes.py | 6 +++--- mvt/android/modules/adb/rootbinaries.py | 6 +++--- mvt/android/modules/adb/sms.py | 6 +++--- mvt/android/modules/adb/whatsapp.py | 6 +++--- mvt/android/modules/backup/__init__.py | 6 +++--- mvt/android/modules/backup/sms.py | 6 +++--- mvt/common/__init__.py | 6 +++--- mvt/common/indicators.py | 6 +++--- mvt/common/module.py | 6 +++--- mvt/common/options.py | 6 +++--- mvt/common/url.py | 6 +++--- mvt/common/utils.py | 6 +++--- mvt/ios/__init__.py | 6 +++--- mvt/ios/cli.py | 6 +++--- mvt/ios/decrypt.py | 6 +++--- mvt/ios/modules/__init__.py | 6 +++--- mvt/ios/modules/fs/__init__.py | 6 +++--- mvt/ios/modules/fs/base.py | 6 +++--- mvt/ios/modules/fs/cache_files.py | 6 +++--- mvt/ios/modules/fs/calls.py | 6 +++--- mvt/ios/modules/fs/chrome_favicon.py | 6 +++--- mvt/ios/modules/fs/chrome_history.py | 6 +++--- mvt/ios/modules/fs/contacts.py | 6 +++--- mvt/ios/modules/fs/filesystem.py | 6 +++--- mvt/ios/modules/fs/firefox_favicon.py | 6 +++--- mvt/ios/modules/fs/firefox_history.py | 6 +++--- mvt/ios/modules/fs/idstatuscache.py | 6 +++--- mvt/ios/modules/fs/interactionc.py | 6 +++--- mvt/ios/modules/fs/locationd.py | 6 +++--- mvt/ios/modules/fs/manifest.py | 6 +++--- mvt/ios/modules/fs/net_base.py | 6 +++--- mvt/ios/modules/fs/net_datausage.py | 6 +++--- mvt/ios/modules/fs/net_netusage.py | 6 +++--- mvt/ios/modules/fs/safari_browserstate.py | 6 +++--- mvt/ios/modules/fs/safari_favicon.py | 6 +++--- mvt/ios/modules/fs/safari_history.py | 6 +++--- mvt/ios/modules/fs/sms.py | 6 +++--- mvt/ios/modules/fs/sms_attachments.py | 6 +++--- mvt/ios/modules/fs/version_history.py | 6 +++--- mvt/ios/modules/fs/webkit_base.py | 6 +++--- mvt/ios/modules/fs/webkit_indexeddb.py | 6 +++--- mvt/ios/modules/fs/webkit_localstorage.py | 6 +++--- mvt/ios/modules/fs/webkit_resource_load_statistics.py | 6 +++--- mvt/ios/modules/fs/webkit_safariviewservice.py | 6 +++--- mvt/ios/modules/fs/webkit_session_resource_log.py | 6 +++--- mvt/ios/modules/fs/whatsapp.py | 6 +++--- mvt/ios/versions.py | 6 +++--- setup.py | 6 +++--- 66 files changed, 202 insertions(+), 195 deletions(-) create mode 100644 AUTHORS diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..7b28589 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,7 @@ +MVT was originally authored by Claudio Guarnieri . + +For an up-to-date list of all contributors visit: + https://github.com/mvt-project/mvt/graphs/contributors + +Or run: + git shortlog -s -n diff --git a/dev/mvt-android b/dev/mvt-android index bd1ef58..53ae739 100755 --- a/dev/mvt-android +++ b/dev/mvt-android @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import os import sys diff --git a/dev/mvt-ios b/dev/mvt-ios index 70a016e..a01f417 100755 --- a/dev/mvt-ios +++ b/dev/mvt-ios @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import os import sys diff --git a/mvt/__init__.py b/mvt/__init__.py index 2512a63..92def7d 100644 --- a/mvt/__init__.py +++ b/mvt/__init__.py @@ -1,4 +1,4 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ diff --git a/mvt/android/__init__.py b/mvt/android/__init__.py index 2ae7e01..249dc30 100644 --- a/mvt/android/__init__.py +++ b/mvt/android/__init__.py @@ -1,6 +1,6 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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 .cli import cli diff --git a/mvt/android/cli.py b/mvt/android/cli.py index 50c3762..f4f5e97 100644 --- a/mvt/android/cli.py +++ b/mvt/android/cli.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import argparse import logging diff --git a/mvt/android/download_apks.py b/mvt/android/download_apks.py index e2855f6..5c79729 100644 --- a/mvt/android/download_apks.py +++ b/mvt/android/download_apks.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import json import logging diff --git a/mvt/android/lookups/__init__.py b/mvt/android/lookups/__init__.py index 2512a63..92def7d 100644 --- a/mvt/android/lookups/__init__.py +++ b/mvt/android/lookups/__init__.py @@ -1,4 +1,4 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ diff --git a/mvt/android/lookups/koodous.py b/mvt/android/lookups/koodous.py index 7fb94e9..5498bfe 100644 --- a/mvt/android/lookups/koodous.py +++ b/mvt/android/lookups/koodous.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging diff --git a/mvt/android/lookups/virustotal.py b/mvt/android/lookups/virustotal.py index d1f4440..4fb949f 100644 --- a/mvt/android/lookups/virustotal.py +++ b/mvt/android/lookups/virustotal.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging diff --git a/mvt/android/modules/__init__.py b/mvt/android/modules/__init__.py index 2512a63..92def7d 100644 --- a/mvt/android/modules/__init__.py +++ b/mvt/android/modules/__init__.py @@ -1,4 +1,4 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ diff --git a/mvt/android/modules/adb/__init__.py b/mvt/android/modules/adb/__init__.py index 175d81e..e64fbc3 100644 --- a/mvt/android/modules/adb/__init__.py +++ b/mvt/android/modules/adb/__init__.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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 .chrome_history import ChromeHistory from .dumpsys_batterystats import DumpsysBatterystats diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index 2ddb95d..622d509 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging import os diff --git a/mvt/android/modules/adb/chrome_history.py b/mvt/android/modules/adb/chrome_history.py index 211130d..dc61f37 100644 --- a/mvt/android/modules/adb/chrome_history.py +++ b/mvt/android/modules/adb/chrome_history.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging import os diff --git a/mvt/android/modules/adb/dumpsys_batterystats.py b/mvt/android/modules/adb/dumpsys_batterystats.py index d272af8..c4b3cf4 100644 --- a/mvt/android/modules/adb/dumpsys_batterystats.py +++ b/mvt/android/modules/adb/dumpsys_batterystats.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging import os diff --git a/mvt/android/modules/adb/dumpsys_packages.py b/mvt/android/modules/adb/dumpsys_packages.py index 7324e44..becb5f3 100644 --- a/mvt/android/modules/adb/dumpsys_packages.py +++ b/mvt/android/modules/adb/dumpsys_packages.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging import os diff --git a/mvt/android/modules/adb/dumpsys_procstats.py b/mvt/android/modules/adb/dumpsys_procstats.py index 051762c..44e2cd2 100644 --- a/mvt/android/modules/adb/dumpsys_procstats.py +++ b/mvt/android/modules/adb/dumpsys_procstats.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging import os diff --git a/mvt/android/modules/adb/packages.py b/mvt/android/modules/adb/packages.py index a28b2b7..dcfe50f 100644 --- a/mvt/android/modules/adb/packages.py +++ b/mvt/android/modules/adb/packages.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging import os diff --git a/mvt/android/modules/adb/processes.py b/mvt/android/modules/adb/processes.py index 0be051b..135dedb 100644 --- a/mvt/android/modules/adb/processes.py +++ b/mvt/android/modules/adb/processes.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging diff --git a/mvt/android/modules/adb/rootbinaries.py b/mvt/android/modules/adb/rootbinaries.py index babce47..6b9258a 100644 --- a/mvt/android/modules/adb/rootbinaries.py +++ b/mvt/android/modules/adb/rootbinaries.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging import os diff --git a/mvt/android/modules/adb/sms.py b/mvt/android/modules/adb/sms.py index eab9031..bc85265 100644 --- a/mvt/android/modules/adb/sms.py +++ b/mvt/android/modules/adb/sms.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging import os diff --git a/mvt/android/modules/adb/whatsapp.py b/mvt/android/modules/adb/whatsapp.py index 3345072..5c28a7c 100644 --- a/mvt/android/modules/adb/whatsapp.py +++ b/mvt/android/modules/adb/whatsapp.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import base64 import logging diff --git a/mvt/android/modules/backup/__init__.py b/mvt/android/modules/backup/__init__.py index 138b300..10736b7 100644 --- a/mvt/android/modules/backup/__init__.py +++ b/mvt/android/modules/backup/__init__.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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 .sms import SMS diff --git a/mvt/android/modules/backup/sms.py b/mvt/android/modules/backup/sms.py index 5426498..b6919f2 100644 --- a/mvt/android/modules/backup/sms.py +++ b/mvt/android/modules/backup/sms.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import json import os diff --git a/mvt/common/__init__.py b/mvt/common/__init__.py index 2512a63..92def7d 100644 --- a/mvt/common/__init__.py +++ b/mvt/common/__init__.py @@ -1,4 +1,4 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ diff --git a/mvt/common/indicators.py b/mvt/common/indicators.py index 8ca747d..a53dc89 100644 --- a/mvt/common/indicators.py +++ b/mvt/common/indicators.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import json import os diff --git a/mvt/common/module.py b/mvt/common/module.py index 2975812..1989bcb 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import csv import glob diff --git a/mvt/common/options.py b/mvt/common/options.py index 794c704..10d724c 100644 --- a/mvt/common/options.py +++ b/mvt/common/options.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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: https://gist.github.com/stanchan/bce1c2d030c76fe9223b5ff6ad0f03db diff --git a/mvt/common/url.py b/mvt/common/url.py index 57c7723..df04e55 100644 --- a/mvt/common/url.py +++ b/mvt/common/url.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import requests from tld import get_tld diff --git a/mvt/common/utils.py b/mvt/common/utils.py index b3061b9..25d6cb1 100644 --- a/mvt/common/utils.py +++ b/mvt/common/utils.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import datetime import hashlib diff --git a/mvt/ios/__init__.py b/mvt/ios/__init__.py index 2ae7e01..249dc30 100644 --- a/mvt/ios/__init__.py +++ b/mvt/ios/__init__.py @@ -1,6 +1,6 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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 .cli import cli diff --git a/mvt/ios/cli.py b/mvt/ios/cli.py index a332596..c98d9f7 100644 --- a/mvt/ios/cli.py +++ b/mvt/ios/cli.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging import os diff --git a/mvt/ios/decrypt.py b/mvt/ios/decrypt.py index db93bc8..dcecc88 100644 --- a/mvt/ios/decrypt.py +++ b/mvt/ios/decrypt.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import binascii import logging diff --git a/mvt/ios/modules/__init__.py b/mvt/ios/modules/__init__.py index 2512a63..92def7d 100644 --- a/mvt/ios/modules/__init__.py +++ b/mvt/ios/modules/__init__.py @@ -1,4 +1,4 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ diff --git a/mvt/ios/modules/fs/__init__.py b/mvt/ios/modules/fs/__init__.py index 94620f3..7fec68e 100644 --- a/mvt/ios/modules/fs/__init__.py +++ b/mvt/ios/modules/fs/__init__.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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 .cache_files import CacheFiles from .calls import Calls diff --git a/mvt/ios/modules/fs/base.py b/mvt/ios/modules/fs/base.py index dbc3751..918e3ad 100644 --- a/mvt/ios/modules/fs/base.py +++ b/mvt/ios/modules/fs/base.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import glob import io diff --git a/mvt/ios/modules/fs/cache_files.py b/mvt/ios/modules/fs/cache_files.py index eff3421..80fd166 100644 --- a/mvt/ios/modules/fs/cache_files.py +++ b/mvt/ios/modules/fs/cache_files.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import os import sqlite3 diff --git a/mvt/ios/modules/fs/calls.py b/mvt/ios/modules/fs/calls.py index 974fcb2..45c91ea 100644 --- a/mvt/ios/modules/fs/calls.py +++ b/mvt/ios/modules/fs/calls.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 diff --git a/mvt/ios/modules/fs/chrome_favicon.py b/mvt/ios/modules/fs/chrome_favicon.py index dc50b8a..7b6014b 100644 --- a/mvt/ios/modules/fs/chrome_favicon.py +++ b/mvt/ios/modules/fs/chrome_favicon.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 diff --git a/mvt/ios/modules/fs/chrome_history.py b/mvt/ios/modules/fs/chrome_history.py index 39a878a..e0573a5 100644 --- a/mvt/ios/modules/fs/chrome_history.py +++ b/mvt/ios/modules/fs/chrome_history.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 diff --git a/mvt/ios/modules/fs/contacts.py b/mvt/ios/modules/fs/contacts.py index ac99842..cea082d 100644 --- a/mvt/ios/modules/fs/contacts.py +++ b/mvt/ios/modules/fs/contacts.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 diff --git a/mvt/ios/modules/fs/filesystem.py b/mvt/ios/modules/fs/filesystem.py index 6198516..692ee58 100644 --- a/mvt/ios/modules/fs/filesystem.py +++ b/mvt/ios/modules/fs/filesystem.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import datetime import os diff --git a/mvt/ios/modules/fs/firefox_favicon.py b/mvt/ios/modules/fs/firefox_favicon.py index 225c7a6..f946576 100644 --- a/mvt/ios/modules/fs/firefox_favicon.py +++ b/mvt/ios/modules/fs/firefox_favicon.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 from datetime import datetime diff --git a/mvt/ios/modules/fs/firefox_history.py b/mvt/ios/modules/fs/firefox_history.py index d6dcfcd..6690747 100644 --- a/mvt/ios/modules/fs/firefox_history.py +++ b/mvt/ios/modules/fs/firefox_history.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 from datetime import datetime diff --git a/mvt/ios/modules/fs/idstatuscache.py b/mvt/ios/modules/fs/idstatuscache.py index 443b623..cb230e3 100644 --- a/mvt/ios/modules/fs/idstatuscache.py +++ b/mvt/ios/modules/fs/idstatuscache.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import collections import glob diff --git a/mvt/ios/modules/fs/interactionc.py b/mvt/ios/modules/fs/interactionc.py index a685a93..a406d10 100644 --- a/mvt/ios/modules/fs/interactionc.py +++ b/mvt/ios/modules/fs/interactionc.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 from base64 import b64encode diff --git a/mvt/ios/modules/fs/locationd.py b/mvt/ios/modules/fs/locationd.py index 62fcd77..700be01 100644 --- a/mvt/ios/modules/fs/locationd.py +++ b/mvt/ios/modules/fs/locationd.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import glob import os diff --git a/mvt/ios/modules/fs/manifest.py b/mvt/ios/modules/fs/manifest.py index 7347522..8f230bc 100644 --- a/mvt/ios/modules/fs/manifest.py +++ b/mvt/ios/modules/fs/manifest.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import datetime import io diff --git a/mvt/ios/modules/fs/net_base.py b/mvt/ios/modules/fs/net_base.py index c9b1dd6..e1ab93b 100644 --- a/mvt/ios/modules/fs/net_base.py +++ b/mvt/ios/modules/fs/net_base.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import operator import sqlite3 diff --git a/mvt/ios/modules/fs/net_datausage.py b/mvt/ios/modules/fs/net_datausage.py index dc9d319..633bffc 100644 --- a/mvt/ios/modules/fs/net_datausage.py +++ b/mvt/ios/modules/fs/net_datausage.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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 .net_base import NetBase diff --git a/mvt/ios/modules/fs/net_netusage.py b/mvt/ios/modules/fs/net_netusage.py index 5f0e853..72bd55f 100644 --- a/mvt/ios/modules/fs/net_netusage.py +++ b/mvt/ios/modules/fs/net_netusage.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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 .net_base import NetBase diff --git a/mvt/ios/modules/fs/safari_browserstate.py b/mvt/ios/modules/fs/safari_browserstate.py index 8e79ce7..0836185 100644 --- a/mvt/ios/modules/fs/safari_browserstate.py +++ b/mvt/ios/modules/fs/safari_browserstate.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import io import sqlite3 diff --git a/mvt/ios/modules/fs/safari_favicon.py b/mvt/ios/modules/fs/safari_favicon.py index 3bd5c36..0a931ef 100644 --- a/mvt/ios/modules/fs/safari_favicon.py +++ b/mvt/ios/modules/fs/safari_favicon.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 diff --git a/mvt/ios/modules/fs/safari_history.py b/mvt/ios/modules/fs/safari_history.py index 4c498a9..487ac5a 100644 --- a/mvt/ios/modules/fs/safari_history.py +++ b/mvt/ios/modules/fs/safari_history.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 diff --git a/mvt/ios/modules/fs/sms.py b/mvt/ios/modules/fs/sms.py index b94dbed..313f57d 100644 --- a/mvt/ios/modules/fs/sms.py +++ b/mvt/ios/modules/fs/sms.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 from base64 import b64encode diff --git a/mvt/ios/modules/fs/sms_attachments.py b/mvt/ios/modules/fs/sms_attachments.py index 89f0cf3..8835e88 100644 --- a/mvt/ios/modules/fs/sms_attachments.py +++ b/mvt/ios/modules/fs/sms_attachments.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import sqlite3 from base64 import b64encode diff --git a/mvt/ios/modules/fs/version_history.py b/mvt/ios/modules/fs/version_history.py index a9bf2cd..1b091de 100644 --- a/mvt/ios/modules/fs/version_history.py +++ b/mvt/ios/modules/fs/version_history.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import datetime import json diff --git a/mvt/ios/modules/fs/webkit_base.py b/mvt/ios/modules/fs/webkit_base.py index ff1c8ef..75979b3 100644 --- a/mvt/ios/modules/fs/webkit_base.py +++ b/mvt/ios/modules/fs/webkit_base.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import datetime import os diff --git a/mvt/ios/modules/fs/webkit_indexeddb.py b/mvt/ios/modules/fs/webkit_indexeddb.py index 55d65ec..a4aa2a0 100644 --- a/mvt/ios/modules/fs/webkit_indexeddb.py +++ b/mvt/ios/modules/fs/webkit_indexeddb.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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 .webkit_base import WebkitBase diff --git a/mvt/ios/modules/fs/webkit_localstorage.py b/mvt/ios/modules/fs/webkit_localstorage.py index 8b4fddb..868b601 100644 --- a/mvt/ios/modules/fs/webkit_localstorage.py +++ b/mvt/ios/modules/fs/webkit_localstorage.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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 .webkit_base import WebkitBase diff --git a/mvt/ios/modules/fs/webkit_resource_load_statistics.py b/mvt/ios/modules/fs/webkit_resource_load_statistics.py index 4ac91ec..2fe4a03 100644 --- a/mvt/ios/modules/fs/webkit_resource_load_statistics.py +++ b/mvt/ios/modules/fs/webkit_resource_load_statistics.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import datetime import os diff --git a/mvt/ios/modules/fs/webkit_safariviewservice.py b/mvt/ios/modules/fs/webkit_safariviewservice.py index 33f993c..c0b5a82 100644 --- a/mvt/ios/modules/fs/webkit_safariviewservice.py +++ b/mvt/ios/modules/fs/webkit_safariviewservice.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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 .webkit_base import WebkitBase diff --git a/mvt/ios/modules/fs/webkit_session_resource_log.py b/mvt/ios/modules/fs/webkit_session_resource_log.py index 5f5f7b3..c7b5c2f 100644 --- a/mvt/ios/modules/fs/webkit_session_resource_log.py +++ b/mvt/ios/modules/fs/webkit_session_resource_log.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import glob import os diff --git a/mvt/ios/modules/fs/whatsapp.py b/mvt/ios/modules/fs/whatsapp.py index afdd9e7..8e973df 100644 --- a/mvt/ios/modules/fs/whatsapp.py +++ b/mvt/ios/modules/fs/whatsapp.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import logging import sqlite3 diff --git a/mvt/ios/versions.py b/mvt/ios/versions.py index 1fab0ee..ed41711 100644 --- a/mvt/ios/versions.py +++ b/mvt/ios/versions.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ IPHONE_MODELS = [ {"description": "iPhone 4S", "identifier": "iPhone4,1"}, diff --git a/setup.py b/setup.py index adc0eb0..c1b1c5a 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # Mobile Verification Toolkit (MVT) -# Copyright (c) 2021 MVT Project Developers. -# See the file 'LICENSE' for usage and copying permissions, or find a copy at -# https://github.com/mvt-project/mvt/blob/main/LICENSE +# 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/ import os From 706c429595fedb41725d23f6e352d0733b13d0fb Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Mon, 2 Aug 2021 11:04:06 -0400 Subject: [PATCH 87/99] mvt-ios decrypt-backup: Improve error messages for known cases The two most common reasons that `mvt-ios decrypt-backup` can fail are wrong passwords and not pointing to an actual backup. We can distinguish these cases based on the kinds of errors thrown from iOSbackup (at least for the current versions that i'm testing with). When we encounter those particular exceptions, just report a simple summary and don't overwhelm the user with a backtrace. If we encounter an unexpected exception, leave the reporting as-is. Closes: #28, #36 --- mvt/ios/decrypt.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mvt/ios/decrypt.py b/mvt/ios/decrypt.py index dcecc88..cd1f368 100644 --- a/mvt/ios/decrypt.py +++ b/mvt/ios/decrypt.py @@ -78,8 +78,13 @@ class DecryptBackup: cleartextpassword=password, backuproot=os.path.dirname(self.backup_path)) except Exception as e: - log.exception(e) - log.critical("Failed to decrypt backup. Did you provide the correct password?") + if isinstance(e, KeyError) and len(e.args) > 0 and e.args[0] == b"KEY": + log.critical("Failed to decrypt backup. Password is probably wrong.") + elif isinstance(e, FileNotFoundError) and os.path.basename(e.filename) == "Manifest.plist": + log.critical(f"Failed to find backup at {self.backup_path}. Did you need to specify the full path?") + else: + log.exception(e) + log.critical("Failed to decrypt backup. Did you provide the correct password? Did you point to the right backup path?") def decrypt_with_key_file(self, key_file): """Decrypts an encrypted iOS backup using a key file. From 33e90c17078b49d167ab9c1622ee634b5110cb01 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Mon, 2 Aug 2021 11:26:00 -0400 Subject: [PATCH 88/99] mvt-ios sqlite3 db recovery: fix quoting sent to sqlite3 .clone In b2afce5c79b9305028e1ff7bb226a0ef3d9affc5, the db filename is wrapped in double-quotes when passing it to the sqlite3 tool's `.clone` helper command. For parsing safety, we avoid performing this cleanup if the filename itself has a double-quote character in it. Otherwise, a malformed filename could lead to arbitrary injection into the sqlite3 command. In be2468004613fe3a40dcf6c8acebd8c866d635d3, the sqlite3 wrapping changes to single-quotes. Either the safety check should be amended to block pathnames with single-quotes, or the sqlite3 wrapping should revert to double-quotes. I opted for the latter here because i think single-quotes are more likely than double-quotes to show up in pathnames (e.g. a folder named "Daniel's files"), but either change would be fine, of course. --- mvt/ios/modules/fs/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mvt/ios/modules/fs/base.py b/mvt/ios/modules/fs/base.py index 918e3ad..2306be2 100644 --- a/mvt/ios/modules/fs/base.py +++ b/mvt/ios/modules/fs/base.py @@ -56,7 +56,7 @@ class IOSExtraction(MVTModule): bak_path = f"{file_path}.bak" shutil.move(file_path, bak_path) - ret = subprocess.call(["sqlite3", bak_path, f".clone '{file_path}'"], + ret = subprocess.call(["sqlite3", bak_path, f".clone \"{file_path}\""], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if ret != 0: raise DatabaseCorruptedError("Recovery of database failed") From 12e0f144001bf67f3a423d7a10d2f5a372fb5ec3 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 3 Aug 2021 10:24:38 +0200 Subject: [PATCH 89/99] Added note on running MVT on Windows --- docs/install.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/install.md b/docs/install.md index 3bd8642..8c5b54f 100644 --- a/docs/install.md +++ b/docs/install.md @@ -24,6 +24,12 @@ brew install python3 libusb sqlite3 *libusb* is not required if you intend to only use `mvt-ios` and not `mvt-android`. +## MVT on Windows + +MVT is currently not officially supported to run natively on Windows. While most functionality should work out of the box, there are known issues especially with `mvt-android`. + +It is recommended to try installing and running MVT from [Windows Subsystem Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/about) and follow Linux installation instructions for your distribution of choice. + ## Installing MVT If you haven't done so, you can add this to your `.bashrc` or `.zshrc` file in order to add locally installed Pypi binaries to your `$PATH`: From 995ebc02cf8d2671ce69f1eaf27ecba2877b9066 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 3 Aug 2021 10:28:28 +0200 Subject: [PATCH 90/99] Fixing language --- docs/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.md b/docs/install.md index 8c5b54f..313cf34 100644 --- a/docs/install.md +++ b/docs/install.md @@ -26,7 +26,7 @@ brew install python3 libusb sqlite3 ## MVT on Windows -MVT is currently not officially supported to run natively on Windows. While most functionality should work out of the box, there are known issues especially with `mvt-android`. +MVT does not currently officially support running natively on Windows. While most functionality should work out of the box, there are known issues especially with `mvt-android`. It is recommended to try installing and running MVT from [Windows Subsystem Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/about) and follow Linux installation instructions for your distribution of choice. From 1e8278aeec45ab19e94789f6f43a77b8c4273044 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 3 Aug 2021 15:51:58 +0200 Subject: [PATCH 91/99] Updated README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eeb6d45..d595595 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ You will need some dependencies, so please check the [documentation](https://doc Alternatively, you can decide to run MVT and all relevant tools through a [Docker container](https://docs.mvt.re/en/latest/docker.html). +**Please note:** [MVT does not currently support running natively on Windows.](https://docs.mvt.re/en/latest/install.html#mvt-on-windows) ## Usage From 7ae9ecbf5a8e29b8c3fe83a0bd4767a29c895030 Mon Sep 17 00:00:00 2001 From: Nex Date: Tue, 3 Aug 2021 17:25:16 +0200 Subject: [PATCH 92/99] Removed newline --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d595595..16964c4 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,6 @@ MVT provides two commands `mvt-ios` and `mvt-android` with the following subcomm Check out [the documentation to see how to use them](https://docs.mvt.re/). - ## License The purpose of MVT is to facilitate the ***consensual forensic analysis*** of devices of those who might be targets of sophisticated mobile spyware attacks, especially members of civil society and marginalized communities. We do not want MVT to enable privacy violations of non-consenting individuals. Therefore, the goal of this license is to prohibit the use of MVT (and any other software licensed the same) for the purpose of *adversarial forensics*. From 0bc660a2b30e636783396f9722c8d7127241946e Mon Sep 17 00:00:00 2001 From: Nex Date: Wed, 4 Aug 2021 19:14:06 +0200 Subject: [PATCH 93/99] Updated documentation (closes: #3) --- docs/android/methodology.md | 7 +------ docs/install.md | 10 ++++++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/android/methodology.md b/docs/android/methodology.md index 2c57488..c064da9 100644 --- a/docs/android/methodology.md +++ b/docs/android/methodology.md @@ -1,8 +1,3 @@ # Methodology for Android forensic -For different technical reasons, it is more complex to do a forensic analysis of an Android phone. - -Currently MVT allows to perform two different checks on an Android phone: - -* Download APKs installed in order to analyze them -* Extract Android backup in order to look for suspicious SMS +TODO diff --git a/docs/install.md b/docs/install.md index 313cf34..68d3891 100644 --- a/docs/install.md +++ b/docs/install.md @@ -12,6 +12,8 @@ sudo apt install python3 python3-pip libusb-1.0-0 sqlite3 *libusb-1.0-0* is not required if you intend to only use `mvt-ios` and not `mvt-android`. +When working with Android devices you should additionally install [Android SDK Platform Tools](https://developer.android.com/studio/releases/platform-tools). If you prefer to install a package made available by your distribution of choice, please make sure the version is recent to ensure compatibility with modern Android devices. + ## Dependencies on Mac Running MVT on Mac requires Xcode and [homebrew](https://brew.sh) to be installed. @@ -24,6 +26,14 @@ brew install python3 libusb sqlite3 *libusb* is not required if you intend to only use `mvt-ios` and not `mvt-android`. +When working with Android devices you should additionally install Android SDK Platform Tools: + +```bash +brew install --cask android-platform-tools +``` + +Or by downloading the [official binary releases](https://developer.android.com/studio/releases/platform-tools). + ## MVT on Windows MVT does not currently officially support running natively on Windows. While most functionality should work out of the box, there are known issues especially with `mvt-android`. From 76e6138d77c910712e23d7c6189c262ffa32cb82 Mon Sep 17 00:00:00 2001 From: Nex Date: Thu, 5 Aug 2021 08:49:34 +0200 Subject: [PATCH 94/99] Catching check if root exception more grafully (closes: #5) --- mvt/android/modules/adb/base.py | 6 ++++-- mvt/common/module.py | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/mvt/android/modules/adb/base.py b/mvt/android/modules/adb/base.py index 622d509..74c13dc 100644 --- a/mvt/android/modules/adb/base.py +++ b/mvt/android/modules/adb/base.py @@ -17,7 +17,7 @@ from adb_shell.auth.sign_pythonrsa import PythonRSASigner from adb_shell.exceptions import AdbCommandFailureException, DeviceAuthError from usb1 import USBErrorAccess, USBErrorBusy -from mvt.common.module import MVTModule +from mvt.common.module import MVTModule, InsufficientPrivileges log = logging.getLogger(__name__) @@ -105,7 +105,7 @@ class AndroidExtraction(MVTModule): """Check if we have a `su` binary, otherwise raise an Exception. """ if not self._adb_check_if_root(): - raise Exception("The Android device does not seem to have a `su` binary. Cannot run this module.") + raise InsufficientPrivileges("The Android device does not seem to have a `su` binary. Cannot run this module.") def _adb_command_as_root(self, command): """Execute an adb shell command. @@ -120,6 +120,8 @@ class AndroidExtraction(MVTModule): :returns: Boolean indicating whether the file exists or not """ + # TODO: Need to support checking files without root privileges as well. + # Connect to the device over adb. self._adb_connect() # Check if we have root, if not raise an Exception. diff --git a/mvt/common/module.py b/mvt/common/module.py index 1989bcb..4a37850 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -21,6 +21,9 @@ class DatabaseNotFoundError(Exception): class DatabaseCorruptedError(Exception): pass +class InsufficientPrivileges(Exception): + pass + class MVTModule(object): """This class provides a base for all extraction modules.""" @@ -150,6 +153,8 @@ def run_module(module): except NotImplementedError: module.log.exception("The run() procedure of module %s was not implemented yet!", module.__class__.__name__) + except InsufficientPrivileges as e: + module.log.info("Insufficient privileges for module %s: %s", module.__class.__name__, e) except DatabaseNotFoundError as e: module.log.info("There might be no data to extract by module %s: %s", module.__class__.__name__, e) From 2302c9fb1c8561ead160e97d06cc6909362547dd Mon Sep 17 00:00:00 2001 From: Nex Date: Thu, 5 Aug 2021 08:56:41 +0200 Subject: [PATCH 95/99] Fixed language --- mvt/ios/decrypt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mvt/ios/decrypt.py b/mvt/ios/decrypt.py index cd1f368..fe50af5 100644 --- a/mvt/ios/decrypt.py +++ b/mvt/ios/decrypt.py @@ -81,10 +81,10 @@ class DecryptBackup: if isinstance(e, KeyError) and len(e.args) > 0 and e.args[0] == b"KEY": log.critical("Failed to decrypt backup. Password is probably wrong.") elif isinstance(e, FileNotFoundError) and os.path.basename(e.filename) == "Manifest.plist": - log.critical(f"Failed to find backup at {self.backup_path}. Did you need to specify the full path?") + log.critical(f"Failed to find a valid backup at {self.backup_path}. Did you point to the right backup path?") else: log.exception(e) - log.critical("Failed to decrypt backup. Did you provide the correct password? Did you point to the right backup path?") + log.critical("Failed to decrypt backup. Did you provide the correct password? Did you point to the right backup path?") def decrypt_with_key_file(self, key_file): """Decrypts an encrypted iOS backup using a key file. From 9f696dcb72ca646863acda7b2b73f3aad0d05b25 Mon Sep 17 00:00:00 2001 From: Nex Date: Thu, 5 Aug 2021 09:03:02 +0200 Subject: [PATCH 96/99] Added version 14.7.1 --- mvt/ios/versions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mvt/ios/versions.py b/mvt/ios/versions.py index ed41711..fc75a33 100644 --- a/mvt/ios/versions.py +++ b/mvt/ios/versions.py @@ -221,6 +221,7 @@ IPHONE_IOS_VERSIONS = [ {"build": "18E212", "version": "14.5.1"}, {"build": "18F72", "version": "14.6"}, {"build": "18G69", "version": "14.7"}, + {"build": "18G82", "version": "14.7.1"}, ] def get_device_desc_from_id(identifier, devices_list=IPHONE_MODELS): From 7b00f03f03c04a980511ffa9f737a6820f908885 Mon Sep 17 00:00:00 2001 From: Nex Date: Thu, 5 Aug 2021 09:04:22 +0200 Subject: [PATCH 97/99] Bumped version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c1b1c5a..abaa2e5 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ import os from setuptools import find_packages, setup __package_name__ = "mvt" -__version__ = "1.0.15" +__version__ = "1.0.16" __description__ = "Mobile Verification Toolkit" this_directory = os.path.abspath(os.path.dirname(__file__)) From e5f7727c80272f5535f9858e0707ed259370fb03 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 6 Aug 2021 18:40:06 +0200 Subject: [PATCH 98/99] Fixed typo (closes: #157) --- mvt/common/module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mvt/common/module.py b/mvt/common/module.py index 4a37850..bf40202 100644 --- a/mvt/common/module.py +++ b/mvt/common/module.py @@ -154,7 +154,7 @@ def run_module(module): module.log.exception("The run() procedure of module %s was not implemented yet!", module.__class__.__name__) except InsufficientPrivileges as e: - module.log.info("Insufficient privileges for module %s: %s", module.__class.__name__, e) + module.log.info("Insufficient privileges for module %s: %s", module.__class__.__name__, e) except DatabaseNotFoundError as e: module.log.info("There might be no data to extract by module %s: %s", module.__class__.__name__, e) From 24c89183a357560c8ff1e0a9f1c310cb343ba867 Mon Sep 17 00:00:00 2001 From: Nex Date: Fri, 6 Aug 2021 18:44:16 +0200 Subject: [PATCH 99/99] Bumped version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index abaa2e5..621ef1d 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ import os from setuptools import find_packages, setup __package_name__ = "mvt" -__version__ = "1.0.16" +__version__ = "1.0.17" __description__ = "Mobile Verification Toolkit" this_directory = os.path.abspath(os.path.dirname(__file__))