* Defer indicator loading until first use * Load indicators once at the start of a run The lazy property loads indicators wherever they are first read, which in Command.run() is inside the module loop, after init(). For check-androidqf that means the whole acquisition is walked before a missing --iocs file is reported, and the STIX parsing lines land between the module list and the first module. Read the indicators once after the module list is settled, so that a bad --module name still loads nothing, and hand the same object to every module. check-iocs reads them once too, so that a wrong --iocs path is reported even when no stored result matches a module. * Drop two test assertions the change does not need Retrying after a failed indicator load is incidental to the property rather than a requirement, so nothing should pin it. The exact wording of the check-backup rejection belongs to that command's own tests; exit code 1 already shows the path was rejected before indicators were loaded. * Create the output folder and command.log when a run starts Command.__init__ created the --output folder and attached the command.log handler, so `--list-modules -o out` or a rejected target path left an empty folder behind for a run that never happened. Do both at the top of run(), before the module list is resolved so that its warnings still reach the log. The nested commands run by check-androidqf re-attach the handler at the start of their runs, as they did at construction. Lines the CLI logs between construction and run(), such as the target path being checked, no longer reach command.log; info.json records the target path. This is the remaining part of #888. * Cache the indicators with functools.cached_property A property with a setter and a backing attribute does by hand what cached_property does: compute on first read, store the result on the instance, and accept assignment, which is how nested commands receive their parent's indicators. A failed load is still not cached. * Announce the target from the command so that it reaches command.log The CLI logged which backup, filesystem or acquisition it was about to check just before run(), which is now before command.log exists, so the line only reached the console. Each command logs it from init() instead, which runs once the log is attached, and run() logs the module list after init() so that the target still comes first. Nested commands without a target path log nothing, as before. * Log the Android backup path from the typed local mypy cannot determine the type of target_path in this command, which the surrounding lines already silence, so read the announced path from the local that carries the type instead of adding another ignore. --------- Co-authored-by: bitmeta69 <206962233+bitmeta69@users.noreply.github.com> Co-authored-by: besendorf <janik@besendorf.org> Co-authored-by: Donncha Ó Cearbhaill <donncha.ocearbhaill@amnesty.org>
Mobile Verification Toolkit
Important
We recently merged the "v3" branch. This introduced breaking changes. If you relied on mvt output in other scripts They might have broken. More details: https://github.com/mvt-project/mvt/issues/757
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 in July 2021 in the context of the Pegasus Project along with a technical forensic methodology. It continues to be maintained by Amnesty International and other contributors.
Note
MVT is a forensic research tool intended for technologists and investigators. It requires understanding digital forensics 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 reputable expert assistance.
Indicators of Compromise
MVT supports using public indicators of compromise (IOCs) to scan mobile devices for potential traces of targeting or infection by known spyware campaigns. This includes IOCs published by Amnesty International and other research groups.
Warning
Public indicators of compromise are insufficient to determine that a device is "clean", and not targeted with a particular spyware tool. Reliance on public indicators alone can miss recent forensic traces and give a false sense of security.
Reliable and comprehensive digital forensic support and triage requires access to non-public indicators, research and threat intelligence.
Such support is available to civil society through Amnesty International's Security Lab or through our forensic partnership with Access Now’s Digital Security Helpline.
More information about using indicators of compromise with MVT is available in the documentation.
Installation
MVT can be installed from sources or from PyPI (you will need some dependencies, check the documentation):
pip3 install mvt
You can also install MVT from PyPI with uv. First, install uv:
curl -LsSf https://astral.sh/uv/install.sh | sh
Then install MVT as a command-line tool:
uv tool install mvt
For alternative installation options and known issues, please refer to the documentation as well as GitHub Issues.
Usage
MVT provides three commands: mvt-ios and mvt-android analyse acquisitions from devices of that platform, and mvt hosts what belongs to neither: version, completion, plugins and download-iocs (version and download-iocs remain available on the platform commands for now). Running mvt on its own shows the installed version, update notices and the available commands. Check out the documentation to learn how to use them!
Pass --verbose to any of the three commands, before the command name (mvt-ios --verbose check-backup ...), for debug output. The --verbose option the check-* commands accept after their name still works but is kept for compatibility only and will be removed in a future release.
Shell completion
MVT can generate a shell completion script for Bash, Zsh, and Fish which covers mvt, mvt-ios and mvt-android:
mvt completion
The command prints setup instructions by default. To generate the completion script directly, pass the shell name:
mvt completion bash
MVT only writes completion files or shell configuration when --install is passed. See the command completion documentation for details.
Plugin packages extend MVT with additional forensic modules, which run inside
the check-* commands, and with top-level commands on mvt, mvt-ios and
mvt-android. See the
development documentation for
writing and installing them, and the
custom CLI command documentation
for the entry points a package registers commands in.
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. In order to achieve this, MVT is released under its own license. Read more here.
