From 8c855b645d9e71269f5b666272c17e5569dae43f Mon Sep 17 00:00:00 2001 From: febrezo Date: Tue, 20 Jul 2021 12:10:37 +0200 Subject: [PATCH 01/39] 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 02/39] 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 03/39] 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 04/39] 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 05/39] 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 684aed8d1177d449af12aed7131dd9eb424c5385 Mon Sep 17 00:00:00 2001 From: febrezo Date: Thu, 22 Jul 2021 17:43:32 +0200 Subject: [PATCH 06/39] 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 fa49203c9bebc752f9c3ef11319408e525bc51cc Mon Sep 17 00:00:00 2001 From: Bryan Scheetz Date: Thu, 22 Jul 2021 22:49:26 -0400 Subject: [PATCH 07/39] 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 8048ed8c3a3dcddbdfc03d96f1b9b79db95f74b7 Mon Sep 17 00:00:00 2001 From: Pavel Kirkovsky Date: Fri, 23 Jul 2021 02:08:15 -0700 Subject: [PATCH 08/39] 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 ad3faa186bd54d7f487d3b262900213c9e72b2fa Mon Sep 17 00:00:00 2001 From: goshawk22 Date: Fri, 23 Jul 2021 15:35:56 +0100 Subject: [PATCH 09/39] 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 03523a40c07516796fa5f7f442fad30e35969794 Mon Sep 17 00:00:00 2001 From: Trigus42 Date: Sat, 24 Jul 2021 12:09:59 +0200 Subject: [PATCH 10/39] 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 11/39] 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 12/39] 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 13/39] 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 14/39] 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 15/39] 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 16/39] 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 17/39] 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 18/39] 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 19/39] 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 20/39] 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 13ce55f4ac3cedd16f1d81b56bc400501452a030 Mon Sep 17 00:00:00 2001 From: Nex Date: Sun, 25 Jul 2021 15:51:24 +0200 Subject: [PATCH 21/39] 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 22/39] 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 23/39] 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 24/39] 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 25/39] 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 26/39] 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 27/39] 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 28/39] 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 29/39] 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 30/39] 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 31/39] 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 32/39] 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 33/39] 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 34/39] 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 35/39] 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 36/39] 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 37/39] 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 38/39] 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 39/39] 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: