mirror of
https://github.com/mvt-project/mvt
synced 2025-10-21 22:42:15 +02:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad3bc3470e | ||
|
|
2c5ae696b1 | ||
|
|
5d2ff32e3a | ||
|
|
2838bac63f | ||
|
|
b7df87a62f | ||
|
|
013282dbba | ||
|
|
ab33789f06 | ||
|
|
a1571c127d | ||
|
|
61f33f7ecb | ||
|
|
4a6b483ce3 | ||
|
|
101098cbb7 | ||
|
|
fd3ef76873 | ||
|
|
fb52f73556 | ||
|
|
acc950377f | ||
|
|
c8a0327768 | ||
|
|
1d075abde9 |
19
.github/workflows/add-issue-to-project.yml
vendored
Normal file
19
.github/workflows/add-issue-to-project.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Add issue to project
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add issue to project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v0.5.0
|
||||
with:
|
||||
# You can target a project in a different organization
|
||||
# to the issue
|
||||
project-url: https://github.com/orgs/mvt-project/projects/1
|
||||
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
|
||||
3
.github/workflows/python-package.yml
vendored
3
.github/workflows/python-package.yml
vendored
@@ -42,8 +42,9 @@ jobs:
|
||||
- name: Test with pytest and coverage
|
||||
run: pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=mvt tests/ | tee pytest-coverage.txt
|
||||
- name: Pytest coverage comment
|
||||
continue-on-error: true # Workflows running on a fork can't post comments
|
||||
uses: MishaKav/pytest-coverage-comment@main
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
pytest-coverage-path: ./pytest-coverage.txt
|
||||
junitxml-path: ./pytest.xml
|
||||
junitxml-path: ./pytest.xml
|
||||
|
||||
@@ -57,12 +57,12 @@ RUN git clone https://github.com/libimobiledevice/libplist \
|
||||
|
||||
# Installing MVT
|
||||
# --------------
|
||||
RUN pip3 install mvt
|
||||
RUN pip3 install git+https://github.com/mvt-project/mvt.git@main
|
||||
|
||||
# Installing ABE
|
||||
# --------------
|
||||
RUN mkdir /opt/abe \
|
||||
&& wget https://github.com/nelenkov/android-backup-extractor/releases/download/20210709062403-4c55371/abe.jar -O /opt/abe/abe.jar \
|
||||
&& wget https://github.com/nelenkov/android-backup-extractor/releases/download/master-20221109063121-8fdfc5e/abe.jar -O /opt/abe/abe.jar \
|
||||
# Create alias for abe
|
||||
&& echo 'alias abe="java -jar /opt/abe/abe.jar"' >> ~/.bashrc
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ from .base import AndroidQFModule
|
||||
|
||||
|
||||
class DumpsysAccessibility(DumpsysAccessibilityArtifact, AndroidQFModule):
|
||||
"""This module analyse dumpsys accessbility"""
|
||||
"""This module analyses dumpsys accessibility"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -53,7 +53,7 @@ class CmdCheckIOCS(Command):
|
||||
if self.module_name and iocs_module.__name__ != self.module_name:
|
||||
continue
|
||||
|
||||
if iocs_module().get_slug() != name_only:
|
||||
if iocs_module.get_slug() != name_only:
|
||||
continue
|
||||
|
||||
log.info(
|
||||
|
||||
@@ -10,7 +10,7 @@ from .version import MVT_VERSION
|
||||
|
||||
|
||||
def check_updates() -> None:
|
||||
# First we check for MVT version udpates.
|
||||
# First we check for MVT version updates.
|
||||
mvt_updates = MVTUpdates()
|
||||
try:
|
||||
latest_version = mvt_updates.check()
|
||||
|
||||
@@ -74,12 +74,13 @@ class MVTModule:
|
||||
log.info('Loaded %d results from "%s"', len(results), json_path)
|
||||
return cls(results=results, log=log)
|
||||
|
||||
def get_slug(self) -> str:
|
||||
@classmethod
|
||||
def get_slug(cls) -> str:
|
||||
"""Use the module's class name to retrieve a slug"""
|
||||
if self.slug:
|
||||
return self.slug
|
||||
if cls.slug:
|
||||
return cls.slug
|
||||
|
||||
sub = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", self.__class__.__name__)
|
||||
sub = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", cls.__name__)
|
||||
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", sub).lower()
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
|
||||
@@ -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.4.3"
|
||||
MVT_VERSION = "2.5.0"
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
},
|
||||
{
|
||||
"identifier": "iPhone14,8",
|
||||
"decription": "iPhone 14 Plus"
|
||||
"description": "iPhone 14 Plus"
|
||||
},
|
||||
{
|
||||
"identifier": "iPhone15,2",
|
||||
|
||||
@@ -960,6 +960,14 @@
|
||||
"version": "16.7.2",
|
||||
"build": "20H115"
|
||||
},
|
||||
{
|
||||
"version": "16.7.3",
|
||||
"build": "20H232"
|
||||
},
|
||||
{
|
||||
"version": "16.7.4",
|
||||
"build": "20H240"
|
||||
},
|
||||
{
|
||||
"version": "17.0",
|
||||
"build": "21A327"
|
||||
@@ -995,5 +1003,17 @@
|
||||
{
|
||||
"version": "17.1.1",
|
||||
"build": "21B91"
|
||||
},
|
||||
{
|
||||
"version": "17.1.2",
|
||||
"build": "21B101"
|
||||
},
|
||||
{
|
||||
"version": "17.2",
|
||||
"build": "21C62"
|
||||
},
|
||||
{
|
||||
"version": "17.2.1",
|
||||
"build": "21C66"
|
||||
}
|
||||
]
|
||||
@@ -8,7 +8,6 @@ import io
|
||||
import logging
|
||||
import os
|
||||
import plistlib
|
||||
import sqlite3
|
||||
from typing import Optional
|
||||
|
||||
from mvt.common.module import DatabaseNotFoundError
|
||||
@@ -124,7 +123,7 @@ class Manifest(IOSExtraction):
|
||||
|
||||
self.log.info("Found Manifest.db database at path: %s", manifest_db_path)
|
||||
|
||||
conn = sqlite3.connect(manifest_db_path)
|
||||
conn = self._open_sqlite_db(manifest_db_path)
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute("SELECT * FROM Files;")
|
||||
|
||||
@@ -49,7 +49,7 @@ class IOSExtraction(MVTModule):
|
||||
"""
|
||||
# TODO: Find a better solution.
|
||||
if not forced:
|
||||
conn = sqlite3.connect(file_path)
|
||||
conn = self._open_sqlite_db(file_path)
|
||||
cur = conn.cursor()
|
||||
|
||||
try:
|
||||
@@ -91,6 +91,9 @@ class IOSExtraction(MVTModule):
|
||||
|
||||
self.log.info("Database at path %s recovered successfully!", file_path)
|
||||
|
||||
def _open_sqlite_db(self, file_path: str) -> sqlite3.Connection:
|
||||
return sqlite3.connect(f"file:{file_path}?immutable=1", uri=True)
|
||||
|
||||
def _get_backup_files_from_manifest(
|
||||
self, relative_path: Optional[str] = None, domain: Optional[str] = None
|
||||
) -> Iterator[dict]:
|
||||
@@ -109,7 +112,7 @@ class IOSExtraction(MVTModule):
|
||||
base_sql = "SELECT fileID, domain, relativePath FROM Files WHERE "
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect(manifest_db_path)
|
||||
conn = self._open_sqlite_db(manifest_db_path)
|
||||
cur = conn.cursor()
|
||||
if relative_path and domain:
|
||||
cur.execute(
|
||||
|
||||
@@ -85,7 +85,7 @@ class Analytics(IOSExtraction):
|
||||
def _extract_analytics_data(self):
|
||||
artifact = self.file_path.split("/")[-1]
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
|
||||
try:
|
||||
|
||||
@@ -64,7 +64,7 @@ class CacheFiles(IOSExtraction):
|
||||
def _process_cache_file(self, file_path):
|
||||
self.log.info("Processing cache file at path: %s", file_path)
|
||||
|
||||
conn = sqlite3.connect(file_path)
|
||||
conn = self._open_sqlite_db(file_path)
|
||||
cur = conn.cursor()
|
||||
|
||||
try:
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# https://license.mvt.re/1.1/
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.common.utils import convert_mactime_to_iso
|
||||
@@ -61,7 +60,7 @@ class SafariFavicon(IOSExtraction):
|
||||
self.detected.append(result)
|
||||
|
||||
def _process_favicon_db(self, file_path):
|
||||
conn = sqlite3.connect(file_path)
|
||||
conn = self._open_sqlite_db(file_path)
|
||||
|
||||
# Fetch valid icon cache.
|
||||
cur = conn.cursor()
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# https://license.mvt.re/1.1/
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.common.utils import convert_mactime_to_iso
|
||||
@@ -82,7 +81,7 @@ class Calendar(IOSExtraction):
|
||||
"""
|
||||
Parse the calendar database
|
||||
"""
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute(
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# https://license.mvt.re/1.1/
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.common.utils import convert_mactime_to_iso
|
||||
@@ -53,7 +52,7 @@ class Calls(IOSExtraction):
|
||||
)
|
||||
self.log.info("Found Calls database at path: %s", self.file_path)
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# https://license.mvt.re/1.1/
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.common.utils import convert_chrometime_to_datetime, convert_datetime_to_iso
|
||||
@@ -66,7 +65,7 @@ class ChromeFavicon(IOSExtraction):
|
||||
)
|
||||
self.log.info("Found Chrome favicon cache database at path: %s", self.file_path)
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
|
||||
# Fetch icon cache
|
||||
cur = conn.cursor()
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# https://license.mvt.re/1.1/
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.common.utils import convert_chrometime_to_datetime, convert_datetime_to_iso
|
||||
@@ -67,7 +66,7 @@ class ChromeHistory(IOSExtraction):
|
||||
)
|
||||
self.log.info("Found Chrome history database at path: %s", self.file_path)
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
|
||||
@@ -44,7 +44,7 @@ class Contacts(IOSExtraction):
|
||||
)
|
||||
self.log.info("Found Contacts database at path: %s", self.file_path)
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
cur.execute(
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# https://license.mvt.re/1.1/
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.common.utils import convert_unix_to_iso
|
||||
@@ -68,7 +67,7 @@ class FirefoxFavicon(IOSExtraction):
|
||||
)
|
||||
self.log.info("Found Firefox favicon database at path: %s", self.file_path)
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# https://license.mvt.re/1.1/
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.common.utils import convert_unix_to_iso
|
||||
@@ -68,7 +67,7 @@ class FirefoxHistory(IOSExtraction):
|
||||
)
|
||||
self.log.info("Found Firefox history database at path: %s", self.file_path)
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
|
||||
@@ -280,7 +280,7 @@ class InteractionC(IOSExtraction):
|
||||
)
|
||||
self.log.info("Found InteractionC database at path: %s", self.file_path)
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
|
||||
try:
|
||||
|
||||
@@ -76,7 +76,7 @@ class SafariBrowserState(IOSExtraction):
|
||||
|
||||
def _process_browser_state_db(self, db_path):
|
||||
self._recover_sqlite_db_if_needed(db_path)
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn = self._open_sqlite_db(db_path)
|
||||
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sqlite3
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.common.url import URL
|
||||
@@ -115,7 +114,7 @@ class SafariHistory(IOSExtraction):
|
||||
|
||||
def _process_history_db(self, history_path):
|
||||
self._recover_sqlite_db_if_needed(history_path)
|
||||
conn = sqlite3.connect(history_path)
|
||||
conn = self._open_sqlite_db(history_path)
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
|
||||
@@ -83,7 +83,7 @@ class Shortcuts(IOSExtraction):
|
||||
)
|
||||
self.log.info("Found Shortcuts database at path: %s", self.file_path)
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
conn.text_factory = bytes
|
||||
cur = conn.cursor()
|
||||
try:
|
||||
|
||||
@@ -44,20 +44,25 @@ class SMS(IOSExtraction):
|
||||
def serialize(self, record: dict) -> Union[dict, list]:
|
||||
text = record["text"].replace("\n", "\\n")
|
||||
sms_data = f"{record['service']}: {record['guid']} \"{text}\" from {record['phone_number']} ({record['account']})"
|
||||
return [
|
||||
records = [
|
||||
{
|
||||
"timestamp": record["isodate"],
|
||||
"module": self.__class__.__name__,
|
||||
"event": "sms_received",
|
||||
"data": sms_data,
|
||||
},
|
||||
{
|
||||
"timestamp": record["isodate_read"],
|
||||
"module": self.__class__.__name__,
|
||||
"event": "sms_read",
|
||||
"data": sms_data,
|
||||
},
|
||||
]
|
||||
# If the message was read, we add an extra event.
|
||||
if record["isodate_read"]:
|
||||
records.append(
|
||||
{
|
||||
"timestamp": record["isodate_read"],
|
||||
"module": self.__class__.__name__,
|
||||
"event": "sms_read",
|
||||
"data": sms_data,
|
||||
}
|
||||
)
|
||||
return records
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
for message in self.results:
|
||||
@@ -86,7 +91,7 @@ class SMS(IOSExtraction):
|
||||
self.log.info("Found SMS database at path: %s", self.file_path)
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
@@ -103,7 +108,7 @@ class SMS(IOSExtraction):
|
||||
conn.close()
|
||||
if "database disk image is malformed" in str(exc):
|
||||
self._recover_sqlite_db_if_needed(self.file_path, forced=True)
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# https://license.mvt.re/1.1/
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from base64 import b64encode
|
||||
from typing import Optional, Union
|
||||
|
||||
@@ -56,6 +55,10 @@ class SMSAttachments(IOSExtraction):
|
||||
|
||||
def check_indicators(self) -> None:
|
||||
for attachment in self.results:
|
||||
# Check for known malicious filenames.
|
||||
if self.indicators.check_file_path(attachment["filename"]):
|
||||
self.detected.append(attachment)
|
||||
|
||||
if (
|
||||
attachment["filename"].startswith("/var/tmp/")
|
||||
and attachment["filename"].endswith("-1")
|
||||
@@ -72,7 +75,7 @@ class SMSAttachments(IOSExtraction):
|
||||
self._find_ios_database(backup_ids=SMS_BACKUP_IDS, root_paths=SMS_ROOT_PATHS)
|
||||
self.log.info("Found SMS database at path: %s", self.file_path)
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
|
||||
@@ -95,7 +95,7 @@ class TCC(IOSExtraction):
|
||||
self.detected.append(result)
|
||||
|
||||
def process_db(self, file_path):
|
||||
conn = sqlite3.connect(file_path)
|
||||
conn = self._open_sqlite_db(file_path)
|
||||
cur = conn.cursor()
|
||||
db_version = "v3"
|
||||
try:
|
||||
|
||||
@@ -73,7 +73,7 @@ class WebkitResourceLoadStatistics(IOSExtraction):
|
||||
|
||||
self._recover_sqlite_db_if_needed(db_path)
|
||||
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn = self._open_sqlite_db(db_path)
|
||||
cur = conn.cursor()
|
||||
|
||||
try:
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# https://license.mvt.re/1.1/
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from typing import Optional, Union
|
||||
|
||||
from mvt.common.utils import check_for_links, convert_mactime_to_iso
|
||||
@@ -69,7 +68,7 @@ class Whatsapp(IOSExtraction):
|
||||
)
|
||||
self.log.info("Found WhatsApp database at path: %s", self.file_path)
|
||||
|
||||
conn = sqlite3.connect(self.file_path)
|
||||
conn = self._open_sqlite_db(self.file_path)
|
||||
cur = conn.cursor()
|
||||
|
||||
# Query all messages and join tables which can contain media attachments
|
||||
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user