diff --git a/mvt/android/modules/adb/packages.py b/mvt/android/modules/adb/packages.py index b5e7678..2b01be1 100644 --- a/mvt/android/modules/adb/packages.py +++ b/mvt/android/modules/adb/packages.py @@ -71,6 +71,16 @@ SECURITY_PACKAGES = [ "com.samsung.android.app.omcagent", "com.samsung.android.securitylogagent", "com.sec.android.soagent", +] +SYSTEM_UPDATE_PACKAGES = [ + "com.android.updater", + "com.google.android.gms", + "com.huawei.android.hwouc", + "com.lge.lgdmsclient", + "com.motorola.ccc.ota", + "com.oneplus.opbackup", + "com.oppo.ota", + "com.transsion.systemupdate", "com.wssyncmldm", ] @@ -133,6 +143,10 @@ class Packages(AndroidExtraction): self.log.warning("Found a security package disabled: \"%s\"", result["package_name"]) + if result["package_name"] in SYSTEM_UPDATE_PACKAGES and result["disabled"]: + self.log.warning("System OTA update package \"%s\" disabled on the phone", + result["package_name"]) + if not self.indicators: continue diff --git a/mvt/android/modules/androidqf/__init__.py b/mvt/android/modules/androidqf/__init__.py index bbbd341..9b78510 100644 --- a/mvt/android/modules/androidqf/__init__.py +++ b/mvt/android/modules/androidqf/__init__.py @@ -10,6 +10,7 @@ from .dumpsys_receivers import DumpsysReceivers from .getprop import Getprop from .processes import Processes from .settings import Settings +from .sms import SMS ANDROIDQF_MODULES = [DumpsysActivities, DumpsysReceivers, DumpsysAccessibility, - DumpsysAppops, Processes, Getprop, Settings] + DumpsysAppops, Processes, Getprop, Settings, SMS] diff --git a/mvt/android/modules/androidqf/sms.py b/mvt/android/modules/androidqf/sms.py new file mode 100644 index 0000000..fdaf669 --- /dev/null +++ b/mvt/android/modules/androidqf/sms.py @@ -0,0 +1,85 @@ +# Mobile Verification Toolkit (MVT) - Private +# Copyright (c) 2021-2022 Claudio Guarnieri. +# This file is part of MVT Private and its content is confidential. +# Please refer to the project maintainers before sharing with others. + +import getpass +import logging +from typing import Optional + +from mvt.android.parsers.backup import (AndroidBackupParsingError, + InvalidBackupPassword, parse_ab_header, + parse_backup_file, parse_tar_for_sms) + +from .base import AndroidQFModule + + +class SMS(AndroidQFModule): + """This module analyse SMS file in backup""" + + 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) + + def check_indicators(self) -> None: + if not self.indicators: + return + + for message in self.results: + if "body" not in message: + continue + + if self.indicators.check_domains(message["links"]): + self.detected.append(message) + + def parse_backup(self, data): + header = parse_ab_header(data) + if not header["backup"]: + self.log.critical("Invalid backup format, backup.ab was not analysed") + return + + password = None + if header["encryption"] != "none": + password = getpass.getpass(prompt="Backup Password: ", stream=None) + try: + tardata = parse_backup_file(data, password=password) + except InvalidBackupPassword: + self.log.critical("Invalid backup password") + return + except AndroidBackupParsingError: + self.log.critical("Impossible to parse this backup file, please use" + " Android Backup Extractor instead") + return + + if not tardata: + return + + try: + self.results = parse_tar_for_sms(tardata) + except AndroidBackupParsingError: + self.log.info("Impossible to read SMS from the Android Backup, " + "please extract the SMS and try extracting it with " + "Android Backup Extractor") + return + + def run(self) -> None: + files = self._get_files_by_pattern("*/backup.ab") + if not files: + self.log.info("No backup data found") + return + + with open(files[0], "rb") as handle: + data = handle.read() + + self.parse_backup(data) + self.log.info("Identified %d SMS in backup data", + len(self.results)) diff --git a/tests/android/test_androidqf_getprop.py b/tests/android/test_androidqf_getprop.py new file mode 100644 index 0000000..450e45d --- /dev/null +++ b/tests/android/test_androidqf_getprop.py @@ -0,0 +1,22 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 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 os +import logging + +from mvt.android.modules.androidqf.getprop import Getprop +from mvt.common.module import run_module + +from ..utils import get_artifact_folder + + +class TestAndroidqfGetpropAnalysis: + + def test_androidqf_getprop(self): + m = Getprop(target_path=os.path.join(get_artifact_folder(), "androidqf"), log=logging) + run_module(m) + assert len(m.results) == 10 + assert len(m.timeline) == 0 + assert len(m.detected) == 0 diff --git a/tests/android/test_androidqf_processes.py b/tests/android/test_androidqf_processes.py new file mode 100644 index 0000000..d207979 --- /dev/null +++ b/tests/android/test_androidqf_processes.py @@ -0,0 +1,21 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 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 os +import logging + +from mvt.android.modules.androidqf.processes import Processes +from mvt.common.module import run_module + +from ..utils import get_artifact_folder + + +class TestAndroidqfProcessesAnalysis: + def test_androidqf_processes(self): + m = Processes(target_path=os.path.join(get_artifact_folder(), "androidqf"), log=logging) + run_module(m) + assert len(m.results) == 15 + assert len(m.timeline) == 0 + assert len(m.detected) == 0 diff --git a/tests/android/test_androidqf_sms.py b/tests/android/test_androidqf_sms.py new file mode 100644 index 0000000..c4393c8 --- /dev/null +++ b/tests/android/test_androidqf_sms.py @@ -0,0 +1,21 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 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 os +import logging + +from mvt.android.modules.androidqf.sms import SMS +from mvt.common.module import run_module + +from ..utils import get_artifact_folder + + +class TestAndroidqfSMSAnalysis: + def test_androidqf_sms(self): + m = SMS(target_path=os.path.join(get_artifact_folder(), "androidqf"), log=logging) + run_module(m) + assert len(m.results) == 2 + assert len(m.timeline) == 0 + assert len(m.detected) == 0 diff --git a/tests/artifacts/androidqf/backup.ab b/tests/artifacts/androidqf/backup.ab new file mode 100644 index 0000000..ee6a076 Binary files /dev/null and b/tests/artifacts/androidqf/backup.ab differ diff --git a/tests/artifacts/androidqf/getprop.txt b/tests/artifacts/androidqf/getprop.txt new file mode 100644 index 0000000..3f29346 --- /dev/null +++ b/tests/artifacts/androidqf/getprop.txt @@ -0,0 +1,10 @@ +[dalvik.vm.appimageformat]: [lz4] +[dalvik.vm.dex2oat-Xms]: [64m] +[dalvik.vm.dex2oat-Xmx]: [512m] +[dalvik.vm.dex2oat-max-image-block-size]: [524288] +[dalvik.vm.dex2oat-minidebuginfo]: [true] +[dalvik.vm.dex2oat-resolve-startup-strings]: [true] +[dalvik.vm.dexopt.secondary]: [true] +[dalvik.vm.heapgrowthlimit]: [128m] +[dalvik.vm.heapmaxfree]: [8m] +[dalvik.vm.heapminfree]: [512k] diff --git a/tests/artifacts/androidqf/ps.txt b/tests/artifacts/androidqf/ps.txt new file mode 100644 index 0000000..335d72f --- /dev/null +++ b/tests/artifacts/androidqf/ps.txt @@ -0,0 +1,16 @@ +USER PID PPID VSZ RSS WCHAN ADDR S NAME +root 1 0 57912 2084 0 0 S init +root 2 0 0 0 0 0 S [kthreadd] +root 3 2 0 0 0 0 S [ksoftirqd/0] +root 5 2 0 0 0 0 S [kworker/0:0H] +root 6 2 0 0 0 0 S [kworker/u16:0] +root 7 2 0 0 0 0 S [rcu_preempt] +root 8 2 0 0 0 0 S [rcu_sched] +root 9 2 0 0 0 0 S [rcu_bh] +root 10 2 0 0 0 0 S [migration/0] +root 11 2 0 0 0 0 D [tz_worker_threa] +root 12 2 0 0 0 0 S [watchdog/0] +root 13 2 0 0 0 0 S [watchdog/1] +root 14 2 0 0 0 0 D [tz_worker_threa] +root 15 2 0 0 0 0 S [migration/1] +root 16 2 0 0 0 0 S [ksoftirqd/1] diff --git a/tests/test_check_android_androidqf.py b/tests/test_check_android_androidqf.py new file mode 100644 index 0000000..c58fc6a --- /dev/null +++ b/tests/test_check_android_androidqf.py @@ -0,0 +1,20 @@ +# Mobile Verification Toolkit (MVT) +# Copyright (c) 2021-2022 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 os +from click.testing import CliRunner + +from mvt.android.cli import check_androidqf + +from .utils import get_artifact_folder + + +class TestCheckAndroidqfCommand: + + def test_check(self): + runner = CliRunner() + path = os.path.join(get_artifact_folder(), "androidqf") + result = runner.invoke(check_androidqf, [path]) + assert result.exit_code == 0 diff --git a/tests/test_check_bugreport.py b/tests/test_check_android_bugreport.py similarity index 100% rename from tests/test_check_bugreport.py rename to tests/test_check_android_bugreport.py diff --git a/tests/test_check_backup.py b/tests/test_check_ios_backup.py similarity index 100% rename from tests/test_check_backup.py rename to tests/test_check_ios_backup.py