2022-06-15 15:41:19 +00:00
# Mobile Verification Toolkit (MVT)
2023-09-09 15:55:27 +00:00
# Copyright (c) 2021-2023 The MVT Authors.
2022-06-15 15:41:19 +00:00
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
2022-06-17 15:48:07 +00:00
import json
2022-06-15 15:41:19 +00:00
import logging
import os
import sys
2022-06-17 15:48:07 +00:00
from datetime import datetime
2023-03-01 21:43:08 +00:00
from typing import Optional
2022-06-15 15:41:19 +00:00
from mvt . common . indicators import Indicators
2023-03-01 21:43:08 +00:00
from mvt . common . module import MVTModule , run_module , save_timeline
2023-06-01 21:40:26 +00:00
from mvt . common . utils import (
convert_datetime_to_iso ,
generate_hashes_from_path ,
get_sha256_from_file_path ,
)
2022-06-17 15:48:07 +00:00
from mvt . common . version import MVT_VERSION
2022-06-15 15:41:19 +00:00
2022-08-12 14:20:16 +00:00
class Command :
2022-08-16 11:39:55 +00:00
def __init__ (
self ,
2022-08-17 13:52:17 +00:00
target_path : Optional [ str ] = None ,
results_path : Optional [ str ] = None ,
2022-08-17 13:37:12 +00:00
ioc_files : Optional [ list ] = None ,
2022-08-17 13:52:17 +00:00
module_name : Optional [ str ] = None ,
serial : Optional [ str ] = None ,
2023-07-17 16:29:43 +00:00
module_options : Optional [ dict ] = None ,
2023-06-01 21:40:26 +00:00
hashes : bool = False ,
2022-08-16 11:39:55 +00:00
log : logging . Logger = logging . getLogger ( __name__ ) ,
) - > None :
2022-06-17 12:56:39 +00:00
self . name = " "
2022-08-12 14:20:16 +00:00
self . modules = [ ]
2022-06-17 12:56:39 +00:00
2022-06-15 15:41:19 +00:00
self . target_path = target_path
self . results_path = results_path
2022-08-17 13:58:53 +00:00
self . ioc_files = ioc_files if ioc_files else [ ]
2022-06-15 15:41:19 +00:00
self . module_name = module_name
self . serial = serial
self . log = log
2023-07-17 16:29:43 +00:00
# This dictionary can contain options that will be passed down from
# the Command to all modules. This can for example be used to pass
# down a password to decrypt a backup or flags which are need by some modules.
self . module_options = module_options if module_options else { }
2022-08-05 11:52:51 +00:00
# This list will contain all executed modules.
# We can use this to reference e.g. self.executed[0].results.
self . executed = [ ]
2022-09-05 10:12:36 +00:00
self . detected_count = 0
2023-02-21 19:16:32 +00:00
self . hashes = hashes
self . hash_values = [ ]
2022-06-15 15:41:19 +00:00
self . timeline = [ ]
self . timeline_detected = [ ]
2023-04-25 09:13:46 +00:00
# Load IOCs
self . _create_storage ( )
self . _setup_logging ( )
self . iocs = Indicators ( log = log )
self . iocs . load_indicators_files ( self . ioc_files )
2022-06-17 20:30:46 +00:00
def _create_storage ( self ) - > None :
2022-06-15 15:41:19 +00:00
if self . results_path and not os . path . exists ( self . results_path ) :
try :
os . makedirs ( self . results_path )
2022-08-13 00:14:24 +00:00
except Exception as exc :
2023-06-01 21:40:26 +00:00
self . log . critical (
" Unable to create output folder %s : %s " , self . results_path , exc
)
2022-06-15 15:41:19 +00:00
sys . exit ( 1 )
2023-04-25 09:13:46 +00:00
def _setup_logging ( self ) :
2022-06-17 15:48:07 +00:00
if not self . results_path :
return
2023-04-25 09:13:46 +00:00
logger = logging . getLogger ( " mvt " )
2023-06-01 21:40:26 +00:00
file_handler = logging . FileHandler (
os . path . join ( self . results_path , " command.log " )
)
formatter = logging . Formatter (
" %(asctime)s - %(name)s - " " %(levelname)s - %(message)s "
)
2022-08-12 14:20:16 +00:00
file_handler . setLevel ( logging . DEBUG )
file_handler . setFormatter ( formatter )
logger . addHandler ( file_handler )
2022-06-17 15:48:07 +00:00
2022-06-17 20:30:46 +00:00
def _store_timeline ( self ) - > None :
2022-06-17 15:16:00 +00:00
if not self . results_path :
2022-06-17 12:56:39 +00:00
return
if len ( self . timeline ) > 0 :
2023-06-01 21:40:26 +00:00
save_timeline (
self . timeline , os . path . join ( self . results_path , " timeline.csv " )
)
2022-06-17 12:56:39 +00:00
if len ( self . timeline_detected ) > 0 :
2023-06-01 21:40:26 +00:00
save_timeline (
self . timeline_detected ,
os . path . join ( self . results_path , " timeline_detected.csv " ) ,
)
2022-06-15 15:41:19 +00:00
2022-06-17 20:30:46 +00:00
def _store_info ( self ) - > None :
2022-06-17 12:56:39 +00:00
if not self . results_path :
return
2022-06-17 20:30:46 +00:00
target_path = None
if self . target_path :
target_path = os . path . abspath ( self . target_path )
2022-06-17 15:48:07 +00:00
info = {
2022-06-17 20:30:46 +00:00
" target_path " : target_path ,
2022-06-17 15:48:07 +00:00
" mvt_version " : MVT_VERSION ,
2022-08-13 00:14:24 +00:00
" date " : convert_datetime_to_iso ( datetime . now ( ) ) ,
2022-06-17 15:48:07 +00:00
" ioc_files " : [ ] ,
" hashes " : [ ] ,
}
for coll in self . iocs . ioc_collections :
2022-08-06 12:49:05 +00:00
ioc_file_path = coll . get ( " stix2_file_path " , " " )
if ioc_file_path and ioc_file_path not in info [ " ioc_files " ] :
info [ " ioc_files " ] . append ( ioc_file_path )
2022-06-17 15:48:07 +00:00
2023-02-21 19:16:32 +00:00
if self . target_path and ( os . environ . get ( " MVT_HASH_FILES " ) or self . hashes ) :
self . generate_hashes ( )
info [ " hashes " ] = self . hash_values
2022-06-17 15:48:07 +00:00
2022-08-12 17:14:05 +00:00
info_path = os . path . join ( self . results_path , " info.json " )
with open ( info_path , " w+ " , encoding = " utf-8 " ) as handle :
2022-06-17 15:48:07 +00:00
json . dump ( info , handle , indent = 4 )
2023-02-21 19:16:32 +00:00
if self . target_path and ( os . environ . get ( " MVT_HASH_FILES " ) or self . hashes ) :
info_hash = get_sha256_from_file_path ( info_path )
2023-06-01 21:40:26 +00:00
self . log . info ( ' Reference hash of the info.json file: " %s " ' , info_hash )
2023-02-21 19:16:32 +00:00
def generate_hashes ( self ) - > None :
"""
Compute hashes for files in the target_path
"""
if not self . target_path :
return
for file in generate_hashes_from_path ( self . target_path , self . log ) :
self . hash_values . append ( file )
2022-06-17 20:30:46 +00:00
def list_modules ( self ) - > None :
2023-06-01 21:40:26 +00:00
self . log . info ( " Following is the list of available %s modules: " , self . name )
2022-06-17 15:48:07 +00:00
for module in self . modules :
self . log . info ( " - %s " , module . __name__ )
2022-06-15 15:41:19 +00:00
2022-06-17 20:30:46 +00:00
def init ( self ) - > None :
2022-06-15 15:41:19 +00:00
raise NotImplementedError
2023-02-21 20:18:36 +00:00
def module_init ( self , module : MVTModule ) - > None :
2022-06-15 15:41:19 +00:00
raise NotImplementedError
2022-06-30 08:26:33 +00:00
def finish ( self ) - > None :
raise NotImplementedError
2024-04-08 16:47:59 +00:00
def _show_disable_adb_warning ( self ) - > None :
""" Warn if ADB is enabled """
if type ( self ) . __name__ in [ " CmdAndroidCheckADB " , " CmdAndroidCheckAndroidQF " ] :
self . log . info (
" Please disable Developer Options and ADB (Android Debug Bridge) on the device once finished with the acquisition. "
" ADB is a powerful tool which can allow unauthorized access to the device. "
)
def _show_support_message ( self ) - > None :
support_message = " Please seek reputable expert help if you have serious concerns about a possible spyware attack. Such support is available to human rights defenders and civil society through Amnesty International ' s Security Lab at https://securitylab.amnesty.org/get-help/?c=mvt "
if self . detected_count == 0 :
self . log . info (
f " [bold]NOTE:[/bold] Using MVT with public indicators of compromise (IOCs) [bold]WILL NOT[/bold] automatically detect advanced attacks. \n \n { support_message } " ,
extra = { " markup " : True } ,
)
else :
self . log . warning (
f " [bold]NOTE: Detected indicators of compromise[/bold]. Only expert review can confirm if the detected indicators are signs of an attack. \n \n { support_message } " ,
extra = { " markup " : True } ,
)
2022-06-17 20:30:46 +00:00
def run ( self ) - > None :
2022-06-15 15:41:19 +00:00
try :
self . init ( )
except NotImplementedError :
pass
for module in self . modules :
if self . module_name and module . __name__ != self . module_name :
continue
2023-06-01 21:40:26 +00:00
# FIXME: do we need the logger here
2022-06-17 12:56:39 +00:00
module_logger = logging . getLogger ( module . __module__ )
2023-06-01 21:40:26 +00:00
m = module (
target_path = self . target_path ,
results_path = self . results_path ,
2023-07-17 16:29:43 +00:00
module_options = self . module_options ,
2023-06-01 21:40:26 +00:00
log = module_logger ,
)
2022-06-15 15:41:19 +00:00
if self . iocs . total_ioc_count :
m . indicators = self . iocs
m . indicators . log = m . log
if self . serial :
m . serial = self . serial
try :
self . module_init ( m )
except NotImplementedError :
pass
run_module ( m )
2022-08-05 11:52:51 +00:00
self . executed . append ( m )
2022-09-05 10:12:36 +00:00
self . detected_count + = len ( m . detected )
2022-06-15 15:41:19 +00:00
self . timeline . extend ( m . timeline )
self . timeline_detected . extend ( m . timeline_detected )
2022-06-30 08:26:33 +00:00
try :
self . finish ( )
except NotImplementedError :
pass
2023-02-21 19:16:32 +00:00
self . _store_timeline ( )
self . _store_info ( )
2024-04-08 16:47:59 +00:00
self . _show_disable_adb_warning ( )
self . _show_support_message ( )