From 732db070f2d13709688b7d2982c4492c0956d13e Mon Sep 17 00:00:00 2001 From: febrezo Date: Tue, 20 Jul 2021 03:09:53 +0200 Subject: [PATCH] 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: