mirror of
https://github.com/mvt-project/mvt.git
synced 2026-02-16 02:12:46 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99e14ad8b0 | ||
|
|
deaa68a2e0 | ||
|
|
07f819bf5f | ||
|
|
51fdfce7f4 | ||
|
|
41e05a107e | ||
|
|
e559fb223b | ||
|
|
b69bb92f3d | ||
|
|
42e8e41b7d | ||
|
|
00b7314395 | ||
|
|
39a8bf236d | ||
|
|
d268b17284 | ||
|
|
66c015bc23 | ||
|
|
ba0106c476 | ||
|
|
41826d7951 | ||
|
|
4e0a393a02 | ||
|
|
c3dc4174fc | ||
|
|
e1d1b6c5de | ||
|
|
d0a893841b | ||
|
|
d4e99661c7 | ||
|
|
6a00d3a14d | ||
|
|
a863209abb | ||
|
|
4c7db02da4 | ||
|
|
92dfefbdeb | ||
|
|
8988adcf77 | ||
|
|
91667b0ded | ||
|
|
2365175dbd | ||
|
|
528d43b914 | ||
|
|
f952ba5119 | ||
|
|
d61b2751f1 | ||
|
|
b4ed2c6ed4 | ||
|
|
3eed1d6edf | ||
|
|
83ef545cd1 | ||
|
|
5d4fbec62b | ||
|
|
fa7d6166f4 | ||
|
|
067402831a |
18
Dockerfile
18
Dockerfile
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
# Ref. https://github.com/mvt-project/mvt
|
||||
|
||||
@@ -7,13 +7,12 @@ LABEL vcs-url="https://github.com/mvt-project/mvt"
|
||||
LABEL description="MVT is a forensic tool to look for signs of infection in smartphone devices."
|
||||
|
||||
ENV PIP_NO_CACHE_DIR=1
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Fixing major OS dependencies
|
||||
# ----------------------------
|
||||
RUN apt update \
|
||||
&& apt install -y python3 python3-pip libusb-1.0-0-dev \
|
||||
&& apt install -y wget unzip\
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get -y install default-jre-headless \
|
||||
&& apt install -y python3 python3-pip libusb-1.0-0-dev wget unzip default-jre-headless adb \
|
||||
|
||||
# Install build tools for libimobiledevice
|
||||
# ----------------------------------------
|
||||
@@ -67,18 +66,9 @@ RUN mkdir /opt/abe \
|
||||
# Create alias for abe
|
||||
&& echo 'alias abe="java -jar /opt/abe/abe.jar"' >> ~/.bashrc
|
||||
|
||||
# Install Android Platform Tools
|
||||
# ------------------------------
|
||||
|
||||
RUN mkdir /opt/android \
|
||||
&& wget -q https://dl.google.com/android/repository/platform-tools-latest-linux.zip \
|
||||
&& unzip platform-tools-latest-linux.zip -d /opt/android \
|
||||
# Create alias for adb
|
||||
&& echo 'alias adb="/opt/android/platform-tools/adb"' >> ~/.bashrc
|
||||
|
||||
# Generate adb key folder
|
||||
# ------------------------------
|
||||
RUN mkdir /root/.android && /opt/android/platform-tools/adb keygen /root/.android/adbkey
|
||||
RUN mkdir /root/.android && adb keygen /root/.android/adbkey
|
||||
|
||||
# Setup investigations environment
|
||||
# --------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Using Docker simplifies having all the required dependencies and tools (including most recent versions of [libimobiledevice](https://libimobiledevice.org)) readily installed.
|
||||
Using Docker simplifies having all the required dependencies and tools (including most recent versions of [libimobiledevice](https://libimobiledevice.org)) readily installed. Note that this requires a Linux host, as Docker for Windows and Mac [doesn't support passing through USB devices](https://docs.docker.com/desktop/faqs/#can-i-pass-through-a-usb-device-to-a-container).
|
||||
|
||||
Install Docker following the [official documentation](https://docs.docker.com/get-docker/).
|
||||
|
||||
@@ -10,11 +10,6 @@ cd mvt
|
||||
docker build -t mvt .
|
||||
```
|
||||
|
||||
Optionally, you may need to specify your platform to Docker in order to build successfully (Apple M1)
|
||||
```bash
|
||||
docker build --platform amd64 -t mvt .
|
||||
```
|
||||
|
||||
Test if the image was created successfully:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
If you have correctly [installed libimobiledevice](../install.md) you can easily generate an iTunes backup using the `idevicebackup2` tool included in the suite. First, you might want to ensure that backup encryption is enabled (**note: encrypted backup contain more data than unencrypted backups**):
|
||||
|
||||
```bash
|
||||
idevicebackup2 -i backup encryption on
|
||||
idevicebackup2 -i encryption on
|
||||
```
|
||||
|
||||
Note that if a backup password was previously set on this device, you might need to use the same or change it. You can try changing password using `idevicebackup2 -i backup changepw`, or by turning off encryption (`idevicebackup2 -i backup encryption off`) and turning it back on again.
|
||||
Note that if a backup password was previously set on this device, you might need to use the same or change it. You can try changing password using `idevicebackup2 -i changepw`, or by turning off encryption (`idevicebackup2 -i encryption off`) and turning it back on again.
|
||||
|
||||
If you are not able to recover or change the password, you should try to disable encryption and obtain an unencrypted backup.
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ from mvt.common.logo import logo
|
||||
from mvt.common.updates import IndicatorsUpdates
|
||||
|
||||
from .cmd_check_adb import CmdAndroidCheckADB
|
||||
from .cmd_check_androidqf import CmdAndroidCheckAndroidQF
|
||||
from .cmd_check_backup import CmdAndroidCheckBackup
|
||||
from .cmd_check_bugreport import CmdAndroidCheckBugreport
|
||||
from .cmd_download_apks import DownloadAPKs
|
||||
@@ -121,9 +122,9 @@ def check_adb(ctx, serial, iocs, output, fast, list_modules, module):
|
||||
|
||||
cmd.run()
|
||||
|
||||
if len(cmd.timeline_detected) > 0:
|
||||
if cmd.detected_count > 0:
|
||||
log.warning("The analysis of the Android device produced %d detections!",
|
||||
len(cmd.timeline_detected))
|
||||
cmd.detected_count)
|
||||
|
||||
|
||||
#==============================================================================
|
||||
@@ -151,9 +152,9 @@ def check_bugreport(ctx, iocs, output, list_modules, module, bugreport_path):
|
||||
|
||||
cmd.run()
|
||||
|
||||
if len(cmd.timeline_detected) > 0:
|
||||
if cmd.detected_count > 0:
|
||||
log.warning("The analysis of the Android bug report produced %d detections!",
|
||||
len(cmd.timeline_detected))
|
||||
cmd.detected_count)
|
||||
|
||||
|
||||
#==============================================================================
|
||||
@@ -179,9 +180,39 @@ def check_backup(ctx, iocs, output, list_modules, backup_path):
|
||||
|
||||
cmd.run()
|
||||
|
||||
if len(cmd.timeline_detected) > 0:
|
||||
if cmd.detected_count > 0:
|
||||
log.warning("The analysis of the Android backup produced %d detections!",
|
||||
len(cmd.timeline_detected))
|
||||
cmd.detected_count)
|
||||
|
||||
|
||||
#==============================================================================
|
||||
# Command: check-androidqf
|
||||
#==============================================================================
|
||||
@cli.command("check-androidqf", help="Check data collected with AndroidQF")
|
||||
@click.option("--iocs", "-i", type=click.Path(exists=True), multiple=True,
|
||||
default=[], help=HELP_MSG_IOC)
|
||||
@click.option("--output", "-o", type=click.Path(exists=False),
|
||||
help=HELP_MSG_OUTPUT)
|
||||
@click.option("--list-modules", "-l", is_flag=True, help=HELP_MSG_LIST_MODULES)
|
||||
@click.option("--module", "-m", help=HELP_MSG_MODULE)
|
||||
@click.argument("ANDROIDQF_PATH", type=click.Path(exists=True))
|
||||
@click.pass_context
|
||||
def check_androidqf(ctx, iocs, output, list_modules, module, androidqf_path):
|
||||
cmd = CmdAndroidCheckAndroidQF(target_path=androidqf_path,
|
||||
results_path=output, ioc_files=iocs,
|
||||
module_name=module)
|
||||
|
||||
if list_modules:
|
||||
cmd.list_modules()
|
||||
return
|
||||
|
||||
log.info("Checking AndroidQF acquisition at path: %s", androidqf_path)
|
||||
|
||||
cmd.run()
|
||||
|
||||
if cmd.detected_count > 0:
|
||||
log.warning("The analysis of the AndroidQF acquisition produced %d detections!",
|
||||
cmd.detected_count)
|
||||
|
||||
|
||||
#==============================================================================
|
||||
|
||||
32
mvt/android/cmd_check_androidqf.py
Normal file
32
mvt/android/cmd_check_androidqf.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# 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 logging
|
||||
from typing import Optional
|
||||
|
||||
from mvt.common.command import Command
|
||||
|
||||
from .modules.androidqf import ANDROIDQF_MODULES
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CmdAndroidCheckAndroidQF(Command):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
target_path: Optional[str] = None,
|
||||
results_path: Optional[str] = None,
|
||||
ioc_files: Optional[list] = None,
|
||||
module_name: Optional[str] = None,
|
||||
serial: Optional[str] = None,
|
||||
fast_mode: Optional[bool] = False,
|
||||
) -> None:
|
||||
super().__init__(target_path=target_path, results_path=results_path,
|
||||
ioc_files=ioc_files, module_name=module_name,
|
||||
serial=serial, fast_mode=fast_mode, log=log)
|
||||
|
||||
self.name = "check-androidqf"
|
||||
self.modules = ANDROIDQF_MODULES
|
||||
@@ -58,3 +58,7 @@ class CmdAndroidCheckBugreport(Command):
|
||||
module.from_zip(self.bugreport_archive, self.bugreport_files)
|
||||
else:
|
||||
module.from_folder(self.target_path, self.bugreport_files)
|
||||
|
||||
def finish(self) -> None:
|
||||
if self.bugreport_archive:
|
||||
self.bugreport_archive.close()
|
||||
|
||||
@@ -272,8 +272,8 @@ class AndroidExtraction(MVTModule):
|
||||
self._adb_command(f"rm -f {new_remote_path}")
|
||||
|
||||
def _generate_backup(self, package_name: str) -> bytes:
|
||||
self.log.warning("Please check phone and accept Android backup prompt. "
|
||||
"You may need to set a backup password. \a")
|
||||
self.log.info("Please check phone and accept Android backup prompt. "
|
||||
"You may need to set a backup password. \a")
|
||||
|
||||
# TODO: Base64 encoding as temporary fix to avoid byte-mangling over
|
||||
# the shell transport...
|
||||
@@ -300,7 +300,7 @@ class AndroidExtraction(MVTModule):
|
||||
except InvalidBackupPassword:
|
||||
self.log.error("You provided the wrong password! Please try again...")
|
||||
|
||||
self.log.warn("All attempts to decrypt backup with password failed!")
|
||||
self.log.error("All attempts to decrypt backup with password failed!")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -66,6 +66,23 @@ ROOT_PACKAGES = [
|
||||
"com.kingouser.com",
|
||||
"com.topjohnwu.magisk",
|
||||
]
|
||||
SECURITY_PACKAGES = [
|
||||
"com.policydm",
|
||||
"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",
|
||||
]
|
||||
|
||||
|
||||
class Packages(AndroidExtraction):
|
||||
@@ -122,6 +139,14 @@ class Packages(AndroidExtraction):
|
||||
self.detected.append(result)
|
||||
continue
|
||||
|
||||
if result["package_name"] in SECURITY_PACKAGES and result["disabled"]:
|
||||
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
|
||||
|
||||
@@ -192,7 +217,6 @@ class Packages(AndroidExtraction):
|
||||
|
||||
@staticmethod
|
||||
def parse_package_for_details(output: str) -> dict:
|
||||
# Get only the package information
|
||||
lines = []
|
||||
in_packages = False
|
||||
for line in output.splitlines():
|
||||
|
||||
@@ -30,7 +30,21 @@ class Processes(AndroidExtraction):
|
||||
return
|
||||
|
||||
for result in self.results:
|
||||
ioc = self.indicators.check_app_id(result.get("name", ""))
|
||||
proc_name = result.get("proc_name", "")
|
||||
if not proc_name:
|
||||
continue
|
||||
|
||||
# Skipping this process because of false positives.
|
||||
if result["proc_name"] == "gatekeeperd":
|
||||
continue
|
||||
|
||||
ioc = self.indicators.check_app_id(proc_name)
|
||||
if ioc:
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
continue
|
||||
|
||||
ioc = self.indicators.check_process(proc_name)
|
||||
if ioc:
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
@@ -38,7 +52,7 @@ class Processes(AndroidExtraction):
|
||||
def run(self) -> None:
|
||||
self._adb_connect()
|
||||
|
||||
output = self._adb_command("ps -e")
|
||||
output = self._adb_command("ps -A")
|
||||
|
||||
for line in output.splitlines()[1:]:
|
||||
line = line.strip()
|
||||
|
||||
@@ -158,7 +158,7 @@ class SMS(AndroidExtraction):
|
||||
except InsufficientPrivileges:
|
||||
pass
|
||||
|
||||
self.log.warn("No SMS database found. Trying extraction of SMS data "
|
||||
self.log.info("No SMS database found. Trying extraction of SMS data "
|
||||
"using Android backup feature.")
|
||||
self._extract_sms_adb()
|
||||
|
||||
|
||||
18
mvt/android/modules/androidqf/__init__.py
Normal file
18
mvt/android/modules/androidqf/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# 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/
|
||||
|
||||
from .dumpsys_accessibility import DumpsysAccessibility
|
||||
from .dumpsys_activities import DumpsysActivities
|
||||
from .dumpsys_appops import DumpsysAppops
|
||||
from .dumpsys_packages import DumpsysPackages
|
||||
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, SMS,
|
||||
DumpsysPackages]
|
||||
38
mvt/android/modules/androidqf/base.py
Normal file
38
mvt/android/modules/androidqf/base.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# 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 fnmatch
|
||||
import logging
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from mvt.common.module import MVTModule
|
||||
|
||||
|
||||
class AndroidQFModule(MVTModule):
|
||||
"""This class provides a base for all Android Data analysis modules."""
|
||||
|
||||
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._path = target_path
|
||||
self._files = []
|
||||
|
||||
for root, dirs, files in os.walk(target_path):
|
||||
for name in files:
|
||||
self._files.append(os.path.join(root, name))
|
||||
|
||||
def _get_files_by_pattern(self, pattern):
|
||||
return fnmatch.filter(self._files, pattern)
|
||||
68
mvt/android/modules/androidqf/dumpsys_accessibility.py
Normal file
68
mvt/android/modules/androidqf/dumpsys_accessibility.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# 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 logging
|
||||
from typing import Optional
|
||||
|
||||
from mvt.android.parsers import parse_dumpsys_accessibility
|
||||
|
||||
from .base import AndroidQFModule
|
||||
|
||||
|
||||
class DumpsysAccessibility(AndroidQFModule):
|
||||
"""This module analyse dumpsys accessbility"""
|
||||
|
||||
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 result in self.results:
|
||||
ioc = self.indicators.check_app_id(result["package_name"])
|
||||
if ioc:
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
|
||||
def run(self) -> None:
|
||||
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
|
||||
if not dumpsys_file:
|
||||
return
|
||||
|
||||
lines = []
|
||||
in_accessibility = False
|
||||
with open(dumpsys_file[0]) as handle:
|
||||
for line in handle:
|
||||
if line.strip().startswith("DUMP OF SERVICE accessibility:"):
|
||||
in_accessibility = True
|
||||
continue
|
||||
|
||||
if not in_accessibility:
|
||||
continue
|
||||
|
||||
if line.strip().startswith("-------------------------------------------------------------------------------"): # pylint: disable=line-too-long
|
||||
break
|
||||
|
||||
lines.append(line.rstrip())
|
||||
|
||||
self.results = parse_dumpsys_accessibility("\n".join(lines))
|
||||
|
||||
for result in self.results:
|
||||
self.log.info("Found installed accessibility service \"%s\"",
|
||||
result.get("service"))
|
||||
|
||||
self.log.info("Identified a total of %d accessibility services",
|
||||
len(self.results))
|
||||
66
mvt/android/modules/androidqf/dumpsys_activities.py
Normal file
66
mvt/android/modules/androidqf/dumpsys_activities.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# 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 logging
|
||||
from typing import Optional
|
||||
|
||||
from mvt.android.parsers import parse_dumpsys_activity_resolver_table
|
||||
|
||||
from .base import AndroidQFModule
|
||||
|
||||
|
||||
class DumpsysActivities(AndroidQFModule):
|
||||
"""This module extracts details on receivers for risky activities."""
|
||||
|
||||
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 = results if results else {}
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
if not self.indicators:
|
||||
return
|
||||
|
||||
for intent, activities in self.results.items():
|
||||
for activity in activities:
|
||||
ioc = self.indicators.check_app_id(activity["package_name"])
|
||||
if ioc:
|
||||
activity["matched_indicator"] = ioc
|
||||
self.detected.append({intent: activity})
|
||||
|
||||
def run(self) -> None:
|
||||
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
|
||||
if not dumpsys_file:
|
||||
return
|
||||
|
||||
lines = []
|
||||
in_package = False
|
||||
with open(dumpsys_file[0]) as handle:
|
||||
for line in handle:
|
||||
if line.strip() == "DUMP OF SERVICE package:":
|
||||
in_package = True
|
||||
continue
|
||||
|
||||
if not in_package:
|
||||
continue
|
||||
|
||||
if line.strip().startswith("------------------------------------------------------------------------------"): # pylint: disable=line-too-long
|
||||
break
|
||||
|
||||
lines.append(line.rstrip())
|
||||
|
||||
self.results = parse_dumpsys_activity_resolver_table("\n".join(lines))
|
||||
|
||||
self.log.info("Extracted activities for %d intents", len(self.results))
|
||||
83
mvt/android/modules/androidqf/dumpsys_appops.py
Normal file
83
mvt/android/modules/androidqf/dumpsys_appops.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# 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 logging
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.android.parsers import parse_dumpsys_appops
|
||||
|
||||
from .base import AndroidQFModule
|
||||
|
||||
|
||||
class DumpsysAppops(AndroidQFModule):
|
||||
|
||||
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 serialize(self, record: dict) -> Union[dict, list]:
|
||||
records = []
|
||||
for perm in record["permissions"]:
|
||||
if "entries" not in perm:
|
||||
continue
|
||||
|
||||
for entry in perm["entries"]:
|
||||
if "timestamp" in entry:
|
||||
records.append({
|
||||
"timestamp": entry["timestamp"],
|
||||
"module": self.__class__.__name__,
|
||||
"event": entry["access"],
|
||||
"data": f"{record['package_name']} access to "
|
||||
f"{perm['name']} : {entry['access']}",
|
||||
})
|
||||
|
||||
return records
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
for result in self.results:
|
||||
if self.indicators:
|
||||
ioc = self.indicators.check_app_id(result.get("package_name"))
|
||||
if ioc:
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
continue
|
||||
|
||||
for perm in result["permissions"]:
|
||||
if (perm["name"] == "REQUEST_INSTALL_PACKAGES"
|
||||
and perm["access"] == "allow"):
|
||||
self.log.info("Package %s with REQUEST_INSTALL_PACKAGES permission",
|
||||
result["package_name"])
|
||||
|
||||
def run(self) -> None:
|
||||
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
|
||||
if not dumpsys_file:
|
||||
return
|
||||
|
||||
lines = []
|
||||
in_package = False
|
||||
with open(dumpsys_file[0]) as handle:
|
||||
for line in handle:
|
||||
if line.startswith("DUMP OF SERVICE appops:"):
|
||||
in_package = True
|
||||
continue
|
||||
|
||||
if in_package:
|
||||
if line.startswith("-------------------------------------------------------------------------------"): # pylint: disable=line-too-long
|
||||
break
|
||||
|
||||
lines.append(line.rstrip())
|
||||
|
||||
self.results = parse_dumpsys_appops("\n".join(lines))
|
||||
self.log.info("Identified %d applications in AppOps Manager",
|
||||
len(self.results))
|
||||
108
mvt/android/modules/androidqf/dumpsys_packages.py
Normal file
108
mvt/android/modules/androidqf/dumpsys_packages.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# 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 logging
|
||||
from datetime import datetime
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.android.modules.adb.packages import (DANGEROUS_PERMISSIONS,
|
||||
DANGEROUS_PERMISSIONS_THRESHOLD,
|
||||
ROOT_PACKAGES)
|
||||
from mvt.android.parsers.dumpsys import parse_dumpsys_packages
|
||||
from mvt.common.utils import convert_datetime_to_iso
|
||||
|
||||
from .base import AndroidQFModule
|
||||
|
||||
|
||||
class DumpsysPackages(AndroidQFModule):
|
||||
"""This module analyse dumpsys packages"""
|
||||
|
||||
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 serialize(self, record: dict) -> Union[dict, list]:
|
||||
entries = []
|
||||
for entry in ["timestamp", "first_install_time", "last_update_time"]:
|
||||
if entry in record:
|
||||
entries.append({
|
||||
"timestamp": record[entry],
|
||||
"module": self.__class__.__name__,
|
||||
"event": entry,
|
||||
"data": f"Package {record['package_name']} "
|
||||
f"({record['uid']})",
|
||||
})
|
||||
|
||||
return entries
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
for result in self.results:
|
||||
if result["package_name"] in ROOT_PACKAGES:
|
||||
self.log.warning("Found an installed package related to "
|
||||
"rooting/jailbreaking: \"%s\"",
|
||||
result["package_name"])
|
||||
self.detected.append(result)
|
||||
continue
|
||||
|
||||
if not self.indicators:
|
||||
continue
|
||||
|
||||
ioc = self.indicators.check_app_id(result.get("package_name", ""))
|
||||
if ioc:
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
|
||||
def run(self) -> None:
|
||||
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
|
||||
if len(dumpsys_file) != 1:
|
||||
self.log.info("Dumpsys file not found")
|
||||
return
|
||||
|
||||
with open(dumpsys_file[0]) as handle:
|
||||
data = handle.read().split("\n")
|
||||
|
||||
package = []
|
||||
in_service = False
|
||||
in_package_list = False
|
||||
for line in data:
|
||||
if line.strip().startswith("DUMP OF SERVICE package:"):
|
||||
in_service = True
|
||||
continue
|
||||
|
||||
if in_service and line.startswith("Packages:"):
|
||||
in_package_list = True
|
||||
continue
|
||||
|
||||
if not in_service or not in_package_list:
|
||||
continue
|
||||
|
||||
if line.strip() == "":
|
||||
break
|
||||
|
||||
package.append(line)
|
||||
|
||||
self.results = parse_dumpsys_packages("\n".join(package))
|
||||
|
||||
for result in self.results:
|
||||
dangerous_permissions_count = 0
|
||||
for perm in result["permissions"]:
|
||||
if perm["name"] in DANGEROUS_PERMISSIONS:
|
||||
dangerous_permissions_count += 1
|
||||
|
||||
if dangerous_permissions_count >= DANGEROUS_PERMISSIONS_THRESHOLD:
|
||||
self.log.info("Found package \"%s\" requested %d potentially dangerous permissions",
|
||||
result["package_name"],
|
||||
dangerous_permissions_count)
|
||||
|
||||
self.log.info("Extracted details on %d packages", len(self.results))
|
||||
86
mvt/android/modules/androidqf/dumpsys_receivers.py
Normal file
86
mvt/android/modules/androidqf/dumpsys_receivers.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# 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 logging
|
||||
from typing import Optional
|
||||
|
||||
from mvt.android.modules.adb.dumpsys_receivers import (
|
||||
INTENT_DATA_SMS_RECEIVED, INTENT_NEW_OUTGOING_CALL,
|
||||
INTENT_NEW_OUTGOING_SMS, INTENT_PHONE_STATE, INTENT_SMS_RECEIVED)
|
||||
from mvt.android.parsers import parse_dumpsys_receiver_resolver_table
|
||||
|
||||
from .base import AndroidQFModule
|
||||
|
||||
|
||||
class DumpsysReceivers(AndroidQFModule):
|
||||
"""This module analyse dumpsys receivers"""
|
||||
|
||||
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 = results if results else {}
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
if not self.indicators:
|
||||
return
|
||||
|
||||
for intent, receivers in self.results.items():
|
||||
for receiver in receivers:
|
||||
if intent == INTENT_NEW_OUTGOING_SMS:
|
||||
self.log.info("Found a receiver to intercept outgoing SMS messages: \"%s\"",
|
||||
receiver["receiver"])
|
||||
elif intent == INTENT_SMS_RECEIVED:
|
||||
self.log.info("Found a receiver to intercept incoming SMS messages: \"%s\"",
|
||||
receiver["receiver"])
|
||||
elif intent == INTENT_DATA_SMS_RECEIVED:
|
||||
self.log.info("Found a receiver to intercept incoming data SMS message: \"%s\"",
|
||||
receiver["receiver"])
|
||||
elif intent == INTENT_PHONE_STATE:
|
||||
self.log.info("Found a receiver monitoring "
|
||||
"telephony state/incoming calls: \"%s\"",
|
||||
receiver["receiver"])
|
||||
elif intent == INTENT_NEW_OUTGOING_CALL:
|
||||
self.log.info("Found a receiver monitoring outgoing calls: \"%s\"",
|
||||
receiver["receiver"])
|
||||
|
||||
ioc = self.indicators.check_app_id(receiver["package_name"])
|
||||
if ioc:
|
||||
receiver["matched_indicator"] = ioc
|
||||
self.detected.append({intent: receiver})
|
||||
|
||||
def run(self) -> None:
|
||||
dumpsys_file = self._get_files_by_pattern("*/dumpsys.txt")
|
||||
if not dumpsys_file:
|
||||
return
|
||||
|
||||
in_receivers = False
|
||||
lines = []
|
||||
with open(dumpsys_file[0]) as handle:
|
||||
for line in handle:
|
||||
if line.strip() == "DUMP OF SERVICE package:":
|
||||
in_receivers = True
|
||||
continue
|
||||
|
||||
if not in_receivers:
|
||||
continue
|
||||
|
||||
if line.strip().startswith("------------------------------------------------------------------------------"): # pylint: disable=line-too-long
|
||||
break
|
||||
|
||||
lines.append(line.rstrip())
|
||||
|
||||
self.results = parse_dumpsys_receiver_resolver_table("\n".join(lines))
|
||||
|
||||
self.log.info("Extracted receivers for %d intents", len(self.results))
|
||||
66
mvt/android/modules/androidqf/getprop.py
Normal file
66
mvt/android/modules/androidqf/getprop.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# 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 logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
from mvt.android.parsers import getprop
|
||||
|
||||
from .base import AndroidQFModule
|
||||
|
||||
INTERESTING_PROPERTIES = [
|
||||
"gsm.sim.operator.alpha",
|
||||
"gsm.sim.operator.iso-country",
|
||||
"persist.sys.timezone",
|
||||
"ro.boot.serialno",
|
||||
"ro.build.version.sdk",
|
||||
"ro.build.version.security_patch",
|
||||
"ro.product.cpu.abi",
|
||||
"ro.product.locale",
|
||||
"ro.product.vendor.manufacturer",
|
||||
"ro.product.vendor.model",
|
||||
"ro.product.vendor.name"
|
||||
]
|
||||
|
||||
|
||||
class Getprop(AndroidQFModule):
|
||||
"""This module extracts data from get properties."""
|
||||
|
||||
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 = {}
|
||||
|
||||
def run(self) -> None:
|
||||
getprop_files = self._get_files_by_pattern("*/getprop.txt")
|
||||
if not getprop_files:
|
||||
self.log.info("getprop.txt file not found")
|
||||
return
|
||||
|
||||
with open(getprop_files[0]) as f:
|
||||
data = f.read()
|
||||
|
||||
self.results = getprop.parse_getprop(data)
|
||||
for entry in self.results:
|
||||
if entry in INTERESTING_PROPERTIES:
|
||||
self.log.info("%s: %s", entry, self.results[entry])
|
||||
if entry == "ro.build.version.security_patch":
|
||||
last_patch = datetime.strptime(self.results[entry], "%Y-%m-%d")
|
||||
if (datetime.now() - last_patch) > timedelta(days=6*31):
|
||||
self.log.warning("This phone has not received security "
|
||||
"updates for more than six months "
|
||||
"(last update: %s)", self.results[entry])
|
||||
|
||||
self.log.info("Extracted a total of %d properties", len(self.results))
|
||||
92
mvt/android/modules/androidqf/processes.py
Normal file
92
mvt/android/modules/androidqf/processes.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# 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 logging
|
||||
from typing import Optional
|
||||
|
||||
from .base import AndroidQFModule
|
||||
|
||||
|
||||
class Processes(AndroidQFModule):
|
||||
"""This module analyse running processes"""
|
||||
|
||||
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 result in self.results:
|
||||
proc_name = result.get("proc_name", "")
|
||||
if not proc_name:
|
||||
continue
|
||||
|
||||
# Skipping this process because of false positives.
|
||||
if result["proc_name"] == "gatekeeperd":
|
||||
continue
|
||||
|
||||
ioc = self.indicators.check_app_id(proc_name)
|
||||
if ioc:
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
continue
|
||||
|
||||
ioc = self.indicators.check_process(proc_name)
|
||||
if ioc:
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
|
||||
def _parse_ps(self, data):
|
||||
for line in data.split("\n")[1:]:
|
||||
proc = line.split()
|
||||
|
||||
# Sometimes WCHAN is empty.
|
||||
if len(proc) == 8:
|
||||
proc = proc[:5] + [''] + proc[5:]
|
||||
|
||||
# Sometimes there is the security label.
|
||||
if proc[0].startswith("u:r"):
|
||||
label = proc[0]
|
||||
proc = proc[1:]
|
||||
else:
|
||||
label = ""
|
||||
|
||||
# Sometimes there is no WCHAN.
|
||||
if len(proc) < 9:
|
||||
proc = proc[:5] + [""] + proc[5:]
|
||||
|
||||
self.results.append({
|
||||
"user": proc[0],
|
||||
"pid": int(proc[1]),
|
||||
"ppid": int(proc[2]),
|
||||
"virtual_memory_size": int(proc[3]),
|
||||
"resident_set_size": int(proc[4]),
|
||||
"wchan": proc[5],
|
||||
"aprocress": proc[6],
|
||||
"stat": proc[7],
|
||||
"proc_name": proc[8].strip("[]"),
|
||||
"label": label,
|
||||
})
|
||||
|
||||
def run(self) -> None:
|
||||
ps_files = self._get_files_by_pattern("*/ps.txt")
|
||||
if not ps_files:
|
||||
return
|
||||
|
||||
with open(ps_files[0]) as handle:
|
||||
self._parse_ps(handle.read())
|
||||
|
||||
self.log.info("Identified %d running processes", len(self.results))
|
||||
58
mvt/android/modules/androidqf/settings.py
Normal file
58
mvt/android/modules/androidqf/settings.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# 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 logging
|
||||
from typing import Optional
|
||||
|
||||
from mvt.android.modules.adb.settings import ANDROID_DANGEROUS_SETTINGS
|
||||
|
||||
from .base import AndroidQFModule
|
||||
|
||||
|
||||
class Settings(AndroidQFModule):
|
||||
"""This module analyse setting files"""
|
||||
|
||||
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 = {}
|
||||
|
||||
def run(self) -> None:
|
||||
for setting_file in self._get_files_by_pattern("*/settings_*.txt"):
|
||||
namespace = setting_file[setting_file.rfind("_")+1:-4]
|
||||
|
||||
self.results[namespace] = {}
|
||||
|
||||
with open(setting_file) as handle:
|
||||
for line in handle:
|
||||
line = line.strip()
|
||||
try:
|
||||
key, value = line.split("=", 1)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
try:
|
||||
self.results[namespace][key] = value
|
||||
except IndexError:
|
||||
continue
|
||||
|
||||
for danger in ANDROID_DANGEROUS_SETTINGS:
|
||||
if (danger["key"] == key
|
||||
and danger["safe_value"] != value):
|
||||
self.log.warning("Found suspicious setting \"%s = %s\" (%s)",
|
||||
key, value, danger["description"])
|
||||
break
|
||||
|
||||
self.log.info("Identified %d settings",
|
||||
sum([len(val) for val in self.results.values()]))
|
||||
85
mvt/android/modules/androidqf/sms.py
Normal file
85
mvt/android/modules/androidqf/sms.py
Normal file
@@ -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))
|
||||
@@ -6,10 +6,10 @@
|
||||
import logging
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.android.parsers.dumpsys import parse_dumpsys_packages
|
||||
from mvt.android.modules.adb.packages import (DANGEROUS_PERMISSIONS,
|
||||
DANGEROUS_PERMISSIONS_THRESHOLD,
|
||||
ROOT_PACKAGES)
|
||||
from mvt.android.parsers.dumpsys import parse_dumpsys_packages
|
||||
|
||||
from .base import BugReportModule
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ class Command:
|
||||
# We can use this to reference e.g. self.executed[0].results.
|
||||
self.executed = []
|
||||
|
||||
self.detected_count = 0
|
||||
|
||||
self.timeline = []
|
||||
self.timeline_detected = []
|
||||
|
||||
@@ -196,6 +198,8 @@ class Command:
|
||||
|
||||
self.executed.append(m)
|
||||
|
||||
self.detected_count += len(m.detected)
|
||||
|
||||
self.timeline.extend(m.timeline)
|
||||
self.timeline_detected.extend(m.timeline_detected)
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ class Indicators:
|
||||
|
||||
paths = os.environ["MVT_STIX2"].split(":")
|
||||
for path in paths:
|
||||
print(path)
|
||||
if os.path.isfile(path):
|
||||
self.parse_stix2(path)
|
||||
else:
|
||||
@@ -437,6 +436,27 @@ class Indicators:
|
||||
|
||||
return None
|
||||
|
||||
def check_file_path_process(self, file_path: str) -> Union[dict, None]:
|
||||
"""Check the provided file path contains a process name from the
|
||||
list of indicators
|
||||
|
||||
:param file_path: File path or file name to check against file
|
||||
indicators
|
||||
:type file_path: str
|
||||
:returns: Indicator details if matched, otherwise None
|
||||
|
||||
"""
|
||||
if not file_path:
|
||||
return None
|
||||
|
||||
for ioc in self.get_iocs("processes"):
|
||||
parts = file_path.split("/")
|
||||
if ioc["value"] in parts:
|
||||
self.log.warning("Found known suspicious process name mentioned in file at "
|
||||
"path \"%s\" matching indicators from \"%s\"",
|
||||
file_path, ioc["name"])
|
||||
return ioc
|
||||
|
||||
def check_profile(self, profile_uuid: str) -> Union[dict, None]:
|
||||
"""Check the provided configuration profile UUID against the list of
|
||||
indicators.
|
||||
|
||||
@@ -19,7 +19,7 @@ def check_updates() -> None:
|
||||
else:
|
||||
if latest_version:
|
||||
rich_print(f"\t\t[bold]Version {latest_version} is available! "
|
||||
"Upgrade mvt![/bold]")
|
||||
"Upgrade mvt with `pip3 install -U mvt`[/bold]")
|
||||
|
||||
# Then we check for indicators files updates.
|
||||
ioc_updates = IndicatorsUpdates()
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# Use of this software is governed by the MVT License 1.1 that can be found at
|
||||
# https://license.mvt.re/1.1/
|
||||
|
||||
MVT_VERSION = "2.1.5"
|
||||
MVT_VERSION = "2.2.1"
|
||||
|
||||
@@ -162,9 +162,9 @@ def check_backup(ctx, iocs, output, fast, list_modules, module, backup_path):
|
||||
|
||||
cmd.run()
|
||||
|
||||
if len(cmd.timeline_detected) > 0:
|
||||
if cmd.detected_count > 0:
|
||||
log.warning("The analysis of the backup produced %d detections!",
|
||||
len(cmd.timeline_detected))
|
||||
cmd.detected_count)
|
||||
|
||||
|
||||
#==============================================================================
|
||||
@@ -192,9 +192,9 @@ def check_fs(ctx, iocs, output, fast, list_modules, module, dump_path):
|
||||
|
||||
cmd.run()
|
||||
|
||||
if len(cmd.timeline_detected) > 0:
|
||||
if cmd.detected_count > 0:
|
||||
log.warning("The analysis of the iOS filesystem produced %d detections!",
|
||||
len(cmd.timeline_detected))
|
||||
cmd.detected_count)
|
||||
|
||||
|
||||
#==============================================================================
|
||||
|
||||
@@ -80,9 +80,6 @@ class Manifest(IOSExtraction):
|
||||
return records
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
if not self.indicators:
|
||||
return
|
||||
|
||||
for result in self.results:
|
||||
if not result.get("relative_path"):
|
||||
continue
|
||||
@@ -95,6 +92,9 @@ class Manifest(IOSExtraction):
|
||||
self.detected.append(result)
|
||||
continue
|
||||
|
||||
if not self.indicators:
|
||||
continue
|
||||
|
||||
if self.indicators.check_file_path("/" + result["relative_path"]):
|
||||
self.detected.append(result)
|
||||
continue
|
||||
|
||||
@@ -11,6 +11,7 @@ from mvt.common.utils import convert_datetime_to_iso
|
||||
|
||||
from ..base import IOSExtraction
|
||||
|
||||
# CONF_PROFILES_EVENTS_ID = "aeb25de285ea542f7ac7c2070cddd1961e369df1"
|
||||
CONF_PROFILES_EVENTS_RELPATH = "Library/ConfigurationProfiles/MCProfileEvents.plist"
|
||||
|
||||
|
||||
|
||||
@@ -107,8 +107,12 @@ class IOSExtraction(MVTModule):
|
||||
(relative_path, domain))
|
||||
else:
|
||||
if relative_path:
|
||||
cur.execute(f"{base_sql} relativePath = ?;",
|
||||
(relative_path,))
|
||||
if "*" in relative_path:
|
||||
cur.execute(f"{base_sql} relativePath LIKE ?;",
|
||||
(relative_path.replace("*", "%"),))
|
||||
else:
|
||||
cur.execute(f"{base_sql} relativePath = ?;",
|
||||
(relative_path,))
|
||||
elif domain:
|
||||
cur.execute(f"{base_sql} domain = ?;", (domain,))
|
||||
except Exception as exc:
|
||||
@@ -166,14 +170,15 @@ class IOSExtraction(MVTModule):
|
||||
if file_path:
|
||||
break
|
||||
|
||||
# If this file does not exist we might be processing a full
|
||||
# filesystem dump (checkra1n all the things!).
|
||||
if not file_path or not os.path.exists(file_path):
|
||||
# We reset the file_path.
|
||||
file_path = None
|
||||
for found_path in self._get_fs_files_from_patterns(root_paths):
|
||||
file_path = found_path
|
||||
break
|
||||
if root_paths:
|
||||
# If this file does not exist we might be processing a full
|
||||
# filesystem dump (checkra1n all the things!).
|
||||
if not file_path or not os.path.exists(file_path):
|
||||
# We reset the file_path.
|
||||
file_path = None
|
||||
for found_path in self._get_fs_files_from_patterns(root_paths):
|
||||
file_path = found_path
|
||||
break
|
||||
|
||||
# If we do not find any, we fail.
|
||||
if file_path:
|
||||
|
||||
@@ -57,14 +57,10 @@ class Filesystem(IOSExtraction):
|
||||
if self.fast_mode:
|
||||
continue
|
||||
|
||||
for ioc in self.indicators.get_iocs("processes"):
|
||||
parts = result["path"].split("/")
|
||||
if ioc["value"] in parts:
|
||||
self.log.warning("Found known suspicious process name mentioned in file at "
|
||||
"path \"%s\" matching indicators from \"%s\"",
|
||||
result["path"], ioc["name"])
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
ioc = self.indicators.check_file_path_process(result["path"])
|
||||
if ioc:
|
||||
result["matched_indicator"] = ioc
|
||||
self.detected.append(result)
|
||||
|
||||
def run(self) -> None:
|
||||
for root, dirs, files in os.walk(self.target_path):
|
||||
|
||||
@@ -42,6 +42,9 @@ IPHONE_MODELS = [
|
||||
{"identifier": "iPhone14,5", "description": "iPhone 13"},
|
||||
{"identifier": "iPhone14,2", "description": "iPhone 13 Pro"},
|
||||
{"identifier": "iPhone14,3", "description": "iPhone 13 Pro Max"},
|
||||
{"identifier": "iPhone14,8", "decription": "iPhone 14 Plus"},
|
||||
{"identifier": "iPhone15,2", "description": "iPhone 14 Pro"},
|
||||
{"identifier": "iPhone15,3", "description": "iPhone 14 Pro Max"}
|
||||
]
|
||||
|
||||
IPHONE_IOS_VERSIONS = [
|
||||
@@ -243,6 +246,10 @@ IPHONE_IOS_VERSIONS = [
|
||||
{"build": "19F77", "version": "15.5"},
|
||||
{"build": "19G71", "version": "15.6"},
|
||||
{"build": "19G82", "version": "15.6.1"},
|
||||
{"build": "19H12", "version": "15.7"},
|
||||
{"build": "20A362", "version": "16.0"},
|
||||
{"build": "20B82", "version": "16.1"},
|
||||
{"build": "20B101", "version": "16.1.1"}
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -22,16 +22,16 @@ include_package_data = True
|
||||
python_requires = >= 3.8
|
||||
install_requires =
|
||||
click >=8.1.3
|
||||
rich >=12.4.4
|
||||
rich >=12.6.0
|
||||
tld >=0.12.6
|
||||
requests >=2.28.1
|
||||
simplejson >=3.17.6
|
||||
packaging >=21.3
|
||||
appdirs >=1.4.4
|
||||
iOSbackup >=0.9.921
|
||||
iOSbackup >=0.9.923
|
||||
adb-shell >=0.4.3
|
||||
libusb1 >=3.0.0
|
||||
cryptography >=37.0.4
|
||||
cryptography >=38.0.1
|
||||
pyyaml >=6.0
|
||||
|
||||
[options.packages.find]
|
||||
|
||||
0
tests/android_androidqf/__init__.py
Normal file
0
tests/android_androidqf/__init__.py
Normal file
22
tests/android_androidqf/test_dumpsysaccessbility.py
Normal file
22
tests/android_androidqf/test_dumpsysaccessbility.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# 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 logging
|
||||
|
||||
from mvt.common.module import run_module
|
||||
|
||||
from mvt.android.modules.androidqf.dumpsys_accessibility import \
|
||||
DumpsysAccessibility
|
||||
|
||||
from ..utils import get_android_androidqf
|
||||
|
||||
|
||||
class TestDumpsysAccessibilityModule:
|
||||
def test_parsing(self):
|
||||
data_path = get_android_androidqf()
|
||||
m = DumpsysAccessibility(target_path=data_path)
|
||||
run_module(m)
|
||||
assert len(m.results) == 4
|
||||
assert len(m.detected) == 0
|
||||
22
tests/android_androidqf/test_dumpsysappops.py
Normal file
22
tests/android_androidqf/test_dumpsysappops.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# 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 logging
|
||||
|
||||
from mvt.common.module import run_module
|
||||
|
||||
from mvt.android.modules.androidqf.dumpsys_appops import DumpsysAppops
|
||||
|
||||
from ..utils import get_android_androidqf
|
||||
|
||||
|
||||
class TestDumpsysAppOpsModule:
|
||||
def test_parsing(self):
|
||||
data_path = get_android_androidqf()
|
||||
m = DumpsysAppops(target_path=data_path)
|
||||
run_module(m)
|
||||
assert len(m.results) == 12
|
||||
assert len(m.timeline) == 16
|
||||
assert len(m.detected) == 0
|
||||
37
tests/android_androidqf/test_dumpsyspackages.py
Normal file
37
tests/android_androidqf/test_dumpsyspackages.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# 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 logging
|
||||
|
||||
from mvt.common.indicators import Indicators
|
||||
from mvt.common.module import run_module
|
||||
|
||||
from mvt.android.modules.androidqf.dumpsys_packages import DumpsysPackages
|
||||
|
||||
from ..utils import get_android_androidqf
|
||||
|
||||
|
||||
class TestDumpsysPackagesModule:
|
||||
def test_parsing(self):
|
||||
data_path = get_android_androidqf()
|
||||
m = DumpsysPackages(target_path=data_path)
|
||||
run_module(m)
|
||||
assert len(m.results) == 2
|
||||
assert len(m.detected) == 0
|
||||
assert len(m.timeline) == 6
|
||||
assert m.results[0]["package_name"] == "com.samsung.android.provider.filterprovider"
|
||||
|
||||
def test_detection_pkgname(self, indicator_file):
|
||||
data_path = get_android_androidqf()
|
||||
m = DumpsysPackages(target_path=data_path)
|
||||
ind = Indicators(log=logging.getLogger())
|
||||
ind.parse_stix2(indicator_file)
|
||||
ind.ioc_collections[0]["app_ids"].append("com.sec.android.app.DataCreate")
|
||||
m.indicators = ind
|
||||
run_module(m)
|
||||
assert len(m.results) == 2
|
||||
assert len(m.detected) == 1
|
||||
assert len(m.timeline) == 6
|
||||
assert m.detected[0]["package_name"] == "com.sec.android.app.DataCreate"
|
||||
21
tests/android_androidqf/test_dumpsysreceivers.py
Normal file
21
tests/android_androidqf/test_dumpsysreceivers.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# 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 logging
|
||||
|
||||
from mvt.common.module import run_module
|
||||
|
||||
from mvt.android.modules.androidqf.dumpsys_receivers import DumpsysReceivers
|
||||
|
||||
from ..utils import get_android_androidqf
|
||||
|
||||
|
||||
class TestDumpsysReceiversModule:
|
||||
def test_parsing(self):
|
||||
data_path = get_android_androidqf()
|
||||
m = DumpsysReceivers(target_path=data_path)
|
||||
run_module(m)
|
||||
assert len(m.results) == 4
|
||||
assert len(m.detected) == 0
|
||||
22
tests/android_androidqf/test_getprop.py
Normal file
22
tests/android_androidqf/test_getprop.py
Normal file
@@ -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
|
||||
21
tests/android_androidqf/test_processes.py
Normal file
21
tests/android_androidqf/test_processes.py
Normal file
@@ -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
|
||||
20
tests/android_androidqf/test_settings.py
Normal file
20
tests/android_androidqf/test_settings.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# 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.
|
||||
|
||||
from mvt.common.module import run_module
|
||||
|
||||
from mvt.android.modules.androidqf.settings import Settings
|
||||
|
||||
from ..utils import get_android_androidqf
|
||||
|
||||
|
||||
class TestSettingsModule:
|
||||
def test_parsing(self):
|
||||
data_path = get_android_androidqf()
|
||||
m = Settings(target_path=data_path)
|
||||
run_module(m)
|
||||
assert len(m.results) == 1
|
||||
assert "random" in m.results.keys()
|
||||
assert len(m.detected) == 0
|
||||
21
tests/android_androidqf/test_sms.py
Normal file
21
tests/android_androidqf/test_sms.py
Normal file
@@ -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
|
||||
BIN
tests/artifacts/androidqf/backup.ab
Normal file
BIN
tests/artifacts/androidqf/backup.ab
Normal file
Binary file not shown.
254
tests/artifacts/androidqf/dumpsys.txt
Normal file
254
tests/artifacts/androidqf/dumpsys.txt
Normal file
@@ -0,0 +1,254 @@
|
||||
some random text here
|
||||
|
||||
DUMP OF SERVICE accessibility:
|
||||
ACCESSIBILITY MANAGER (dumpsys accessibility)
|
||||
|
||||
User state[attributes:{id=0, currentUser=true
|
||||
mIsNavBarMagnificationAssignedToAccessibilityButton = false
|
||||
|
||||
mIsNavBarMagnifierWindowAssignedToAccessibilityButton = false
|
||||
mIsNavBarAmplifyAmbientSoundAssignedToAccessibilityButton = fals
|
||||
e
|
||||
mIsAmplifyAmbientSoundEnabled = false
|
||||
mIsBixbyRunning = false
|
||||
mIsMagniferWindowEnabled = false
|
||||
mIsFollowTypingFocusEnabled = false
|
||||
mIsTapDurationEnabled = false
|
||||
mIsTouchBlockingEnabled = false
|
||||
mIsStickyKeysEnabled = false
|
||||
mIsBounceKeysEnabled = false
|
||||
mIsTouchExplorationEnabled = false
|
||||
mIsTextHighContrastEnabled = false
|
||||
mIsDisplayMagnificationEnabled = false
|
||||
mIsNavBarMagnificationEnabled = false
|
||||
mIsAutoclickEnabled = false
|
||||
mIsPerformGesturesEnabled = false
|
||||
mIsFilterKeyEventsEnabled = false
|
||||
mAccessibilityFocusOnlyInActiveWindow = true
|
||||
mUserNonInteractiveUiTimeout = 0
|
||||
mUserInteractiveUiTimeout = 0
|
||||
mBindInstantServiceAllowed = false
|
||||
mIsGestureNaviBar = false
|
||||
}
|
||||
installed services: {
|
||||
installed services: {
|
||||
0 : com.android.settings/com.samsung.android.settings.development.gpuwatch.GPUWatchInterceptor
|
||||
1 : com.samsung.accessibility/.universalswitch.UniversalSwitchService
|
||||
2 : com.samsung.accessibility/com.samsung.android.app.talkback.TalkBackService
|
||||
3 : com.sec.android.app.camera/com.samsung.android.glview.AccessibilityGestureHandler
|
||||
}
|
||||
enabled services: {
|
||||
}
|
||||
binding services: {
|
||||
}
|
||||
bound services:{
|
||||
}
|
||||
AccessibilityInputFilter:{
|
||||
}]
|
||||
|
||||
--------- 0.004s was the duration of dumpsys accessibility, ending at: 2022-01-28 17:37:22
|
||||
----------------------------
|
||||
-------------------------------------------------------------------------------
|
||||
DUMP OF SERVICE package:
|
||||
Database versions:
|
||||
Internal:
|
||||
sdkVersion=29 databaseVersion=3
|
||||
fingerprint=samsung/a40
|
||||
External:
|
||||
sdkVersion=28 databaseVersion=3
|
||||
fingerprint=samsung/a40
|
||||
|
||||
Verifiers:
|
||||
Required: com.android.vending (uid=10019)
|
||||
|
||||
Intent Filter Verifier:
|
||||
Using: com.google.android.gms (uid=10012)
|
||||
|
||||
Receiver Resolver Table:
|
||||
Non-Data Actions:
|
||||
com.android.storagemanager.automatic.SHOW_NOTIFICATION:
|
||||
23fa699 com.android.storagemanager/.automatic.NotificationController
|
||||
android.intent.action.PHONE_STATE:
|
||||
85c0aa6 com.facebook.katana/com.facebook.confirmation.util.BackgroundVoiceCallReceiver
|
||||
a713de8 com.samsung.android.app.contacts/com.samsung.android.contacts.notification.CallStateBroadcastReceiver
|
||||
ba10b0b com.sec.hearingadjust/.Receiver
|
||||
c843c01 com.samsung.android.messaging/.ui.receiver.notification.CmcPhoneStateUpdateReceiver
|
||||
com.samsung.intent.internal.stk.user_action:
|
||||
2062d63 com.android.stk/.StkCmdReceiver
|
||||
android.intent.action.NEW_OUTGOING_CALL:
|
||||
1ff4352 com.sec.android.app.safetyassurance/.emergencyreporthelper.EmergencyReportStartMonitorReceiver
|
||||
2906244 com.google.android.gms/.chimera.GmsIntentOperationService$PersistentTrustedReceiver
|
||||
|
||||
Active APEX packages:
|
||||
|
||||
|
||||
Inactive APEX packages:
|
||||
|
||||
|
||||
Factory APEX packages:
|
||||
|
||||
Packages:
|
||||
Package [com.samsung.android.provider.filterprovider] (4be8eeb):
|
||||
userId=1000
|
||||
sharedUser=SharedUserSetting{b8a0e41 android.uid.system/1000}
|
||||
pkg=Package{1660fe6 com.samsung.android.provider.filterprovider}
|
||||
codePath=/system/app/FilterProvider
|
||||
resourcePath=/system/app/FilterProvider
|
||||
legacyNativeLibraryDir=/system/app/FilterProvider/lib
|
||||
primaryCpuAbi=armeabi-v7a
|
||||
secondaryCpuAbi=null
|
||||
versionCode=500700000 minSdk=28 targetSdk=28
|
||||
versionName=5.0.07
|
||||
splits=[base]
|
||||
apkSigningVersion=2
|
||||
applicationInfo=ApplicationInfo{b6df792 com.samsung.android.provider.filterprovider}
|
||||
flags=[ SYSTEM HAS_CODE ALLOW_CLEAR_USER_DATA ]
|
||||
privateFlags=[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE ]
|
||||
dataDir=/data/user/0/com.samsung.android.provider.filterprovider
|
||||
supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity]
|
||||
usesLibraries:
|
||||
android.hidl.manager-V1.0-java
|
||||
android.hidl.base-V1.0-java
|
||||
usesLibraryFiles:
|
||||
/system/framework/android.hidl.manager-V1.0-java.jar
|
||||
/system/framework/android.hidl.base-V1.0-java.jar
|
||||
timeStamp=2008-12-31 16:00:00
|
||||
firstInstallTime=2008-12-31 16:00:00
|
||||
lastUpdateTime=2008-12-31 16:00:00
|
||||
signatures=PackageSignatures{3310927 version:2, signatures:[b378e95c], past signatures:[]}
|
||||
installPermissionsFixed=true
|
||||
pkgFlags=[ SYSTEM HAS_CODE ALLOW_CLEAR_USER_DATA ]
|
||||
Package [com.sec.android.app.DataCreate] (8c78be6):
|
||||
userId=10143
|
||||
pkg=Package{7d4f7d4 com.sec.android.app.DataCreate}
|
||||
codePath=/system/app/AutomationTest_FB
|
||||
resourcePath=/system/app/AutomationTest_FB
|
||||
legacyNativeLibraryDir=/system/app/AutomationTest_FB/lib
|
||||
primaryCpuAbi=null
|
||||
secondaryCpuAbi=null
|
||||
versionCode=1 minSdk=29 targetSdk=29
|
||||
versionName=1.0
|
||||
splits=[base]
|
||||
apkSigningVersion=2
|
||||
applicationInfo=ApplicationInfo{b284d7d com.sec.android.app.DataCreate}
|
||||
flags=[ SYSTEM HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]
|
||||
privateFlags=[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ALLOW_AUDIO_PLAYBACK_CAPTURE ]
|
||||
dataDir=/data/user/0/com.sec.android.app.DataCreate
|
||||
supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity]
|
||||
timeStamp=2008-12-31 16:00:00
|
||||
firstInstallTime=2008-12-31 16:00:00
|
||||
lastUpdateTime=2008-12-31 16:00:00
|
||||
|
||||
|
||||
APEX session state:
|
||||
|
||||
Active install Logging info:
|
||||
[]
|
||||
1642419683196: "Ver":"", "Session":"0",
|
||||
--------- 2.929s was the duration of dumpsys package, ending at: 2022-01-28 17:37:43
|
||||
-------------------------------------------------------------------------------
|
||||
DUMP OF SERVICE appops:
|
||||
Current AppOps Service state:
|
||||
Settings:
|
||||
top_state_settle_time=+30s0ms
|
||||
fg_service_state_settle_time=+10s0ms
|
||||
bg_state_settle_time=+1s0ms
|
||||
|
||||
Op mode watchers:
|
||||
Op COARSE_LOCATION:
|
||||
#0: ModeCallback{b8f1a14 watchinguid=-1 flags=0x1 from uid=1000 pid=4098}
|
||||
#1: ModeCallback{e9062d4 watchinguid=-1 flags=0x1 from uid=u0a12 pid=13172}
|
||||
Op READ_CALL_LOG:
|
||||
#0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098}
|
||||
Op WRITE_CALL_LOG:
|
||||
#0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098}
|
||||
Op READ_SMS:
|
||||
#0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098}
|
||||
Op RECEIVE_SMS:
|
||||
#0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098}
|
||||
Op RECEIVE_MMS:
|
||||
#0: ModeCallback{4b4eb4e watchinguid=-1 flags=0x0 from uid=1000 pid=4098}
|
||||
|
||||
Uid 0:
|
||||
state=cch
|
||||
Package com.android.phone:
|
||||
MANAGE_IPSEC_TUNNELS (allow):
|
||||
Package com.sec.epdg:
|
||||
MANAGE_IPSEC_TUNNELS (deny):
|
||||
Uid 1000:
|
||||
state=pers
|
||||
LEGACY_STORAGE: mode=allow
|
||||
Package com.samsung.android.provider.filterprovider:
|
||||
READ_EXTERNAL_STORAGE (allow):
|
||||
WRITE_EXTERNAL_STORAGE (allow):
|
||||
Package com.samsung.android.smartswitchassistant:
|
||||
READ_EXTERNAL_STORAGE (allow):
|
||||
WRITE_EXTERNAL_STORAGE (allow):
|
||||
Package com.samsung.clipboardsaveservice:
|
||||
READ_EXTERNAL_STORAGE (allow):
|
||||
WRITE_EXTERNAL_STORAGE (allow):
|
||||
RUN_IN_BACKGROUND (allow):
|
||||
Package com.skms.android.agent:
|
||||
READ_EXTERNAL_STORAGE (allow):
|
||||
WRITE_EXTERNAL_STORAGE (allow):
|
||||
Package com.sec.factory.camera:
|
||||
RECORD_AUDIO (allow):
|
||||
RUN_IN_BACKGROUND (allow):
|
||||
Access: [pers-s] 2022-03-29 18:37:30.315 (-4h50m23s772ms)
|
||||
Uid u0a103:
|
||||
state=cch
|
||||
COARSE_LOCATION: mode=ignore
|
||||
LEGACY_STORAGE: mode=allow
|
||||
Package com.facebook.katana:
|
||||
READ_CONTACTS (allow):
|
||||
Access: [bg-tpd] 2022-03-07 18:05:34.325 (-22d4h22m19s762ms)
|
||||
WRITE_SMS (ignore):
|
||||
Reject: [fg-s]2021-05-19 22:02:52.054 (-314d1h25m2s33ms)
|
||||
Reject: [bg-s]2022-03-10 19:35:06.426 (-19d2h52m47s661ms)
|
||||
Reject: [cch-s]2022-03-29 18:48:02.923 (-4h39m51s164ms)
|
||||
WAKE_LOCK (allow):
|
||||
Access: [fg-s] 2021-05-19 22:02:49.186 (-314d1h25m4s901ms)
|
||||
Access: [bg-s] 2022-03-29 23:03:03.763 (-24m50s324ms) duration=+33ms
|
||||
Access: [cch-s] 2022-03-07 14:57:11.635 (-22d7h30m42s452ms)
|
||||
TOAST_WINDOW (allow):
|
||||
READ_PHONE_STATE (allow):
|
||||
Access: [fg-s] 2021-05-19 22:02:53.336 (-314d1h25m0s751ms)
|
||||
Access: [bg-s] 2022-03-24 21:06:52.731 (-5d1h21m1s356ms)
|
||||
Access: [cch-s] 2022-03-29 18:57:58.524 (-4h29m55s563ms)
|
||||
READ_EXTERNAL_STORAGE (allow):
|
||||
WRITE_EXTERNAL_STORAGE (allow):
|
||||
READ_DEVICE_IDENTIFIERS (deny):
|
||||
Reject: [fg-s]2021-05-19 22:02:53.434 (-314d1h25m0s653ms)
|
||||
Reject: [bg-s]2022-03-24 21:06:56.538 (-5d1h20m57s549ms)
|
||||
Reject: [cch-s]2022-03-29 18:57:58.644 (-4h29m55s443ms)
|
||||
Uid u0a104:
|
||||
state=cch
|
||||
COARSE_LOCATION: mode=ignore
|
||||
LEGACY_STORAGE: mode=ignore
|
||||
Package org.mozilla.firefox:
|
||||
REQUEST_INSTALL_PACKAGES (allow):
|
||||
Uid u0a105:
|
||||
state=cch
|
||||
Package com.android.carrierdefaultapp:
|
||||
READ_EXTERNAL_STORAGE (allow):
|
||||
WRITE_EXTERNAL_STORAGE (allow):
|
||||
Uid u0a106:
|
||||
state=cch
|
||||
LEGACY_STORAGE: mode=allow
|
||||
Package com.samsung.safetyinformation:
|
||||
READ_EXTERNAL_STORAGE (allow):
|
||||
WRITE_EXTERNAL_STORAGE (allow):
|
||||
Uid u0a107:
|
||||
state=cch
|
||||
LEGACY_STORAGE: mode=allow
|
||||
Package com.sec.android.app.clockpackage:
|
||||
WAKE_LOCK (allow):
|
||||
Access: [bg-s] 2022-03-29 18:38:31.440 (-4h49m22s647ms) duration=+126ms
|
||||
Access: [cch-s] 2021-06-07 12:47:06.642 (-295d10h40m47s445ms)
|
||||
TOAST_WINDOW (allow):
|
||||
READ_EXTERNAL_STORAGE (allow):
|
||||
WRITE_EXTERNAL_STORAGE (allow):
|
||||
|
||||
|
||||
|
||||
|
||||
10
tests/artifacts/androidqf/getprop.txt
Normal file
10
tests/artifacts/androidqf/getprop.txt
Normal file
@@ -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]
|
||||
16
tests/artifacts/androidqf/ps.txt
Normal file
16
tests/artifacts/androidqf/ps.txt
Normal file
@@ -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]
|
||||
9
tests/artifacts/androidqf/settings_random.txt
Normal file
9
tests/artifacts/androidqf/settings_random.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
samsung_errorlog_agree=0
|
||||
package_verifier_enable=1
|
||||
package_verifier_user_consent=1
|
||||
navigationbar_hide_bar=1
|
||||
navigationbar_key_order=0
|
||||
navigationbar_pressure_user_level=3
|
||||
navigationbar_recently_used_color=
|
||||
navigationbar_unlock_with_home_button=0
|
||||
navigationbar_use_theme_default=0
|
||||
0
tests/ios_fs/__init__.py
Normal file
0
tests/ios_fs/__init__.py
Normal file
34
tests/ios_fs/test_filesystem.py
Normal file
34
tests/ios_fs/test_filesystem.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# 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 logging
|
||||
|
||||
from mvt.common.indicators import Indicators
|
||||
from mvt.common.module import run_module
|
||||
from mvt.ios.modules.fs.filesystem import Filesystem
|
||||
|
||||
from ..utils import get_ios_backup_folder
|
||||
|
||||
|
||||
class TestFilesystem:
|
||||
|
||||
def test_filesystem(self):
|
||||
m = Filesystem(target_path=get_ios_backup_folder())
|
||||
run_module(m)
|
||||
assert len(m.results) == 10
|
||||
assert len(m.timeline) == 10
|
||||
assert len(m.detected) == 0
|
||||
|
||||
def test_detection(self, indicator_file):
|
||||
m = Filesystem(target_path=get_ios_backup_folder())
|
||||
ind = Indicators(log=logging.getLogger())
|
||||
ind.parse_stix2(indicator_file)
|
||||
# Adds a filename that exist in the folder
|
||||
ind.ioc_collections[0]["processes"].append("64d0019cb3d46bfc8cce545a8ba54b93e7ea9347")
|
||||
m.indicators = ind
|
||||
run_module(m)
|
||||
assert len(m.results) == 10
|
||||
assert len(m.timeline) == 10
|
||||
assert len(m.detected) == 1
|
||||
20
tests/test_check_android_androidqf.py
Normal file
20
tests/test_check_android_androidqf.py
Normal file
@@ -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
|
||||
@@ -28,5 +28,9 @@ def get_android_backup_folder():
|
||||
return os.path.join(os.path.dirname(__file__), "artifacts", "android_backup")
|
||||
|
||||
|
||||
def get_android_androidqf():
|
||||
return os.path.join(os.path.dirname(__file__), "artifacts", "androidqf")
|
||||
|
||||
|
||||
def get_indicator_file():
|
||||
print("PYTEST env", os.getenv("PYTEST_CURRENT_TEST"))
|
||||
|
||||
Reference in New Issue
Block a user