2022-06-15 15:41:19 +00:00
|
|
|
# Mobile Verification Toolkit (MVT)
|
2023-02-08 19:18:16 +00:00
|
|
|
# Copyright (c) 2021-2023 Claudio Guarnieri.
|
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 hashlib
|
|
|
|
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
|
2022-08-16 11:39:55 +00:00
|
|
|
from typing import Callable, Optional
|
2022-06-15 15:41:19 +00:00
|
|
|
|
|
|
|
from mvt.common.indicators import Indicators
|
2023-02-21 20:18:36 +00:00
|
|
|
from mvt.common.module import run_module, save_timeline, MVTModule
|
2023-02-21 19:16:32 +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-06-15 15:41:19 +00:00
|
|
|
|
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,
|
2022-08-16 11:39:55 +00:00
|
|
|
fast_mode: Optional[bool] = False,
|
2023-02-21 19:16:32 +00:00
|
|
|
hashes: Optional[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.fast_mode = fast_mode
|
|
|
|
self.log = log
|
|
|
|
|
|
|
|
self.iocs = Indicators(log=log)
|
2022-08-17 13:58:53 +00:00
|
|
|
self.iocs.load_indicators_files(self.ioc_files)
|
2022-06-15 15:41:19 +00:00
|
|
|
|
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 = []
|
|
|
|
|
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:
|
2022-06-15 15:41:19 +00:00
|
|
|
self.log.critical("Unable to create output folder %s: %s",
|
2022-08-13 00:14:24 +00:00
|
|
|
self.results_path, exc)
|
2022-06-15 15:41:19 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2022-06-17 20:30:46 +00:00
|
|
|
def _add_log_file_handler(self, logger: logging.Logger) -> None:
|
2022-06-17 15:48:07 +00:00
|
|
|
if not self.results_path:
|
|
|
|
return
|
|
|
|
|
2022-08-12 17:14:05 +00:00
|
|
|
file_handler = logging.FileHandler(os.path.join(self.results_path,
|
|
|
|
"command.log"))
|
2022-08-13 15:50:00 +00:00
|
|
|
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:
|
|
|
|
save_timeline(self.timeline,
|
|
|
|
os.path.join(self.results_path, "timeline.csv"))
|
|
|
|
|
|
|
|
if len(self.timeline_detected) > 0:
|
|
|
|
save_timeline(self.timeline_detected,
|
2022-08-12 17:14:05 +00:00
|
|
|
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)
|
|
|
|
self.log.warning("Reference hash of the info.json file : %s", info_hash)
|
|
|
|
|
|
|
|
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:
|
2022-08-12 17:14:05 +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
|
|
|
|
|
2022-06-17 20:30:46 +00:00
|
|
|
def run(self) -> None:
|
2022-06-15 15:41:19 +00:00
|
|
|
self._create_storage()
|
2022-06-17 12:56:39 +00:00
|
|
|
self._add_log_file_handler(self.log)
|
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
|
|
|
|
|
2022-06-17 12:56:39 +00:00
|
|
|
module_logger = logging.getLogger(module.__module__)
|
|
|
|
self._add_log_file_handler(module_logger)
|
|
|
|
|
|
|
|
m = module(target_path=self.target_path,
|
|
|
|
results_path=self.results_path,
|
|
|
|
fast_mode=self.fast_mode,
|
|
|
|
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()
|