Compare commits

..

8 Commits

Author SHA1 Message Date
Donncha Ó Cearbhaill
ccca58de63 Add dumpsys parser for 'ip link'/network interfaces 2023-05-20 21:22:18 +01:00
Donncha Ó Cearbhaill
3787dc48cd Fix bug where getprop sections where missing due to non-standard section header 2023-05-18 11:28:10 +02:00
tek
f814244ff8 Fixes bug in bugreport getprop module 2023-05-06 11:20:10 -04:00
tek
11730f164f Fixes an issue in androidqf SMS module 2023-05-06 11:04:42 -04:00
Sebastian Pederiva
912fb060cb Fix error when creating report: csv.Error (#341) 2023-05-02 17:09:16 +02:00
tek
a9edf4a9fe Bumps version 2023-04-25 12:20:45 +02:00
tek
ea7b9066ba Improves iOS app detection 2023-04-25 11:21:55 +02:00
tek
fd81e3aa13 Adds verbose mode 2023-04-25 11:13:46 +02:00
17 changed files with 191 additions and 49 deletions

View File

@@ -6,14 +6,15 @@
import logging import logging
import click import click
from rich.logging import RichHandler
from mvt.common.cmd_check_iocs import CmdCheckIOCS from mvt.common.cmd_check_iocs import CmdCheckIOCS
from mvt.common.help import (HELP_MSG_FAST, HELP_MSG_HASHES, HELP_MSG_IOC, from mvt.common.help import (HELP_MSG_FAST, HELP_MSG_HASHES, HELP_MSG_IOC,
HELP_MSG_LIST_MODULES, HELP_MSG_MODULE, HELP_MSG_LIST_MODULES, HELP_MSG_MODULE,
HELP_MSG_OUTPUT, HELP_MSG_SERIAL) HELP_MSG_OUTPUT, HELP_MSG_SERIAL,
HELP_MSG_VERBOSE)
from mvt.common.logo import logo from mvt.common.logo import logo
from mvt.common.updates import IndicatorsUpdates from mvt.common.updates import IndicatorsUpdates
from mvt.common.utils import init_logging, set_verbose_logging
from .cmd_check_adb import CmdAndroidCheckADB from .cmd_check_adb import CmdAndroidCheckADB
from .cmd_check_androidqf import CmdAndroidCheckAndroidQF from .cmd_check_androidqf import CmdAndroidCheckAndroidQF
@@ -25,11 +26,8 @@ from .modules.adb.packages import Packages
from .modules.backup import BACKUP_MODULES from .modules.backup import BACKUP_MODULES
from .modules.bugreport import BUGREPORT_MODULES from .modules.bugreport import BUGREPORT_MODULES
# Setup logging using Rich. init_logging()
LOG_FORMAT = "[%(name)s] %(message)s" log = logging.getLogger("mvt")
logging.basicConfig(level="INFO", format=LOG_FORMAT, handlers=[
RichHandler(show_path=False, log_time_format="%X")])
log = logging.getLogger(__name__)
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@@ -63,8 +61,10 @@ def version():
@click.option("--from-file", "-f", type=click.Path(exists=True), @click.option("--from-file", "-f", type=click.Path(exists=True),
help="Instead of acquiring from phone, load an existing packages.json file for " help="Instead of acquiring from phone, load an existing packages.json file for "
"lookups (mainly for debug purposes)") "lookups (mainly for debug purposes)")
@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE)
@click.pass_context @click.pass_context
def download_apks(ctx, all_apks, virustotal, output, from_file, serial): def download_apks(ctx, all_apks, virustotal, output, from_file, serial, verbose):
set_verbose_logging(verbose)
try: try:
if from_file: if from_file:
download = DownloadAPKs.from_json(from_file) download = DownloadAPKs.from_json(from_file)
@@ -112,8 +112,10 @@ def download_apks(ctx, all_apks, virustotal, output, from_file, serial):
@click.option("--fast", "-f", is_flag=True, help=HELP_MSG_FAST) @click.option("--fast", "-f", is_flag=True, help=HELP_MSG_FAST)
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
@click.option("--module", "-m", help=HELP_MSG_MODULE) @click.option("--module", "-m", help=HELP_MSG_MODULE)
@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE)
@click.pass_context @click.pass_context
def check_adb(ctx, serial, iocs, output, fast, list_modules, module): def check_adb(ctx, serial, iocs, output, fast, list_modules, module, verbose):
set_verbose_logging(verbose)
cmd = CmdAndroidCheckADB(results_path=output, ioc_files=iocs, cmd = CmdAndroidCheckADB(results_path=output, ioc_files=iocs,
module_name=module, serial=serial, fast_mode=fast) module_name=module, serial=serial, fast_mode=fast)
@@ -141,9 +143,11 @@ def check_adb(ctx, serial, iocs, output, fast, list_modules, module):
help=HELP_MSG_OUTPUT) help=HELP_MSG_OUTPUT)
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
@click.option("--module", "-m", help=HELP_MSG_MODULE) @click.option("--module", "-m", help=HELP_MSG_MODULE)
@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE)
@click.argument("BUGREPORT_PATH", type=click.Path(exists=True)) @click.argument("BUGREPORT_PATH", type=click.Path(exists=True))
@click.pass_context @click.pass_context
def check_bugreport(ctx, iocs, output, list_modules, module, bugreport_path): def check_bugreport(ctx, iocs, output, list_modules, module, verbose, bugreport_path):
set_verbose_logging(verbose)
# Always generate hashes as bug reports are small. # Always generate hashes as bug reports are small.
cmd = CmdAndroidCheckBugreport(target_path=bugreport_path, cmd = CmdAndroidCheckBugreport(target_path=bugreport_path,
results_path=output, ioc_files=iocs, results_path=output, ioc_files=iocs,
@@ -172,9 +176,11 @@ def check_bugreport(ctx, iocs, output, list_modules, module, bugreport_path):
@click.option("--output", "-o", type=click.Path(exists=False), @click.option("--output", "-o", type=click.Path(exists=False),
help=HELP_MSG_OUTPUT) help=HELP_MSG_OUTPUT)
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE)
@click.argument("BACKUP_PATH", type=click.Path(exists=True)) @click.argument("BACKUP_PATH", type=click.Path(exists=True))
@click.pass_context @click.pass_context
def check_backup(ctx, iocs, output, list_modules, backup_path): def check_backup(ctx, iocs, output, list_modules, verbose, backup_path):
set_verbose_logging(verbose)
# Always generate hashes as backups are generally small. # Always generate hashes as backups are generally small.
cmd = CmdAndroidCheckBackup(target_path=backup_path, results_path=output, cmd = CmdAndroidCheckBackup(target_path=backup_path, results_path=output,
ioc_files=iocs, hashes=True) ioc_files=iocs, hashes=True)
@@ -204,9 +210,11 @@ def check_backup(ctx, iocs, output, list_modules, backup_path):
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
@click.option("--module", "-m", help=HELP_MSG_MODULE) @click.option("--module", "-m", help=HELP_MSG_MODULE)
@click.option("--hashes", "-H", is_flag=True, help=HELP_MSG_HASHES) @click.option("--hashes", "-H", is_flag=True, help=HELP_MSG_HASHES)
@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE)
@click.argument("ANDROIDQF_PATH", type=click.Path(exists=True)) @click.argument("ANDROIDQF_PATH", type=click.Path(exists=True))
@click.pass_context @click.pass_context
def check_androidqf(ctx, iocs, output, list_modules, module, hashes, androidqf_path): def check_androidqf(ctx, iocs, output, list_modules, module, hashes, verbose, androidqf_path):
set_verbose_logging(verbose)
cmd = CmdAndroidCheckAndroidQF(target_path=androidqf_path, cmd = CmdAndroidCheckAndroidQF(target_path=androidqf_path,
results_path=output, ioc_files=iocs, results_path=output, ioc_files=iocs,
module_name=module, hashes=hashes) module_name=module, hashes=hashes)

View File

@@ -21,15 +21,13 @@ log = logging.getLogger(__name__)
class DownloadAPKs(AndroidExtraction): class DownloadAPKs(AndroidExtraction):
"""DownloadAPKs is the main class operating the download of APKs """DownloadAPKs is the main class operating the download of APKs
from the device. from the device.
""" """
def __init__( def __init__(
self, self,
results_path: Optional[str] = None, results_path: Optional[str] = None,
all_apks: Optional[bool] = False, all_apks: Optional[bool] = False,
packages: Optional[list] = None packages: Optional[list] = None,
) -> None: ) -> None:
"""Initialize module. """Initialize module.
:param results_path: Path to the folder where data should be stored :param results_path: Path to the folder where data should be stored

View File

@@ -38,7 +38,7 @@ class SMS(AndroidQFModule):
if "body" not in message: if "body" not in message:
continue continue
if self.indicators.check_domains(message["links"]): if self.indicators.check_domains(message.get("links", [])):
self.detected.append(message) self.detected.append(message)
def parse_backup(self, data): def parse_backup(self, data):

View File

@@ -12,6 +12,8 @@ from .dbinfo import DBInfo
from .getprop import Getprop from .getprop import Getprop
from .packages import Packages from .packages import Packages
from .receivers import Receivers from .receivers import Receivers
from .network_interfaces import NetworkInterfaces
BUGREPORT_MODULES = [Accessibility, Activities, Appops, BatteryDaily, BUGREPORT_MODULES = [Accessibility, Activities, Appops, BatteryDaily,
BatteryHistory, DBInfo, Getprop, Packages, Receivers] BatteryHistory, DBInfo, Getprop, Packages, Receivers,
NetworkInterfaces]

View File

@@ -39,8 +39,9 @@ class Getprop(BugReportModule):
lines = [] lines = []
in_getprop = False in_getprop = False
for line in content.decode(errors="ignore").splitlines(): for line in content.decode(errors="ignore").splitlines():
if line.strip() == "------ SYSTEM PROPERTIES (getprop) ------": if line.strip().startswith("------ SYSTEM PROPERTIES"):
in_getprop = True in_getprop = True
continue continue
@@ -55,13 +56,14 @@ class Getprop(BugReportModule):
self.results = parse_getprop("\n".join(lines)) self.results = parse_getprop("\n".join(lines))
# Alert if phone is outdated. # Alert if phone is outdated.
security_patch = self.results.get("ro.build.version.security_patch", "") for entry in self.results:
if security_patch: if entry["name"] == "ro.build.version.security_patch":
patch_date = datetime.strptime(security_patch, "%Y-%m-%d") security_patch = entry["value"]
if (datetime.now() - patch_date) > timedelta(days=6*30): patch_date = datetime.strptime(security_patch, "%Y-%m-%d")
self.log.warning("This phone has not received security updates " if (datetime.now() - patch_date) > timedelta(days=6*30):
"for more than six months (last update: %s)", self.log.warning("This phone has not received security updates "
security_patch) "for more than six months (last update: %s)",
security_patch)
self.log.info("Extracted %d Android system properties", self.log.info("Extracted %d Android system properties",
len(self.results)) len(self.results))

View File

@@ -0,0 +1,58 @@
# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2023 Claudio Guarnieri.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
from typing import Optional
from mvt.android.parsers import parse_dumpsys_network_interfaces
from .base import BugReportModule
class NetworkInterfaces(BugReportModule):
"""This module extracts network interfaces from 'ip link' command."""
def __init__(
self,
file_path: Optional[str] = None,
target_path: Optional[str] = None,
results_path: Optional[str] = None,
fast_mode: Optional[bool] = False,
log: logging.Logger = logging.getLogger(__name__),
results: Optional[list] = None
) -> None:
super().__init__(file_path=file_path, target_path=target_path,
results_path=results_path, fast_mode=fast_mode,
log=log, results=results)
self.results = {} if not results else results
def run(self) -> None:
content = self._get_dumpstate_file()
if not content:
self.log.error("Unable to find dumpstate file. "
"Did you provide a valid bug report archive?")
return
lines = []
in_getprop = False
for line in content.decode(errors="ignore").splitlines():
if line.strip().startswith("------ NETWORK INTERFACES"):
in_getprop = True
continue
if not in_getprop:
continue
if line.strip().startswith("------"):
break
lines.append(line)
self.results = parse_dumpsys_network_interfaces("\n".join(lines))
self.log.info("Extracted information about %d Android network interfaces",
len(self.results))

View File

@@ -7,5 +7,7 @@ from .dumpsys import (parse_dumpsys_accessibility,
parse_dumpsys_activity_resolver_table, parse_dumpsys_activity_resolver_table,
parse_dumpsys_appops, parse_dumpsys_battery_daily, parse_dumpsys_appops, parse_dumpsys_battery_daily,
parse_dumpsys_battery_history, parse_dumpsys_dbinfo, parse_dumpsys_battery_history, parse_dumpsys_dbinfo,
parse_dumpsys_receiver_resolver_table) parse_dumpsys_receiver_resolver_table,
parse_dumpsys_network_interfaces,
)
from .getprop import parse_getprop from .getprop import parse_getprop

View File

@@ -519,3 +519,39 @@ def parse_dumpsys_packages(output: str) -> List[Dict[str, Any]]:
results.append(package) results.append(package)
return results return results
def parse_dumpsys_network_interfaces(output: str) -> List[Dict[str, str]]:
"""
Parse network interfaces (output of the 'ip link' command)
"""
results = []
interface_rxp = re.compile(r"(?P<if_number>\d+): (?P<if_name>[\S\d]+): (?P<if_options>\<.*)")
mac_or_ip_line_rxp = re.compile(r"\W+ (?P<link_type>[\S]+) (?P<mac_or_ip_address>[a-f0-9\:\.\/]+) (.*)")
interface = None
for line in output.splitlines():
interface_match = re.match(interface_rxp, line)
if interface_match:
interface = {
"interface_number": interface_match.group("if_number"),
"name": interface_match.group("if_name"),
"options": interface_match.group("if_options"),
}
continue
elif interface:
mac_line_match = re.match(mac_or_ip_line_rxp, line)
mac_or_ip_address = mac_line_match.group("mac_or_ip_address")
if len(mac_or_ip_address) == 17:
interface["mac_address"] = mac_or_ip_address
else:
interface["address"] = mac_or_ip_address
interface["link_type"] = mac_line_match.group("link_type")
interface["link_line"] = line
results.append(interface)
interface = None
return results

View File

@@ -42,20 +42,21 @@ class Command:
self.fast_mode = fast_mode self.fast_mode = fast_mode
self.log = log self.log = log
self.iocs = Indicators(log=log)
self.iocs.load_indicators_files(self.ioc_files)
# This list will contain all executed modules. # This list will contain all executed modules.
# We can use this to reference e.g. self.executed[0].results. # We can use this to reference e.g. self.executed[0].results.
self.executed = [] self.executed = []
self.detected_count = 0 self.detected_count = 0
self.hashes = hashes self.hashes = hashes
self.hash_values = [] self.hash_values = []
self.timeline = [] self.timeline = []
self.timeline_detected = [] self.timeline_detected = []
# Load IOCs
self._create_storage()
self._setup_logging()
self.iocs = Indicators(log=log)
self.iocs.load_indicators_files(self.ioc_files)
def _create_storage(self) -> None: def _create_storage(self) -> None:
if self.results_path and not os.path.exists(self.results_path): if self.results_path and not os.path.exists(self.results_path):
try: try:
@@ -65,10 +66,11 @@ class Command:
self.results_path, exc) self.results_path, exc)
sys.exit(1) sys.exit(1)
def _add_log_file_handler(self, logger: logging.Logger) -> None: def _setup_logging(self):
if not self.results_path: if not self.results_path:
return return
logger = logging.getLogger("mvt")
file_handler = logging.FileHandler(os.path.join(self.results_path, file_handler = logging.FileHandler(os.path.join(self.results_path,
"command.log")) "command.log"))
formatter = logging.Formatter("%(asctime)s - %(name)s - " formatter = logging.Formatter("%(asctime)s - %(name)s - "
@@ -150,8 +152,6 @@ class Command:
raise NotImplementedError raise NotImplementedError
def run(self) -> None: def run(self) -> None:
self._create_storage()
self._add_log_file_handler(self.log)
try: try:
self.init() self.init()
@@ -162,8 +162,8 @@ class Command:
if self.module_name and module.__name__ != self.module_name: if self.module_name and module.__name__ != self.module_name:
continue continue
# FIXME: do we need the logger here
module_logger = logging.getLogger(module.__module__) module_logger = logging.getLogger(module.__module__)
self._add_log_file_handler(module_logger)
m = module(target_path=self.target_path, m = module(target_path=self.target_path,
results_path=self.results_path, results_path=self.results_path,

View File

@@ -10,6 +10,7 @@ HELP_MSG_FAST = "Avoid running time/resource consuming features"
HELP_MSG_LIST_MODULES = "Print list of available modules and exit" HELP_MSG_LIST_MODULES = "Print list of available modules and exit"
HELP_MSG_MODULE = "Name of a single module you would like to run instead of all" HELP_MSG_MODULE = "Name of a single module you would like to run instead of all"
HELP_MSG_HASHES = "Generate hashes of all the files analyzed" HELP_MSG_HASHES = "Generate hashes of all the files analyzed"
HELP_MSG_VERBOSE = "Verbose mode"
# Android-specific. # Android-specific.
HELP_MSG_SERIAL = "Specify a device serial number or HOST:PORT connection string" HELP_MSG_SERIAL = "Specify a device serial number or HOST:PORT connection string"

View File

@@ -214,7 +214,7 @@ def save_timeline(timeline: list, timeline_path: str) -> None:
""" """
with open(timeline_path, "a+", encoding="utf-8") as handle: with open(timeline_path, "a+", encoding="utf-8") as handle:
csvoutput = csv.writer(handle, delimiter=",", quotechar="\"", csvoutput = csv.writer(handle, delimiter=",", quotechar="\"",
quoting=csv.QUOTE_ALL) quoting=csv.QUOTE_ALL, escapechar='\\')
csvoutput.writerow(["UTC Timestamp", "Plugin", "Event", "Description"]) csvoutput.writerow(["UTC Timestamp", "Plugin", "Event", "Description"])
for event in sorted(timeline, key=lambda x: x["timestamp"] for event in sorted(timeline, key=lambda x: x["timestamp"]

View File

@@ -5,10 +5,13 @@
import datetime import datetime
import hashlib import hashlib
import logging
import os import os
import re import re
from typing import Any, Iterator, Union from typing import Any, Iterator, Union
from rich.logging import RichHandler
def convert_chrometime_to_datetime(timestamp: int) -> datetime.datetime: def convert_chrometime_to_datetime(timestamp: int) -> datetime.datetime:
"""Converts Chrome timestamp to a datetime. """Converts Chrome timestamp to a datetime.
@@ -197,3 +200,28 @@ def generate_hashes_from_path(path: str, log) -> Iterator[dict]:
continue continue
yield {"file_path": file_path, "sha256": sha256} yield {"file_path": file_path, "sha256": sha256}
def init_logging(verbose: bool = False):
"""
Initialise logging for the MVT module
"""
# Setup logging using Rich.
log = logging.getLogger("mvt")
log.setLevel(logging.DEBUG)
consoleHandler = RichHandler(show_path=False, log_time_format="%X")
consoleHandler.setFormatter(logging.Formatter("[%(name)s] %(message)s"))
if verbose:
consoleHandler.setLevel(logging.DEBUG)
else:
consoleHandler.setLevel(logging.INFO)
log.addHandler(consoleHandler)
def set_verbose_logging(verbose: bool = False):
log = logging.getLogger("mvt")
handler = log.handlers[0]
if verbose:
handler.setLevel(logging.DEBUG)
else:
handler.setLevel(logging.INFO)

View File

@@ -3,4 +3,4 @@
# Use of this software is governed by the MVT License 1.1 that can be found at # Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/ # https://license.mvt.re/1.1/
MVT_VERSION = "2.2.5" MVT_VERSION = "2.2.6"

View File

@@ -8,17 +8,17 @@ import logging
import os import os
import click import click
from rich.logging import RichHandler
from rich.prompt import Prompt from rich.prompt import Prompt
from mvt.common.cmd_check_iocs import CmdCheckIOCS from mvt.common.cmd_check_iocs import CmdCheckIOCS
from mvt.common.help import (HELP_MSG_FAST, HELP_MSG_HASHES, HELP_MSG_IOC, from mvt.common.help import (HELP_MSG_FAST, HELP_MSG_HASHES, HELP_MSG_IOC,
HELP_MSG_LIST_MODULES, HELP_MSG_MODULE, HELP_MSG_LIST_MODULES, HELP_MSG_MODULE,
HELP_MSG_OUTPUT) HELP_MSG_OUTPUT, HELP_MSG_VERBOSE)
from mvt.common.logo import logo from mvt.common.logo import logo
from mvt.common.options import MutuallyExclusiveOption from mvt.common.options import MutuallyExclusiveOption
from mvt.common.updates import IndicatorsUpdates from mvt.common.updates import IndicatorsUpdates
from mvt.common.utils import generate_hashes_from_path from mvt.common.utils import (generate_hashes_from_path, init_logging,
set_verbose_logging)
from .cmd_check_backup import CmdIOSCheckBackup from .cmd_check_backup import CmdIOSCheckBackup
from .cmd_check_fs import CmdIOSCheckFS from .cmd_check_fs import CmdIOSCheckFS
@@ -27,11 +27,8 @@ from .modules.backup import BACKUP_MODULES
from .modules.fs import FS_MODULES from .modules.fs import FS_MODULES
from .modules.mixed import MIXED_MODULES from .modules.mixed import MIXED_MODULES
# Setup logging using Rich. init_logging()
LOG_FORMAT = "[%(name)s] %(message)s" log = logging.getLogger("mvt")
logging.basicConfig(level="INFO", format=LOG_FORMAT, handlers=[
RichHandler(show_path=False, log_time_format="%X")])
log = logging.getLogger(__name__)
# Set this environment variable to a password if needed. # Set this environment variable to a password if needed.
MVT_IOS_BACKUP_PASSWORD = "MVT_IOS_BACKUP_PASSWORD" MVT_IOS_BACKUP_PASSWORD = "MVT_IOS_BACKUP_PASSWORD"
@@ -166,9 +163,12 @@ def extract_key(password, key_file, backup_path):
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
@click.option("--module", "-m", help=HELP_MSG_MODULE) @click.option("--module", "-m", help=HELP_MSG_MODULE)
@click.option("--hashes", "-H", is_flag=True, help=HELP_MSG_HASHES) @click.option("--hashes", "-H", is_flag=True, help=HELP_MSG_HASHES)
@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE)
@click.argument("BACKUP_PATH", type=click.Path(exists=True)) @click.argument("BACKUP_PATH", type=click.Path(exists=True))
@click.pass_context @click.pass_context
def check_backup(ctx, iocs, output, fast, list_modules, module, hashes, backup_path): def check_backup(ctx, iocs, output, fast, list_modules, module, hashes, verbose, backup_path):
set_verbose_logging(verbose)
cmd = CmdIOSCheckBackup(target_path=backup_path, results_path=output, cmd = CmdIOSCheckBackup(target_path=backup_path, results_path=output,
ioc_files=iocs, module_name=module, fast_mode=fast, ioc_files=iocs, module_name=module, fast_mode=fast,
hashes=hashes) hashes=hashes)
@@ -199,9 +199,11 @@ def check_backup(ctx, iocs, output, fast, list_modules, module, hashes, backup_p
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES) @click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
@click.option("--module", "-m", help=HELP_MSG_MODULE) @click.option("--module", "-m", help=HELP_MSG_MODULE)
@click.option("--hashes", "-H", is_flag=True, help=HELP_MSG_HASHES) @click.option("--hashes", "-H", is_flag=True, help=HELP_MSG_HASHES)
@click.option("--verbose", "-v", is_flag=True, help=HELP_MSG_VERBOSE)
@click.argument("DUMP_PATH", type=click.Path(exists=True)) @click.argument("DUMP_PATH", type=click.Path(exists=True))
@click.pass_context @click.pass_context
def check_fs(ctx, iocs, output, fast, list_modules, module, hashes, dump_path): def check_fs(ctx, iocs, output, fast, list_modules, module, hashes, verbose, dump_path):
set_verbose_logging(verbose)
cmd = CmdIOSCheckFS(target_path=dump_path, results_path=output, cmd = CmdIOSCheckFS(target_path=dump_path, results_path=output,
ioc_files=iocs, module_name=module, fast_mode=fast, ioc_files=iocs, module_name=module, fast_mode=fast,
hashes=hashes) hashes=hashes)

View File

@@ -61,8 +61,8 @@ class Applications(IOSExtraction):
self.detected.append(result) self.detected.append(result)
continue continue
if result.get("sourceApp", "com.apple.AppStore") != "com.apple.AppStore": if result.get("sourceApp", "com.apple.AppStore") not in ["com.apple.AppStore", "com.apple.dmd", "dmd"]:
self.log.warning("Suspicious app not installed from the App Store: %s", result["softwareVersionBundleId"]) self.log.warning("Suspicious app not installed from the App Store or MDM: %s", result["softwareVersionBundleId"])
self.detected.append(result) self.detected.append(result)
def _parse_itunes_timestamp(self, entry: Dict[str, Any]) -> None: def _parse_itunes_timestamp(self, entry: Dict[str, Any]) -> None:

View File

View File

@@ -8,6 +8,7 @@ from pathlib import Path
from mvt.android.modules.bugreport.appops import Appops from mvt.android.modules.bugreport.appops import Appops
from mvt.android.modules.bugreport.packages import Packages from mvt.android.modules.bugreport.packages import Packages
from mvt.android.modules.bugreport.getprop import Getprop
from mvt.common.module import run_module from mvt.common.module import run_module
from ..utils import get_artifact_folder from ..utils import get_artifact_folder
@@ -40,3 +41,7 @@ class TestBugreportAnalysis:
assert m.results[1]["package_name"] == "com.instagram.android" assert m.results[1]["package_name"] == "com.instagram.android"
assert len(m.results[0]["permissions"]) == 4 assert len(m.results[0]["permissions"]) == 4
assert len(m.results[1]["permissions"]) == 32 assert len(m.results[1]["permissions"]) == 32
def test_getprop_module(self):
m = self.launch_bug_report_module(Getprop)
assert len(m.results) == 0