mirror of
https://github.com/mvt-project/mvt
synced 2025-10-21 22:42:15 +02:00
Compare commits
63 Commits
fix/create
...
update-che
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fe66e8d5a | ||
|
|
779842567d | ||
|
|
d3cc8cf590 | ||
|
|
b8a42eaf8f | ||
|
|
62b880fbff | ||
|
|
0778d448df | ||
|
|
f020655a1a | ||
|
|
91c34e6664 | ||
|
|
b4a8dd226a | ||
|
|
88213e12c9 | ||
|
|
f75b8e186a | ||
|
|
5babc1fcf3 | ||
|
|
b723ebf28e | ||
|
|
616e870212 | ||
|
|
847b0e087b | ||
|
|
86a0772eb2 | ||
|
|
a41b772e9e | ||
|
|
f12cf7dec5 | ||
|
|
3b144b263b | ||
|
|
7d0be9db4f | ||
|
|
4e120b2640 | ||
|
|
dbe9e5db9b | ||
|
|
0b00398729 | ||
|
|
87034d2c7a | ||
|
|
ab9abfaded | ||
|
|
ef2ffb0dda | ||
|
|
595a2f6536 | ||
|
|
8ead44a31e | ||
|
|
5c19d02a73 | ||
|
|
14ebc9ee4e | ||
|
|
de53cc07f8 | ||
|
|
22e066fc4a | ||
|
|
242052b8ec | ||
|
|
1df61b5bbf | ||
|
|
b691de2cc0 | ||
|
|
10915f250c | ||
|
|
c60cef4009 | ||
|
|
dda798df8e | ||
|
|
ffe6ad2014 | ||
|
|
a125b20fc5 | ||
|
|
49108e67e2 | ||
|
|
883b450601 | ||
|
|
ce813568ff | ||
|
|
93303f181a | ||
|
|
bee453a090 | ||
|
|
42106aa4d6 | ||
|
|
95076c8f71 | ||
|
|
c9ac12f336 | ||
|
|
486e3e7e9b | ||
|
|
be1fc3bd8b | ||
|
|
4757cff262 | ||
|
|
61f51caf31 | ||
|
|
511063fd0e | ||
|
|
88bc5672cb | ||
|
|
0fce0acf7a | ||
|
|
61f95d07d3 | ||
|
|
3dedd169c4 | ||
|
|
e34e03d3a3 | ||
|
|
34374699ce | ||
|
|
cf5aa7c89f | ||
|
|
2766739512 | ||
|
|
9c84afb4b0 | ||
|
|
80fc8bd879 |
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "pip" # See documentation for possible values
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ['3.8', '3.9', '3.10'] # , '3.11']
|
python-version: ['3.10', '3.11', '3.12', '3.13']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ RUN git clone https://github.com/libimobiledevice/usbmuxd && cd usbmuxd \
|
|||||||
|
|
||||||
|
|
||||||
# Create main image
|
# Create main image
|
||||||
FROM ubuntu:22.04 as main
|
FROM ubuntu:24.04 as main
|
||||||
|
|
||||||
LABEL org.opencontainers.image.url="https://mvt.re"
|
LABEL org.opencontainers.image.url="https://mvt.re"
|
||||||
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"
|
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"
|
||||||
@@ -135,8 +135,7 @@ COPY --from=build-usbmuxd /build /
|
|||||||
COPY . mvt/
|
COPY . mvt/
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y git python3-pip \
|
&& apt-get install -y git python3-pip \
|
||||||
&& PIP_NO_CACHE_DIR=1 pip3 install --upgrade pip \
|
&& PIP_NO_CACHE_DIR=1 pip3 install --break-system-packages ./mvt \
|
||||||
&& PIP_NO_CACHE_DIR=1 pip3 install ./mvt \
|
|
||||||
&& apt-get remove -y python3-pip git && apt-get autoremove -y \
|
&& apt-get remove -y python3-pip git && apt-get autoremove -y \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& rm -rf mvt
|
&& rm -rf mvt
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -23,7 +23,7 @@ install:
|
|||||||
python3 -m pip install --upgrade -e .
|
python3 -m pip install --upgrade -e .
|
||||||
|
|
||||||
test-requirements:
|
test-requirements:
|
||||||
python3 -m pip install --upgrade -r test-requirements.txt
|
python3 -m pip install --upgrade --group dev
|
||||||
|
|
||||||
generate-proto-parsers:
|
generate-proto-parsers:
|
||||||
# Generate python parsers for protobuf files
|
# Generate python parsers for protobuf files
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
mkdocs==1.6.1
|
mkdocs==1.6.1
|
||||||
mkdocs-autorefs==1.2.0
|
mkdocs-autorefs==1.4.3
|
||||||
mkdocs-material==9.5.42
|
mkdocs-material==9.6.20
|
||||||
mkdocs-material-extensions==1.3.1
|
mkdocs-material-extensions==1.3.1
|
||||||
mkdocstrings==0.23.0
|
mkdocstrings==0.30.1
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "mvt"
|
name = "mvt"
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
authors = [
|
authors = [{ name = "Claudio Guarnieri", email = "nex@nex.sx" }]
|
||||||
{name = "Claudio Guarnieri", email = "nex@nex.sx"}
|
|
||||||
]
|
|
||||||
maintainers = [
|
maintainers = [
|
||||||
{ name = "Etienne Maynier", email = "tek@randhome.io" },
|
{ name = "Etienne Maynier", email = "tek@randhome.io" },
|
||||||
{ name = "Donncha Ó Cearbhaill", email = "donncha.ocearbhaill@amnesty.org" },
|
{ name = "Donncha Ó Cearbhaill", email = "donncha.ocearbhaill@amnesty.org" },
|
||||||
{name = "Rory Flynn", email = "rory.flynn@amnesty.org"}
|
{ name = "Rory Flynn", email = "rory.flynn@amnesty.org" },
|
||||||
]
|
]
|
||||||
description = "Mobile Verification Toolkit"
|
description = "Mobile Verification Toolkit"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -16,28 +14,30 @@ classifiers = [
|
|||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Intended Audience :: Information Technology",
|
"Intended Audience :: Information Technology",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Programming Language :: Python"
|
"Programming Language :: Python",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"click >=8.1.3",
|
"click==8.2.1",
|
||||||
"rich >=12.6.0",
|
"rich==14.1.0",
|
||||||
"tld >=0.12.6",
|
"tld==0.13.1",
|
||||||
"requests >=2.28.1",
|
"requests==2.32.4",
|
||||||
"simplejson >=3.17.6",
|
"simplejson==3.20.1",
|
||||||
"packaging >=21.3",
|
"packaging==25.0",
|
||||||
"appdirs >=1.4.4",
|
"appdirs==1.4.4",
|
||||||
"iOSbackup >=0.9.923",
|
"iOSbackup==0.9.925",
|
||||||
"adb-shell[usb] >=0.4.3",
|
"adb-shell[usb]==0.4.4",
|
||||||
"libusb1 >=3.0.0",
|
"libusb1==3.3.1",
|
||||||
"cryptography >=42.0.5",
|
"cryptography==45.0.6",
|
||||||
"pyyaml >=6.0",
|
"PyYAML>=6.0.2",
|
||||||
"pyahocorasick >= 2.0.0",
|
"pyahocorasick==2.2.0",
|
||||||
"betterproto >=1.2.0",
|
"betterproto==1.2.5",
|
||||||
"pydantic >= 2.10.0",
|
"pydantic==2.11.7",
|
||||||
"pydantic-settings >= 2.7.0",
|
"pydantic-settings==2.10.1",
|
||||||
'backports.zoneinfo; python_version < "3.9"',
|
"NSKeyedUnArchiver==1.5.2",
|
||||||
|
"python-dateutil==2.9.0.post0",
|
||||||
|
"tzdata==2025.2",
|
||||||
]
|
]
|
||||||
requires-python = ">= 3.8"
|
requires-python = ">= 3.10"
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
homepage = "https://docs.mvt.re/en/latest/"
|
homepage = "https://docs.mvt.re/en/latest/"
|
||||||
@@ -47,14 +47,25 @@ repository = "https://github.com/mvt-project/mvt"
|
|||||||
mvt-ios = "mvt.ios:cli"
|
mvt-ios = "mvt.ios:cli"
|
||||||
mvt-android = "mvt.android:cli"
|
mvt-android = "mvt.android:cli"
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"requests>=2.31.0",
|
||||||
|
"pytest>=7.4.3",
|
||||||
|
"pytest-cov>=4.1.0",
|
||||||
|
"pytest-github-actions-annotate-failures>=0.2.0",
|
||||||
|
"pytest-mock>=3.14.0",
|
||||||
|
"stix2>=3.0.1",
|
||||||
|
"ruff>=0.1.6",
|
||||||
|
"mypy>=1.7.1",
|
||||||
|
"betterproto[compiler]",
|
||||||
|
]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=61.0"]
|
requires = ["setuptools>=61.0"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
omit = [
|
omit = ["tests/*"]
|
||||||
"tests/*",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.coverage.html]
|
[tool.coverage.html]
|
||||||
directory = "htmlcov"
|
directory = "htmlcov"
|
||||||
@@ -67,9 +78,7 @@ packages = "src"
|
|||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
addopts = "-ra -q --cov=mvt --cov-report html --junitxml=pytest.xml --cov-report=term-missing:skip-covered"
|
addopts = "-ra -q --cov=mvt --cov-report html --junitxml=pytest.xml --cov-report=term-missing:skip-covered"
|
||||||
testpaths = [
|
testpaths = ["tests"]
|
||||||
"tests"
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
select = ["C90", "E", "F", "W"] # flake8 default set
|
select = ["C90", "E", "F", "W"] # flake8 default set
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
# https://license.mvt.re/1.1/
|
# https://license.mvt.re/1.1/
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import binascii
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from .artifact import AndroidArtifact
|
from .artifact import AndroidArtifact
|
||||||
@@ -89,11 +90,16 @@ class DumpsysADBArtifact(AndroidArtifact):
|
|||||||
else:
|
else:
|
||||||
key_base64, user = user_key, b""
|
key_base64, user = user_key, b""
|
||||||
|
|
||||||
|
try:
|
||||||
key_raw = base64.b64decode(key_base64)
|
key_raw = base64.b64decode(key_base64)
|
||||||
key_fingerprint = hashlib.md5(key_raw).hexdigest().upper()
|
key_fingerprint = hashlib.md5(key_raw).hexdigest().upper()
|
||||||
key_fingerprint_colon = ":".join(
|
key_fingerprint_colon = ":".join(
|
||||||
[key_fingerprint[i : i + 2] for i in range(0, len(key_fingerprint), 2)]
|
[key_fingerprint[i : i + 2] for i in range(0, len(key_fingerprint), 2)]
|
||||||
)
|
)
|
||||||
|
except binascii.Error:
|
||||||
|
# Impossible to parse base64
|
||||||
|
key_fingerprint_colon = ""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"user": user.decode("utf-8"),
|
"user": user.decode("utf-8"),
|
||||||
"fingerprint": key_fingerprint_colon,
|
"fingerprint": key_fingerprint_colon,
|
||||||
|
|||||||
@@ -51,11 +51,6 @@ ANDROID_DANGEROUS_SETTINGS = [
|
|||||||
"key": "send_action_app_error",
|
"key": "send_action_app_error",
|
||||||
"safe_value": "1",
|
"safe_value": "1",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"description": "enabled installation of non Google Play apps",
|
|
||||||
"key": "install_non_market_apps",
|
|
||||||
"safe_value": "0",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "enabled accessibility services",
|
"description": "enabled accessibility services",
|
||||||
"key": "accessibility_enabled",
|
"key": "accessibility_enabled",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from typing import List, Optional, Union
|
|||||||
|
|
||||||
import pydantic
|
import pydantic
|
||||||
import betterproto
|
import betterproto
|
||||||
|
from dateutil import parser
|
||||||
|
|
||||||
from mvt.common.utils import convert_datetime_to_iso
|
from mvt.common.utils import convert_datetime_to_iso
|
||||||
from mvt.android.parsers.proto.tombstone import Tombstone
|
from mvt.android.parsers.proto.tombstone import Tombstone
|
||||||
@@ -52,7 +53,7 @@ class TombstoneCrashResult(pydantic.BaseModel):
|
|||||||
file_name: str
|
file_name: str
|
||||||
file_timestamp: str # We store the timestamp as a string to avoid timezone issues
|
file_timestamp: str # We store the timestamp as a string to avoid timezone issues
|
||||||
build_fingerprint: str
|
build_fingerprint: str
|
||||||
revision: int
|
revision: str
|
||||||
arch: Optional[str] = None
|
arch: Optional[str] = None
|
||||||
timestamp: str # We store the timestamp as a string to avoid timezone issues
|
timestamp: str # We store the timestamp as a string to avoid timezone issues
|
||||||
process_uptime: Optional[int] = None
|
process_uptime: Optional[int] = None
|
||||||
@@ -62,7 +63,7 @@ class TombstoneCrashResult(pydantic.BaseModel):
|
|||||||
process_name: Optional[str] = None
|
process_name: Optional[str] = None
|
||||||
binary_path: Optional[str] = None
|
binary_path: Optional[str] = None
|
||||||
selinux_label: Optional[str] = None
|
selinux_label: Optional[str] = None
|
||||||
uid: Optional[int] = None
|
uid: int
|
||||||
signal_info: SignalInfo
|
signal_info: SignalInfo
|
||||||
cause: Optional[str] = None
|
cause: Optional[str] = None
|
||||||
extra: Optional[str] = None
|
extra: Optional[str] = None
|
||||||
@@ -124,7 +125,9 @@ class TombstoneCrashArtifact(AndroidArtifact):
|
|||||||
Parse Android tombstone crash files from a protobuf object.
|
Parse Android tombstone crash files from a protobuf object.
|
||||||
"""
|
"""
|
||||||
tombstone_pb = Tombstone().parse(data)
|
tombstone_pb = Tombstone().parse(data)
|
||||||
tombstone_dict = tombstone_pb.to_dict(betterproto.Casing.SNAKE)
|
tombstone_dict = tombstone_pb.to_dict(
|
||||||
|
betterproto.Casing.SNAKE, include_default_values=True
|
||||||
|
)
|
||||||
|
|
||||||
# Add some extra metadata
|
# Add some extra metadata
|
||||||
tombstone_dict["timestamp"] = self._parse_timestamp_string(
|
tombstone_dict["timestamp"] = self._parse_timestamp_string(
|
||||||
@@ -184,7 +187,7 @@ class TombstoneCrashArtifact(AndroidArtifact):
|
|||||||
raise ValueError(f"Expected key {key}, got {line_key}")
|
raise ValueError(f"Expected key {key}, got {line_key}")
|
||||||
|
|
||||||
value_clean = value.strip().strip("'")
|
value_clean = value.strip().strip("'")
|
||||||
if destination_key in ["uid", "revision"]:
|
if destination_key == "uid":
|
||||||
tombstone[destination_key] = int(value_clean)
|
tombstone[destination_key] = int(value_clean)
|
||||||
elif destination_key == "process_uptime":
|
elif destination_key == "process_uptime":
|
||||||
# eg. "Process uptime: 40s"
|
# eg. "Process uptime: 40s"
|
||||||
@@ -252,12 +255,7 @@ class TombstoneCrashArtifact(AndroidArtifact):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_timestamp_string(timestamp: str) -> str:
|
def _parse_timestamp_string(timestamp: str) -> str:
|
||||||
timestamp_date, timezone = timestamp.split("+")
|
timestamp_parsed = parser.parse(timestamp)
|
||||||
# Truncate microseconds before parsing
|
|
||||||
timestamp_without_micro = timestamp_date.split(".")[0] + "+" + timezone
|
|
||||||
timestamp_parsed = datetime.datetime.strptime(
|
|
||||||
timestamp_without_micro, "%Y-%m-%d %H:%M:%S%z"
|
|
||||||
)
|
|
||||||
|
|
||||||
# HACK: Swap the local timestamp to UTC, so keep the original time and avoid timezone conversion.
|
# HACK: Swap the local timestamp to UTC, so keep the original time and avoid timezone conversion.
|
||||||
local_timestamp = timestamp_parsed.replace(tzinfo=datetime.timezone.utc)
|
local_timestamp = timestamp_parsed.replace(tzinfo=datetime.timezone.utc)
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ from mvt.common.help import (
|
|||||||
HELP_MSG_HASHES,
|
HELP_MSG_HASHES,
|
||||||
HELP_MSG_CHECK_IOCS,
|
HELP_MSG_CHECK_IOCS,
|
||||||
HELP_MSG_STIX2,
|
HELP_MSG_STIX2,
|
||||||
|
HELP_MSG_DISABLE_UPDATE_CHECK,
|
||||||
|
HELP_MSG_DISABLE_INDICATOR_UPDATE_CHECK,
|
||||||
)
|
)
|
||||||
from mvt.common.logo import logo
|
from mvt.common.logo import logo
|
||||||
from mvt.common.updates import IndicatorsUpdates
|
from mvt.common.updates import IndicatorsUpdates
|
||||||
@@ -53,12 +55,37 @@ log = logging.getLogger("mvt")
|
|||||||
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
|
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
|
||||||
|
|
||||||
|
|
||||||
|
def _get_disable_flags(ctx):
|
||||||
|
"""Helper function to safely get disable flags from context."""
|
||||||
|
if ctx.obj is None:
|
||||||
|
return False, False
|
||||||
|
return (
|
||||||
|
ctx.obj.get("disable_version_check", False),
|
||||||
|
ctx.obj.get("disable_indicator_check", False),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Main
|
# Main
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@click.group(invoke_without_command=False)
|
@click.group(invoke_without_command=False)
|
||||||
def cli():
|
@click.option(
|
||||||
logo()
|
"--disable-update-check", is_flag=True, help=HELP_MSG_DISABLE_UPDATE_CHECK
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--disable-indicator-update-check",
|
||||||
|
is_flag=True,
|
||||||
|
help=HELP_MSG_DISABLE_INDICATOR_UPDATE_CHECK,
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def cli(ctx, disable_update_check, disable_indicator_update_check):
|
||||||
|
ctx.ensure_object(dict)
|
||||||
|
ctx.obj["disable_version_check"] = disable_update_check
|
||||||
|
ctx.obj["disable_indicator_check"] = disable_indicator_update_check
|
||||||
|
logo(
|
||||||
|
disable_version_check=disable_update_check,
|
||||||
|
disable_indicator_check=disable_indicator_update_check,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -166,6 +193,8 @@ def check_adb(
|
|||||||
module_name=module,
|
module_name=module,
|
||||||
serial=serial,
|
serial=serial,
|
||||||
module_options=module_options,
|
module_options=module_options,
|
||||||
|
disable_version_check=_get_disable_flags(ctx)[0],
|
||||||
|
disable_indicator_check=_get_disable_flags(ctx)[1],
|
||||||
)
|
)
|
||||||
|
|
||||||
if list_modules:
|
if list_modules:
|
||||||
@@ -212,6 +241,8 @@ def check_bugreport(ctx, iocs, output, list_modules, module, verbose, bugreport_
|
|||||||
ioc_files=iocs,
|
ioc_files=iocs,
|
||||||
module_name=module,
|
module_name=module,
|
||||||
hashes=True,
|
hashes=True,
|
||||||
|
disable_version_check=_get_disable_flags(ctx)[0],
|
||||||
|
disable_indicator_check=_get_disable_flags(ctx)[1],
|
||||||
)
|
)
|
||||||
|
|
||||||
if list_modules:
|
if list_modules:
|
||||||
@@ -274,6 +305,8 @@ def check_backup(
|
|||||||
"interactive": not non_interactive,
|
"interactive": not non_interactive,
|
||||||
"backup_password": cli_load_android_backup_password(log, backup_password),
|
"backup_password": cli_load_android_backup_password(log, backup_password),
|
||||||
},
|
},
|
||||||
|
disable_version_check=_get_disable_flags(ctx)[0],
|
||||||
|
disable_indicator_check=_get_disable_flags(ctx)[1],
|
||||||
)
|
)
|
||||||
|
|
||||||
if list_modules:
|
if list_modules:
|
||||||
@@ -338,6 +371,8 @@ def check_androidqf(
|
|||||||
"interactive": not non_interactive,
|
"interactive": not non_interactive,
|
||||||
"backup_password": cli_load_android_backup_password(log, backup_password),
|
"backup_password": cli_load_android_backup_password(log, backup_password),
|
||||||
},
|
},
|
||||||
|
disable_version_check=_get_disable_flags(ctx)[0],
|
||||||
|
disable_indicator_check=_get_disable_flags(ctx)[1],
|
||||||
)
|
)
|
||||||
|
|
||||||
if list_modules:
|
if list_modules:
|
||||||
@@ -372,7 +407,13 @@ def check_androidqf(
|
|||||||
@click.argument("FOLDER", type=click.Path(exists=True))
|
@click.argument("FOLDER", type=click.Path(exists=True))
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def check_iocs(ctx, iocs, list_modules, module, folder):
|
def check_iocs(ctx, iocs, list_modules, module, folder):
|
||||||
cmd = CmdCheckIOCS(target_path=folder, ioc_files=iocs, module_name=module)
|
cmd = CmdCheckIOCS(
|
||||||
|
target_path=folder,
|
||||||
|
ioc_files=iocs,
|
||||||
|
module_name=module,
|
||||||
|
disable_version_check=_get_disable_flags(ctx)[0],
|
||||||
|
disable_indicator_check=_get_disable_flags(ctx)[1],
|
||||||
|
)
|
||||||
cmd.modules = BACKUP_MODULES + ADB_MODULES + BUGREPORT_MODULES
|
cmd.modules = BACKUP_MODULES + ADB_MODULES + BUGREPORT_MODULES
|
||||||
|
|
||||||
if list_modules:
|
if list_modules:
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class CmdAndroidCheckADB(Command):
|
|||||||
module_name: Optional[str] = None,
|
module_name: Optional[str] = None,
|
||||||
serial: Optional[str] = None,
|
serial: Optional[str] = None,
|
||||||
module_options: Optional[dict] = None,
|
module_options: Optional[dict] = None,
|
||||||
|
disable_version_check: bool = False,
|
||||||
|
disable_indicator_check: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
target_path=target_path,
|
target_path=target_path,
|
||||||
@@ -31,6 +33,8 @@ class CmdAndroidCheckADB(Command):
|
|||||||
serial=serial,
|
serial=serial,
|
||||||
module_options=module_options,
|
module_options=module_options,
|
||||||
log=log,
|
log=log,
|
||||||
|
disable_version_check=disable_version_check,
|
||||||
|
disable_indicator_check=disable_indicator_check,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.name = "check-adb"
|
self.name = "check-adb"
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ class CmdAndroidCheckAndroidQF(Command):
|
|||||||
serial: Optional[str] = None,
|
serial: Optional[str] = None,
|
||||||
module_options: Optional[dict] = None,
|
module_options: Optional[dict] = None,
|
||||||
hashes: bool = False,
|
hashes: bool = False,
|
||||||
|
disable_version_check: bool = False,
|
||||||
|
disable_indicator_check: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
target_path=target_path,
|
target_path=target_path,
|
||||||
@@ -36,6 +38,8 @@ class CmdAndroidCheckAndroidQF(Command):
|
|||||||
module_options=module_options,
|
module_options=module_options,
|
||||||
hashes=hashes,
|
hashes=hashes,
|
||||||
log=log,
|
log=log,
|
||||||
|
disable_version_check=disable_version_check,
|
||||||
|
disable_indicator_check=disable_indicator_check,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.name = "check-androidqf"
|
self.name = "check-androidqf"
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ class CmdAndroidCheckBackup(Command):
|
|||||||
serial: Optional[str] = None,
|
serial: Optional[str] = None,
|
||||||
module_options: Optional[dict] = None,
|
module_options: Optional[dict] = None,
|
||||||
hashes: bool = False,
|
hashes: bool = False,
|
||||||
|
disable_version_check: bool = False,
|
||||||
|
disable_indicator_check: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
target_path=target_path,
|
target_path=target_path,
|
||||||
@@ -46,6 +48,8 @@ class CmdAndroidCheckBackup(Command):
|
|||||||
module_options=module_options,
|
module_options=module_options,
|
||||||
hashes=hashes,
|
hashes=hashes,
|
||||||
log=log,
|
log=log,
|
||||||
|
disable_version_check=disable_version_check,
|
||||||
|
disable_indicator_check=disable_indicator_check,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.name = "check-backup"
|
self.name = "check-backup"
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ class CmdAndroidCheckBugreport(Command):
|
|||||||
serial: Optional[str] = None,
|
serial: Optional[str] = None,
|
||||||
module_options: Optional[dict] = None,
|
module_options: Optional[dict] = None,
|
||||||
hashes: bool = False,
|
hashes: bool = False,
|
||||||
|
disable_version_check: bool = False,
|
||||||
|
disable_indicator_check: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
target_path=target_path,
|
target_path=target_path,
|
||||||
@@ -37,6 +39,8 @@ class CmdAndroidCheckBugreport(Command):
|
|||||||
module_options=module_options,
|
module_options=module_options,
|
||||||
hashes=hashes,
|
hashes=hashes,
|
||||||
log=log,
|
log=log,
|
||||||
|
disable_version_check=disable_version_check,
|
||||||
|
disable_indicator_check=disable_indicator_check,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.name = "check-bugreport"
|
self.name = "check-bugreport"
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ class Packages(AndroidExtraction):
|
|||||||
result["matched_indicator"] = ioc
|
result["matched_indicator"] = ioc
|
||||||
self.detected.append(result)
|
self.detected.append(result)
|
||||||
|
|
||||||
@staticmethod
|
def check_virustotal(self, packages: list) -> None:
|
||||||
def check_virustotal(packages: list) -> None:
|
|
||||||
hashes = []
|
hashes = []
|
||||||
for package in packages:
|
for package in packages:
|
||||||
for file in package.get("files", []):
|
for file in package.get("files", []):
|
||||||
@@ -143,8 +142,15 @@ class Packages(AndroidExtraction):
|
|||||||
|
|
||||||
for package in packages:
|
for package in packages:
|
||||||
for file in package.get("files", []):
|
for file in package.get("files", []):
|
||||||
|
if "package_name" in package:
|
||||||
row = [package["package_name"], file["path"]]
|
row = [package["package_name"], file["path"]]
|
||||||
|
elif "name" in package:
|
||||||
|
row = [package["name"], file["path"]]
|
||||||
|
else:
|
||||||
|
self.log.error(
|
||||||
|
f"Package {package} has no name or package_name. packages.json or apks.json is malformed"
|
||||||
|
)
|
||||||
|
continue
|
||||||
if file["sha256"] in detections:
|
if file["sha256"] in detections:
|
||||||
detection = detections[file["sha256"]]
|
detection = detections[file["sha256"]]
|
||||||
positives = detection.split("/")[0]
|
positives = detection.split("/")[0]
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ def parse_sms_file(data):
|
|||||||
entry.pop("mms_body")
|
entry.pop("mms_body")
|
||||||
|
|
||||||
body = entry.get("body", None)
|
body = entry.get("body", None)
|
||||||
|
message_links = None
|
||||||
if body:
|
if body:
|
||||||
message_links = check_for_links(entry["body"])
|
message_links = check_for_links(entry["body"])
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class CmdCheckIOCS(Command):
|
|||||||
module_name: Optional[str] = None,
|
module_name: Optional[str] = None,
|
||||||
serial: Optional[str] = None,
|
serial: Optional[str] = None,
|
||||||
module_options: Optional[dict] = None,
|
module_options: Optional[dict] = None,
|
||||||
|
disable_version_check: bool = False,
|
||||||
|
disable_indicator_check: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
target_path=target_path,
|
target_path=target_path,
|
||||||
@@ -31,6 +33,8 @@ class CmdCheckIOCS(Command):
|
|||||||
serial=serial,
|
serial=serial,
|
||||||
module_options=module_options,
|
module_options=module_options,
|
||||||
log=log,
|
log=log,
|
||||||
|
disable_version_check=disable_version_check,
|
||||||
|
disable_indicator_check=disable_indicator_check,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.name = "check-iocs"
|
self.name = "check-iocs"
|
||||||
@@ -65,6 +69,10 @@ class CmdCheckIOCS(Command):
|
|||||||
m = iocs_module.from_json(
|
m = iocs_module.from_json(
|
||||||
file_path, log=logging.getLogger(iocs_module.__module__)
|
file_path, log=logging.getLogger(iocs_module.__module__)
|
||||||
)
|
)
|
||||||
|
if not m:
|
||||||
|
log.warning("No result from this module, skipping it")
|
||||||
|
continue
|
||||||
|
|
||||||
if self.iocs.total_ioc_count > 0:
|
if self.iocs.total_ioc_count > 0:
|
||||||
m.indicators = self.iocs
|
m.indicators = self.iocs
|
||||||
m.indicators.log = m.log
|
m.indicators.log = m.log
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ class Command:
|
|||||||
module_options: Optional[dict] = None,
|
module_options: Optional[dict] = None,
|
||||||
hashes: bool = False,
|
hashes: bool = False,
|
||||||
log: logging.Logger = logging.getLogger(__name__),
|
log: logging.Logger = logging.getLogger(__name__),
|
||||||
|
disable_version_check: bool = False,
|
||||||
|
disable_indicator_check: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.name = ""
|
self.name = ""
|
||||||
self.modules = []
|
self.modules = []
|
||||||
@@ -42,6 +44,8 @@ class Command:
|
|||||||
self.module_name = module_name
|
self.module_name = module_name
|
||||||
self.serial = serial
|
self.serial = serial
|
||||||
self.log = log
|
self.log = log
|
||||||
|
self.disable_version_check = disable_version_check
|
||||||
|
self.disable_indicator_check = disable_indicator_check
|
||||||
|
|
||||||
# This dictionary can contain options that will be passed down from
|
# This dictionary can contain options that will be passed down from
|
||||||
# the Command to all modules. This can for example be used to pass
|
# the Command to all modules. This can for example be used to pass
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ HELP_MSG_HASHES = "Generate hashes of all the files analyzed"
|
|||||||
HELP_MSG_VERBOSE = "Verbose mode"
|
HELP_MSG_VERBOSE = "Verbose mode"
|
||||||
HELP_MSG_CHECK_IOCS = "Compare stored JSON results to provided indicators"
|
HELP_MSG_CHECK_IOCS = "Compare stored JSON results to provided indicators"
|
||||||
HELP_MSG_STIX2 = "Download public STIX2 indicators"
|
HELP_MSG_STIX2 = "Download public STIX2 indicators"
|
||||||
|
HELP_MSG_DISABLE_UPDATE_CHECK = "Disable MVT version update check"
|
||||||
|
HELP_MSG_DISABLE_INDICATOR_UPDATE_CHECK = "Disable indicators update check"
|
||||||
|
|
||||||
# IOS Specific
|
# IOS Specific
|
||||||
HELP_MSG_DECRYPT_BACKUP = "Decrypt an encrypted iTunes backup"
|
HELP_MSG_DECRYPT_BACKUP = "Decrypt an encrypted iTunes backup"
|
||||||
|
|||||||
@@ -12,9 +12,13 @@ from .updates import IndicatorsUpdates, MVTUpdates
|
|||||||
from .version import MVT_VERSION
|
from .version import MVT_VERSION
|
||||||
|
|
||||||
|
|
||||||
def check_updates() -> None:
|
def check_updates(
|
||||||
|
disable_version_check: bool = False, disable_indicator_check: bool = False
|
||||||
|
) -> None:
|
||||||
log = logging.getLogger("mvt")
|
log = logging.getLogger("mvt")
|
||||||
|
|
||||||
# First we check for MVT version updates.
|
# First we check for MVT version updates.
|
||||||
|
if not disable_version_check:
|
||||||
try:
|
try:
|
||||||
mvt_updates = MVTUpdates()
|
mvt_updates = MVTUpdates()
|
||||||
latest_version = mvt_updates.check()
|
latest_version = mvt_updates.check()
|
||||||
@@ -24,15 +28,18 @@ def check_updates() -> None:
|
|||||||
"You may be working offline. Please update MVT regularly."
|
"You may be working offline. Please update MVT regularly."
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error("Error encountered when trying to check latest MVT version: %s", e)
|
log.error(
|
||||||
|
"Error encountered when trying to check latest MVT version: %s", e
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if latest_version:
|
if latest_version:
|
||||||
rich_print(
|
rich_print(
|
||||||
f"\t\t[bold]Version {latest_version} is available! "
|
f"\t\t[bold]Version {latest_version} is available! "
|
||||||
"Upgrade mvt with `pip3 install -U mvt`[/bold]"
|
"Upgrade mvt with `pip3 install -U mvt` or with `pipx upgrade mvt`[/bold]"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Then we check for indicators files updates.
|
# Then we check for indicators files updates.
|
||||||
|
if not disable_indicator_check:
|
||||||
ioc_updates = IndicatorsUpdates()
|
ioc_updates = IndicatorsUpdates()
|
||||||
|
|
||||||
# Before proceeding, we check if we have downloaded an indicators index.
|
# Before proceeding, we check if we have downloaded an indicators index.
|
||||||
@@ -63,7 +70,9 @@ def check_updates() -> None:
|
|||||||
"You may be working offline. Please update MVT indicators regularly."
|
"You may be working offline. Please update MVT indicators regularly."
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error("Error encountered when trying to check latest MVT indicators: %s", e)
|
log.error(
|
||||||
|
"Error encountered when trying to check latest MVT indicators: %s", e
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if ioc_to_update:
|
if ioc_to_update:
|
||||||
rich_print(
|
rich_print(
|
||||||
@@ -74,12 +83,14 @@ def check_updates() -> None:
|
|||||||
rich_print("\t\tYour indicators files seem to be up to date.")
|
rich_print("\t\tYour indicators files seem to be up to date.")
|
||||||
|
|
||||||
|
|
||||||
def logo() -> None:
|
def logo(
|
||||||
|
disable_version_check: bool = False, disable_indicator_check: bool = False
|
||||||
|
) -> None:
|
||||||
rich_print("\n")
|
rich_print("\n")
|
||||||
rich_print("\t[bold]MVT[/bold] - Mobile Verification Toolkit")
|
rich_print("\t[bold]MVT[/bold] - Mobile Verification Toolkit")
|
||||||
rich_print("\t\thttps://mvt.re")
|
rich_print("\t\thttps://mvt.re")
|
||||||
rich_print(f"\t\tVersion: {MVT_VERSION}")
|
rich_print(f"\t\tVersion: {MVT_VERSION}")
|
||||||
|
|
||||||
check_updates()
|
check_updates(disable_version_check, disable_indicator_check)
|
||||||
|
|
||||||
rich_print("\n")
|
rich_print("\n")
|
||||||
|
|||||||
@@ -69,10 +69,14 @@ class MVTModule:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, json_path: str, log: logging.Logger):
|
def from_json(cls, json_path: str, log: logging.Logger):
|
||||||
with open(json_path, "r", encoding="utf-8") as handle:
|
with open(json_path, "r", encoding="utf-8") as handle:
|
||||||
|
try:
|
||||||
results = json.load(handle)
|
results = json.load(handle)
|
||||||
if log:
|
if log:
|
||||||
log.info('Loaded %d results from "%s"', len(results), json_path)
|
log.info('Loaded %d results from "%s"', len(results), json_path)
|
||||||
return cls(results=results, log=log)
|
return cls(results=results, log=log)
|
||||||
|
except json.decoder.JSONDecodeError as err:
|
||||||
|
log.error('Error to decode the json "%s" file: "%s"', json_path, err)
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_slug(cls) -> str:
|
def get_slug(cls) -> str:
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ INDICATORS_CHECK_FREQUENCY = 12
|
|||||||
|
|
||||||
class MVTUpdates:
|
class MVTUpdates:
|
||||||
def check(self) -> str:
|
def check(self) -> str:
|
||||||
res = requests.get(settings.PYPI_UPDATE_URL, timeout=15)
|
try:
|
||||||
|
res = requests.get(settings.PYPI_UPDATE_URL, timeout=5)
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
log.error("Failed to check for updates, skipping updates: %s", e)
|
||||||
|
return ""
|
||||||
data = res.json()
|
data = res.json()
|
||||||
latest_version = data.get("info", {}).get("version", "")
|
latest_version = data.get("info", {}).get("version", "")
|
||||||
|
|
||||||
@@ -93,7 +97,12 @@ class IndicatorsUpdates:
|
|||||||
url = self.github_raw_url.format(
|
url = self.github_raw_url.format(
|
||||||
self.index_owner, self.index_repo, self.index_branch, self.index_path
|
self.index_owner, self.index_repo, self.index_branch, self.index_path
|
||||||
)
|
)
|
||||||
res = requests.get(url, timeout=15)
|
try:
|
||||||
|
res = requests.get(url, timeout=5)
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
log.error("Failed to retrieve indicators index from %s: %s", url, e)
|
||||||
|
return None
|
||||||
|
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
log.error(
|
log.error(
|
||||||
"Failed to retrieve indicators index located at %s (error %d)",
|
"Failed to retrieve indicators index located at %s (error %d)",
|
||||||
@@ -105,7 +114,12 @@ class IndicatorsUpdates:
|
|||||||
return yaml.safe_load(res.content)
|
return yaml.safe_load(res.content)
|
||||||
|
|
||||||
def download_remote_ioc(self, ioc_url: str) -> Optional[str]:
|
def download_remote_ioc(self, ioc_url: str) -> Optional[str]:
|
||||||
|
try:
|
||||||
res = requests.get(ioc_url, timeout=15)
|
res = requests.get(ioc_url, timeout=15)
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
log.error("Failed to download indicators file from %s: %s", ioc_url, e)
|
||||||
|
return None
|
||||||
|
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
log.error(
|
log.error(
|
||||||
"Failed to download indicators file from %s (error %d)",
|
"Failed to download indicators file from %s (error %d)",
|
||||||
@@ -171,7 +185,12 @@ class IndicatorsUpdates:
|
|||||||
file_commit_url = (
|
file_commit_url = (
|
||||||
f"https://api.github.com/repos/{owner}/{repo}/commits?path={path}"
|
f"https://api.github.com/repos/{owner}/{repo}/commits?path={path}"
|
||||||
)
|
)
|
||||||
res = requests.get(file_commit_url, timeout=15)
|
try:
|
||||||
|
res = requests.get(file_commit_url, timeout=5)
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
log.error("Failed to get details about file %s: %s", file_commit_url, e)
|
||||||
|
return -1
|
||||||
|
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
log.error(
|
log.error(
|
||||||
"Failed to get details about file %s (error %d)",
|
"Failed to get details about file %s (error %d)",
|
||||||
|
|||||||
@@ -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.6.0"
|
MVT_VERSION = "2.6.1"
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ from mvt.common.help import (
|
|||||||
HELP_MSG_CHECK_IOCS,
|
HELP_MSG_CHECK_IOCS,
|
||||||
HELP_MSG_STIX2,
|
HELP_MSG_STIX2,
|
||||||
HELP_MSG_CHECK_IOS_BACKUP,
|
HELP_MSG_CHECK_IOS_BACKUP,
|
||||||
|
HELP_MSG_DISABLE_UPDATE_CHECK,
|
||||||
|
HELP_MSG_DISABLE_INDICATOR_UPDATE_CHECK,
|
||||||
)
|
)
|
||||||
from .cmd_check_backup import CmdIOSCheckBackup
|
from .cmd_check_backup import CmdIOSCheckBackup
|
||||||
from .cmd_check_fs import CmdIOSCheckFS
|
from .cmd_check_fs import CmdIOSCheckFS
|
||||||
@@ -53,12 +55,37 @@ MVT_IOS_BACKUP_PASSWORD = "MVT_IOS_BACKUP_PASSWORD"
|
|||||||
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
|
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
|
||||||
|
|
||||||
|
|
||||||
|
def _get_disable_flags(ctx):
|
||||||
|
"""Helper function to safely get disable flags from context."""
|
||||||
|
if ctx.obj is None:
|
||||||
|
return False, False
|
||||||
|
return (
|
||||||
|
ctx.obj.get("disable_version_check", False),
|
||||||
|
ctx.obj.get("disable_indicator_check", False),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Main
|
# Main
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@click.group(invoke_without_command=False)
|
@click.group(invoke_without_command=False)
|
||||||
def cli():
|
@click.option(
|
||||||
logo()
|
"--disable-update-check", is_flag=True, help=HELP_MSG_DISABLE_UPDATE_CHECK
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--disable-indicator-update-check",
|
||||||
|
is_flag=True,
|
||||||
|
help=HELP_MSG_DISABLE_INDICATOR_UPDATE_CHECK,
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def cli(ctx, disable_update_check, disable_indicator_update_check):
|
||||||
|
ctx.ensure_object(dict)
|
||||||
|
ctx.obj["disable_version_check"] = disable_update_check
|
||||||
|
ctx.obj["disable_indicator_check"] = disable_indicator_update_check
|
||||||
|
logo(
|
||||||
|
disable_version_check=disable_update_check,
|
||||||
|
disable_indicator_check=disable_indicator_update_check,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -219,6 +246,8 @@ def check_backup(
|
|||||||
module_name=module,
|
module_name=module,
|
||||||
module_options=module_options,
|
module_options=module_options,
|
||||||
hashes=hashes,
|
hashes=hashes,
|
||||||
|
disable_version_check=_get_disable_flags(ctx)[0],
|
||||||
|
disable_indicator_check=_get_disable_flags(ctx)[1],
|
||||||
)
|
)
|
||||||
|
|
||||||
if list_modules:
|
if list_modules:
|
||||||
@@ -266,6 +295,8 @@ def check_fs(ctx, iocs, output, fast, list_modules, module, hashes, verbose, dum
|
|||||||
module_name=module,
|
module_name=module,
|
||||||
module_options=module_options,
|
module_options=module_options,
|
||||||
hashes=hashes,
|
hashes=hashes,
|
||||||
|
disable_version_check=_get_disable_flags(ctx)[0],
|
||||||
|
disable_indicator_check=_get_disable_flags(ctx)[1],
|
||||||
)
|
)
|
||||||
|
|
||||||
if list_modules:
|
if list_modules:
|
||||||
@@ -300,7 +331,13 @@ def check_fs(ctx, iocs, output, fast, list_modules, module, hashes, verbose, dum
|
|||||||
@click.argument("FOLDER", type=click.Path(exists=True))
|
@click.argument("FOLDER", type=click.Path(exists=True))
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def check_iocs(ctx, iocs, list_modules, module, folder):
|
def check_iocs(ctx, iocs, list_modules, module, folder):
|
||||||
cmd = CmdCheckIOCS(target_path=folder, ioc_files=iocs, module_name=module)
|
cmd = CmdCheckIOCS(
|
||||||
|
target_path=folder,
|
||||||
|
ioc_files=iocs,
|
||||||
|
module_name=module,
|
||||||
|
disable_version_check=_get_disable_flags(ctx)[0],
|
||||||
|
disable_indicator_check=_get_disable_flags(ctx)[1],
|
||||||
|
)
|
||||||
cmd.modules = BACKUP_MODULES + FS_MODULES + MIXED_MODULES
|
cmd.modules = BACKUP_MODULES + FS_MODULES + MIXED_MODULES
|
||||||
|
|
||||||
if list_modules:
|
if list_modules:
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ class CmdIOSCheckBackup(Command):
|
|||||||
serial: Optional[str] = None,
|
serial: Optional[str] = None,
|
||||||
module_options: Optional[dict] = None,
|
module_options: Optional[dict] = None,
|
||||||
hashes: bool = False,
|
hashes: bool = False,
|
||||||
|
disable_version_check: bool = False,
|
||||||
|
disable_indicator_check: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
target_path=target_path,
|
target_path=target_path,
|
||||||
@@ -34,6 +36,8 @@ class CmdIOSCheckBackup(Command):
|
|||||||
module_options=module_options,
|
module_options=module_options,
|
||||||
hashes=hashes,
|
hashes=hashes,
|
||||||
log=log,
|
log=log,
|
||||||
|
disable_version_check=disable_version_check,
|
||||||
|
disable_indicator_check=disable_indicator_check,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.name = "check-backup"
|
self.name = "check-backup"
|
||||||
|
|||||||
@@ -24,16 +24,19 @@ class CmdIOSCheckFS(Command):
|
|||||||
serial: Optional[str] = None,
|
serial: Optional[str] = None,
|
||||||
module_options: Optional[dict] = None,
|
module_options: Optional[dict] = None,
|
||||||
hashes: bool = False,
|
hashes: bool = False,
|
||||||
|
disable_version_check: bool = False,
|
||||||
|
disable_indicator_check: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
target_path=target_path,
|
target_path=target_path,
|
||||||
results_path=results_path,
|
results_path=results_path,
|
||||||
ioc_files=ioc_files,
|
ioc_files=ioc_files,
|
||||||
module_name=module_name,
|
module_name=module_name,
|
||||||
serial=serial,
|
|
||||||
module_options=module_options,
|
module_options=module_options,
|
||||||
hashes=hashes,
|
hashes=hashes,
|
||||||
log=log,
|
log=log,
|
||||||
|
disable_version_check=disable_version_check,
|
||||||
|
disable_indicator_check=disable_indicator_check,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.name = "check-fs"
|
self.name = "check-fs"
|
||||||
|
|||||||
@@ -891,6 +891,14 @@
|
|||||||
"version": "15.8.2",
|
"version": "15.8.2",
|
||||||
"build": "19H384"
|
"build": "19H384"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"version": "15.8.4",
|
||||||
|
"build": "19H390"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "15.8.5",
|
||||||
|
"build": "19H394"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"build": "20A362",
|
"build": "20A362",
|
||||||
"version": "16.0"
|
"version": "16.0"
|
||||||
@@ -992,6 +1000,14 @@
|
|||||||
"version": "16.7.8",
|
"version": "16.7.8",
|
||||||
"build": "20H343"
|
"build": "20H343"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"version": "16.7.11",
|
||||||
|
"build": "20H360"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.7.12",
|
||||||
|
"build": "20H364"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "17.0",
|
"version": "17.0",
|
||||||
"build": "21A327"
|
"build": "21A327"
|
||||||
@@ -1076,6 +1092,10 @@
|
|||||||
"version": "17.6.1",
|
"version": "17.6.1",
|
||||||
"build": "21G101"
|
"build": "21G101"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"version": "17.7.7",
|
||||||
|
"build": "21H433"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "18",
|
"version": "18",
|
||||||
"build": "22A3354"
|
"build": "22A3354"
|
||||||
@@ -1107,5 +1127,41 @@
|
|||||||
{
|
{
|
||||||
"version": "18.3.1",
|
"version": "18.3.1",
|
||||||
"build": "22D72"
|
"build": "22D72"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "18.4",
|
||||||
|
"build": "22E240"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "18.4.1",
|
||||||
|
"build": "22E252"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "18.5",
|
||||||
|
"build": "22F76"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "18.6",
|
||||||
|
"build": "22G86"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "18.6.1",
|
||||||
|
"build": "22G90"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "18.6.2",
|
||||||
|
"build": "22G100"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "18.7",
|
||||||
|
"build": "22H20"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "26",
|
||||||
|
"build": "23A341"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "26.0.1",
|
||||||
|
"build": "23A355"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -43,6 +43,8 @@ class GlobalPreferences(IOSExtraction):
|
|||||||
self.log.warning("Lockdown mode enabled")
|
self.log.warning("Lockdown mode enabled")
|
||||||
else:
|
else:
|
||||||
self.log.warning("Lockdown mode disabled")
|
self.log.warning("Lockdown mode disabled")
|
||||||
|
return
|
||||||
|
self.log.warning("Lockdown mode disabled")
|
||||||
|
|
||||||
def process_file(self, file_path: str) -> None:
|
def process_file(self, file_path: str) -> None:
|
||||||
with open(file_path, "rb") as handle:
|
with open(file_path, "rb") as handle:
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ class SafariBrowserState(IOSExtraction):
|
|||||||
)
|
)
|
||||||
except sqlite3.OperationalError:
|
except sqlite3.OperationalError:
|
||||||
# Old version iOS <12 likely
|
# Old version iOS <12 likely
|
||||||
|
try:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
@@ -103,6 +104,8 @@ class SafariBrowserState(IOSExtraction):
|
|||||||
ORDER BY last_viewed_time;
|
ORDER BY last_viewed_time;
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
except sqlite3.OperationalError as e:
|
||||||
|
self.log.error(f"Error executing query: {e}")
|
||||||
|
|
||||||
for row in cur:
|
for row in cur:
|
||||||
session_entries = []
|
session_entries = []
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ class TCC(IOSExtraction):
|
|||||||
)
|
)
|
||||||
db_version = "v2"
|
db_version = "v2"
|
||||||
except sqlite3.OperationalError:
|
except sqlite3.OperationalError:
|
||||||
|
try:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""SELECT
|
"""SELECT
|
||||||
service, client, client_type, allowed,
|
service, client, client_type, allowed,
|
||||||
@@ -123,6 +124,8 @@ class TCC(IOSExtraction):
|
|||||||
FROM access;"""
|
FROM access;"""
|
||||||
)
|
)
|
||||||
db_version = "v1"
|
db_version = "v1"
|
||||||
|
except sqlite3.OperationalError as e:
|
||||||
|
self.log.error(f"Error parsing TCC database: {e}")
|
||||||
|
|
||||||
for row in cur:
|
for row in cur:
|
||||||
service = row[0]
|
service = row[0]
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
requests>=2.31.0
|
|
||||||
pytest>=7.4.3
|
|
||||||
pytest-cov>=4.1.0
|
|
||||||
pytest-github-actions-annotate-failures>=0.2.0
|
|
||||||
pytest-mock>=3.14.0
|
|
||||||
stix2>=3.0.1
|
|
||||||
ruff>=0.1.6
|
|
||||||
mypy>=1.7.1
|
|
||||||
betterproto[compiler]
|
|
||||||
@@ -64,4 +64,4 @@ class TestTombstoneCrashArtifact:
|
|||||||
# We often don't know the time offset for a log entry and so can't convert everything to UTC.
|
# We often don't know the time offset for a log entry and so can't convert everything to UTC.
|
||||||
# MVT should output the local time only:
|
# MVT should output the local time only:
|
||||||
# So original 2023-04-12 12:32:40.518290770+0200 -> 2023-04-12 12:32:40.000000
|
# So original 2023-04-12 12:32:40.518290770+0200 -> 2023-04-12 12:32:40.000000
|
||||||
assert tombstone_result.get("timestamp") == "2023-04-12 12:32:40.000000"
|
assert tombstone_result.get("timestamp") == "2023-04-12 12:32:40.518290"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user